diff --git a/AUTHORS b/AUTHORS
index 5db4407..e61fc3ea 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1353,6 +1353,7 @@
 TeamSpeak Systems GmbH <*@teamspeak.com>
 The Chromium Authors <*@chromium.org>
 The MathWorks, Inc. <binod.pant@mathworks.com>
+THEO Technologies <*@theoplayer.com>
 Torchmobile Inc.
 Upwork <*@cloud.upwork.com>
 Venture 3 Systems LLC <*@venture3systems.com>
diff --git a/DEPS b/DEPS
index ca8973b8..958a953 100644
--- a/DEPS
+++ b/DEPS
@@ -245,15 +245,15 @@
   # 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': '3e1354a592bcd52f18ca6422f1d653a47d65c03f',
+  'skia_revision': '30fdea3d8fd5ed39de28acae75586d7c4f08ad11',
   # 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': '58ade8cbfb17a14f7936ddc8b4b731f15db3d32b',
+  'v8_revision': '5da4293254a42479d73853b54bc9a54b1f17fd84',
   # 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': '9d11a341c6151289eb1225ec1a70cbe6e82d51ac',
+  'angle_revision': '96fd9b72d07c54398af2fec01bcd697ad334e6b2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -276,7 +276,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling googletest
   # and whatever else without interference from each other.
-  'googletest_revision': '6b74da4757a549563d7c37c8fae3e704662a043b',
+  'googletest_revision': '1d9f7c5fb2f56b4321b9eec0731b874eb6eef466',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling lighttpd
   # and whatever else without interference from each other.
@@ -292,7 +292,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': 'a25e85ed95dc855e42e6bb55138e27d362c5ea1e',
+  'freetype_revision': 'e838c37c2c1575eb12116ce6303ffacc72521ce8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -312,7 +312,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'dc1213869e713601ec7f9fde1acf22f45dec72d1',
+  'catapult_revision': '486f67f3ec352fcd8275b8de68a29a38e2face70',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -320,7 +320,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': 'c1e414725a7a21233fd27848ba2603e8328ec9d6',
+  'devtools_frontend_revision': '6bcbe0f26d957c72a4a1aab42b4ed3c8079915c8',
   # 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.
@@ -360,11 +360,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '5bab76a64d25dbc32c6beb372ae9724321f9c2b0',
+  'dawn_revision': 'a62f8e402c081cd38307863d813cf7712a492a3b',
   # 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': 'b0fda3339a7e0c24d8bea7cfb6d9ea306e2686a1',
+  'quiche_revision': '0c4a41bcc60ecf1abc0ea76f4d93c2aee858828f',
   # 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.
@@ -404,11 +404,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'libcxxabi_revision':    'a0ace9923ef509e8f64c1e46b8a7aad7e335df5a',
+  'libcxxabi_revision':    '8829496cf6362cc08e1b3008e1769e6fb595fb9d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'libunwind_revision':    '58d16473609e85108b8008be4c6e0e66391c6fa4',
+  'libunwind_revision':    '834e4a3c8647ea6192c16894baca5584516e5b7c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -617,7 +617,7 @@
     Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248',
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + '8ce1a6e7c619a9da864007ee111488edf7f296c1',
+    'url': Var('chromium_git') + '/website.git' + '@' + '258a2f0e63b26255a141c8271f8ca28d74e21cb9',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -722,7 +722,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': 'vA_jsPe65KdcCsmQ4fg10mYT8K0SPDwBEZ-x0b-2DnMC',
+          'version': 'X0K72Q0oWNCB9COz9YmGrqRQH8YjsXJCY0Z5Z1HeokAC',
         },
       ],
       'dep_type': 'cipd',
@@ -733,7 +733,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 's7qu7MJuaO1dxFvnILm-lgdafQvYtB5ldFO8l4PqVPsC',
+          'version': 'r9dwsEQKJtLPxQr0UqgaKdec-l5KzjS3YwE4Z3Wjv7MC',
         },
       ],
       'dep_type': 'cipd',
@@ -744,7 +744,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/windows-amd64',
-          'version': 'hYyQwE6vYM1J9CgYv7T1S92lkrZodIDGayRLtUQL3sAC',
+          'version': 'rP6393GKO8WxDY6duK2K4NCQ32JNexdF5gs6BnxjjtAC',
         },
       ],
       'dep_type': 'cipd',
@@ -805,7 +805,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'A2SRkmLGieck6AyFqq-ZI_WZV5i32fH-eIfxK5dytRIC',
+          'version': 'B9rhutyqLDhWWNhiyH6A1gC63mYwYyQfnP4O5533VnwC',
       },
     ],
     'condition': 'checkout_android',
@@ -838,7 +838,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/android_build_tools/aapt2',
-              'version': 'ZHiDoiurxMLwajQq_CwBH9zaDW5xwYWYGbedxIslTm8C',
+              'version': '0yR8wK_fSMgdVKwnx4nRPi-amaLV7Kcr4Os6mg_DGI4C',
           },
       ],
       'condition': 'checkout_android',
@@ -1024,7 +1024,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '89b58730aff41923385b8ae22082e9161c1d3c28',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '1fc15953a44635ac693b455bdd9274e88f9dc301',
       'condition': 'checkout_chromeos',
   },
 
@@ -1044,7 +1044,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'd3cc7ad85ed680907978c3d125b51db0f6ca5ea8',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c3e25c828dd3aac5f03cab26bf12cc788d111532',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1096,7 +1096,7 @@
     Var('chromium_git') + '/external/github.com/google/gemmlowp.git' + '@' + '13d57703abca3005d97b19df1f2db731607a7dc2',
 
   'src/third_party/grpc/src': {
-      'url': Var('chromium_git') + '/external/github.com/grpc/grpc.git' + '@' + '47f58f5b8d338a6861febc8ee1a602c23a5af70a',
+      'url': Var('chromium_git') + '/external/github.com/grpc/grpc.git' + '@' + '754913545189b819829284b79ac5a4d31fddbdcc',
   },
 
   'src/third_party/freetype/src':
@@ -1246,7 +1246,7 @@
   },
 
   'src/third_party/leveldatabase/src':
-    Var('chromium_git') + '/external/leveldb.git' + '@' + '8f464e7f68fd9d50ed39b2866ef8dac9c837439d',
+    Var('chromium_git') + '/external/leveldb.git' + '@' + '1b51a3a96821e5fd5175288724c95c1bde57b2f0',
 
   'src/third_party/libFuzzer/src':
     Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git' + '@' +  Var('libfuzzer_revision'),
@@ -1313,7 +1313,7 @@
   },
 
   'src/third_party/libvpx/source/libvpx':
-    Var('chromium_git') + '/webm/libvpx.git' + '@' +  'ab35ee100a38347433af24df05a5e1578172a2ae',
+    Var('chromium_git') + '/webm/libvpx.git' + '@' +  '51415c4076578d3cbc32fcd0d683161c3e887814',
 
   'src/third_party/libwebm/source':
     Var('chromium_git') + '/webm/libwebm.git' + '@' + 'e4fbea0c9751ae8aa86629b197a28d8276a2b0da',
@@ -1588,7 +1588,7 @@
   },
 
   'src/third_party/tflite/src':
-    Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + 'a4289fcb7a4fc982886aa2d11c7b0d87e9815ed7',
+    Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + 'ceedf1794a703715d88605b5ac493517e8fa2499',
 
   'src/third_party/turbine': {
       'packages': [
@@ -1609,7 +1609,7 @@
   'src/third_party/usrsctp/usrsctplib':
     Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + '62d7d0c928c9a040dce96aa2f16c00e7e67d59cb',
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@e00bdeb5d931056da2f55ea3efdfba1ca6ee8853',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@d891854a594a8c22bbe5fdea9cf89d93cd04d3e6',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '5e49f57a6e71a026a54eb42e366de09a4142d24e',
@@ -1648,7 +1648,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'fca7b339442bd70c5dc49bb33ee7f9466b560a97',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '400d8bdd60566ebadc7941de2b32e267b89020bb',
+    Var('webrtc_git') + '/src.git' + '@' + 'cb87ec9557b69c2c4e036673c39bb013f28329b9',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1691,18 +1691,6 @@
       'dep_type': 'cipd',
       'condition': 'checkout_win',
   },
-  # TODO(crbug.com/1280002): Remove this entry once the autoroller has been
-  # updated to roll the mac_amd64 version.
-  'src/tools/skia_goldctl/mac': {
-      'packages': [
-        {
-          'package': 'skia/tools/goldctl/mac-amd64',
-          'version': 'zLP4FDegN-yg3uvzWUdf4zNRVHDwHd0VLm86FkZCP_MC',
-        },
-      ],
-      'dep_type': 'cipd',
-      'condition': 'checkout_mac',
-  },
 
   'src/tools/skia_goldctl/mac_amd64': {
       'packages': [
@@ -1730,7 +1718,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@489c2e67375054674e4321278a114f0739272d4a',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b91bc92c92c7c822fe546935d41239d6143f1ea6',
     'condition': 'checkout_src_internal',
   },
 
@@ -1760,7 +1748,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'vpGiXiWnrVMhHPErgqfY6iKTMSxlZUYgIxHRFmbQBEwC',
+        'version': '3nCu8a_FU3cnomexOmxmf7_tyLV8iUyMFPYWuXgiHrsC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1771,7 +1759,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': '9182keYpYybgZHn2nP9gpYD1sSyjqCrjRvDM-4V7Eg0C',
+        'version': 'g1FUIvTfX-NhpTsdAnv08anrQ4C79ky8d_IKuVOYK-UC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1782,7 +1770,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'Vt7JWDElraHtItHlW1gnrXKuTV4wZ0ZwvNaUb6ZBVFcC',
+        'version': 'pbbjlLSzUiFVGgHd-cHyKYjTUCv-IUx41IwhCfr8UJgC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/browser/aw_debug.cc b/android_webview/browser/aw_debug.cc
index b4ee1ff..151b4275 100644
--- a/android_webview/browser/aw_debug.cc
+++ b/android_webview/browser/aw_debug.cc
@@ -9,78 +9,11 @@
 #include "base/android/jni_string.h"
 #include "base/no_destructor.h"
 #include "components/crash/core/common/crash_key.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/render_process_host_creation_observer.h"
 
 using content::RenderProcessHost;
 
 namespace android_webview {
 
-class AwDebugCpuAffinity : public content::RenderProcessHostCreationObserver {
- public:
-  AwDebugCpuAffinity(bool enable_idle_throttling,
-                     int32_t policy,
-                     int32_t min_time_ms,
-                     float min_cputime_ratio)
-      : enable_idle_throttling_(enable_idle_throttling),
-        policy_(policy),
-        min_time_ms_(min_time_ms),
-        min_cputime_ratio_(min_cputime_ratio) {
-    for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
-         !i.IsAtEnd(); i.Advance()) {
-      RenderProcessHost* process_host = i.GetCurrentValue();
-      if (process_host) {
-        if (enable_idle_throttling_)
-          EnableIdleThrottling(process_host);
-        else
-          SetAffinityForProcessHost(process_host);
-      }
-    }
-  }
-
-  // content::RenderProcessHostCreationObserver:
-  void OnRenderProcessHostCreated(RenderProcessHost* process_host) override {
-    if (enable_idle_throttling_)
-      EnableIdleThrottling(process_host);
-    else
-      SetAffinityForProcessHost(process_host);
-  }
-
- private:
-  void SetAffinityForProcessHost(RenderProcessHost* process_host) {
-    process_host->PostTaskWhenProcessIsReady(base::BindOnce(
-        &AwRenderProcess::SetCpuAffinityToLittleCores,
-        base::Unretained(
-            AwRenderProcess::GetInstanceForRenderProcessHost(process_host))));
-  }
-
-  void EnableIdleThrottling(RenderProcessHost* process_host) {
-    process_host->PostTaskWhenProcessIsReady(base::BindOnce(
-        &AwRenderProcess::EnableIdleThrottling,
-        base::Unretained(
-            AwRenderProcess::GetInstanceForRenderProcessHost(process_host)),
-        policy_, min_time_ms_, min_cputime_ratio_));
-  }
-
-  bool enable_idle_throttling_{false};
-  int32_t policy_;
-  int32_t min_time_ms_;
-  float min_cputime_ratio_;
-};
-
-static void JNI_AwDebug_SetCpuAffinityToLittleCores(JNIEnv* env) {
-  static base::NoDestructor<AwDebugCpuAffinity> aw_debug_cpu_affinity(false, 1,
-                                                                      0, 0);
-}
-
-static void JNI_AwDebug_EnableIdleThrottling(JNIEnv* env,
-                                             int policy,
-                                             int min_time_ms,
-                                             float min_cputime_ratio) {
-  static base::NoDestructor<AwDebugCpuAffinity> aw_debug_cpu_affinity(
-      true, policy, min_time_ms, min_cputime_ratio);
-}
-
 static void JNI_AwDebug_SetSupportLibraryWebkitVersionCrashKey(
     JNIEnv* env,
     const base::android::JavaParamRef<jstring>& version) {
diff --git a/android_webview/browser/aw_render_process.cc b/android_webview/browser/aw_render_process.cc
index 77555e97..40486f2c 100644
--- a/android_webview/browser/aw_render_process.cc
+++ b/android_webview/browser/aw_render_process.cc
@@ -66,17 +66,6 @@
   renderer_remote_->SetJsOnlineProperty(network_up);
 }
 
-void AwRenderProcess::SetCpuAffinityToLittleCores() {
-  renderer_remote_->SetCpuAffinityToLittleCores();
-}
-
-void AwRenderProcess::EnableIdleThrottling(int32_t policy,
-                                           int32_t min_time_ms,
-                                           float min_cputime_ratio) {
-  renderer_remote_->EnableIdleThrottling(policy, min_time_ms,
-                                         min_cputime_ratio);
-}
-
 void AwRenderProcess::Ready() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
diff --git a/android_webview/browser/aw_render_process.h b/android_webview/browser/aw_render_process.h
index e942c5b3..105f373 100644
--- a/android_webview/browser/aw_render_process.h
+++ b/android_webview/browser/aw_render_process.h
@@ -39,10 +39,6 @@
 
   void ClearCache();
   void SetJsOnlineProperty(bool network_up);
-  void SetCpuAffinityToLittleCores();
-  void EnableIdleThrottling(int32_t policy,
-                            int32_t min_time_ms,
-                            float min_cputime_ratio);
 
  private:
   void Ready();
diff --git a/android_webview/browser/cookie_manager.cc b/android_webview/browser/cookie_manager.cc
index 388cf31..e9c2286 100644
--- a/android_webview/browser/cookie_manager.cc
+++ b/android_webview/browser/cookie_manager.cc
@@ -313,9 +313,12 @@
   DCHECK(cookie_store_task_runner_->RunsTasksInCurrentSequence());
 
   if (!cookie_store_) {
+    // TODO(https://crbug.com/1286070): Provide a non-hardcoded value for
+    // the 'first_party_sets_enabled' argument below.
     content::CookieStoreConfig cookie_config(
-        cookie_store_path_, true /* restore_old_session_cookies */,
-        true /* persist_session_cookies */);
+        cookie_store_path_, /* restore_old_session_cookies= */ true,
+        /* persist_session_cookies= */ true,
+        /* first_party_sets_enabled= */ false);
     cookie_config.client_task_runner = cookie_store_task_runner_;
     cookie_config.background_task_runner =
         cookie_store_backend_thread_.task_runner();
diff --git a/android_webview/browser/gfx/begin_frame_source_webview.cc b/android_webview/browser/gfx/begin_frame_source_webview.cc
index bd114b6..b6a26df 100644
--- a/android_webview/browser/gfx/begin_frame_source_webview.cc
+++ b/android_webview/browser/gfx/begin_frame_source_webview.cc
@@ -126,7 +126,9 @@
 }
 
 RootBeginFrameSourceWebView::RootBeginFrameSourceWebView()
-    : begin_frame_source_(kNotRestartableId, 60.0f),
+    : begin_frame_source_(kNotRestartableId,
+                          60.0f,
+                          /*requires_align_with_java=*/true),
       j_object_(Java_RootBeginFrameSourceWebView_Constructor(
           base::android::AttachCurrentThread(),
           reinterpret_cast<jlong>(this))) {
@@ -154,4 +156,4 @@
   after_begin_frame_callbacks_.emplace_back(std::move(callback));
 }
 
-}  // namespace android_webview
\ No newline at end of file
+}  // namespace android_webview
diff --git a/android_webview/common/mojom/renderer.mojom b/android_webview/common/mojom/renderer.mojom
index 7fd4b953..8c9b46d 100644
--- a/android_webview/common/mojom/renderer.mojom
+++ b/android_webview/common/mojom/renderer.mojom
@@ -13,12 +13,4 @@
 
   // Adjusts the javascript 'online' property value.
   SetJsOnlineProperty(bool network_up);
-
-  // Set renderer process CPU affinity to little cores. Temporarily added for an
-  // experiment; to be deleted after its conclusion (https://crbug.com/1111789).
-  SetCpuAffinityToLittleCores();
-
-  // Enable idle throttling in the renderer. Temporarily added for an
-  // experiment; to be deleted after its conclusion (https://crbug.com/1166695).
-  EnableIdleThrottling(int32 policy, int32 min_time_ms, float min_cputime_ratio);
 };
diff --git a/android_webview/java/src/org/chromium/android_webview/AwDebug.java b/android_webview/java/src/org/chromium/android_webview/AwDebug.java
index d0cb05b..d6c9fea 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwDebug.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwDebug.java
@@ -4,13 +4,10 @@
 
 package org.chromium.android_webview;
 
-import androidx.annotation.IntDef;
-
 import org.chromium.base.Log;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.base.annotations.UsedByReflection;
-import org.chromium.base.metrics.RecordHistogram;
 
 import java.io.File;
 
@@ -41,41 +38,8 @@
         AwDebugJni.get().setSupportLibraryWebkitVersionCrashKey(version);
     }
 
-    // Used to record the UMA histogram Android.WebView.AwDebugCall. Since these values are
-    // persisted to logs, they should never be renumbered or reused.
-    @IntDef({AwDebugCall.SET_CPU_AFFINITY_TO_LITTLE_CORES, AwDebugCall.ENABLE_IDLE_THROTTLING})
-    @interface AwDebugCall {
-        int SET_CPU_AFFINITY_TO_LITTLE_CORES = 0;
-        int ENABLE_IDLE_THROTTLING = 1;
-        int ENABLE_POWER_SCHEDULER = 2;
-        int COUNT = 3;
-    }
-
-    @UsedByReflection("")
-    public static void setCpuAffinityToLittleCores() {
-        RecordHistogram.recordEnumeratedHistogram("Android.WebView.AwDebugCall",
-                AwDebugCall.SET_CPU_AFFINITY_TO_LITTLE_CORES, AwDebugCall.COUNT);
-        AwDebugJni.get().setCpuAffinityToLittleCores();
-    }
-
-    @UsedByReflection("")
-    public static void enableIdleThrottling() {
-        RecordHistogram.recordEnumeratedHistogram("Android.WebView.AwDebugCall",
-                AwDebugCall.ENABLE_IDLE_THROTTLING, AwDebugCall.COUNT);
-        AwDebugJni.get().enableIdleThrottling(3, 500, 0.5f);
-    }
-
-    @UsedByReflection("")
-    public static void enablePowerScheduler(int policy, int minTimeMs, float minCputimeRatio) {
-        RecordHistogram.recordEnumeratedHistogram("Android.WebView.AwDebugCall",
-                AwDebugCall.ENABLE_POWER_SCHEDULER, AwDebugCall.COUNT);
-        AwDebugJni.get().enableIdleThrottling(policy, minTimeMs, minCputimeRatio);
-    }
-
     @NativeMethods
     interface Natives {
         void setSupportLibraryWebkitVersionCrashKey(String version);
-        void setCpuAffinityToLittleCores();
-        void enableIdleThrottling(int policy, int minTimeMs, float minCputimeRatio);
     }
 }
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 5525049..ed06076 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
@@ -165,6 +165,10 @@
                     "Fixes memory leaks in the renderer- and browser-form caches."),
             Flag.baseFeature(AutofillFeatures.AUTOFILL_USE_UNASSOCIATED_LISTED_ELEMENTS,
                     "Caches unowned listed elements in the document."),
+            Flag.baseFeature(AutofillFeatures.AUTOFILL_PARSING_PATTERN_PROVIDER,
+                    "Enables Autofill to use its new method to retrieve parsing patterns."),
+            Flag.baseFeature(AutofillFeatures.AUTOFILL_PAGE_LANGUAGE_DETECTION,
+                    "Enables Autofill to retrieve the page language for form parsing."),
             Flag.baseFeature(FeatureConstants.KEYBOARD_ACCESSORY_PAYMENT_VIRTUAL_CARD_FEATURE,
                     "When enabled, merchant bound virtual cards will be offered in the keyboard "
                             + "accessory."),
diff --git a/android_webview/renderer/aw_render_thread_observer.cc b/android_webview/renderer/aw_render_thread_observer.cc
index 88ae28f..c41dc93 100644
--- a/android_webview/renderer/aw_render_thread_observer.cc
+++ b/android_webview/renderer/aw_render_thread_observer.cc
@@ -45,18 +45,4 @@
   blink::WebNetworkStateNotifier::SetOnLine(network_up);
 }
 
-void AwRenderThreadObserver::SetCpuAffinityToLittleCores() {
-  power_scheduler::PowerScheduler::GetInstance()->SetPolicy(
-      power_scheduler::SchedulingPolicy::kLittleCoresOnly);
-}
-
-void AwRenderThreadObserver::EnableIdleThrottling(int32_t policy,
-                                                  int32_t min_time_ms,
-                                                  float min_cputime_ratio) {
-  power_scheduler::SchedulingPolicyParams params{
-      (power_scheduler::SchedulingPolicy)policy,
-      base::Milliseconds(min_time_ms), min_cputime_ratio};
-  power_scheduler::PowerScheduler::GetInstance()->SetPolicy(params);
-}
-
 }  // namespace android_webview
diff --git a/android_webview/renderer/aw_render_thread_observer.h b/android_webview/renderer/aw_render_thread_observer.h
index 72096b07..fc89d0c 100644
--- a/android_webview/renderer/aw_render_thread_observer.h
+++ b/android_webview/renderer/aw_render_thread_observer.h
@@ -30,10 +30,6 @@
   // mojom::Renderer overrides:
   void ClearCache() override;
   void SetJsOnlineProperty(bool network_up) override;
-  void SetCpuAffinityToLittleCores() override;
-  void EnableIdleThrottling(int32_t policy,
-                            int32_t min_time_ms,
-                            float min_cputime_ratio) override;
 
   void OnRendererAssociatedRequest(
       mojo::PendingAssociatedReceiver<mojom::Renderer> receiver);
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 5686b5669..87fecd5 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1068,6 +1068,8 @@
     "system/holding_space/holding_space_item_view.h",
     "system/holding_space/holding_space_item_views_section.cc",
     "system/holding_space/holding_space_item_views_section.h",
+    "system/holding_space/holding_space_progress_icon_animation.cc",
+    "system/holding_space/holding_space_progress_icon_animation.h",
     "system/holding_space/holding_space_progress_indicator.cc",
     "system/holding_space/holding_space_progress_indicator.h",
     "system/holding_space/holding_space_progress_indicator_animation.cc",
@@ -2509,6 +2511,7 @@
     "system/caps_lock_notification_controller_unittest.cc",
     "system/geolocation/geolocation_controller_unittest.cc",
     "system/gesture_education/gesture_education_notification_controller_unittest.cc",
+    "system/holding_space/holding_space_animation_registry_unittest.cc",
     "system/holding_space/holding_space_progress_indicator_unittest.cc",
     "system/holding_space/holding_space_tray_unittest.cc",
     "system/hps/hps_configuration_unittest.cc",
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc
index cb10d6b..34a76c8 100644
--- a/ash/accelerators/accelerator_controller_impl.cc
+++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -2664,10 +2664,14 @@
                << location_info;
     return;
   }
-  info_in_dict->GetString(kVolumeButtonRegion,
-                          &side_volume_button_location_.region);
-  info_in_dict->GetString(kVolumeButtonSide,
-                          &side_volume_button_location_.side);
+
+  const std::string* region = info_in_dict->FindStringKey(kVolumeButtonRegion);
+  if (region)
+    side_volume_button_location_.region = *region;
+
+  const std::string* side = info_in_dict->FindStringKey(kVolumeButtonSide);
+  if (side)
+    side_volume_button_location_.side = *side;
 }
 
 void AcceleratorControllerImpl::UpdateTabletModeVolumeAdjustHistogram() {
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index dc4d27a..1186b25 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -1592,10 +1592,10 @@
   // Tests that |side_volume_button_location_| is read correctly if the location
   // file exists.
   base::DictionaryValue location;
-  location.SetString(AcceleratorControllerImpl::kVolumeButtonRegion,
-                     AcceleratorControllerImpl::kVolumeButtonRegionScreen);
-  location.SetString(AcceleratorControllerImpl::kVolumeButtonSide,
-                     AcceleratorControllerImpl::kVolumeButtonSideLeft);
+  location.SetStringKey(AcceleratorControllerImpl::kVolumeButtonRegion,
+                        AcceleratorControllerImpl::kVolumeButtonRegionScreen);
+  location.SetStringKey(AcceleratorControllerImpl::kVolumeButtonSide,
+                        AcceleratorControllerImpl::kVolumeButtonSideLeft);
   std::string json_location;
   base::JSONWriter::Write(location, &json_location);
   base::ScopedTempDir file_tmp_dir;
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index 4b9c5ae..809bd77a 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -89,8 +89,9 @@
 
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
-  // TODO(crbug.com/1204554): Rename to fullscreen_presenter().
-  AppListPresenterImpl* presenter() { return fullscreen_presenter_.get(); }
+  AppListPresenterImpl* fullscreen_presenter() {
+    return fullscreen_presenter_.get();
+  }
 
   // AppListController:
   void SetClient(AppListClient* client) override;
diff --git a/ash/app_list/app_list_controller_impl_unittest.cc b/ash/app_list/app_list_controller_impl_unittest.cc
index 991cabb..e541f89 100644
--- a/ash/app_list/app_list_controller_impl_unittest.cc
+++ b/ash/app_list/app_list_controller_impl_unittest.cc
@@ -92,7 +92,7 @@
 }
 
 AppListView* GetAppListView() {
-  return Shell::Get()->app_list_controller()->presenter()->GetView();
+  return Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView();
 }
 
 ContentsView* GetContentsView() {
@@ -119,13 +119,13 @@
 }
 
 void ShowAppListNow(AppListViewState state) {
-  Shell::Get()->app_list_controller()->presenter()->Show(
+  Shell::Get()->app_list_controller()->fullscreen_presenter()->Show(
       state, display::Screen::GetScreen()->GetPrimaryDisplay().id(),
       base::TimeTicks::Now(), /*show_source*/ absl::nullopt);
 }
 
 void DismissAppListNow() {
-  Shell::Get()->app_list_controller()->presenter()->Dismiss(
+  Shell::Get()->app_list_controller()->fullscreen_presenter()->Dismiss(
       base::TimeTicks::Now());
 }
 
@@ -223,7 +223,7 @@
     ShowAppListNow(AppListViewState::kPeeking);
     EXPECT_TRUE(Shell::Get()
                     ->app_list_controller()
-                    ->presenter()
+                    ->fullscreen_presenter()
                     ->IsVisibleDeprecated());
     shelf->SetAlignment(alignment);
     EXPECT_EQ(AppListViewState::kClosed, GetAppListView()->app_list_state());
@@ -480,6 +480,13 @@
 // Verifies that in tablet mode, the AppListView has correct bounds when the
 // virtual keyboard is dismissed (see https://crbug.com/944133).
 TEST_F(AppListControllerImplTest, CheckAppListViewBoundsWhenDismissVKeyboard) {
+  // This isn't relevant with ProductivityLauncher, which uses separate widgets
+  // in clamshell versus tablet mode. See bug above. Also, the clamshell
+  // launcher closes when transitioning into tablet mode. This test can be
+  // deleted when ProductivityLauncher is the default.
+  if (features::IsProductivityLauncherEnabled())
+    return;
+
   Shell::Get()->keyboard_controller()->SetEnableFlag(
       keyboard::KeyboardEnableFlag::kShelfEnabled);
 
@@ -673,6 +680,12 @@
 // closed.
 TEST_F(AppListControllerImplTest,
        CloseAppListShownFromOverviewAfterTabletExit) {
+  // This test is not relevant for ProductivityLauncher because it uses separate
+  // widgets in clamshell and tablet mode. This test can be deleted when
+  // ProductivityLauncher is the default.
+  if (features::IsProductivityLauncherEnabled())
+    return;
+
   auto* shell = Shell::Get();
   auto* tablet_mode_controller = shell->tablet_mode_controller();
   // Move to tablet mode and back.
@@ -1132,6 +1145,11 @@
 // Write this test case for precaution (https://crbug.com/947105).
 TEST_F(AppListControllerImplMetricsTest,
        PresentationMetricsForTabletNotRecordedInClamshell) {
+  // ProductivityLauncher does not support app list dragging. This test can be
+  // deleted when ProductivityLauncher is the default.
+  if (features::IsProductivityLauncherEnabled())
+    return;
+
   // Wait until the construction of TabletModeController finishes.
   base::RunLoop().RunUntilIdle();
 
diff --git a/ash/app_list/app_list_metrics_unittest.cc b/ash/app_list/app_list_metrics_unittest.cc
index f98bb46..7a875f4 100644
--- a/ash/app_list/app_list_metrics_unittest.cc
+++ b/ash/app_list/app_list_metrics_unittest.cc
@@ -131,7 +131,7 @@
     // first search container.
     ContentsView* contents_view = Shell::Get()
                                       ->app_list_controller()
-                                      ->presenter()
+                                      ->fullscreen_presenter()
                                       ->GetView()
                                       ->app_list_main_view()
                                       ->contents_view();
@@ -164,7 +164,7 @@
     SearchResultContainerView* suggestions_container_ =
         Shell::Get()
             ->app_list_controller()
-            ->presenter()
+            ->fullscreen_presenter()
             ->GetView()
             ->app_list_main_view()
             ->contents_view()
@@ -188,7 +188,7 @@
     model->AddItem(std::make_unique<AppListItem>("item 3"));
 
     AppListView::TestApi test_api(
-        Shell::Get()->app_list_controller()->presenter()->GetView());
+        Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView());
 
     // Focus the first item in the root app grid.
     test_api.GetRootAppsGridView()->GetItemViewAt(0)->RequestFocus();
diff --git a/ash/app_list/app_list_presenter_impl_unittest.cc b/ash/app_list/app_list_presenter_impl_unittest.cc
index 1eee713..3f7d232d 100644
--- a/ash/app_list/app_list_presenter_impl_unittest.cc
+++ b/ash/app_list/app_list_presenter_impl_unittest.cc
@@ -44,7 +44,7 @@
   ~AppListPresenterImplTest() override = default;
 
   AppListPresenterImpl* presenter() {
-    return Shell::Get()->app_list_controller()->presenter();
+    return Shell::Get()->app_list_controller()->fullscreen_presenter();
   }
 
   // Shows the app list on the primary display.
diff --git a/ash/app_list/app_list_presenter_unittest.cc b/ash/app_list/app_list_presenter_unittest.cc
index 621ec3fa..6d182da 100644
--- a/ash/app_list/app_list_presenter_unittest.cc
+++ b/ash/app_list/app_list_presenter_unittest.cc
@@ -566,7 +566,7 @@
  protected:
   void OpenAppListInFullscreen() {
     AppListPresenterImpl* presenter =
-        Shell::Get()->app_list_controller()->presenter();
+        Shell::Get()->app_list_controller()->fullscreen_presenter();
     presenter->Show(AppListViewState::kFullscreenAllApps,
                     GetPrimaryDisplay().id(), base::TimeTicks::Now(),
                     /*show_source=*/absl::nullopt);
diff --git a/ash/app_list/app_list_test_api.cc b/ash/app_list/app_list_test_api.cc
index 32e7622..445abb73 100644
--- a/ash/app_list/app_list_test_api.cc
+++ b/ash/app_list/app_list_test_api.cc
@@ -43,7 +43,7 @@
 namespace {
 
 AppListView* GetAppListView() {
-  return Shell::Get()->app_list_controller()->presenter()->GetView();
+  return Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView();
 }
 
 PagedAppsGridView* GetPagedAppsGridView() {
diff --git a/ash/app_list/app_list_unittest.cc b/ash/app_list/app_list_unittest.cc
index a5c34ef..a50525f 100644
--- a/ash/app_list/app_list_unittest.cc
+++ b/ash/app_list/app_list_unittest.cc
@@ -29,7 +29,7 @@
   auto* controller = Shell::Get()->app_list_controller();
   if (features::IsProductivityLauncherEnabled())
     return controller->bubble_presenter_for_test()->IsShowing();
-  return controller->presenter()->GetTargetVisibility();
+  return controller->fullscreen_presenter()->GetTargetVisibility();
 }
 
 }  // namespace
diff --git a/ash/app_list/test/app_list_test_helper.cc b/ash/app_list/test/app_list_test_helper.cc
index c0b4fa0..4329f8b 100644
--- a/ash/app_list/test/app_list_test_helper.cc
+++ b/ash/app_list/test/app_list_test_helper.cc
@@ -149,7 +149,7 @@
 }
 
 AppListView* AppListTestHelper::GetAppListView() {
-  return app_list_controller_->presenter()->GetView();
+  return app_list_controller_->fullscreen_presenter()->GetView();
 }
 
 SearchBoxView* AppListTestHelper::GetSearchBoxView() {
diff --git a/ash/app_list/views/assistant/assistant_test_api_impl.cc b/ash/app_list/views/assistant/assistant_test_api_impl.cc
index 1e20643..f4d317f3 100644
--- a/ash/app_list/views/assistant/assistant_test_api_impl.cc
+++ b/ash/app_list/views/assistant/assistant_test_api_impl.cc
@@ -248,7 +248,7 @@
 
 ContentsView* AssistantTestApiImpl::contents_view_or_null() const {
   auto* app_list_view =
-      Shell::Get()->app_list_controller()->presenter()->GetView();
+      Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView();
 
   if (!app_list_view)
     return nullptr;
diff --git a/ash/app_list/views/search_result_list_view.cc b/ash/app_list/views/search_result_list_view.cc
index 1cf232f..7c747dbf 100644
--- a/ash/app_list/views/search_result_list_view.cc
+++ b/ash/app_list/views/search_result_list_view.cc
@@ -505,14 +505,6 @@
   }
 }
 
-void SearchResultListView::VisibilityChanged(View* starting_from,
-                                             bool is_visible) {
-  SearchResultContainerView::VisibilityChanged(starting_from, is_visible);
-  // We only do this work when is_visible is false.
-  if (is_visible)
-    return;
-}
-
 std::vector<SearchResult*> SearchResultListView::GetAssistantResults() {
   // Only show Assistant results if there are no tiles. There is not enough
   // room in launcher to display Assistant results if there are tiles visible.
diff --git a/ash/app_list/views/search_result_list_view.h b/ash/app_list/views/search_result_list_view.h
index f54ed52..6355d01 100644
--- a/ash/app_list/views/search_result_list_view.h
+++ b/ash/app_list/views/search_result_list_view.h
@@ -121,10 +121,6 @@
 
   views::Label* title_label_for_test() { return title_label_; }
 
- protected:
-  // Overridden from views::View:
-  void VisibilityChanged(View* starting_from, bool is_visible) override;
-
  private:
   friend class test::SearchResultListViewTest;
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index ffd6a92..5c39282 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -10,6 +10,7 @@
     <output filename="grit/ash_strings.h" type="rc_header">
       <emit emit_type='prepend'></emit>
     </output>
+    <output filename="ash_strings_af.pak" type="data_package" lang="af" />
     <output filename="ash_strings_am.pak" type="data_package" lang="am" />
     <output filename="ash_strings_ar.pak" type="data_package" lang="ar" />
     <output filename="ash_strings_bg.pak" type="data_package" lang="bg" />
@@ -66,6 +67,7 @@
     <output filename="ash_strings_vi.pak" type="data_package" lang="vi" />
     <output filename="ash_strings_zh-CN.pak" type="data_package" lang="zh-CN" />
     <output filename="ash_strings_zh-TW.pak" type="data_package" lang="zh-TW" />
+    <output filename="ash_strings_zu.pak" type="data_package" lang="zu" />
 
     <!-- Pseudolocales -->
     <output filename="ash_strings_ar-XB.pak" type="data_package" lang="ar-XB" />
diff --git a/ash/autotest_private_api_utils.cc b/ash/autotest_private_api_utils.cc
index efffce7..1fde90c9 100644
--- a/ash/autotest_private_api_utils.cc
+++ b/ash/autotest_private_api_utils.cc
@@ -127,7 +127,7 @@
 
 bool WaitForLauncherAnimation(base::OnceClosure closure) {
   auto* app_list_view =
-      Shell::Get()->app_list_controller()->presenter()->GetView();
+      Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView();
   if (!app_list_view) {
     std::move(closure).Run();
     return true;
@@ -180,7 +180,7 @@
   // Don't wait if the launcher is already in the target state and not
   // animating.
   auto* app_list_view =
-      Shell::Get()->app_list_controller()->presenter()->GetView();
+      Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView();
   bool animating =
       app_list_view &&
       app_list_view->GetWidget()->GetLayer()->GetAnimator()->is_animating();
diff --git a/ash/capture_mode/capture_mode_unittests.cc b/ash/capture_mode/capture_mode_unittests.cc
index 5c72d1e..c23087b 100644
--- a/ash/capture_mode/capture_mode_unittests.cc
+++ b/ash/capture_mode/capture_mode_unittests.cc
@@ -4788,6 +4788,11 @@
     CaptureModeTest::SetUp();
     auto* projector_controller = ProjectorController::Get();
     projector_controller->SetClient(&projector_client_);
+    ON_CALL(projector_client_, StopSpeechRecognition)
+        .WillByDefault(testing::Invoke([]() {
+          ProjectorController::Get()->OnSpeechRecognitionStopped();
+        }));
+
     // Simulate the availability of speech recognition.
     projector_controller->OnSpeechRecognitionAvailabilityChanged(
         SpeechRecognitionAvailability::kAvailable);
diff --git a/ash/clipboard/clipboard_nudge_controller.cc b/ash/clipboard/clipboard_nudge_controller.cc
index 8407727..145f372f 100644
--- a/ash/clipboard/clipboard_nudge_controller.cc
+++ b/ash/clipboard/clipboard_nudge_controller.cc
@@ -127,7 +127,7 @@
   if (!prefs)
     return;
   const int shown_count = GetNewFeatureBadgeShownCount(prefs);
-  DictionaryPrefUpdate update(prefs, prefs::kMultipasteNudges);
+  DictionaryPrefUpdateDeprecated update(prefs, prefs::kMultipasteNudges);
   update->SetIntPath(kNewFeatureBadgeCount, shown_count + 1);
   base::UmaHistogramBoolean(kNewBadge_ShowCount, true);
   if (new_feature_last_shown_time_.ShouldLogFeatureOpenTime()) {
@@ -199,7 +199,7 @@
 void ClipboardNudgeController::OnActiveUserPrefServiceChanged(
     PrefService* prefs) {
   // Reset the nudge prefs so that the nudge can be shown again.
-  DictionaryPrefUpdate update(prefs, prefs::kMultipasteNudges);
+  DictionaryPrefUpdateDeprecated update(prefs, prefs::kMultipasteNudges);
   update->SetIntPath(kShownCount, 0);
   update->SetPath(kLastTimeShown, base::TimeToValue(base::Time()));
   update->SetIntPath(kNewFeatureBadgeCount, 0);
@@ -251,7 +251,7 @@
   if (!prefs)
     return;
   const int shown_count = GetShownCount(prefs);
-  DictionaryPrefUpdate update(prefs, prefs::kMultipasteNudges);
+  DictionaryPrefUpdateDeprecated update(prefs, prefs::kMultipasteNudges);
   update->SetIntPath(kShownCount, shown_count + 1);
   update->SetPath(kLastTimeShown, base::TimeToValue(GetTime()));
 }
diff --git a/ash/components/arc/metrics/stability_metrics_manager.cc b/ash/components/arc/metrics/stability_metrics_manager.cc
index ace7f95..cb29007c 100644
--- a/ash/components/arc/metrics/stability_metrics_manager.cc
+++ b/ash/components/arc/metrics/stability_metrics_manager.cc
@@ -70,7 +70,7 @@
 
 void StabilityMetricsManager::ResetMetrics() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DictionaryPrefUpdate update(local_state_, prefs::kStabilityMetrics);
+  DictionaryPrefUpdateDeprecated update(local_state_, prefs::kStabilityMetrics);
   update->DictClear();
 }
 
@@ -83,7 +83,7 @@
 
 void StabilityMetricsManager::SetArcEnabledState(bool enabled) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DictionaryPrefUpdate update(local_state_, prefs::kStabilityMetrics);
+  DictionaryPrefUpdateDeprecated update(local_state_, prefs::kStabilityMetrics);
   update->SetKey(kArcEnabledStateKey, base::Value(enabled));
 }
 
@@ -104,7 +104,7 @@
 void StabilityMetricsManager::SetArcNativeBridgeType(
     NativeBridgeType native_bridge_type) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DictionaryPrefUpdate update(local_state_, prefs::kStabilityMetrics);
+  DictionaryPrefUpdateDeprecated update(local_state_, prefs::kStabilityMetrics);
   update->SetKey(kArcNativeBridgeTypeKey,
                  base::Value(static_cast<int>(native_bridge_type)));
 }
diff --git a/ash/components/arc/video_accelerator/gpu_arc_video_decoder.cc b/ash/components/arc/video_accelerator/gpu_arc_video_decoder.cc
index a680bece..bcbb52e 100644
--- a/ash/components/arc/video_accelerator/gpu_arc_video_decoder.cc
+++ b/ash/components/arc/video_accelerator/gpu_arc_video_decoder.cc
@@ -53,13 +53,17 @@
   // Invalidate all weak pointers to stop incoming callbacks.
   weak_this_factory_.InvalidateWeakPtrs();
 
-  // The number of active instances should always be larger than zero. But if a
-  // bug causes an underflow we will permanently be unable to create new
-  // decoders, so an extra check is performed here (see b/173700103).
-  if (decoder_ && num_instances_ > 0) {
-    num_instances_--;
+  if (decoder_) {
+    // Destroy |decoder_| now in case it needs to use *|this| during tear-down.
+    decoder_.reset();
+
+    // The number of active instances should always be larger than zero. But if
+    // a bug causes an underflow we will permanently be unable to create new
+    // decoders, so an extra check is performed here (see b/173700103).
+    if (num_instances_ > 0) {
+      num_instances_--;
+    }
   }
-  decoder_.reset();
 
   client_video_frames_.clear();
   video_frame_pool_.reset();
diff --git a/ash/components/arc/video_accelerator/gpu_arc_video_frame_pool.cc b/ash/components/arc/video_accelerator/gpu_arc_video_frame_pool.cc
index 1343df1..77532ad 100644
--- a/ash/components/arc/video_accelerator/gpu_arc_video_frame_pool.cc
+++ b/ash/components/arc/video_accelerator/gpu_arc_video_frame_pool.cc
@@ -15,6 +15,7 @@
 #include "media/base/decode_status.h"
 #include "media/base/format_utils.h"
 #include "media/base/video_types.h"
+#include "media/gpu/buffer_validation.h"
 #include "media/gpu/macros.h"
 #include "ui/gfx/buffer_format_util.h"
 
@@ -272,12 +273,24 @@
     }
     gmb_handle.type = gfx::NATIVE_PIXMAP;
     gmb_handle.native_pixmap_handle = std::move(protected_native_pixmap);
+
+    // Explicitly verify the GPU Memory Buffer Handle here. Note that we do not
+    // do this for non-protected content because the verification happens on
+    // creation in that path.
+    if (!media::VerifyGpuMemoryBufferHandle(pixel_format, coded_size_,
+                                            gmb_handle)) {
+      VLOGF(1) << "Invalid GpuMemoryBufferHandle for protected content";
+      return gfx::GpuMemoryBufferHandle();
+    }
   } else {
     std::vector<base::ScopedFD> fds = DuplicateFD(std::move(fd), planes.size());
     if (fds.empty()) {
       VLOGF(1) << "Failed to duplicate fd";
       return gfx::GpuMemoryBufferHandle();
     }
+
+    // Verification of the GPU Memory Buffer Handle is handled under the hood in
+    // this call.
     auto handle = CreateGpuMemoryBufferHandle(
         pixel_format, modifier, coded_size_, std::move(fds), planes);
     if (!handle) {
diff --git a/ash/components/audio/audio_devices_pref_handler_impl.cc b/ash/components/audio/audio_devices_pref_handler_impl.cc
index 7b5eefe..488f010 100644
--- a/ash/components/audio/audio_devices_pref_handler_impl.cc
+++ b/ash/components/audio/audio_devices_pref_handler_impl.cc
@@ -166,8 +166,8 @@
     std::string old_device_id = GetVersionedDeviceIdString(device, 1);
     device_mute_settings_->RemoveKey(old_device_id);
   }
-  device_mute_settings_->SetInteger(GetDeviceIdString(device),
-                                    mute ? kPrefMuteOn : kPrefMuteOff);
+  device_mute_settings_->SetIntKey(GetDeviceIdString(device),
+                                   mute ? kPrefMuteOn : kPrefMuteOff);
   SaveDevicesMutePref();
 }
 
@@ -310,7 +310,8 @@
 }
 
 void AudioDevicesPrefHandlerImpl::SaveDevicesMutePref() {
-  DictionaryPrefUpdate dict_update(local_state_, prefs::kAudioDevicesMute);
+  DictionaryPrefUpdateDeprecated dict_update(local_state_,
+                                             prefs::kAudioDevicesMute);
   dict_update->DictClear();
   dict_update->MergeDictionary(device_mute_settings_.get());
 }
@@ -323,8 +324,8 @@
 }
 
 void AudioDevicesPrefHandlerImpl::SaveDevicesVolumePref() {
-  DictionaryPrefUpdate dict_update(local_state_,
-                                   prefs::kAudioDevicesVolumePercent);
+  DictionaryPrefUpdateDeprecated dict_update(local_state_,
+                                             prefs::kAudioDevicesVolumePercent);
   dict_update->DictClear();
   dict_update->MergeDictionary(device_volume_settings_.get());
 }
@@ -337,8 +338,8 @@
 }
 
 void AudioDevicesPrefHandlerImpl::SaveDevicesGainPref() {
-  DictionaryPrefUpdate dict_update(local_state_,
-                                   prefs::kAudioDevicesGainPercent);
+  DictionaryPrefUpdateDeprecated dict_update(local_state_,
+                                             prefs::kAudioDevicesGainPercent);
   dict_update->DictClear();
   dict_update->MergeDictionary(device_gain_settings_.get());
 }
@@ -351,7 +352,8 @@
 }
 
 void AudioDevicesPrefHandlerImpl::SaveDevicesStatePref() {
-  DictionaryPrefUpdate dict_update(local_state_, prefs::kAudioDevicesState);
+  DictionaryPrefUpdateDeprecated dict_update(local_state_,
+                                             prefs::kAudioDevicesState);
   dict_update->DictClear();
   dict_update->MergeDictionary(device_state_settings_.get());
 }
@@ -376,7 +378,7 @@
     // If there was no recorded value for deprecated device ID, use value from
     // global mute pref.
     int old_mute = local_state_->GetInteger(prefs::kAudioMute);
-    device_mute_settings_->SetInteger(device_key, old_mute);
+    device_mute_settings_->SetIntKey(device_key, old_mute);
   }
   SaveDevicesMutePref();
 }
diff --git a/ash/components/audio/audio_devices_pref_handler_impl.h b/ash/components/audio/audio_devices_pref_handler_impl.h
index 300a136..c39665f 100644
--- a/ash/components/audio/audio_devices_pref_handler_impl.h
+++ b/ash/components/audio/audio_devices_pref_handler_impl.h
@@ -114,6 +114,7 @@
   // Notifies the AudioPrefObserver for audio policy pref changes.
   void NotifyAudioPolicyChange();
 
+  // TODO(crbug.com/1187061): Refactor these off of base::DictionaryValue.
   std::unique_ptr<base::DictionaryValue> device_mute_settings_;
   std::unique_ptr<base::DictionaryValue> device_volume_settings_;
   std::unique_ptr<base::DictionaryValue> device_gain_settings_;
diff --git a/ash/components/audio/audio_devices_pref_handler_impl_unittest.cc b/ash/components/audio/audio_devices_pref_handler_impl_unittest.cc
index 6de3e05..d13b4f7 100644
--- a/ash/components/audio/audio_devices_pref_handler_impl_unittest.cc
+++ b/ash/components/audio/audio_devices_pref_handler_impl_unittest.cc
@@ -112,8 +112,8 @@
     // are set when pref value sets up its internal state.
     std::string preset_key = GetPresetDeviceDeprecatedPrefKey();
     {
-      DictionaryPrefUpdate update(pref_service_.get(),
-                                  prefs::kAudioDevicesState);
+      DictionaryPrefUpdateDeprecated update(pref_service_.get(),
+                                            prefs::kAudioDevicesState);
       base::DictionaryValue* pref = update.Get();
       base::DictionaryValue state;
       state.SetBoolean("active", kPresetState.active);
@@ -122,17 +122,17 @@
     }
 
     {
-      DictionaryPrefUpdate update(pref_service_.get(),
-                                  prefs::kAudioDevicesVolumePercent);
+      DictionaryPrefUpdateDeprecated update(pref_service_.get(),
+                                            prefs::kAudioDevicesVolumePercent);
       base::DictionaryValue* pref = update.Get();
       pref->SetDoubleKey(preset_key, kPresetState.sound_level);
     }
 
     {
-      DictionaryPrefUpdate update(pref_service_.get(),
-                                  prefs::kAudioDevicesMute);
+      DictionaryPrefUpdateDeprecated update(pref_service_.get(),
+                                            prefs::kAudioDevicesMute);
       base::DictionaryValue* pref = update.Get();
-      pref->SetInteger(preset_key, static_cast<int>(kPresetState.mute));
+      pref->SetIntKey(preset_key, static_cast<int>(kPresetState.mute));
     }
 
     audio_pref_handler_ = new AudioDevicesPrefHandlerImpl(pref_service_.get());
diff --git a/ash/components/fwupd/firmware_update_manager.cc b/ash/components/fwupd/firmware_update_manager.cc
index a6e4d93a6..191c082 100644
--- a/ash/components/fwupd/firmware_update_manager.cc
+++ b/ash/components/fwupd/firmware_update_manager.cc
@@ -11,6 +11,7 @@
 #include "base/base_paths.h"
 #include "base/check_op.h"
 #include "base/containers/contains.h"
+#include "base/containers/fixed_flat_map.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -36,6 +37,46 @@
 namespace ash {
 
 namespace {
+// State of the fwupd daemon. Enum defined here:
+// https://github.com/fwupd/fwupd/blob/4389f9f913588edae7243a8dbed88ce3788c8bc2/libfwupd/fwupd-enums.h
+enum class FwupdStatus {
+  kUnknown,
+  kIdle,
+  kLoading,
+  kDecompressing,
+  kDeviceRestart,
+  kDeviceWrite,
+  kDeviceVerify,
+  kScheduling,
+  kDownloading,
+  kDeviceRead,
+  kDeviceErase,
+  kWaitingForAuth,
+  kDeviceBusy,
+  kShutdown,
+};
+
+static constexpr auto FwupdStatusStringMap =
+    base::MakeFixedFlatMap<FwupdStatus, const char*>(
+        {{FwupdStatus::kUnknown, "Unknown state"},
+         {FwupdStatus::kIdle, "Idle state"},
+         {FwupdStatus::kLoading, "Loading a resource"},
+         {FwupdStatus::kDecompressing, "Decompressing firmware"},
+         {FwupdStatus::kDeviceRestart, "Restarting the device"},
+         {FwupdStatus::kDeviceWrite, "Writing to a device"},
+         {FwupdStatus::kDeviceVerify, "Verifying (reading) a device"},
+         {FwupdStatus::kScheduling, "Scheduling an offline update"},
+         {FwupdStatus::kDownloading, "A file is downloading"},
+         {FwupdStatus::kDeviceRead, "Reading from a device"},
+         {FwupdStatus::kDeviceErase, "Erasing a device"},
+         {FwupdStatus::kWaitingForAuth, "Waiting for authentication"},
+         {FwupdStatus::kDeviceBusy, "The device is busy"},
+         {FwupdStatus::kShutdown, "The daemon is shutting down"}});
+
+const char* GetFwupdStatusString(FwupdStatus enum_val) {
+  DCHECK(base::Contains(FwupdStatusStringMap, enum_val));
+  return FwupdStatusStringMap.at(enum_val);
+}
 
 const char kBaseRootPath[] = "firmware-updates";
 const char kCachePath[] = "cache";
@@ -81,6 +122,7 @@
   update->device_description = base::UTF8ToUTF16(update_details.description);
   update->priority =
       firmware_update::mojom::UpdatePriority(update_details.priority);
+  update->filepath = update_details.filepath;
   return update;
 }
 
@@ -125,6 +167,30 @@
     return -1;
 }
 
+// TODO(michaelcheco): Determine if more granular states are needed.
+firmware_update::mojom::UpdateState GetUpdateState(FwupdStatus fwupd_status) {
+  switch (fwupd_status) {
+    case FwupdStatus::kUnknown:
+      return firmware_update::mojom::UpdateState::kUnknown;
+    case FwupdStatus::kIdle:
+    case FwupdStatus::kLoading:
+    case FwupdStatus::kDecompressing:
+    case FwupdStatus::kDeviceVerify:
+    case FwupdStatus::kScheduling:
+    case FwupdStatus::kDownloading:
+    case FwupdStatus::kDeviceRead:
+    case FwupdStatus::kDeviceErase:
+    case FwupdStatus::kWaitingForAuth:
+    case FwupdStatus::kDeviceBusy:
+    case FwupdStatus::kShutdown:
+      return firmware_update::mojom::UpdateState::kIdle;
+    case FwupdStatus::kDeviceRestart:
+      return firmware_update::mojom::UpdateState::kRestarting;
+    case FwupdStatus::kDeviceWrite:
+      return firmware_update::mojom::UpdateState::kUpdating;
+  }
+}
+
 }  // namespace
 
 FirmwareUpdateManager::FirmwareUpdateManager()
@@ -163,12 +229,30 @@
 void FirmwareUpdateManager::ObservePeripheralUpdates(
     mojo::PendingRemote<firmware_update::mojom::UpdateObserver> observer) {
   update_list_observers_.Add(std::move(observer));
-
-  if (HasPendingUpdates()) {
-    NotifyUpdateListObservers();
+  if (!HasPendingUpdates()) {
+    RequestAllUpdates();
   }
 }
 
+// TODO(michaelcheco): Handle the case where the app is closed during an
+// install.
+void FirmwareUpdateManager::ResetInstallState() {
+  install_controller_receiver_.reset();
+  update_progress_observer_.reset();
+}
+
+void FirmwareUpdateManager::PrepareForUpdate(
+    const std::string& device_id,
+    PrepareForUpdateCallback callback) {
+  DCHECK(!device_id.empty());
+  inflight_update_id_ = device_id;
+  mojo::PendingRemote<firmware_update::mojom::InstallController>
+      pending_remote = install_controller_receiver_.BindNewPipeAndPassRemote();
+  install_controller_receiver_.set_disconnect_handler(base::BindOnce(
+      &FirmwareUpdateManager::ResetInstallState, base::Unretained(this)));
+  std::move(callback).Run(std::move(pending_remote));
+}
+
 // Query all updates for all devices.
 void FirmwareUpdateManager::RequestAllUpdates() {
   DCHECK(!HasPendingUpdates());
@@ -360,7 +444,13 @@
 }
 
 void FirmwareUpdateManager::OnInstallResponse(bool success) {
-  ++on_install_update_response_count_for_testing_;
+  auto state = success ? firmware_update::mojom::UpdateState::kSuccess
+                       : firmware_update::mojom::UpdateState::kFailed;
+  auto update = ash::firmware_update::mojom::InstallationProgress::New(
+      /**percentage=*/100, state);
+  update_progress_observer_->OnStatusChanged(std::move(update));
+  ResetInstallState();
+  inflight_update_id_.clear();
 }
 
 void FirmwareUpdateManager::BindInterface(
@@ -375,4 +465,32 @@
   receiver_.Bind(std::move(pending_receiver));
 }
 
+void FirmwareUpdateManager::OnPropertiesChangedResponse(
+    chromeos::FwupdProperties* properties) {
+  if (!properties || !update_progress_observer_.is_bound()) {
+    return;
+  }
+  const auto status = FwupdStatus(properties->status.value());
+  const auto percentage = properties->percentage.value();
+  VLOG(1) << "fwupd: OnPropertiesChangedResponse called with Status: "
+          << GetFwupdStatusString(static_cast<FwupdStatus>(status))
+          << " | Percentage: " << percentage;
+  update_progress_observer_->OnStatusChanged(
+      ash::firmware_update::mojom::InstallationProgress::New(
+          percentage, GetUpdateState(status)));
+}
+
+void FirmwareUpdateManager::BeginUpdate() {
+  DCHECK(!inflight_update_id_.empty());
+  // TODO(michaelcheco): Update |StartInstall| to accept the filename.
+  StartInstall(inflight_update_id_, /**release=*/0,
+               /**callback=*/base::DoNothing());
+}
+
+void FirmwareUpdateManager::AddObserver(
+    mojo::PendingRemote<firmware_update::mojom::UpdateProgressObserver>
+        observer) {
+  update_progress_observer_.reset();
+  update_progress_observer_.Bind(std::move(observer));
+}
 }  // namespace ash
diff --git a/ash/components/fwupd/firmware_update_manager.h b/ash/components/fwupd/firmware_update_manager.h
index 9c397e50..5f26edc 100644
--- a/ash/components/fwupd/firmware_update_manager.h
+++ b/ash/components/fwupd/firmware_update_manager.h
@@ -33,7 +33,8 @@
 // FirmwareUpdateManager contains all logic that runs the firmware update SWA.
 class COMPONENT_EXPORT(ASH_FIRMWARE_UPDATE_MANAGER) FirmwareUpdateManager
     : public chromeos::FwupdClient::Observer,
-      public firmware_update::mojom::UpdateProvider {
+      public firmware_update::mojom::UpdateProvider,
+      public firmware_update::mojom::InstallController {
  public:
   FirmwareUpdateManager();
   FirmwareUpdateManager(const FirmwareUpdateManager&) = delete;
@@ -45,6 +46,16 @@
       mojo::PendingRemote<firmware_update::mojom::UpdateObserver> observer)
       override;
 
+  void PrepareForUpdate(const std::string& device_id,
+                        PrepareForUpdateCallback callback) override;
+
+  // firmware_update::mojom::InstallController
+  void BeginUpdate() override;
+
+  void AddObserver(
+      mojo::PendingRemote<firmware_update::mojom::UpdateProgressObserver>
+          observer) override;
+
   // Gets the global instance pointer.
   static FirmwareUpdateManager* Get();
 
@@ -61,7 +72,7 @@
   // TODO(jimmyxgong): Implement this function to send property updates via
   // mojo.
   void OnPropertiesChangedResponse(
-      chromeos::FwupdProperties* properties) override {}
+      chromeos::FwupdProperties* properties) override;
 
   // Query all updates for all devices.
   void RequestAllUpdates();
@@ -82,7 +93,6 @@
   // TODO(swifton): Replace with mock observers.
   int on_device_list_response_count_for_testing_ = 0;
   int on_update_list_response_count_for_testing_ = 0;
-  int on_install_update_response_count_for_testing_ = 0;
 
  private:
   friend class FirmwareUpdateManagerTest;
@@ -124,6 +134,10 @@
     fake_url_for_testing_ = fake_url;
   }
 
+  // Resets the mojo::Receiver |install_controller_receiver_|
+  // and |update_progress_observer_|.
+  void ResetInstallState();
+
   // Map of a device ID to `FwupdDevice` which is waiting for the list
   // of updates.
   base::flat_map<std::string, chromeos::FwupdDevice> devices_pending_update_;
@@ -135,15 +149,26 @@
   // Only used for testing if StartInstall() queries to a fake URL.
   std::string fake_url_for_testing_;
 
+  // The device ID of the device that is currently being updated.
+  std::string inflight_update_id_;
+
   // Remotes for tracking observers that will be notified of changes to the
   // list of firmware updates.
   mojo::RemoteSet<firmware_update::mojom::UpdateObserver>
       update_list_observers_;
 
+  // Remote for tracking observer that will be notified of changes to
+  // the in-progress update.
+  mojo::Remote<firmware_update::mojom::UpdateProgressObserver>
+      update_progress_observer_;
+
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
   mojo::Receiver<firmware_update::mojom::UpdateProvider> receiver_{this};
 
+  mojo::Receiver<firmware_update::mojom::InstallController>
+      install_controller_receiver_{this};
+
   base::WeakPtrFactory<FirmwareUpdateManager> weak_ptr_factory_{this};
 };
 
diff --git a/ash/components/fwupd/firmware_update_manager_unittest.cc b/ash/components/fwupd/firmware_update_manager_unittest.cc
index bad84e5..1bf29f72c 100644
--- a/ash/components/fwupd/firmware_update_manager_unittest.cc
+++ b/ash/components/fwupd/firmware_update_manager_unittest.cc
@@ -11,6 +11,8 @@
 
 #include "ash/components/fwupd/fake_fwupd_download_client.h"
 #include "ash/constants/ash_features.h"
+#include "ash/webui/firmware_update_ui/mojom/firmware_update.mojom-test-utils.h"
+#include "ash/webui/firmware_update_ui/mojom/firmware_update.mojom.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -37,12 +39,16 @@
     "This is a fake update for testing.";
 const uint32_t kFakeUpdatePriorityForTesting = 1;
 const char kFakeUpdateVersionForTesting[] = "1.0.0";
+const char kFakeUpdateUriForTesting[] =
+    "file:///usr/share/fwupd/remotes.d/vendor/firmware/testFirmwarePath-V1.cab";
+const char kFakeUpdateFileNameForTesting[] = "testFirmwarePath-V1.cab";
 const char kFwupdServiceName[] = "org.freedesktop.fwupd";
 const char kFwupdServicePath[] = "/";
 const char kDescriptionKey[] = "Description";
 const char kIdKey[] = "DeviceId";
 const char kNameKey[] = "Name";
 const char kPriorityKey[] = "Urgency";
+const char kUriKey[] = "Uri";
 const char kVersionKey[] = "Version";
 const char kDownloadDir[] = "firmware-updates";
 const char kCacheDir[] = "cache";
@@ -76,6 +82,30 @@
   mojo::Receiver<ash::firmware_update::mojom::UpdateObserver> receiver_{this};
 };
 
+class FakeUpdateProgressObserver
+    : public ash::firmware_update::mojom::UpdateProgressObserver {
+ public:
+  void OnStatusChanged(
+      ash::firmware_update::mojom::InstallationProgressPtr update) override {
+    update_ = std::move(update);
+  }
+
+  mojo::PendingRemote<ash::firmware_update::mojom::UpdateProgressObserver>
+  pending_remote() {
+    return receiver_.BindNewPipeAndPassRemote();
+  }
+
+  const ash::firmware_update::mojom::InstallationProgressPtr& GetLatestUpdate()
+      const {
+    return update_;
+  }
+
+ private:
+  ash::firmware_update::mojom::InstallationProgressPtr update_;
+  mojo::Receiver<ash::firmware_update::mojom::UpdateProgressObserver> receiver_{
+      this};
+};
+
 }  // namespace
 
 using chromeos::FwupdClient;
@@ -110,6 +140,8 @@
     dbus_client_->InitForTesting(bus_.get());
     fake_fwupd_download_client_ = std::make_unique<FakeFwupdDownloadClient>();
     firmware_update_manager_ = std::make_unique<FirmwareUpdateManager>();
+    firmware_update_manager_->BindInterface(
+        update_provider_remote_.BindNewPipeAndPassReceiver());
   }
   FirmwareUpdateManagerTest(const FirmwareUpdateManagerTest&) = delete;
   FirmwareUpdateManagerTest& operator=(const FirmwareUpdateManagerTest&) =
@@ -263,6 +295,11 @@
     dict_writer.AppendVariantOfUint32(kFakeUpdatePriorityForTesting);
     device_array_writer.CloseContainer(&dict_writer);
 
+    device_array_writer.OpenDictEntry(&dict_writer);
+    dict_writer.AppendString(kUriKey);
+    dict_writer.AppendVariantOfString(kFakeUpdateUriForTesting);
+    device_array_writer.CloseContainer(&dict_writer);
+
     response_array_writer.CloseContainer(&device_array_writer);
     response_writer.CloseContainer(&response_array_writer);
 
@@ -294,11 +331,6 @@
     return response;
   }
 
-  int GetOnInstallResponseCallbackCallCountForTesting() {
-    return firmware_update_manager_
-        ->on_install_update_response_count_for_testing_;
-  }
-
   void SetupObserver(FakeUpdateObserver* observer) {
     firmware_update_manager_->ObservePeripheralUpdates(
         observer->pending_remote());
@@ -309,10 +341,38 @@
     return fake_fwupd_download_client_->test_url_loader_factory();
   }
 
+  void SetupProgressObserver(FakeUpdateProgressObserver* observer) {
+    install_controller_remote_->AddObserver(observer->pending_remote());
+    base::RunLoop().RunUntilIdle();
+  }
+
+  bool PrepareForUpdate(const std::string& device_id) {
+    mojo::PendingRemote<ash::firmware_update::mojom::InstallController>
+        pending_remote;
+    ash::firmware_update::mojom::UpdateProviderAsyncWaiter(
+        update_provider_remote_.get())
+        .PrepareForUpdate(device_id, &pending_remote);
+    if (!pending_remote.is_valid())
+      return false;
+
+    install_controller_remote_.Bind(std::move(pending_remote));
+    base::RunLoop().RunUntilIdle();
+    return true;
+  }
+
+  void SetProperties(uint32_t percentage, uint32_t status) {
+    dbus_client_->SetPropertiesForTesting(percentage, status);
+    base::RunLoop().RunUntilIdle();
+  }
+
   // `FwupdClient` must be be before `FirmwareUpdateManager`.
   std::unique_ptr<FwupdClient> dbus_client_;
   std::unique_ptr<FakeFwupdDownloadClient> fake_fwupd_download_client_;
   std::unique_ptr<FirmwareUpdateManager> firmware_update_manager_;
+  mojo::Remote<ash::firmware_update::mojom::UpdateProvider>
+      update_provider_remote_;
+  mojo::Remote<ash::firmware_update::mojom::InstallController>
+      install_controller_remote_;
 
   // Mock bus for simulating calls.
   scoped_refptr<dbus::MockBus> bus_;
@@ -334,7 +394,6 @@
       .WillRepeatedly(Invoke(this, &FirmwareUpdateManagerTest::OnMethodCalled));
 
   dbus_responses_.push_back(CreateEmptyDeviceResponse());
-  firmware_update_manager_->RequestAllUpdates();
   FakeUpdateObserver update_observer;
   SetupObserver(&update_observer);
   const std::vector<firmware_update::mojom::FirmwareUpdatePtr>& updates =
@@ -349,7 +408,6 @@
 
   dbus_responses_.push_back(CreateOneDeviceResponse());
   dbus_responses_.push_back(CreateNoUpdateResponse());
-  firmware_update_manager_->RequestAllUpdates();
   FakeUpdateObserver update_observer;
   SetupObserver(&update_observer);
   const std::vector<firmware_update::mojom::FirmwareUpdatePtr>& updates =
@@ -365,7 +423,6 @@
 
   dbus_responses_.push_back(CreateOneDeviceResponse());
   dbus_responses_.push_back(CreateOneUpdateResponse());
-  firmware_update_manager_->RequestAllUpdates();
   FakeUpdateObserver update_observer;
   SetupObserver(&update_observer);
   const std::vector<firmware_update::mojom::FirmwareUpdatePtr>& updates =
@@ -382,6 +439,7 @@
   EXPECT_EQ(ash::firmware_update::mojom::UpdatePriority(
                 kFakeUpdatePriorityForTesting),
             updates[0]->priority);
+  EXPECT_EQ(kFakeUpdateFileNameForTesting, updates[0]->filepath.value());
 }
 
 TEST_F(FirmwareUpdateManagerTest, RequestAllUpdatesTwoDeviceOneWithUpdate) {
@@ -391,7 +449,6 @@
   dbus_responses_.push_back(CreateTwoDeviceResponse());
   dbus_responses_.push_back(CreateNoUpdateResponse());
   dbus_responses_.push_back(CreateOneUpdateResponse());
-  firmware_update_manager_->RequestAllUpdates();
   FakeUpdateObserver update_observer;
   SetupObserver(&update_observer);
   base::RunLoop().RunUntilIdle();
@@ -425,7 +482,9 @@
   SetFakeUrlForTesting(fake_url);
   GetTestUrlLoaderFactory().AddResponse(fake_url, "");
 
-  EXPECT_EQ(0, GetOnInstallResponseCallbackCallCountForTesting());
+  EXPECT_TRUE(PrepareForUpdate(std::string(kFakeDeviceIdForTesting)));
+  FakeUpdateProgressObserver update_progress_observer;
+  SetupProgressObserver(&update_progress_observer);
   StartInstall(std::string(kFakeDeviceIdForTesting), /*release=*/0);
 
   base::RunLoop().RunUntilIdle();
@@ -441,7 +500,36 @@
   // Check that that expected patch file was created.
   EXPECT_TRUE(base::PathExists(full_path));
 
-  EXPECT_EQ(1, GetOnInstallResponseCallbackCallCountForTesting());
+  EXPECT_EQ(ash::firmware_update::mojom::UpdateState::kSuccess,
+            update_progress_observer.GetLatestUpdate()->state);
+}
+
+TEST_F(FirmwareUpdateManagerTest, OnPropertiesChangedResponse) {
+  EXPECT_TRUE(PrepareForUpdate(std::string(kFakeDeviceIdForTesting)));
+  FakeUpdateProgressObserver update_progress_observer;
+  SetupProgressObserver(&update_progress_observer);
+
+  // Initial state.
+  SetProperties(/**percentage=*/0u, /**status=*/0u);
+  EXPECT_EQ(ash::firmware_update::mojom::UpdateState::kUnknown,
+            update_progress_observer.GetLatestUpdate()->state);
+  EXPECT_EQ(0u, update_progress_observer.GetLatestUpdate()->percentage);
+  // Install in progress.
+  SetProperties(/**percentage=*/1u, /**status=*/5u);
+  EXPECT_EQ(ash::firmware_update::mojom::UpdateState::kUpdating,
+            update_progress_observer.GetLatestUpdate()->state);
+  EXPECT_EQ(1u, update_progress_observer.GetLatestUpdate()->percentage);
+  // Device restarting
+  SetProperties(/**percentage=*/100u, /**status=*/4u);
+  EXPECT_EQ(ash::firmware_update::mojom::UpdateState::kRestarting,
+            update_progress_observer.GetLatestUpdate()->state);
+  EXPECT_EQ(100u, update_progress_observer.GetLatestUpdate()->percentage);
+
+  // Emitted once install is completed and device has been restarted.
+  SetProperties(/**percentage=*/100u, /**status=*/0u);
+  EXPECT_EQ(ash::firmware_update::mojom::UpdateState::kUnknown,
+            update_progress_observer.GetLatestUpdate()->state);
+  EXPECT_EQ(100u, update_progress_observer.GetLatestUpdate()->percentage);
 }
 
 }  // namespace ash
diff --git a/ash/components/proximity_auth/messenger_impl.cc b/ash/components/proximity_auth/messenger_impl.cc
index 4dd83f9..32f1b05d 100644
--- a/ash/components/proximity_auth/messenger_impl.cc
+++ b/ash/components/proximity_auth/messenger_impl.cc
@@ -50,9 +50,8 @@
 // wrapper that should only be called when the |message| is known to specify its
 // message type, i.e. this should not be called for untrusted input.
 std::string GetMessageType(const base::DictionaryValue& message) {
-  std::string type;
-  message.GetString(kTypeKey, &type);
-  return type;
+  const std::string* type = message.FindStringKey(kTypeKey);
+  return type ? *type : std::string();
 }
 
 }  // namespace
@@ -78,8 +77,8 @@
 
 void MessengerImpl::DispatchUnlockEvent() {
   base::DictionaryValue message;
-  message.SetString(kTypeKey, kMessageTypeLocalEvent);
-  message.SetString(kNameKey, kUnlockEventName);
+  message.SetStringKey(kTypeKey, kMessageTypeLocalEvent);
+  message.SetStringKey(kNameKey, kUnlockEventName);
   queued_messages_.push_back(PendingMessage(message));
   ProcessMessageQueue();
 }
@@ -92,15 +91,15 @@
                         &encrypted_message_data_base64);
 
   base::DictionaryValue message;
-  message.SetString(kTypeKey, kMessageTypeDecryptRequest);
-  message.SetString(kEncryptedDataKey, encrypted_message_data_base64);
+  message.SetStringKey(kTypeKey, kMessageTypeDecryptRequest);
+  message.SetStringKey(kEncryptedDataKey, encrypted_message_data_base64);
   queued_messages_.push_back(PendingMessage(message));
   ProcessMessageQueue();
 }
 
 void MessengerImpl::RequestUnlock() {
   base::DictionaryValue message;
-  message.SetString(kTypeKey, kMessageTypeUnlockRequest);
+  message.SetStringKey(kTypeKey, kMessageTypeUnlockRequest);
   queued_messages_.push_back(PendingMessage(message));
   ProcessMessageQueue();
 }
@@ -155,12 +154,12 @@
 
 void MessengerImpl::HandleDecryptResponseMessage(
     const base::DictionaryValue& message) {
-  std::string base64_data;
+  const std::string* base64_data = message.FindStringKey(kDataKey);
   std::string decrypted_data;
-  if (!message.GetString(kDataKey, &base64_data) || base64_data.empty()) {
+  if (!base64_data || base64_data->empty()) {
     PA_LOG(ERROR) << "Decrypt response missing '" << kDataKey << "' value.";
   } else if (!base::Base64UrlDecode(
-                 base64_data, base::Base64UrlDecodePolicy::REQUIRE_PADDING,
+                 *base64_data, base::Base64UrlDecodePolicy::REQUIRE_PADDING,
                  &decrypted_data)) {
     PA_LOG(ERROR) << "Unable to base64-decode decrypt response.";
   }
@@ -197,15 +196,15 @@
   bool success = message_value->GetAsDictionary(&message_dictionary);
   DCHECK(success);
 
-  std::string type;
-  if (!message_dictionary->GetString(kTypeKey, &type)) {
+  const std::string* type = message_dictionary->FindStringKey(kTypeKey);
+  if (!type) {
     PA_LOG(ERROR) << "Missing '" << kTypeKey << "' key in message:\n "
                   << message;
     return;
   }
 
   // Remote status updates can be received out of the blue.
-  if (type == kMessageTypeRemoteStatusUpdate) {
+  if (*type == kMessageTypeRemoteStatusUpdate) {
     HandleRemoteStatusUpdateMessage(*message_dictionary);
     return;
   }
@@ -225,16 +224,16 @@
   else
     NOTREACHED();  // There are no other message types that expect a response.
 
-  if (type != expected_type) {
+  if (*type != expected_type) {
     PA_LOG(ERROR) << "Unexpected '" << kTypeKey << "' value in message. "
-                  << "Expected '" << expected_type << "' but received '" << type
-                  << "'.";
+                  << "Expected '" << expected_type << "' but received '"
+                  << *type << "'.";
     return;
   }
 
-  if (type == kMessageTypeDecryptResponse)
+  if (*type == kMessageTypeDecryptResponse)
     HandleDecryptResponseMessage(*message_dictionary);
-  else if (type == kMessageTypeUnlockResponse)
+  else if (*type == kMessageTypeUnlockResponse)
     HandleUnlockResponseMessage(*message_dictionary);
   else
     NOTREACHED();  // There are no other message types that expect a response.
diff --git a/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc b/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc
index c9fb6a7..2f51340 100644
--- a/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc
+++ b/ash/components/proximity_auth/proximity_auth_local_state_pref_manager.cc
@@ -149,8 +149,8 @@
 
 void ProximityAuthLocalStatePrefManager::SetHasShownLoginDisabledMessage(
     bool has_shown) {
-  DictionaryPrefUpdate update(local_state_,
-                              prefs::kEasyUnlockLocalStateUserPrefs);
+  DictionaryPrefUpdateDeprecated update(local_state_,
+                                        prefs::kEasyUnlockLocalStateUserPrefs);
 
   base::DictionaryValue* current_user_prefs;
   update.Get()->GetDictionaryWithoutPathExpansion(active_user_.GetUserEmail(),
diff --git a/ash/components/proximity_auth/proximity_auth_local_state_pref_manager_unittest.cc b/ash/components/proximity_auth/proximity_auth_local_state_pref_manager_unittest.cc
index 2fafe62..8d91c763 100644
--- a/ash/components/proximity_auth/proximity_auth_local_state_pref_manager_unittest.cc
+++ b/ash/components/proximity_auth/proximity_auth_local_state_pref_manager_unittest.cc
@@ -70,8 +70,8 @@
                         base::Value(kIsEasyUnlockEnabled1));
     user1_prefs->SetKey(prefs::kSmartLockEligiblePrefName,
                         base::Value(kIsSmartLockEligible1));
-    DictionaryPrefUpdate update1(&local_state_,
-                                 prefs::kEasyUnlockLocalStateUserPrefs);
+    DictionaryPrefUpdateDeprecated update1(
+        &local_state_, prefs::kEasyUnlockLocalStateUserPrefs);
     update1->SetKey(user1_.GetUserEmail(),
                     base::Value::FromUniquePtrValue(std::move(user1_prefs)));
 
@@ -86,8 +86,8 @@
                         base::Value(kIsEasyUnlockEnabled2));
     user2_prefs->SetKey(prefs::kSmartLockEligiblePrefName,
                         base::Value(kIsSmartLockEligible2));
-    DictionaryPrefUpdate update2(&local_state_,
-                                 prefs::kEasyUnlockLocalStateUserPrefs);
+    DictionaryPrefUpdateDeprecated update2(
+        &local_state_, prefs::kEasyUnlockLocalStateUserPrefs);
     update2->SetKey(user2_.GetUserEmail(),
                     base::Value::FromUniquePtrValue(std::move(user2_prefs)));
   }
diff --git a/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc b/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc
index 62cf3bb..9d5a8bb 100644
--- a/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc
+++ b/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc
@@ -109,8 +109,8 @@
   user_prefs_dict->SetKey(prefs::kProximityAuthHasShownLoginDisabledMessage,
                           base::Value(has_shown_login_disabled_message));
 
-  DictionaryPrefUpdate update(local_state_,
-                              prefs::kEasyUnlockLocalStateUserPrefs);
+  DictionaryPrefUpdateDeprecated update(local_state_,
+                                        prefs::kEasyUnlockLocalStateUserPrefs);
   update->SetKey(account_id_.GetUserEmail(),
                  base::Value::FromUniquePtrValue(std::move(user_prefs_dict)));
 }
diff --git a/ash/components/proximity_auth/remote_status_update.cc b/ash/components/proximity_auth/remote_status_update.cc
index 28e7675..628397e 100644
--- a/ash/components/proximity_auth/remote_status_update.cc
+++ b/ash/components/proximity_auth/remote_status_update.cc
@@ -40,19 +40,21 @@
 // static
 std::unique_ptr<RemoteStatusUpdate> RemoteStatusUpdate::Deserialize(
     const base::DictionaryValue& serialized_value) {
-  std::string type;
-  if (!serialized_value.GetString(kType, &type) || type != kStatusUpdateType) {
+  const std::string* type = serialized_value.FindStringKey(kType);
+  if (!type || *type != kStatusUpdateType) {
     PA_LOG(ERROR) << "Unable to parse remote status update: unexpected type. "
                   << "Expected: '" << kStatusUpdateType << "', "
-                  << "Saw: '" << type << "'.";
+                  << "Saw: '" << *type << "'.";
     return nullptr;
   }
 
-  std::string user_presence, secure_screen_lock_state, trust_agent_state;
-  if (!serialized_value.GetString(kUserPresence, &user_presence) ||
-      !serialized_value.GetString(kSecureScreenLock,
-                                  &secure_screen_lock_state) ||
-      !serialized_value.GetString(kTrustAgent, &trust_agent_state)) {
+  const std::string* user_presence =
+      serialized_value.FindStringKey(kUserPresence);
+  const std::string* secure_screen_lock_state =
+      serialized_value.FindStringKey(kSecureScreenLock);
+  const std::string* trust_agent_state =
+      serialized_value.FindStringKey(kTrustAgent);
+  if (!user_presence || !secure_screen_lock_state || !trust_agent_state) {
     PA_LOG(ERROR) << "Unable to parse remote status update: missing data value."
                   << " Status update:\n"
                   << serialized_value;
@@ -60,44 +62,45 @@
   }
 
   std::unique_ptr<RemoteStatusUpdate> parsed_update(new RemoteStatusUpdate);
-  if (user_presence == kUserPresent) {
+  if (*user_presence == kUserPresent) {
     parsed_update->user_presence = USER_PRESENT;
-  } else if (user_presence == kUserAbsent) {
+  } else if (*user_presence == kUserAbsent) {
     parsed_update->user_presence = USER_ABSENT;
-  } else if (user_presence == kUserPresenceUnknown) {
+  } else if (*user_presence == kUserPresenceUnknown) {
     parsed_update->user_presence = USER_PRESENCE_UNKNOWN;
-  } else if (user_presence == kUserPresenceSecondary) {
+  } else if (*user_presence == kUserPresenceSecondary) {
     parsed_update->user_presence = USER_PRESENCE_SECONDARY;
-  } else if (user_presence == kUserPresenceBackground) {
+  } else if (*user_presence == kUserPresenceBackground) {
     parsed_update->user_presence = USER_PRESENCE_BACKGROUND;
   } else {
     PA_LOG(ERROR)
         << "Unable to parse remote status update: invalid user presence: '"
-        << user_presence << "'.";
+        << *user_presence << "'.";
     return nullptr;
   }
 
-  if (secure_screen_lock_state == kSecureScreenLockEnabled) {
+  if (*secure_screen_lock_state == kSecureScreenLockEnabled) {
     parsed_update->secure_screen_lock_state = SECURE_SCREEN_LOCK_ENABLED;
-  } else if (secure_screen_lock_state == kSecureScreenLockDisabled) {
+  } else if (*secure_screen_lock_state == kSecureScreenLockDisabled) {
     parsed_update->secure_screen_lock_state = SECURE_SCREEN_LOCK_DISABLED;
-  } else if (secure_screen_lock_state == kSecureScreenLockStateUnknown) {
+  } else if (*secure_screen_lock_state == kSecureScreenLockStateUnknown) {
     parsed_update->secure_screen_lock_state = SECURE_SCREEN_LOCK_STATE_UNKNOWN;
   } else {
     PA_LOG(ERROR) << "Unable to parse remote status update: invalid secure "
-                  << "screen lock state: '" << secure_screen_lock_state << "'.";
+                  << "screen lock state: '" << *secure_screen_lock_state
+                  << "'.";
     return nullptr;
   }
 
-  if (trust_agent_state == kTrustAgentEnabled) {
+  if (*trust_agent_state == kTrustAgentEnabled) {
     parsed_update->trust_agent_state = TRUST_AGENT_ENABLED;
-  } else if (trust_agent_state == kTrustAgentDisabled) {
+  } else if (*trust_agent_state == kTrustAgentDisabled) {
     parsed_update->trust_agent_state = TRUST_AGENT_DISABLED;
-  } else if (trust_agent_state == kTrustAgentUnsupported) {
+  } else if (*trust_agent_state == kTrustAgentUnsupported) {
     parsed_update->trust_agent_state = TRUST_AGENT_UNSUPPORTED;
   } else {
     PA_LOG(ERROR) << "Unable to parse remote status update: invalid trust "
-                  << "agent state: '" << trust_agent_state << "'.";
+                  << "agent state: '" << *trust_agent_state << "'.";
     return nullptr;
   }
 
diff --git a/ash/components/proximity_auth/screenlock_bridge.cc b/ash/components/proximity_auth/screenlock_bridge.cc
index f10b8fe..dbd7ef85 100644
--- a/ash/components/proximity_auth/screenlock_bridge.cc
+++ b/ash/components/proximity_auth/screenlock_bridge.cc
@@ -60,11 +60,11 @@
 std::unique_ptr<base::DictionaryValue>
 ScreenlockBridge::UserPodCustomIconInfo::ToDictionaryValue() const {
   auto result = std::make_unique<base::DictionaryValue>();
-  result->SetString("id", GetIDString());
+  result->SetStringKey("id", GetIDString());
 
   if (!tooltip_.empty()) {
     base::DictionaryValue tooltip_options;
-    tooltip_options.SetString("text", tooltip_);
+    tooltip_options.SetStringKey("text", tooltip_);
     tooltip_options.SetBoolean("autoshow", autoshow_tooltip_);
     result->SetKey("tooltip", std::move(tooltip_options));
   }
diff --git a/ash/components/tether/message_wrapper.cc b/ash/components/tether/message_wrapper.cc
index f4fc97c..2238613 100644
--- a/ash/components/tether/message_wrapper.cc
+++ b/ash/components/tether/message_wrapper.cc
@@ -89,31 +89,30 @@
     return nullptr;
   }
 
-  int message_type;
-  if (!json_dictionary->GetInteger(kJsonTypeKey, &message_type)) {
+  absl::optional<int> message_type = json_dictionary->FindIntKey(kJsonTypeKey);
+  if (!message_type)
     return nullptr;
-  }
 
-  std::string encoded_message;
-  if (!json_dictionary->GetString(kJsonDataKey, &encoded_message)) {
+  const std::string* encoded_message =
+      json_dictionary->FindStringKey(kJsonDataKey);
+  if (!encoded_message)
     return nullptr;
-  }
 
   std::string decoded_message;
-  if (!base::Base64UrlDecode(encoded_message,
+  if (!base::Base64UrlDecode(*encoded_message,
                              base::Base64UrlDecodePolicy::REQUIRE_PADDING,
                              &decoded_message)) {
     return nullptr;
   }
 
   std::unique_ptr<google::protobuf::MessageLite> proto = DecodedMessageToProto(
-      static_cast<MessageType>(message_type), decoded_message);
+      static_cast<MessageType>(*message_type), decoded_message);
   if (!proto) {
     return nullptr;
   }
 
   return base::WrapUnique(new MessageWrapper(
-      static_cast<MessageType>(message_type), std::move(proto)));
+      static_cast<MessageType>(*message_type), std::move(proto)));
 }
 
 MessageWrapper::MessageWrapper(const ConnectTetheringRequest& request)
diff --git a/ash/components/tether/persistent_host_scan_cache_impl.cc b/ash/components/tether/persistent_host_scan_cache_impl.cc
index 233483a7..d5152202 100644
--- a/ash/components/tether/persistent_host_scan_cache_impl.cc
+++ b/ash/components/tether/persistent_host_scan_cache_impl.cc
@@ -31,11 +31,11 @@
   std::unique_ptr<base::DictionaryValue> dictionary =
       std::make_unique<base::DictionaryValue>();
 
-  dictionary->SetString(kTetherNetworkGuidKey, entry.tether_network_guid);
-  dictionary->SetString(kDeviceNameKey, entry.device_name);
-  dictionary->SetString(kCarrierKey, entry.carrier);
-  dictionary->SetInteger(kBatteryPercentageKey, entry.battery_percentage);
-  dictionary->SetInteger(kSignalStrengthKey, entry.signal_strength);
+  dictionary->SetStringKey(kTetherNetworkGuidKey, entry.tether_network_guid);
+  dictionary->SetStringKey(kDeviceNameKey, entry.device_name);
+  dictionary->SetStringKey(kCarrierKey, entry.carrier);
+  dictionary->SetIntKey(kBatteryPercentageKey, entry.battery_percentage);
+  dictionary->SetIntKey(kSignalStrengthKey, entry.signal_strength);
   dictionary->SetBoolean(kSetupRequiredKey, entry.setup_required);
 
   return dictionary;
@@ -45,38 +45,36 @@
     const base::DictionaryValue& dictionary) {
   HostScanCacheEntry::Builder builder;
 
-  std::string tether_network_guid;
-  if (!dictionary.GetString(kTetherNetworkGuidKey, &tether_network_guid) ||
-      tether_network_guid.empty()) {
+  const std::string* tether_network_guid =
+      dictionary.FindStringKey(kTetherNetworkGuidKey);
+  if (!tether_network_guid || tether_network_guid->empty())
     return nullptr;
-  }
-  builder.SetTetherNetworkGuid(tether_network_guid);
+  builder.SetTetherNetworkGuid(*tether_network_guid);
 
-  std::string device_name;
-  if (!dictionary.GetString(kDeviceNameKey, &device_name)) {
+  const std::string* device_name = dictionary.FindStringKey(kDeviceNameKey);
+  if (!device_name)
     return nullptr;
-  }
-  builder.SetDeviceName(device_name);
+  builder.SetDeviceName(*device_name);
 
-  std::string carrier;
-  if (!dictionary.GetString(kCarrierKey, &carrier)) {
+  const std::string* carrier = dictionary.FindStringKey(kCarrierKey);
+  if (!carrier)
     return nullptr;
-  }
-  builder.SetCarrier(carrier);
+  builder.SetCarrier(*carrier);
 
-  int battery_percentage;
-  if (!dictionary.GetInteger(kBatteryPercentageKey, &battery_percentage) ||
-      battery_percentage < 0 || battery_percentage > 100) {
+  absl::optional<int> battery_percentage =
+      dictionary.FindIntKey(kBatteryPercentageKey);
+  if (!battery_percentage || *battery_percentage < 0 ||
+      *battery_percentage > 100) {
     return nullptr;
   }
-  builder.SetBatteryPercentage(battery_percentage);
+  builder.SetBatteryPercentage(*battery_percentage);
 
-  int signal_strength;
-  if (!dictionary.GetInteger(kSignalStrengthKey, &signal_strength) ||
-      signal_strength < 0 || signal_strength > 100) {
+  absl::optional<int> signal_strength =
+      dictionary.FindIntKey(kSignalStrengthKey);
+  if (!signal_strength || *signal_strength < 0 || *signal_strength > 100) {
     return nullptr;
   }
-  builder.SetSignalStrength(signal_strength);
+  builder.SetSignalStrength(*signal_strength);
 
   absl::optional<bool> setup_required =
       dictionary.FindBoolPath(kSetupRequiredKey);
diff --git a/ash/components/trial_group/trial_group_checker.cc b/ash/components/trial_group/trial_group_checker.cc
index e273828..2f11a2e 100644
--- a/ash/components/trial_group/trial_group_checker.cc
+++ b/ash/components/trial_group/trial_group_checker.cc
@@ -82,7 +82,7 @@
   std::string upload_data;
   {
     base::DictionaryValue request;
-    request.SetInteger("group", static_cast<int>(group_id_));
+    request.SetIntKey("group", static_cast<int>(group_id_));
     base::JSONWriter::Write(request, &upload_data);
   }
 
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 8d32c963..813e83fe 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -83,6 +83,14 @@
 constexpr base::FeatureParam<bool> kAmbientModeStreetArtAlbumEnabled{
     &kAmbientModeFeature, "StreetArtAlbumEnabled", false};
 
+// Controls whether to launch the animated screensaver (as opposed to the
+// existing photo slideshow) when entering ambient mode. Currently, there is
+// only one animation theme available ("feel the breeze"), but a FeatureParam
+// will be introduced that allows the user to select a specific theme when more
+// become available.
+const base::Feature kAmbientModeAnimationFeature{
+    "ChromeOSAmbientModeAnimation", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Controls whether to allow Dev channel to use Prod server feature.
 const base::Feature kAmbientModeDevUseProdFeature{
     "ChromeOSAmbientModeDevChannelUseProdServer",
@@ -799,6 +807,10 @@
 const base::Feature kForceProfileMigrationCompletion{
     "ForceProfileMigrationCompletion", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Emergency switch to turn off profile migration.
+const base::Feature kLacrosProfileMigrationForceOff{
+    "LacrosProfileMigrationForceOff", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enable this to turn on profile migration for non-googlers. Currently the
 // feature is only limited to googlers only.
 const base::Feature kLacrosProfileMigrationForAnyUser{
@@ -1058,6 +1070,10 @@
 const base::Feature kQuickUnlockPinAutosubmitBackfill{
     "QuickUnlockPinAutosubmitBackfill", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables redirect to default IdP without interstitial step.
+const base::Feature kRedirectToDefaultIdP{"RedirectToDefaultIdP",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables suppression of Displays notifications other than resolution change.
 const base::Feature kReduceDisplayNotifications{
     "ReduceDisplayNotifications", base::FEATURE_ENABLED_BY_DEFAULT};
@@ -1800,6 +1816,10 @@
   return base::FeatureList::IsEnabled(kQuickSettingsNetworkRevamp);
 }
 
+bool IsRedirectToDefaultIdPEnabled() {
+  return base::FeatureList::IsEnabled(kRedirectToDefaultIdP);
+}
+
 bool IsReduceDisplayNotificationsEnabled() {
   return base::FeatureList::IsEnabled(kReduceDisplayNotifications);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index ac93273..e38b43b6d 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -49,6 +49,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::FeatureParam<bool> kAmbientModeStreetArtAlbumEnabled;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kAmbientModeAnimationFeature;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kAmbientModeDevUseProdFeature;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kAmbientModePhotoPreviewFeature;
@@ -303,6 +305,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kLacrosProfileMigrationForAnyUser;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kLacrosProfileMigrationForceOff;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kLanguagePacksHandwriting;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kLanguageSettingsUpdate2;
@@ -405,6 +409,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kQuickUnlockPinAutosubmitBackfill;
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kRedirectToDefaultIdP;
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kReduceDisplayNotifications;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kReleaseNotesNotificationAllChannels;
@@ -625,6 +631,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsQuickDimEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsQuickSettingsNetworkRevampEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsRedirectToDefaultIdPEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsReduceDisplayNotificationsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsReverseScrollGesturesEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
diff --git a/ash/detachable_base/detachable_base_handler.cc b/ash/detachable_base/detachable_base_handler.cc
index efa333b..555ad78 100644
--- a/ash/detachable_base/detachable_base_handler.cc
+++ b/ash/detachable_base/detachable_base_handler.cc
@@ -79,7 +79,8 @@
   last_used_devices_.erase(user.account_id);
 
   if (local_state_) {
-    DictionaryPrefUpdate update(local_state_, prefs::kDetachableBaseDevices);
+    DictionaryPrefUpdateDeprecated update(local_state_,
+                                          prefs::kDetachableBaseDevices);
     update->RemoveKey(GetKeyForPrefs(user.account_id));
   }
 }
@@ -127,7 +128,8 @@
   last_used_devices_[user.account_id] = authenticated_base_id_;
 
   if (!user.is_ephemeral) {
-    DictionaryPrefUpdate update(local_state_, prefs::kDetachableBaseDevices);
+    DictionaryPrefUpdateDeprecated update(local_state_,
+                                          prefs::kDetachableBaseDevices);
     update->SetPath({GetKeyForPrefs(user.account_id), kLastUsedByUserPrefKey},
                     base::Value(authenticated_base_id_));
   }
diff --git a/ash/display/display_prefs.cc b/ash/display/display_prefs.cc
index 80eb663d..fb576b0 100644
--- a/ash/display/display_prefs.cc
+++ b/ash/display/display_prefs.cc
@@ -87,10 +87,10 @@
 
 void InsetsToValue(const gfx::Insets& insets, base::DictionaryValue* value) {
   DCHECK(value);
-  value->SetInteger(kInsetsTopKey, insets.top());
-  value->SetInteger(kInsetsLeftKey, insets.left());
-  value->SetInteger(kInsetsBottomKey, insets.bottom());
-  value->SetInteger(kInsetsRightKey, insets.right());
+  value->SetIntKey(kInsetsTopKey, insets.top());
+  value->SetIntKey(kInsetsLeftKey, insets.left());
+  value->SetIntKey(kInsetsBottomKey, insets.bottom());
+  value->SetIntKey(kInsetsRightKey, insets.right());
 }
 
 // Unmarshalls the string containing CalibrationPointPairQuad and populates
@@ -129,11 +129,11 @@
   display::TouchCalibrationData::CalibrationPointPairQuad* point_pair_quad =
       &(touch_calibration_data->point_pairs);
 
-  std::string str;
-  if (!value.GetString(kTouchCalibrationPointPairs, &str))
+  const std::string* str = value.FindStringKey(kTouchCalibrationPointPairs);
+  if (!str)
     return false;
 
-  if (!ParseTouchCalibrationStringValue(str, point_pair_quad))
+  if (!ParseTouchCalibrationStringValue(*str, point_pair_quad))
     return false;
 
   absl::optional<int> width = value.FindIntKey(kTouchCalibrationWidth);
@@ -471,7 +471,8 @@
   DCHECK(display::DisplayLayout::Validate(list, display_layout));
   std::string name = display::DisplayIdListToString(list);
 
-  DictionaryPrefUpdate update(pref_service, prefs::kSecondaryDisplays);
+  DictionaryPrefUpdateDeprecated update(pref_service,
+                                        prefs::kSecondaryDisplays);
   base::DictionaryValue* pref_data = update.Get();
   base::Value layout_value(base::Value::Type::DICTIONARY);
   if (pref_data->HasKey(name)) {
@@ -509,7 +510,8 @@
 void StoreCurrentDisplayProperties(PrefService* pref_service) {
   display::DisplayManager* display_manager = GetDisplayManager();
 
-  DictionaryPrefUpdate update(pref_service, prefs::kDisplayProperties);
+  DictionaryPrefUpdateDeprecated update(pref_service,
+                                        prefs::kDisplayProperties);
   base::DictionaryValue* pref_data = update.Get();
 
   // Pre-process data related to legacy touch calibration to opitmize lookup.
@@ -547,22 +549,22 @@
         absl::optional<int> original_rotation =
             original_property->FindIntKey("rotation");
         if (original_rotation) {
-          property_value.SetInteger("rotation", *original_rotation);
+          property_value.SetIntKey("rotation", *original_rotation);
         }
       }
     } else {
-      property_value.SetInteger("rotation",
-                                static_cast<int>(info.GetRotation(
-                                    display::Display::RotationSource::USER)));
+      property_value.SetIntKey("rotation",
+                               static_cast<int>(info.GetRotation(
+                                   display::Display::RotationSource::USER)));
     }
 
     display::ManagedDisplayMode mode;
     if (!display.IsInternal() &&
         display_manager->GetSelectedModeForDisplayId(id, &mode) &&
         !mode.native()) {
-      property_value.SetInteger("width", mode.size().width());
-      property_value.SetInteger("height", mode.size().height());
-      property_value.SetInteger(
+      property_value.SetIntKey("width", mode.size().width());
+      property_value.SetIntKey("height", mode.size().height());
+      property_value.SetIntKey(
           "device-scale-factor",
           static_cast<int>(mode.device_scale_factor() * 1000));
 
@@ -632,10 +634,11 @@
 void StoreDisplayRotationPrefs(PrefService* pref_service,
                                display::Display::Rotation rotation,
                                bool rotation_lock) {
-  DictionaryPrefUpdate update(pref_service, prefs::kDisplayRotationLock);
+  DictionaryPrefUpdateDeprecated update(pref_service,
+                                        prefs::kDisplayRotationLock);
   base::DictionaryValue* pref_data = update.Get();
   pref_data->SetBoolean("lock", rotation_lock);
-  pref_data->SetInteger("orientation", static_cast<int>(rotation));
+  pref_data->SetIntKey("orientation", static_cast<int>(rotation));
 }
 
 void StoreCurrentDisplayRotationLockPrefs(PrefService* pref_service) {
@@ -655,7 +658,8 @@
   display::TouchDeviceManager* touch_device_manager =
       GetDisplayManager()->touch_device_manager();
 
-  DictionaryPrefUpdate update(pref_service, prefs::kDisplayTouchAssociations);
+  DictionaryPrefUpdateDeprecated update(pref_service,
+                                        prefs::kDisplayTouchAssociations);
   base::DictionaryValue* pref_data = update.Get();
   pref_data->DictClear();
 
@@ -705,8 +709,8 @@
 
   // Store the port mappings. What display a touch device connected to a
   // particular port is associated with.
-  DictionaryPrefUpdate update_port(pref_service,
-                                   prefs::kDisplayTouchPortAssociations);
+  DictionaryPrefUpdateDeprecated update_port(
+      pref_service, prefs::kDisplayTouchPortAssociations);
   pref_data = update_port.Get();
   update_port->DictClear();
 
@@ -731,7 +735,8 @@
 
 // Stores mirror info for each external display.
 void StoreExternalDisplayMirrorInfo(PrefService* pref_service) {
-  ListPrefUpdate update(pref_service, prefs::kExternalDisplayMirrorInfo);
+  ListPrefUpdateDeprecated update(pref_service,
+                                  prefs::kExternalDisplayMirrorInfo);
   base::ListValue* pref_data = update.Get();
   pref_data->ClearList();
   const std::set<int64_t>& external_display_mirror_info =
@@ -745,8 +750,8 @@
 void StoreDisplayMixedMirrorModeParams(
     PrefService* pref_service,
     const absl::optional<display::MixedMirrorModeParams>& mixed_params) {
-  DictionaryPrefUpdate update(pref_service,
-                              prefs::kDisplayMixedMirrorModeParams);
+  DictionaryPrefUpdateDeprecated update(pref_service,
+                                        prefs::kDisplayMixedMirrorModeParams);
   base::DictionaryValue* pref_data = update.Get();
   pref_data->DictClear();
 
@@ -903,7 +908,8 @@
 void DisplayPrefs::StoreLegacyTouchDataForTest(
     int64_t display_id,
     const display::TouchCalibrationData& data) {
-  DictionaryPrefUpdate update(local_state_, prefs::kDisplayProperties);
+  DictionaryPrefUpdateDeprecated update(local_state_,
+                                        prefs::kDisplayProperties);
   base::DictionaryValue* pref_data = update.Get();
   base::DictionaryValue property_value;
   TouchDataToValue(data, &property_value);
diff --git a/ash/display/display_prefs_unittest.cc b/ash/display/display_prefs_unittest.cc
index 31f3c81..bee4329 100644
--- a/ash/display/display_prefs_unittest.cc
+++ b/ash/display/display_prefs_unittest.cc
@@ -153,7 +153,8 @@
       int offset,
       int64_t primary_id) {
     std::string name = display::DisplayIdListToString(list);
-    DictionaryPrefUpdate update(local_state(), prefs::kSecondaryDisplays);
+    DictionaryPrefUpdateDeprecated update(local_state(),
+                                          prefs::kSecondaryDisplays);
     display::DisplayLayout display_layout;
     display_layout.placement_list.emplace_back(position, offset);
     display_layout.primary_id = primary_id;
@@ -174,7 +175,8 @@
                                    std::unique_ptr<base::Value> value) {
     std::string name = display::DisplayIdListToString(list);
 
-    DictionaryPrefUpdate update(local_state(), prefs::kSecondaryDisplays);
+    DictionaryPrefUpdateDeprecated update(local_state(),
+                                          prefs::kSecondaryDisplays);
     base::DictionaryValue* pref_data = update.Get();
 
     base::Value* layout_value = pref_data->FindKey(name);
@@ -202,15 +204,16 @@
   }
 
   void StoreDisplayOverscan(int64_t id, const gfx::Insets& insets) {
-    DictionaryPrefUpdate update(local_state(), prefs::kDisplayProperties);
+    DictionaryPrefUpdateDeprecated update(local_state(),
+                                          prefs::kDisplayProperties);
     const std::string name = base::NumberToString(id);
 
     base::DictionaryValue* pref_data = update.Get();
     base::DictionaryValue insets_value;
-    insets_value.SetInteger("insets_top", insets.top());
-    insets_value.SetInteger("insets_left", insets.left());
-    insets_value.SetInteger("insets_bottom", insets.bottom());
-    insets_value.SetInteger("insets_right", insets.right());
+    insets_value.SetIntKey("insets_top", insets.top());
+    insets_value.SetIntKey("insets_left", insets.left());
+    insets_value.SetIntKey("insets_bottom", insets.bottom());
+    insets_value.SetIntKey("insets_right", insets.right());
     pref_data->SetKey(name, std::move(insets_value));
   }
 
@@ -223,7 +226,8 @@
 
   void StoreExternalDisplayMirrorInfo(
       const std::set<int64_t>& external_display_mirror_info) {
-    ListPrefUpdate update(local_state(), prefs::kExternalDisplayMirrorInfo);
+    ListPrefUpdateDeprecated update(local_state(),
+                                    prefs::kExternalDisplayMirrorInfo);
     base::ListValue* pref_data = update.Get();
     pref_data->ClearList();
     for (const auto& id : external_display_mirror_info)
diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc
index e6d5759..d4f5f06 100644
--- a/ash/drag_drop/drag_drop_controller.cc
+++ b/ash/drag_drop/drag_drop_controller.cc
@@ -231,7 +231,10 @@
     }
   }
 
-  if (should_block_during_drag_drop_) {
+  if (test_loop_closure_) {
+    while (!quit_closure_.is_null())
+      test_loop_closure_.Run();
+  } else {
     base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
     quit_closure_ = run_loop.QuitClosure();
     run_loop.Run();
@@ -289,6 +292,24 @@
   }
 }
 
+void DragDropController::SetLoopClosureForTesting(
+    TestLoopClosure closure,
+    base::OnceClosure quit_closure) {
+  test_loop_closure_ = closure;
+  quit_closure_ = std::move(quit_closure);
+}
+
+void DragDropController::SetDisableNestedLoopForTesting(bool disable) {
+  nested_loop_disabled_for_testing_ = disable;
+  if (disable) {
+    base::OnceClosure quit_closure;
+    SetLoopClosureForTesting(base::DoNothing(), std::move(quit_closure));
+  } else {
+    test_loop_closure_.Reset();
+    quit_closure_.Reset();
+  }
+}
+
 void DragDropController::DragCancel() {
   DCHECK(enabled_);
   DoDragCancel(kCancelAnimationDuration);
@@ -604,7 +625,7 @@
   if (!cancel_animation_)
     drag_image_widget_.reset();
 
-  if (should_block_during_drag_drop_ && quit_closure_)
+  if (quit_closure_)
     std::move(quit_closure_).Run();
 }
 
@@ -621,7 +642,7 @@
     drag_image_widget_.reset();
   if (pending_long_tap_) {
     // If not in a nested run loop, we can forward the long tap right now.
-    if (!should_block_during_drag_drop_) {
+    if (nested_loop_disabled_for_testing_) {
       ForwardPendingLongTap();
     } else {
       // See comment about this in OnGestureEvent().
@@ -651,7 +672,7 @@
   // If the drop is async, then |drag_image_widget_| is already reset.
   if (drag_image_widget_)
     StartCanceledAnimation(drag_cancel_animation_duration);
-  if (should_block_during_drag_drop_ && quit_closure_)
+  if (quit_closure_)
     std::move(quit_closure_).Run();
 }
 
diff --git a/ash/drag_drop/drag_drop_controller.h b/ash/drag_drop/drag_drop_controller.h
index 072b9eb..6a0f379 100644
--- a/ash/drag_drop/drag_drop_controller.h
+++ b/ash/drag_drop/drag_drop_controller.h
@@ -51,10 +51,6 @@
 
   ~DragDropController() override;
 
-  void set_should_block_during_drag_drop(bool should_block_during_drag_drop) {
-    should_block_during_drag_drop_ = should_block_during_drag_drop;
-  }
-
   void set_enabled(bool enabled) { enabled_ = enabled; }
 
   void set_toplevel_window_drag_delegate(ToplevelWindowDragDelegate* delegate) {
@@ -86,6 +82,26 @@
   void SetDragImage(const gfx::ImageSkia& image,
                     const gfx::Vector2d& image_offset);
 
+  // Sets the `closure` that will be executed as a replacement of
+  // inner event loop. A test can use this closure to generate events, or
+  // take other actions that should happen during the drag and drop, and
+  // can also check the condition that should be satisfied.
+  // The loop closure is called with a boolean value that indicates
+  // that this is called from the inner loop because the same closure will
+  // often used to generate the event that will eventually enter the drag
+  // and drop inner loop. The `quit_closure` is used for a test
+  // to exit the outer loop in the test.
+  using TestLoopClosure = base::RepeatingCallback<void()>;
+  void SetLoopClosureForTesting(TestLoopClosure closure,
+                                base::OnceClosure quit_closure);
+
+  void SetDisableNestedLoopForTesting(bool disable);
+
+  // Deprecated: Use `SetDisableNestedLoopForTesting`.
+  void set_should_block_during_drag_drop(bool should_block_during_drag_drop) {
+    SetDisableNestedLoopForTesting(!should_block_during_drag_drop);
+  }
+
  protected:
   // Helper method to create a LinearAnimation object that will run the drag
   // cancel animation. Caller take ownership of the returned object. Protected
@@ -162,9 +178,12 @@
   // Window that started the drag.
   aura::Window* drag_source_window_ = nullptr;
 
-  // Indicates whether the caller should be blocked on a drag/drop session.
-  // Only be used for tests.
-  bool should_block_during_drag_drop_ = true;
+  // A closure that allows a test to implement the actions within
+  // drag and drop event loop.
+  TestLoopClosure test_loop_closure_;
+
+  // True if the nested event loop is disabled.
+  bool nested_loop_disabled_for_testing_ = false;
 
   // Closure for quitting nested run loop.
   base::OnceClosure quit_closure_;
diff --git a/ash/drag_drop/drag_drop_controller_unittest.cc b/ash/drag_drop/drag_drop_controller_unittest.cc
index 9351004..fa50c8a 100644
--- a/ash/drag_drop/drag_drop_controller_unittest.cc
+++ b/ash/drag_drop/drag_drop_controller_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/notreached.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/bind.h"
 #include "base/test/gmock_callback_support.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -540,6 +541,19 @@
   std::unique_ptr<TestNewWindowDelegateProvider>
       test_new_window_delegate_provider_;
   NiceMock<MockNewWindowDelegate>* mock_new_window_delegate_ptr_ = nullptr;
+
+  bool quit_ = false;
+
+  void RunWithClosure(base::RepeatingCallback<void(bool)> loop) {
+    quit_ = false;
+
+    drag_drop_controller_->SetLoopClosureForTesting(
+        base::BindLambdaForTesting([&]() { loop.Run(/*inside=*/true); }),
+        base::BindLambdaForTesting([&]() { quit_ = true; }));
+    while (!quit_) {
+      loop.Run(/*inside=*/false);
+    }
+  }
 };
 
 TEST_F(DragDropControllerTest, DragDropInSingleViewTest) {
@@ -762,6 +776,50 @@
   ui::Clipboard::DestroyClipboardForCurrentThread();
 }
 
+// Cancelling followed by closing window should not cause use after free.
+// This happens when closing a browser window while dragging.
+// crbug.com/1282480
+TEST_F(DragDropControllerTest, DragCanceledThenWindowDestroyedDuringDragDrop) {
+  std::unique_ptr<views::Widget> widget = CreateFramelessWidget();
+  DragTestView* drag_view = new DragTestView;
+  AddViewToWidgetAndResize(widget.get(), drag_view);
+  aura::Window* window = widget->GetNativeView();
+
+  ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
+                                     widget->GetNativeView());
+  generator.PressLeftButton();
+
+  int n = 0;
+  bool dragged = false;
+  auto loop_task = [&](bool inside) {
+    generator.MoveMouseBy(0, 1);
+    base::RunLoop().RunUntilIdle();
+    n++;
+
+    if (inside && window) {
+      dragged = true;
+      EXPECT_EQ(window, GetDragWindow());
+      EXPECT_GT(n, drag_view->VerticalDragThreshold());
+    }
+
+    if (n == 18) {
+      drag_drop_controller_->DragCancel();
+      widget->CloseNow();
+      EXPECT_FALSE(this->GetDragWindow());
+      window = nullptr;
+    }
+  };
+  RunWithClosure(base::BindLambdaForTesting(loop_task));
+  EXPECT_TRUE(dragged);
+  EXPECT_FALSE(GetDragWindow());
+  generator.ReleaseLeftButton();
+
+  EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
+  // Drag must have been canceled.
+  EXPECT_TRUE(drag_drop_controller_->drag_canceled_);
+  EXPECT_FALSE(drag_drop_controller_->drop_received_);
+}
+
 TEST_F(DragDropControllerTest, WindowDestroyedDuringDragDrop) {
   std::unique_ptr<views::Widget> widget = CreateFramelessWidget();
   DragTestView* drag_view = new DragTestView;
@@ -772,27 +830,36 @@
                                      widget->GetNativeView());
   generator.PressLeftButton();
 
-  int num_drags = 17;
-  for (int i = 0; i < num_drags; ++i) {
+  int n = 0;
+  bool dragged = false;
+  auto loop_task = [&](bool inside) {
     generator.MoveMouseBy(0, 1);
+    base::RunLoop().RunUntilIdle();
+    n++;
 
-    if (i > drag_view->VerticalDragThreshold())
+    if (inside && window) {
+      dragged = true;
       EXPECT_EQ(window, GetDragWindow());
-  }
+      EXPECT_GT(n, drag_view->VerticalDragThreshold());
+    }
 
-  widget->CloseNow();
+    if (n == 18) {
+      widget->CloseNow();
+      EXPECT_FALSE(this->GetDragWindow());
+      window = nullptr;
+    }
+  };
+  RunWithClosure(base::BindLambdaForTesting(loop_task));
+  EXPECT_TRUE(dragged);
   EXPECT_FALSE(GetDragWindow());
-
-  num_drags = 23;
-  for (int i = 0; i < num_drags; ++i) {
-    generator.MoveMouseBy(0, 1);
-    // We should not crash here.
-  }
-
+  LOG(ERROR) << "N=" << n;
+  EXPECT_GT(n, 50);
   generator.ReleaseLeftButton();
 
   EXPECT_TRUE(drag_drop_controller_->drag_start_received_);
-  EXPECT_TRUE(drag_drop_controller_->drop_received_);
+  // Drag must have been canceled.
+  EXPECT_TRUE(drag_drop_controller_->drag_canceled_);
+  EXPECT_FALSE(drag_drop_controller_->drop_received_);
 }
 
 TEST_F(DragDropControllerTest, SyntheticEventsDuringDragDrop) {
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 7c83d032..39b12eda 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -2091,7 +2091,10 @@
   CurrentBigUserView()->RequestFocus();
 
   Shell::Get()->login_screen_controller()->OnFocusPod(big_user_account_id);
-  UpdateEasyUnlockIconForUser(big_user_account_id);
+
+  if (!base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp)) {
+    UpdateEasyUnlockIconForUser(big_user_account_id);
+  }
 
   // The new auth user might have different last used detachable base - make
   // sure the detachable base pairing error is updated if needed.
diff --git a/ash/login/ui/login_auth_user_view.cc b/ash/login/ui/login_auth_user_view.cc
index 3ce8092..668e3ba1 100644
--- a/ash/login/ui/login_auth_user_view.cc
+++ b/ash/login/ui/login_auth_user_view.cc
@@ -1388,22 +1388,21 @@
     EasyUnlockIconState icon_state,
     const std::u16string& accessibility_label) {
   if (smart_lock_ui_revamp_enabled_) {
-    DCHECK(smart_lock_auth_factor_model_);
-    smart_lock_auth_factor_model_->SetEasyUnlockIconState(icon_state);
-  } else {
-    password_view_->SetEasyUnlockIcon(icon_state, accessibility_label);
+    return;
+  }
 
-    const std::string& user_display_email =
-        current_user().basic_user_info.display_email;
-    if (icon_state == EasyUnlockIconState::UNLOCKED) {
-      password_view_->SetAccessibleName(l10n_util::GetStringFUTF16(
-          IDS_ASH_LOGIN_POD_AUTH_TAP_PASSWORD_FIELD_ACCESSIBLE_NAME,
-          base::UTF8ToUTF16(user_display_email)));
-    } else {
-      password_view_->SetAccessibleName(l10n_util::GetStringFUTF16(
-          IDS_ASH_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME,
-          base::UTF8ToUTF16(user_display_email)));
-    }
+  password_view_->SetEasyUnlockIcon(icon_state, accessibility_label);
+
+  const std::string& user_display_email =
+      current_user().basic_user_info.display_email;
+  if (icon_state == EasyUnlockIconState::UNLOCKED) {
+    password_view_->SetAccessibleName(l10n_util::GetStringFUTF16(
+        IDS_ASH_LOGIN_POD_AUTH_TAP_PASSWORD_FIELD_ACCESSIBLE_NAME,
+        base::UTF8ToUTF16(user_display_email)));
+  } else {
+    password_view_->SetAccessibleName(l10n_util::GetStringFUTF16(
+        IDS_ASH_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME,
+        base::UTF8ToUTF16(user_display_email)));
   }
 }
 
diff --git a/ash/login/ui/smart_lock_auth_factor_model.cc b/ash/login/ui/smart_lock_auth_factor_model.cc
index e922026..12835e4 100644
--- a/ash/login/ui/smart_lock_auth_factor_model.cc
+++ b/ash/login/ui/smart_lock_auth_factor_model.cc
@@ -26,32 +26,6 @@
   }
 }
 
-void SmartLockAuthFactorModel::SetEasyUnlockIconState(
-    EasyUnlockIconState state) {
-  switch (state) {
-    case EasyUnlockIconState::NONE:
-      [[fallthrough]];
-    case EasyUnlockIconState::HARDLOCKED:
-      SetSmartLockState(SmartLockState::kDisabled);
-      break;
-    case EasyUnlockIconState::LOCKED_WITH_PROXIMITY_HINT:
-      SetSmartLockState(SmartLockState::kPhoneFoundLockedAndDistant);
-      break;
-    case EasyUnlockIconState::LOCKED:
-      SetSmartLockState(SmartLockState::kPhoneNotFound);
-      break;
-    case EasyUnlockIconState::LOCKED_TO_BE_ACTIVATED:
-      SetSmartLockState(SmartLockState::kPhoneFoundLockedAndProximate);
-      break;
-    case EasyUnlockIconState::UNLOCKED:
-      SetSmartLockState(SmartLockState::kPhoneAuthenticated);
-      break;
-    case EasyUnlockIconState::SPINNER:
-      SetSmartLockState(SmartLockState::kConnectingToPhone);
-      break;
-  }
-}
-
 void SmartLockAuthFactorModel::SetSmartLockState(SmartLockState state) {
   if (state_ == state)
     return;
diff --git a/ash/login/ui/smart_lock_auth_factor_model.h b/ash/login/ui/smart_lock_auth_factor_model.h
index fe838dd..040382b3 100644
--- a/ash/login/ui/smart_lock_auth_factor_model.h
+++ b/ash/login/ui/smart_lock_auth_factor_model.h
@@ -28,10 +28,6 @@
   // AuthFactorModel:
   void OnArrowButtonTapOrClickEvent() override;
 
-  // TODO(crbug.com/1233614): Remove this once SmartLockState is passed in
-  // instead of EasyUnlockIconState.
-  void SetEasyUnlockIconState(EasyUnlockIconState state);
-
   void SetSmartLockState(SmartLockState state);
   void NotifySmartLockAuthResult(bool result);
 
diff --git a/ash/projector/projector_controller_impl.cc b/ash/projector/projector_controller_impl.cc
index d00aa308..b5ab9704 100644
--- a/ash/projector/projector_controller_impl.cc
+++ b/ash/projector/projector_controller_impl.cc
@@ -151,6 +151,16 @@
       EndRecordingReason::kProjectorTranscriptionError);
 }
 
+void ProjectorControllerImpl::OnSpeechRecognitionStopped() {
+  if (projector_session_->screencast_container_path()) {
+    // Finish saving the screencast if the container is available. The container
+    // might be unavailable if fail in creating the directory.
+    SaveScreencast();
+  }
+
+  projector_session_->Stop();
+}
+
 bool ProjectorControllerImpl::IsEligible() const {
   return speech_recognition_availability_ ==
              SpeechRecognitionAvailability::kAvailable ||
@@ -258,8 +268,6 @@
 
   DCHECK(projector_session_->is_active());
 
-  StopSpeechRecognition();
-
   // TODO(b/197152209): move closing selfie cam to ProjectorUiController.
   if (client_->IsSelfieCamVisible())
     client_->CloseSelfieCam();
@@ -268,16 +276,10 @@
   if (ui_controller_)
     ui_controller_->CloseToolbar();
 
-  if (projector_session_->screencast_container_path()) {
-    // Finish saving the screencast if the container is available. The container
-    // might be unavailable if fail in creating the directory.
-    SaveScreencast();
-  }
+  StopSpeechRecognition();
 
-  projector_session_->Stop();
-
-  // At this point, the screencast might not synced to Drive yet.  Open
-  // Projector App which showing the Gallery view by default.
+  // At this point, the screencast might not synced to Drive yet. Open
+  // Projector App which shows the Gallery view by default.
   client_->OpenProjectorApp();
 }
 
@@ -294,6 +296,8 @@
   }
 
   projector_session_->Stop();
+
+  client_->OpenProjectorApp();
 }
 
 void ProjectorControllerImpl::OnLaserPointerPressed() {
@@ -362,8 +366,10 @@
 }
 
 void ProjectorControllerImpl::StopSpeechRecognition() {
-  if (ProjectorController::AreExtendedProjectorFeaturesDisabled())
+  if (ProjectorController::AreExtendedProjectorFeaturesDisabled()) {
+    OnSpeechRecognitionStopped();
     return;
+  }
 
   DCHECK(speech_recognition_availability_ ==
          SpeechRecognitionAvailability::kAvailable);
diff --git a/ash/projector/projector_controller_impl.h b/ash/projector/projector_controller_impl.h
index 65bc09c6..fc4b8dbc 100644
--- a/ash/projector/projector_controller_impl.h
+++ b/ash/projector/projector_controller_impl.h
@@ -52,6 +52,7 @@
       SpeechRecognitionAvailability availability) override;
   void OnTranscription(const media::SpeechRecognitionResult& result) override;
   void OnTranscriptionError() override;
+  void OnSpeechRecognitionStopped() override;
   bool IsEligible() const override;
   NewScreencastPrecondition GetNewScreencastPrecondition() const override;
   void OnToolSet(const AnnotatorTool& tool) override;
diff --git a/ash/projector/projector_controller_unittest.cc b/ash/projector/projector_controller_unittest.cc
index 65819a50..8724977b9 100644
--- a/ash/projector/projector_controller_unittest.cc
+++ b/ash/projector/projector_controller_unittest.cc
@@ -224,8 +224,9 @@
             mock_client_,
             OnNewScreencastPreconditionChanged(NewScreencastPrecondition(
                 NewScreencastPreconditionState::kEnabled, {})));
-
-        EXPECT_CALL(mock_client_, StopSpeechRecognition());
+        EXPECT_CALL(mock_client_, StopSpeechRecognition())
+            .WillOnce(testing::Invoke(
+                [&]() { controller_->OnSpeechRecognitionStopped(); }));
 
         // Verify that |SaveMetadata| in |ProjectorMetadataController| is called
         // with the expected path.
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index 747cb19..b5b5bd5 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -225,6 +225,8 @@
     "pagination/pagination_model.cc",
     "pagination/pagination_model.h",
     "pagination/pagination_model_observer.h",
+    "personalization_app/user_display_info.cc",
+    "personalization_app/user_display_info.h",
     "power_utils.cc",
     "power_utils.h",
     "presentation_time_recorder.cc",
@@ -368,6 +370,7 @@
     "//components/pref_registry",
     "//components/prefs",
     "//components/sync/model",
+    "//components/user_manager",
     "//mojo/public/cpp/bindings",
     "//net/traffic_annotation",
     "//services/network/public/cpp:cpp",
diff --git a/ash/public/cpp/personalization_app/user_display_info.cc b/ash/public/cpp/personalization_app/user_display_info.cc
new file mode 100644
index 0000000..3977acc
--- /dev/null
+++ b/ash/public/cpp/personalization_app/user_display_info.cc
@@ -0,0 +1,24 @@
+// Copyright 2022 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/personalization_app/user_display_info.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/user_manager/user.h"
+
+namespace ash {
+namespace personalization_app {
+
+UserDisplayInfo::UserDisplayInfo() = default;
+
+UserDisplayInfo::UserDisplayInfo(const user_manager::User& user)
+    : email(user.GetDisplayEmail()),
+      name(base::UTF16ToUTF8(user.GetDisplayName())) {}
+
+UserDisplayInfo::UserDisplayInfo(UserDisplayInfo&&) = default;
+UserDisplayInfo& UserDisplayInfo::operator=(UserDisplayInfo&&) = default;
+
+UserDisplayInfo::~UserDisplayInfo() = default;
+
+}  // namespace personalization_app
+}  // namespace ash
diff --git a/ash/public/cpp/personalization_app/user_display_info.h b/ash/public/cpp/personalization_app/user_display_info.h
new file mode 100644
index 0000000..de07505b
--- /dev/null
+++ b/ash/public/cpp/personalization_app/user_display_info.h
@@ -0,0 +1,40 @@
+// Copyright 2022 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_PERSONALIZATION_APP_USER_DISPLAY_INFO_H_
+#define ASH_PUBLIC_CPP_PERSONALIZATION_APP_USER_DISPLAY_INFO_H_
+
+#include <string>
+
+#include "ash/public/cpp/ash_public_export.h"
+#include "components/user_manager/user.h"
+
+namespace ash {
+
+namespace personalization_app {
+
+struct ASH_PUBLIC_EXPORT UserDisplayInfo {
+  // The display email of the user.
+  std::string email;
+
+  // The display name of the user.
+  std::string name;
+
+  UserDisplayInfo();
+  explicit UserDisplayInfo(const user_manager::User& user_info);
+
+  UserDisplayInfo(const UserDisplayInfo& other) = delete;
+  UserDisplayInfo& operator=(const UserDisplayInfo&) = delete;
+
+  UserDisplayInfo(UserDisplayInfo&& other);
+  UserDisplayInfo& operator=(UserDisplayInfo&&);
+
+  ~UserDisplayInfo();
+};
+
+}  // namespace personalization_app
+
+}  // namespace ash
+
+#endif  // ASH_PUBLIC_CPP_PERSONALIZATION_APP_USER_DISPLAY_INFO_H_
diff --git a/ash/public/cpp/projector/projector_controller.h b/ash/public/cpp/projector/projector_controller.h
index 81b34ef5..fcab963 100644
--- a/ash/public/cpp/projector/projector_controller.h
+++ b/ash/public/cpp/projector/projector_controller.h
@@ -71,6 +71,9 @@
   // Called when there is an error in transcription.
   virtual void OnTranscriptionError() = 0;
 
+  // Called when speech recognition stopped.
+  virtual void OnSpeechRecognitionStopped() = 0;
+
   // Returns true if Projector screen recording feature is available on the
   // device. If on device speech recognition is not available on device, then
   // Projector is not eligible.
diff --git a/ash/public/cpp/shelf_prefs.cc b/ash/public/cpp/shelf_prefs.cc
index 0eddbbc5..1fd01913 100644
--- a/ash/public/cpp/shelf_prefs.cc
+++ b/ash/public/cpp/shelf_prefs.cc
@@ -91,7 +91,8 @@
   if (display_id == display::kInvalidDisplayId)
     return;
 
-  // Avoid DictionaryPrefUpdate's notifications for read but unmodified prefs.
+  // Avoid DictionaryPrefUpdateDeprecated's notifications for read but
+  // unmodified prefs.
   const base::Value* current_shelf_prefs =
       prefs->GetDictionary(prefs::kShelfPreferences);
   DCHECK(current_shelf_prefs);
@@ -106,7 +107,7 @@
   }
 
   // TODO(crbug.com/1187061): Refactor this to remove base::DictionaryValue.
-  DictionaryPrefUpdate update(prefs, prefs::kShelfPreferences);
+  DictionaryPrefUpdateDeprecated update(prefs, prefs::kShelfPreferences);
   base::DictionaryValue* shelf_prefs = update.Get();
   base::DictionaryValue* display_prefs_weak = nullptr;
   if (!shelf_prefs->GetDictionary(display_key, &display_prefs_weak)) {
diff --git a/ash/public/cpp/test/mock_projector_controller.h b/ash/public/cpp/test/mock_projector_controller.h
index ea07bb0..d535390 100644
--- a/ash/public/cpp/test/mock_projector_controller.h
+++ b/ash/public/cpp/test/mock_projector_controller.h
@@ -26,6 +26,7 @@
   MOCK_METHOD1(OnTranscription,
                void(const media::SpeechRecognitionResult& result));
   MOCK_METHOD0(OnTranscriptionError, void());
+  MOCK_METHOD0(OnSpeechRecognitionStopped, void());
   MOCK_METHOD1(SetProjectorToolsVisible, void(bool is_visible));
   MOCK_CONST_METHOD0(IsEligible, bool());
   MOCK_CONST_METHOD0(GetNewScreencastPrecondition, NewScreencastPrecondition());
diff --git a/ash/quick_pair/common/BUILD.gn b/ash/quick_pair/common/BUILD.gn
index 4a2d46a..8080a7c 100644
--- a/ash/quick_pair/common/BUILD.gn
+++ b/ash/quick_pair/common/BUILD.gn
@@ -40,9 +40,11 @@
   ]
 
   deps = [
+    "//ash/constants:constants",
     "//ash/services/quick_pair/public/mojom",
     "//base",
     "//chromeos/components/feature_usage",
+    "//components/prefs",
     "//device/bluetooth",
     "//device/bluetooth/public/cpp",
   ]
@@ -79,8 +81,11 @@
 
   deps = [
     ":common",
+    ":test_support",
+    "//ash/constants:constants",
     "//base/test:test_support",
     "//chromeos/components/feature_usage",
+    "//components/prefs:test_support",
     "//testing/gtest",
   ]
 }
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_feature_usage_metrics_logger.cc b/ash/quick_pair/common/fast_pair/fast_pair_feature_usage_metrics_logger.cc
index 3bd6318..ff7efff3 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_feature_usage_metrics_logger.cc
+++ b/ash/quick_pair/common/fast_pair/fast_pair_feature_usage_metrics_logger.cc
@@ -4,6 +4,10 @@
 
 #include "ash/quick_pair/common/fast_pair/fast_pair_feature_usage_metrics_logger.h"
 
+#include "ash/constants/ash_pref_names.h"
+#include "ash/quick_pair/common/quick_pair_browser_delegate.h"
+#include "components/prefs/pref_service.h"
+
 namespace {
 
 const char kFastPairUmaFeatureName[] = "FastPair";
@@ -29,8 +33,9 @@
 }
 
 bool FastPairFeatureUsageMetricsLogger::IsEnabled() const {
-  // crbug/1250479: Update SFUL IsEnabled to support settings toggle
-  return true;
+  PrefService* pref_service =
+      QuickPairBrowserDelegate::Get()->GetActivePrefService();
+  return pref_service && pref_service->GetBoolean(ash::prefs::kFastPairEnabled);
 }
 
 }  // namespace quick_pair
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_feature_usage_metrics_logger_unittest.cc b/ash/quick_pair/common/fast_pair/fast_pair_feature_usage_metrics_logger_unittest.cc
index d1ecc46..d2c6ee7 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_feature_usage_metrics_logger_unittest.cc
+++ b/ash/quick_pair/common/fast_pair/fast_pair_feature_usage_metrics_logger_unittest.cc
@@ -6,9 +6,14 @@
 
 #include "ash/quick_pair/common/fast_pair/fast_pair_feature_usage_metrics_logger.h"
 
+#include "ash/constants/ash_pref_names.h"
+#include "ash/quick_pair/common/mock_quick_pair_browser_delegate.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "chromeos/components/feature_usage/feature_usage_metrics.h"
+#include "components/prefs/pref_registry.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
@@ -19,9 +24,21 @@
   FastPairFeatureUsageMetricsLoggerTest() = default;
   ~FastPairFeatureUsageMetricsLoggerTest() override = default;
 
-  void SetUp() override {}
+  void SetUp() override {
+    browser_delegate_ = std::make_unique<MockQuickPairBrowserDelegate>();
+    ON_CALL(*browser_delegate_, GetActivePrefService())
+        .WillByDefault(testing::Return(&pref_service_));
+    pref_service_.registry()->RegisterBooleanPref(ash::prefs::kFastPairEnabled,
+                                                  /*default_value=*/true);
+  }
+
+  void SetEnabled(bool is_enabled) {
+    pref_service_.SetBoolean(ash::prefs::kFastPairEnabled, is_enabled);
+  }
 
   base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<MockQuickPairBrowserDelegate> browser_delegate_;
+  TestingPrefServiceSimple pref_service_;
 };
 
 TEST_F(FastPairFeatureUsageMetricsLoggerTest, IsEligible) {
@@ -29,11 +46,18 @@
   EXPECT_TRUE(feature_usage_metrics.IsEligible());
 }
 
-TEST_F(FastPairFeatureUsageMetricsLoggerTest, IsEnabled) {
+TEST_F(FastPairFeatureUsageMetricsLoggerTest, IsEnabled_Enabled) {
   FastPairFeatureUsageMetricsLogger feature_usage_metrics;
   EXPECT_TRUE(feature_usage_metrics.IsEnabled());
 }
 
+TEST_F(FastPairFeatureUsageMetricsLoggerTest, IsEnabled_NotEnabled) {
+  FastPairFeatureUsageMetricsLogger feature_usage_metrics;
+
+  SetEnabled(/*is_enabled=*/false);
+  EXPECT_FALSE(feature_usage_metrics.IsEnabled());
+}
+
 TEST_F(FastPairFeatureUsageMetricsLoggerTest, RecordUsage) {
   FastPairFeatureUsageMetricsLogger feature_usage_metrics;
 
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc b/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
index 62ac3d8..9cd1d444 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
+++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.cc
@@ -153,6 +153,18 @@
     "Bluetooth.ChromeOS.FastPair.Handshake.FailureReason";
 const char kBleScanSessionResult[] =
     "Bluetooth.ChromeOS.FastPair.Scanner.StartSession.Result";
+const char kBleScanFilterResult[] =
+    "Bluetooth.ChromeOS.FastPair.CreateScanFilter.Result";
+const char kFastPairVersion[] =
+    "Bluetooth.ChromeOS.FastPair.Discovered.Version";
+const char kNavigateToSettings[] =
+    "Bluetooth.ChromeOS.FastPair.NavigateToSettings.Result";
+const char kConnectDeviceResult[] =
+    "Bluetooth.ChromeOS.FastPair.ConnectDevice.Result";
+const char kPairDeviceResult[] =
+    "Bluetooth.ChromeOS.FastPair.PairDevice.Result";
+const char kPairDeviceErrorReason[] =
+    "Bluetooth.ChromeOS.FastPair.PairDevice.ErrorReason";
 
 }  // namespace
 
@@ -425,5 +437,32 @@
   base::UmaHistogramBoolean(kBleScanSessionResult, success);
 }
 
+void RecordBluetoothLowEnergyScanFilterResult(bool success) {
+  base::UmaHistogramBoolean(kBleScanFilterResult, success);
+}
+
+void RecordFastPairDiscoveredVersion(FastPairVersion version) {
+  base::UmaHistogramEnumeration(kFastPairVersion, version);
+}
+
+void RecordNavigateToSettingsResult(bool success) {
+  base::UmaHistogramBoolean(kNavigateToSettings, success);
+}
+
+void RecordConnectDeviceResult(bool success) {
+  base::UmaHistogramBoolean(kConnectDeviceResult, success);
+}
+
+void RecordPairDeviceResult(bool success) {
+  base::UmaHistogramBoolean(kPairDeviceResult, success);
+}
+
+void RecordPairDeviceErrorReason(
+    device::BluetoothDevice::ConnectErrorCode error_code) {
+  base::UmaHistogramEnumeration(
+      kPairDeviceErrorReason, error_code,
+      device::BluetoothDevice::NUM_CONNECT_ERROR_CODES);
+}
+
 }  // namespace quick_pair
 }  // namespace ash
diff --git a/ash/quick_pair/common/fast_pair/fast_pair_metrics.h b/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
index 94b60c99..b1c9e15 100644
--- a/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
+++ b/ash/quick_pair/common/fast_pair/fast_pair_metrics.h
@@ -10,6 +10,7 @@
 #include "base/component_export.h"
 #include "base/time/time.h"
 #include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_low_energy_scan_session.h"
 #include "device/bluetooth/bluetooth_socket.h"
 
 namespace ash {
@@ -73,6 +74,15 @@
   kMaxValue = kFailedIncorrectResponseType,
 };
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused. This enum should be kept in sync
+// with the FastPairVersion enum in src/tools/metrics/histograms/enums.xml.
+enum class COMPONENT_EXPORT(QUICK_PAIR_COMMON) FastPairVersion {
+  kVersion1 = 0,
+  kVersion2 = 1,
+  kMaxValue = kVersion2,
+};
+
 COMPONENT_EXPORT(QUICK_PAIR_COMMON)
 void AttemptRecordingFastPairEngagementFlow(const Device& device,
                                             FastPairEngagementFlowEvent event);
@@ -204,8 +214,7 @@
 COMPONENT_EXPORT(QUICK_PAIR_COMMON)
 void RecordFastPairRepositoryCacheResult(bool success);
 
-COMPONENT_EXPORT(QUICK_PAIR_COMMON)
-void RecordHandshakeResult(bool success);
+COMPONENT_EXPORT(QUICK_PAIR_COMMON) void RecordHandshakeResult(bool success);
 
 COMPONENT_EXPORT(QUICK_PAIR_COMMON)
 void RecordHandshakeFailureReason(HandshakeFailureReason failure_reason);
@@ -213,6 +222,29 @@
 COMPONENT_EXPORT(QUICK_PAIR_COMMON)
 void RecordBluetoothLowEnergyScannerStartSessionResult(bool success);
 
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordBluetoothLowEnergyScannerStartSessionErrorReason(
+    device::BluetoothLowEnergyScanSession::ErrorCode error_code);
+
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordBluetoothLowEnergyScanFilterResult(bool success);
+
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordFastPairDiscoveredVersion(FastPairVersion version);
+
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordNavigateToSettingsResult(bool success);
+
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordConnectDeviceResult(bool success);
+
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordPairDeviceResult(bool success);
+
+COMPONENT_EXPORT(QUICK_PAIR_COMMON)
+void RecordPairDeviceErrorReason(
+    device::BluetoothDevice::ConnectErrorCode error_code);
+
 }  // namespace quick_pair
 }  // namespace ash
 
diff --git a/ash/quick_pair/fast_pair_handshake/fast_pair_handshake.h b/ash/quick_pair/fast_pair_handshake/fast_pair_handshake.h
index 021c358..e609e3d6 100644
--- a/ash/quick_pair/fast_pair_handshake/fast_pair_handshake.h
+++ b/ash/quick_pair/fast_pair_handshake/fast_pair_handshake.h
@@ -59,10 +59,6 @@
     return fast_pair_data_encryptor_.get();
   }
 
-  FastPairGattServiceClient* fast_pair_gatt_service_client() {
-    return fast_pair_gatt_service_client_.get();
-  }
-
  protected:
   bool completed_successfully_ = false;
   scoped_refptr<device::BluetoothAdapter> adapter_;
diff --git a/ash/quick_pair/fast_pair_handshake/fast_pair_handshake_impl.cc b/ash/quick_pair/fast_pair_handshake/fast_pair_handshake_impl.cc
index fce55b61..474c92a 100644
--- a/ash/quick_pair/fast_pair_handshake/fast_pair_handshake_impl.cc
+++ b/ash/quick_pair/fast_pair_handshake/fast_pair_handshake_impl.cc
@@ -50,6 +50,7 @@
     std::move(on_complete_callback_).Run(device_, failure.value());
     RecordHandshakeResult(/*success=*/false);
     RecordHandshakeFailureReason(HandshakeFailureReason::kFailedGattInit);
+    fast_pair_gatt_service_client_.reset();
     return;
   }
 
@@ -73,6 +74,7 @@
     RecordHandshakeResult(/*success=*/false);
     RecordHandshakeFailureReason(
         HandshakeFailureReason::kFailedCreateEncryptor);
+    fast_pair_gatt_service_client_.reset();
     return;
   }
 
@@ -106,6 +108,7 @@
     RecordHandshakeResult(/*success=*/false);
     RecordHandshakeFailureReason(HandshakeFailureReason::kFailedWriteResponse);
     std::move(on_complete_callback_).Run(device_, failure.value());
+    fast_pair_gatt_service_client_.reset();
     return;
   }
 
@@ -120,6 +123,9 @@
 void FastPairHandshakeImpl::OnParseDecryptedResponse(
     base::TimeTicks decrypt_start_time,
     const absl::optional<DecryptedResponse>& response) {
+  // We finished with the gatt service now.
+  fast_pair_gatt_service_client_.reset();
+
   if (!response) {
     QP_LOG(WARNING) << __func__ << ": Missing decrypted response from parse.";
     std::move(on_complete_callback_)
diff --git a/ash/quick_pair/keyed_service/BUILD.gn b/ash/quick_pair/keyed_service/BUILD.gn
index ad3b8f0..30a9824 100644
--- a/ash/quick_pair/keyed_service/BUILD.gn
+++ b/ash/quick_pair/keyed_service/BUILD.gn
@@ -33,6 +33,7 @@
     "//ash/services/quick_pair",
     "//base",
     "//chromeos/services/bluetooth_config",
+    "//chromeos/services/bluetooth_config/public/cpp",
     "//components/keyed_service/core",
     "//components/prefs",
     "//components/user_manager",
@@ -51,7 +52,9 @@
 
   deps = [
     ":keyed_service",
+    "//ash/constants:constants",
     "//ash/quick_pair/common",
+    "//ash/quick_pair/common:test_support",
     "//ash/quick_pair/feature_status_tracker:test_support",
     "//ash/quick_pair/message_stream",
     "//ash/quick_pair/message_stream:test_support",
@@ -64,6 +67,7 @@
     "//ash/services/quick_pair:test_support",
     "//base",
     "//base/test:test_support",
+    "//components/prefs:test_support",
     "//components/user_manager:test_support",
     "//device/bluetooth",
     "//device/bluetooth:mocks",
diff --git a/ash/quick_pair/keyed_service/fast_pair_bluetooth_config_delegate.cc b/ash/quick_pair/keyed_service/fast_pair_bluetooth_config_delegate.cc
index 7498f991..3300a293 100644
--- a/ash/quick_pair/keyed_service/fast_pair_bluetooth_config_delegate.cc
+++ b/ash/quick_pair/keyed_service/fast_pair_bluetooth_config_delegate.cc
@@ -4,7 +4,9 @@
 
 #include "ash/quick_pair/keyed_service/fast_pair_bluetooth_config_delegate.h"
 
+#include "ash/quick_pair/repository/fast_pair_repository.h"
 #include "chromeos/services/bluetooth_config/device_name_manager.h"
+#include "chromeos/services/bluetooth_config/public/cpp/device_image_info.h"
 
 namespace ash {
 namespace quick_pair {
@@ -13,6 +15,12 @@
 
 FastPairBluetoothConfigDelegate::~FastPairBluetoothConfigDelegate() = default;
 
+absl::optional<chromeos::bluetooth_config::DeviceImageInfo>
+FastPairBluetoothConfigDelegate::GetDeviceImageInfo(
+    const std::string& device_id) {
+  return FastPairRepository::Get()->GetImagesForDevice(device_id);
+}
+
 void FastPairBluetoothConfigDelegate::SetDeviceNameManager(
     chromeos::bluetooth_config::DeviceNameManager* device_name_manager) {
   device_name_manager_ = device_name_manager;
diff --git a/ash/quick_pair/keyed_service/fast_pair_bluetooth_config_delegate.h b/ash/quick_pair/keyed_service/fast_pair_bluetooth_config_delegate.h
index aba6189..d04d2c1 100644
--- a/ash/quick_pair/keyed_service/fast_pair_bluetooth_config_delegate.h
+++ b/ash/quick_pair/keyed_service/fast_pair_bluetooth_config_delegate.h
@@ -6,9 +6,11 @@
 #define ASH_QUICK_PAIR_KEYED_SERVICE_FAST_PAIR_BLUETOOTH_CONFIG_DELEGATE_H_
 
 #include "chromeos/services/bluetooth_config/fast_pair_delegate.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
 namespace bluetooth_config {
+class DeviceImageInfo;
 class DeviceNameManager;
 }  // namespace bluetooth_config
 }  // namespace chromeos
@@ -29,6 +31,8 @@
   ~FastPairBluetoothConfigDelegate() override;
 
   // chromeos::bluetooth_config::FastPairDelegate
+  absl::optional<chromeos::bluetooth_config::DeviceImageInfo>
+  GetDeviceImageInfo(const std::string& device_id) override;
   void SetDeviceNameManager(chromeos::bluetooth_config::DeviceNameManager*
                                 device_name_manager) override;
 
diff --git a/ash/quick_pair/keyed_service/quick_pair_mediator_unittest.cc b/ash/quick_pair/keyed_service/quick_pair_mediator_unittest.cc
index e200da02..5b91ede1 100644
--- a/ash/quick_pair/keyed_service/quick_pair_mediator_unittest.cc
+++ b/ash/quick_pair/keyed_service/quick_pair_mediator_unittest.cc
@@ -6,7 +6,9 @@
 
 #include <memory>
 
+#include "ash/constants/ash_pref_names.h"
 #include "ash/quick_pair/common/device.h"
+#include "ash/quick_pair/common/mock_quick_pair_browser_delegate.h"
 #include "ash/quick_pair/common/pair_failure.h"
 #include "ash/quick_pair/common/protocol.h"
 #include "ash/quick_pair/feature_status_tracker/fake_feature_status_tracker.h"
@@ -26,6 +28,9 @@
 #include "ash/services/quick_pair/quick_pair_process_manager_impl.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/test/task_environment.h"
+#include "components/prefs/pref_registry.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
@@ -77,6 +82,11 @@
     mock_fast_pair_repository_ =
         static_cast<MockFastPairRepository*>(fast_pair_repository.get());
 
+    browser_delegate_ = std::make_unique<MockQuickPairBrowserDelegate>();
+    ON_CALL(*browser_delegate_, GetActivePrefService())
+        .WillByDefault(testing::Return(&pref_service_));
+    pref_service_.registry()->RegisterBooleanPref(ash::prefs::kFastPairEnabled,
+                                                  /*default_value=*/true);
     mediator_ = std::make_unique<Mediator>(
         std::move(tracker), std::move(scanner_broker),
         std::move(retroactive_pairing_detector),
@@ -97,6 +107,8 @@
   MockPairerBroker* mock_pairer_broker_;
   MockUIBroker* mock_ui_broker_;
   MockFastPairRepository* mock_fast_pair_repository_;
+  std::unique_ptr<MockQuickPairBrowserDelegate> browser_delegate_;
+  TestingPrefServiceSimple pref_service_;
   std::unique_ptr<Mediator> mediator_;
   base::test::SingleThreadTaskEnvironment task_environment_;
 };
diff --git a/ash/quick_pair/keyed_service/quick_pair_metrics_logger_unittest.cc b/ash/quick_pair/keyed_service/quick_pair_metrics_logger_unittest.cc
index 53c3f1a..da944aa 100644
--- a/ash/quick_pair/keyed_service/quick_pair_metrics_logger_unittest.cc
+++ b/ash/quick_pair/keyed_service/quick_pair_metrics_logger_unittest.cc
@@ -6,11 +6,13 @@
 
 #include <memory>
 
+#include "ash/constants/ash_pref_names.h"
 #include "ash/quick_pair/common/account_key_failure.h"
 #include "ash/quick_pair/common/constants.h"
 #include "ash/quick_pair/common/device.h"
 #include "ash/quick_pair/common/fast_pair/fast_pair_metrics.h"
 #include "ash/quick_pair/common/logging.h"
+#include "ash/quick_pair/common/mock_quick_pair_browser_delegate.h"
 #include "ash/quick_pair/common/pair_failure.h"
 #include "ash/quick_pair/common/protocol.h"
 #include "ash/quick_pair/pairing/fake_retroactive_pairing_detector.h"
@@ -26,6 +28,9 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
+#include "components/prefs/pref_registry.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "device/bluetooth/test/mock_bluetooth_device.h"
@@ -140,6 +145,12 @@
     retroactive_device_ = base::MakeRefCounted<Device>(
         kTestMetadataId, kTestAddress, Protocol::kFastPairRetroactive);
 
+    browser_delegate_ = std::make_unique<MockQuickPairBrowserDelegate>();
+    ON_CALL(*browser_delegate_, GetActivePrefService())
+        .WillByDefault(testing::Return(&pref_service_));
+    pref_service_.registry()->RegisterBooleanPref(ash::prefs::kFastPairEnabled,
+                                                  /*default_value=*/true);
+
     metrics_logger_ = std::make_unique<QuickPairMetricsLogger>(
         scanner_broker_.get(), pairer_broker_.get(), ui_broker_.get(),
         retroactive_pairing_detector_.get());
@@ -368,6 +379,9 @@
   scoped_refptr<Device> subsequent_device_;
   scoped_refptr<Device> retroactive_device_;
 
+  std::unique_ptr<MockQuickPairBrowserDelegate> browser_delegate_;
+  TestingPrefServiceSimple pref_service_;
+
   MockScannerBroker* mock_scanner_broker_ = nullptr;
   MockPairerBroker* mock_pairer_broker_ = nullptr;
   MockUIBroker* mock_ui_broker_ = nullptr;
diff --git a/ash/quick_pair/message_stream/message_stream.cc b/ash/quick_pair/message_stream/message_stream.cc
index 066a1cf..895b618 100644
--- a/ash/quick_pair/message_stream/message_stream.cc
+++ b/ash/quick_pair/message_stream/message_stream.cc
@@ -102,11 +102,33 @@
   Receive();
 }
 
+void MessageStream::Disconnect(base::OnceClosure on_disconnect_callback) {
+  // If we already have disconnected the socket, then we can run the callback.
+  // This can happen since the socket might have disconnected previously but
+  // we kept the MessageStream instance alive to preserve messages from the
+  // corresponding device.
+  if (!socket_) {
+    std::move(on_disconnect_callback).Run();
+    return;
+  }
+
+  socket_->Disconnect(base::BindOnce(
+      &MessageStream::OnSocketDisconnectedWithCallback,
+      weak_ptr_factory_.GetWeakPtr(), std::move(on_disconnect_callback)));
+}
+
 void MessageStream::OnSocketDisconnected() {
+  socket_ = nullptr;
   for (auto& obs : observers_)
     obs.OnDisconnected(device_address_);
 }
 
+void MessageStream::OnSocketDisconnectedWithCallback(
+    base::OnceClosure on_disconnect_callback) {
+  OnSocketDisconnected();
+  std::move(on_disconnect_callback).Run();
+}
+
 void MessageStream::ParseMessageStreamSuccess(
     std::vector<mojom::MessageStreamMessagePtr> messages) {
   if (messages.empty()) {
diff --git a/ash/quick_pair/message_stream/message_stream.h b/ash/quick_pair/message_stream/message_stream.h
index b5371fe..67960be 100644
--- a/ash/quick_pair/message_stream/message_stream.h
+++ b/ash/quick_pair/message_stream/message_stream.h
@@ -101,6 +101,8 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
+  void Disconnect(base::OnceClosure on_disconnect_callback);
+
   // Return buffer of messages.
   const base::circular_deque<mojom::MessageStreamMessagePtr>& messages() {
     return messages_;
@@ -110,8 +112,10 @@
   // Attempts to receive data from socket
   void Receive();
 
-  // Socket disconnected callback
+  // Socket disconnected callbacks
   void OnSocketDisconnected();
+  void OnSocketDisconnectedWithCallback(
+      base::OnceClosure on_disconnect_callback);
 
   // Receive data from socket callbacks
   void ReceiveDataSuccess(int buffer_size,
diff --git a/ash/quick_pair/message_stream/message_stream_lookup_impl.cc b/ash/quick_pair/message_stream/message_stream_lookup_impl.cc
index 575e610..6665280 100644
--- a/ash/quick_pair/message_stream/message_stream_lookup_impl.cc
+++ b/ash/quick_pair/message_stream/message_stream_lookup_impl.cc
@@ -77,18 +77,38 @@
 
 void MessageStreamLookupImpl::DeviceRemoved(device::BluetoothAdapter* adapter,
                                             device::BluetoothDevice* device) {
+  if (!device)
+    return;
+
   // Remove message stream if the device removed from the adapter has a
-  // message stream. It isn't expected to already have a MessageStream
-  // associated with it.
-  message_streams_.erase(device->GetAddress());
+  // message stream and disconnect from socket if applicable. It isn't expected
+  // to already have a MessageStream associated with it.
+  EraseMessageStream(device->GetAddress());
 }
 
 void MessageStreamLookupImpl::RemoveMessageStream(
     const std::string& device_address) {
   QP_LOG(VERBOSE) << __func__ << ": device address = " << device_address;
+  EraseMessageStream(device_address);
+}
 
+void MessageStreamLookupImpl::EraseMessageStream(
+    const std::string& device_address) {
   // Remove map entry if it exists. It may not exist if it was failed to be
   // created due to a |ConnectToService| error.
+  if (!base::Contains(message_streams_, device_address))
+    return;
+
+  // If the MessageStream still exists, we can attempt to gracefully disconnect
+  // the socket before erasing (and therefore destructing) the MessageStream
+  // instance.
+  message_streams_[device_address]->Disconnect(
+      base::BindOnce(&MessageStreamLookupImpl::OnSocketDisconnected,
+                     weak_ptr_factory_.GetWeakPtr(), device_address));
+}
+
+void MessageStreamLookupImpl::OnSocketDisconnected(
+    const std::string& device_address) {
   message_streams_.erase(device_address);
 }
 
diff --git a/ash/quick_pair/message_stream/message_stream_lookup_impl.h b/ash/quick_pair/message_stream/message_stream_lookup_impl.h
index 3381519..c6fde61 100644
--- a/ash/quick_pair/message_stream/message_stream_lookup_impl.h
+++ b/ash/quick_pair/message_stream/message_stream_lookup_impl.h
@@ -58,6 +58,14 @@
                    scoped_refptr<device::BluetoothSocket> socket);
   void OnConnectError(const std::string& error_message);
 
+  // Helper function to disconnect socket from a MessageStream instance and
+  // destroy the MessageStream instance. Used by both |RemoveMessageStream| and
+  // |DeviceRemoved|.
+  void EraseMessageStream(const std::string& device_address);
+
+  // Callback for disconnected the socket from the MessageStream.
+  void OnSocketDisconnected(const std::string& device_address);
+
   // Internal method called by BluetoothAdapterFactory to provide the adapter
   // object.
   void OnGetAdapter(scoped_refptr<device::BluetoothAdapter> adapter);
diff --git a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer.cc b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer.cc
index c878c004..8675258 100644
--- a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer.cc
+++ b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer.cc
@@ -112,11 +112,43 @@
     QP_LOG(INFO) << __func__
                  << ": Failed to find handshake. This is only valid if we "
                     "lost the device before this class executed.";
+    std::move(pair_failed_callback_)
+        .Run(device_, PairFailure::kPairingDeviceLost);
+    return;
+  }
+
+  device::BluetoothDevice* bt_device =
+      adapter_->GetDevice(device_->ble_address);
+
+  if (!bt_device) {
+    QP_LOG(WARNING) << __func__ << ": Could not find Bluetooth device.";
+    std::move(pair_failed_callback_)
+        .Run(device_, PairFailure::kPairingDeviceLost);
     return;
   }
 
   DCHECK(fast_pair_handshake_->completed_successfully());
 
+  fast_pair_gatt_service_client_ =
+      FastPairGattServiceClientImpl::Factory::Create(
+          bt_device, adapter_,
+          base::BindRepeating(&FastPairPairer::OnGattClientInitializedCallback,
+                              weak_ptr_factory_.GetWeakPtr()));
+}
+
+FastPairPairer::~FastPairPairer() {
+  adapter_->RemovePairingDelegate(this);
+}
+
+void FastPairPairer::OnGattClientInitializedCallback(
+    absl::optional<PairFailure> failure) {
+  if (failure) {
+    QP_LOG(WARNING) << __func__ << ": Failed to create GATT client due to: "
+                    << failure.value();
+    std::move(pair_failed_callback_).Run(device_, failure.value());
+    return;
+  }
+
   std::string device_address = device_->classic_address().value();
   device::BluetoothDevice* bt_device = adapter_->GetDevice(device_address);
 
@@ -158,17 +190,16 @@
   }
 }
 
-FastPairPairer::~FastPairPairer() {
-  adapter_->RemovePairingDelegate(this);
-}
-
 void FastPairPairer::OnPairConnected(
     absl::optional<device::BluetoothDevice::ConnectErrorCode> error) {
+  RecordPairDeviceResult(/*success=*/!error.has_value());
+
   if (error) {
     QP_LOG(WARNING) << "Failed to starting pairing procedure by pairing to "
                        "device due to error: "
                     << error.value();
     std::move(pair_failed_callback_).Run(device_, PairFailure::kPairingConnect);
+    RecordPairDeviceErrorReason(error.value());
     return;
   }
   QP_LOG(VERBOSE) << "Pair to device successful.";
@@ -176,11 +207,13 @@
 
 void FastPairPairer::OnConnectDevice(device::BluetoothDevice* device) {
   QP_LOG(VERBOSE) << "Connect device successful.";
+  RecordConnectDeviceResult(/*success=*/true);
 }
 
 void FastPairPairer::OnConnectError() {
   QP_LOG(WARNING) << "Failed to starting pairing procedure by connecting to "
                      "device address.";
+  RecordConnectDeviceResult(/*success=*/false);
   std::move(pair_failed_callback_).Run(device_, PairFailure::kAddressConnect);
 }
 
@@ -188,7 +221,7 @@
                                     uint32_t passkey) {
   pairing_device_address_ = device->GetAddress();
   expected_passkey_ = passkey;
-  fast_pair_handshake_->fast_pair_gatt_service_client()->WritePasskeyAsync(
+  fast_pair_gatt_service_client_->WritePasskeyAsync(
       /*message_type=*/0x02, /*passkey=*/expected_passkey_,
       fast_pair_handshake_->fast_pair_data_encryptor(),
       base::BindOnce(&FastPairPairer::OnPasskeyResponse,
@@ -274,7 +307,7 @@
   RAND_bytes(account_key.data(), account_key.size());
   account_key[0] = 0x04;
 
-  fast_pair_handshake_->fast_pair_gatt_service_client()->WriteAccountKey(
+  fast_pair_gatt_service_client_->WriteAccountKey(
       account_key, fast_pair_handshake_->fast_pair_data_encryptor(),
       base::BindOnce(&FastPairPairer::OnWriteAccountKey,
                      weak_ptr_factory_.GetWeakPtr(), account_key));
diff --git a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer.h b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer.h
index 72c6397..6e459f6 100644
--- a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer.h
+++ b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer.h
@@ -96,9 +96,12 @@
       std::array<uint8_t, 16> account_key,
       absl::optional<device::BluetoothGattService::GattErrorCode> error);
 
+  void OnGattClientInitializedCallback(absl::optional<PairFailure> failure);
+
   uint32_t expected_passkey_;
   scoped_refptr<device::BluetoothAdapter> adapter_;
   scoped_refptr<Device> device_;
+  std::unique_ptr<FastPairGattServiceClient> fast_pair_gatt_service_client_;
   std::string pairing_device_address_;
   base::OnceCallback<void(scoped_refptr<Device>)> paired_callback_;
   base::OnceCallback<void(scoped_refptr<Device>, PairFailure)>
diff --git a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_unittest.cc b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_unittest.cc
index d0b1efac..480ec396 100644
--- a/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_unittest.cc
+++ b/ash/quick_pair/pairing/fast_pair/fast_pair_pairer_unittest.cc
@@ -76,6 +76,12 @@
     "Bluetooth.ChromeOS.FastPair.Passkey.Decrypt.Result";
 const char kWriteAccountKeyCharacteristicResultMetric[] =
     "Bluetooth.ChromeOS.FastPair.AccountKey.Write.Result";
+const char kConnectDeviceResult[] =
+    "Bluetooth.ChromeOS.FastPair.ConnectDevice.Result";
+const char kPairDeviceResult[] =
+    "Bluetooth.ChromeOS.FastPair.PairDevice.Result";
+const char kPairDeviceErrorReason[] =
+    "Bluetooth.ChromeOS.FastPair.PairDevice.ErrorReason";
 
 class FakeBluetoothAdapter
     : public testing::NiceMock<device::MockBluetoothAdapter> {
@@ -182,6 +188,35 @@
   bool is_device_paired_ = false;
 };
 
+class FakeFastPairGattServiceClientImplFactory
+    : public ash::quick_pair::FastPairGattServiceClientImpl::Factory {
+ public:
+  ~FakeFastPairGattServiceClientImplFactory() override = default;
+
+  ash::quick_pair::FakeFastPairGattServiceClient*
+  fake_fast_pair_gatt_service_client() {
+    return fake_fast_pair_gatt_service_client_;
+  }
+
+ private:
+  // FastPairGattServiceClientImpl::Factory:
+  std::unique_ptr<ash::quick_pair::FastPairGattServiceClient> CreateInstance(
+      device::BluetoothDevice* device,
+      scoped_refptr<device::BluetoothAdapter> adapter,
+      base::OnceCallback<void(absl::optional<ash::quick_pair::PairFailure>)>
+          on_initialized_callback) override {
+    auto fake_fast_pair_gatt_service_client =
+        std::make_unique<ash::quick_pair::FakeFastPairGattServiceClient>(
+            device, adapter, std::move(on_initialized_callback));
+    fake_fast_pair_gatt_service_client_ =
+        fake_fast_pair_gatt_service_client.get();
+    return fake_fast_pair_gatt_service_client;
+  }
+
+  ash::quick_pair::FakeFastPairGattServiceClient*
+      fake_fast_pair_gatt_service_client_ = nullptr;
+};
+
 }  // namespace
 
 namespace ash {
@@ -189,6 +224,12 @@
 
 class FastPairPairerTest : public AshTestBase {
  public:
+  void SetUp() override {
+    AshTestBase::SetUp();
+    FastPairGattServiceClientImpl::Factory::SetFactoryForTesting(
+        &fast_pair_gatt_service_factory_);
+  }
+
   void SuccessfulDataEncryptorSetUp(bool fast_pair_v1, Protocol protocol) {
     device_ = base::MakeRefCounted<Device>(
         kMetadataId, kBluetoothCanonicalizedAddress, protocol);
@@ -209,9 +250,6 @@
     fake_bluetooth_device_ptr_ = fake_bluetooth_device_.get();
     adapter_->AddMockDevice(std::move(fake_bluetooth_device_));
 
-    gatt_service_client_ = new FakeFastPairGattServiceClient(
-        fake_bluetooth_device_ptr_, adapter_, base::DoNothing());
-
     data_encryptor_ = new FakeFastPairDataEncryptor();
 
     FastPairHandshakeLookup::SetCreateFunctionForTesting(base::BindRepeating(
@@ -226,15 +264,20 @@
     auto fake = std::make_unique<FakeFastPairHandshake>(
         adapter_, device, std::move(callback),
         base::WrapUnique(data_encryptor_),
-        base::WrapUnique(gatt_service_client_));
-
+        std::make_unique<FakeFastPairGattServiceClient>(
+            fake_bluetooth_device_ptr_, adapter_, base::DoNothing()));
     fake->InvokeCallback();
-
     return fake;
   }
 
   base::HistogramTester& histogram_tester() { return histogram_tester_; }
 
+  void RunOnGattClientInitializedCallback(
+      absl::optional<PairFailure> failure = absl::nullopt) {
+    fast_pair_gatt_service_factory_.fake_fast_pair_gatt_service_client()
+        ->RunOnGattClientInitializedCallback(failure);
+  }
+
   void SetDecryptResponseForIncorrectMessageType() {
     DecryptedResponse response(FastPairMessageType::kSeekersPasskey,
                                kAddressBytes, kRequestSaltBytes);
@@ -262,13 +305,15 @@
   void RunWritePasskeyCallback(
       std::vector<uint8_t> data,
       absl::optional<PairFailure> failure = absl::nullopt) {
-    gatt_service_client_->RunWritePasskeyCallback(data, failure);
+    fast_pair_gatt_service_factory_.fake_fast_pair_gatt_service_client()
+        ->RunWritePasskeyCallback(data, failure);
   }
 
   void RunWriteAccountKeyCallback(
       absl::optional<device::BluetoothGattService::GattErrorCode> error =
           absl::nullopt) {
-    gatt_service_client_->RunWriteAccountKeyCallback(error);
+    fast_pair_gatt_service_factory_.fake_fast_pair_gatt_service_client()
+        ->RunWriteAccountKeyCallback(error);
   }
 
   void PairFailedCallback(scoped_refptr<Device> device, PairFailure failure) {
@@ -311,6 +356,7 @@
   absl::optional<PairFailure> failure_ = absl::nullopt;
   std::unique_ptr<FakeBluetoothDevice> fake_bluetooth_device_;
   FakeBluetoothDevice* fake_bluetooth_device_ptr_ = nullptr;
+  FakeFastPairGattServiceClientImplFactory fast_pair_gatt_service_factory_;
   scoped_refptr<FakeBluetoothAdapter> adapter_;
   scoped_refptr<Device> device_;
   base::MockCallback<base::OnceCallback<void(scoped_refptr<Device>)>>
@@ -324,16 +370,32 @@
   std::unique_ptr<FastPairPairer> pairer_;
   base::HistogramTester histogram_tester_;
 
-  FakeFastPairGattServiceClient* gatt_service_client_ = nullptr;
   FakeFastPairDataEncryptor* data_encryptor_ = nullptr;
 
   base::WeakPtrFactory<FastPairPairerTest> weak_ptr_factory_{this};
 };
 
+TEST_F(FastPairPairerTest, FailedCallbackInvokedOnGattError) {
+  SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
+                               /*protocol=*/Protocol::kFastPairInitial);
+  CreatePairer();
+  RunOnGattClientInitializedCallback(PairFailure::kGattServiceDiscoveryTimeout);
+  EXPECT_EQ(GetPairFailure(), PairFailure::kGattServiceDiscoveryTimeout);
+}
+
+TEST_F(FastPairPairerTest, FailedCallbackInvokedOnDeviceNotFound) {
+  SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
+                               /*protocol=*/Protocol::kFastPairInitial);
+  SetGetDeviceFailure();
+  CreatePairer();
+  EXPECT_EQ(GetPairFailure(), PairFailure::kPairingDeviceLost);
+}
+
 TEST_F(FastPairPairerTest, NoCallbackIsInvokedOnGattSuccess_Initial) {
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairInitial);
   CreatePairer();
+  RunOnGattClientInitializedCallback();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
 }
 
@@ -341,6 +403,7 @@
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairRetroactive);
   CreatePairer();
+  RunOnGattClientInitializedCallback();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
 }
 
@@ -348,31 +411,43 @@
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairSubsequent);
   CreatePairer();
+  RunOnGattClientInitializedCallback();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
 }
 
 TEST_F(FastPairPairerTest, SuccessfulDecryptedResponsePairFailure_Initial) {
+  histogram_tester().ExpectTotalCount(kPairDeviceResult, 0);
+  histogram_tester().ExpectTotalCount(kPairDeviceErrorReason, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairInitial);
   SetPairFailure();
   CreatePairer();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), PairFailure::kPairingConnect);
+  histogram_tester().ExpectTotalCount(kPairDeviceResult, 1);
+  histogram_tester().ExpectTotalCount(kPairDeviceErrorReason, 1);
 }
 
 TEST_F(FastPairPairerTest, SuccessfulDecryptedResponsePairFailure_Subsequent) {
+  histogram_tester().ExpectTotalCount(kPairDeviceResult, 0);
+  histogram_tester().ExpectTotalCount(kPairDeviceErrorReason, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairSubsequent);
   SetPairFailure();
   CreatePairer();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), PairFailure::kPairingConnect);
+  histogram_tester().ExpectTotalCount(kPairDeviceResult, 1);
+  histogram_tester().ExpectTotalCount(kPairDeviceErrorReason, 1);
 }
 
 TEST_F(FastPairPairerTest, SuccessfulDecryptedResponsePairSuccess_Initial) {
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairInitial);
   CreatePairer();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
 }
@@ -381,29 +456,36 @@
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairSubsequent);
   CreatePairer();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
 }
 
 TEST_F(FastPairPairerTest, SuccessfulDecryptedResponseConnectFailure_Initial) {
+  histogram_tester().ExpectTotalCount(kConnectDeviceResult, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairInitial);
-  SetGetDeviceFailure();
   SetConnectFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), PairFailure::kAddressConnect);
+  histogram_tester().ExpectTotalCount(kConnectDeviceResult, 1);
 }
 
 TEST_F(FastPairPairerTest,
        SuccessfulDecryptedResponseConnectFailure_Subsequent) {
+  histogram_tester().ExpectTotalCount(kConnectDeviceResult, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairSubsequent);
-  SetGetDeviceFailure();
   SetConnectFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), PairFailure::kAddressConnect);
+  histogram_tester().ExpectTotalCount(kConnectDeviceResult, 1);
 }
 
 TEST_F(FastPairPairerTest, SuccessfulDecryptedResponseConnectSuccess_Initial) {
@@ -413,8 +495,9 @@
       kWritePasskeyCharacteristicPairFailureMetric, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairInitial);
-  SetGetDeviceFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
   histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicResultMetric,
@@ -431,8 +514,9 @@
       kWritePasskeyCharacteristicPairFailureMetric, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairSubsequent);
-  SetGetDeviceFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
   histogram_tester().ExpectTotalCount(kWritePasskeyCharacteristicResultMetric,
@@ -450,8 +534,9 @@
   histogram_tester().ExpectTotalCount(kPasskeyCharacteristicDecryptResult, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairInitial);
-  SetGetDeviceFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
   NotifyConfirmPasskey();
@@ -475,8 +560,9 @@
   histogram_tester().ExpectTotalCount(kPasskeyCharacteristicDecryptResult, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairSubsequent);
-  SetGetDeviceFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
   NotifyConfirmPasskey();
@@ -496,8 +582,9 @@
   histogram_tester().ExpectTotalCount(kPasskeyCharacteristicDecryptResult, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairInitial);
-  SetGetDeviceFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
   SetDecryptPasskeyForIncorrectMessageType();
@@ -515,8 +602,9 @@
   histogram_tester().ExpectTotalCount(kPasskeyCharacteristicDecryptResult, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairSubsequent);
-  SetGetDeviceFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
   SetDecryptPasskeyForIncorrectMessageType();
@@ -533,8 +621,9 @@
   histogram_tester().ExpectTotalCount(kPasskeyCharacteristicDecryptResult, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairInitial);
-  SetGetDeviceFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
   SetDecryptPasskeyForPasskeyMismatch();
@@ -551,8 +640,9 @@
   histogram_tester().ExpectTotalCount(kPasskeyCharacteristicDecryptResult, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairSubsequent);
-  SetGetDeviceFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
   SetDecryptPasskeyForPasskeyMismatch();
@@ -569,8 +659,9 @@
   histogram_tester().ExpectTotalCount(kPasskeyCharacteristicDecryptResult, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairInitial);
-  SetGetDeviceFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
   SetDecryptPasskeyForSuccess();
@@ -587,8 +678,9 @@
   histogram_tester().ExpectTotalCount(kPasskeyCharacteristicDecryptResult, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairSubsequent);
-  SetGetDeviceFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
   SetDecryptPasskeyForSuccess();
@@ -604,8 +696,9 @@
   histogram_tester().ExpectTotalCount(kPasskeyCharacteristicDecryptTime, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairInitial);
-  SetGetDeviceFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
   EXPECT_CALL(paired_callback_, Run);
@@ -625,8 +718,9 @@
   histogram_tester().ExpectTotalCount(kPasskeyCharacteristicDecryptResult, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairSubsequent);
-  SetGetDeviceFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
   EXPECT_CALL(paired_callback_, Run);
@@ -646,9 +740,10 @@
       kWriteAccountKeyCharacteristicResultMetric, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairInitial);
-  SetGetDeviceFailure();
   SetPublicKey();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
   EXPECT_CALL(paired_callback_, Run);
@@ -671,8 +766,9 @@
       kWriteAccountKeyCharacteristicResultMetric, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairRetroactive);
-  SetGetDeviceFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_CALL(pairing_procedure_complete_, Run);
   RunWriteAccountKeyCallback();
@@ -685,8 +781,9 @@
       kWriteAccountKeyCharacteristicResultMetric, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairInitial);
-  SetGetDeviceFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   SetPublicKey();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetPairFailure(), absl::nullopt);
@@ -717,8 +814,9 @@
       kWriteAccountKeyCharacteristicResultMetric, 0);
   SuccessfulDataEncryptorSetUp(/*fast_pair_v1=*/false,
                                /*protocol=*/Protocol::kFastPairRetroactive);
-  SetGetDeviceFailure();
   CreatePairer();
+  SetGetDeviceFailure();
+  RunOnGattClientInitializedCallback();
   base::RunLoop().RunUntilIdle();
   EXPECT_CALL(account_key_failure_callback_, Run);
   RunWriteAccountKeyCallback(
diff --git a/ash/quick_pair/repository/fake_fast_pair_repository.cc b/ash/quick_pair/repository/fake_fast_pair_repository.cc
index 772d442..7d9d7b5 100644
--- a/ash/quick_pair/repository/fake_fast_pair_repository.cc
+++ b/ash/quick_pair/repository/fake_fast_pair_repository.cc
@@ -92,7 +92,7 @@
 }
 
 // Unimplemented.
-absl::optional<const chromeos::bluetooth_config::DeviceImageInfo>
+absl::optional<chromeos::bluetooth_config::DeviceImageInfo>
 FakeFastPairRepository::GetImagesForDevice(const std::string& device_id) {
   return absl::nullopt;
 }
diff --git a/ash/quick_pair/repository/fake_fast_pair_repository.h b/ash/quick_pair/repository/fake_fast_pair_repository.h
index ad55b91..eb911efe 100644
--- a/ash/quick_pair/repository/fake_fast_pair_repository.h
+++ b/ash/quick_pair/repository/fake_fast_pair_repository.h
@@ -58,7 +58,7 @@
   void FetchDeviceImages(scoped_refptr<Device> device) override;
   bool PersistDeviceImages(scoped_refptr<Device> device) override;
   bool EvictDeviceImages(const device::BluetoothDevice* device) override;
-  absl::optional<const chromeos::bluetooth_config::DeviceImageInfo>
+  absl::optional<chromeos::bluetooth_config::DeviceImageInfo>
   GetImagesForDevice(const std::string& device_id) override;
 
  private:
diff --git a/ash/quick_pair/repository/fast_pair/device_image_store.cc b/ash/quick_pair/repository/fast_pair/device_image_store.cc
index 34b3c7d..e0ffe1ab 100644
--- a/ash/quick_pair/repository/fast_pair/device_image_store.cc
+++ b/ash/quick_pair/repository/fast_pair/device_image_store.cc
@@ -91,7 +91,7 @@
   return true;
 }
 
-absl::optional<const chromeos::bluetooth_config::DeviceImageInfo>
+absl::optional<chromeos::bluetooth_config::DeviceImageInfo>
 DeviceImageStore::GetImagesForDeviceModel(const std::string& model_id) {
   // Lazily load saved images from prefs the first time we get an image.
   if (!loaded_images_from_prefs_) {
diff --git a/ash/quick_pair/repository/fast_pair/device_image_store.h b/ash/quick_pair/repository/fast_pair/device_image_store.h
index 0978f33..55ce6d6 100644
--- a/ash/quick_pair/repository/fast_pair/device_image_store.h
+++ b/ash/quick_pair/repository/fast_pair/device_image_store.h
@@ -71,7 +71,7 @@
 
   // Returns a DeviceImageInfo of device images belonging to |model_id|, if
   // found.
-  absl::optional<const chromeos::bluetooth_config::DeviceImageInfo>
+  absl::optional<chromeos::bluetooth_config::DeviceImageInfo>
   GetImagesForDeviceModel(const std::string& model_id);
 
  private:
diff --git a/ash/quick_pair/repository/fast_pair/device_image_store_unittest.cc b/ash/quick_pair/repository/fast_pair/device_image_store_unittest.cc
index 14ca8042..43a132f 100644
--- a/ash/quick_pair/repository/fast_pair/device_image_store_unittest.cc
+++ b/ash/quick_pair/repository/fast_pair/device_image_store_unittest.cc
@@ -136,7 +136,7 @@
   device_image_store_->SaveDeviceImages(kTestModelId, device_metadata_.get(),
                                         base::DoNothing());
 
-  absl::optional<const DeviceImageInfo> images =
+  absl::optional<DeviceImageInfo> images =
       device_image_store_->GetImagesForDeviceModel(kTestModelId);
   EXPECT_TRUE(images);
 
@@ -150,7 +150,7 @@
 
 TEST_F(DeviceImageStoreTest, GetImagesForDeviceModelInvalidUninitialized) {
   // Don't initialize the dictionary with any results.
-  absl::optional<const DeviceImageInfo> images =
+  absl::optional<DeviceImageInfo> images =
       device_image_store_->GetImagesForDeviceModel(kTestModelId);
   EXPECT_FALSE(images);
 }
@@ -159,7 +159,7 @@
   device_image_store_->SaveDeviceImages(kTestModelId, device_metadata_.get(),
                                         base::DoNothing());
   // Look for a model ID that wasn't added.
-  absl::optional<const DeviceImageInfo> images =
+  absl::optional<DeviceImageInfo> images =
       device_image_store_->GetImagesForDeviceModel("DEF456");
   EXPECT_FALSE(images);
 }
diff --git a/ash/quick_pair/repository/fast_pair/pending_write_store.cc b/ash/quick_pair/repository/fast_pair/pending_write_store.cc
index 39bbb9c..6a97aeb 100644
--- a/ash/quick_pair/repository/fast_pair/pending_write_store.cc
+++ b/ash/quick_pair/repository/fast_pair/pending_write_store.cc
@@ -43,7 +43,8 @@
     QP_LOG(WARNING) << __func__ << ": No user pref service available.";
     return;
   }
-  DictionaryPrefUpdate update(pref_service, kFastPairPendingWritesPref);
+  DictionaryPrefUpdateDeprecated update(pref_service,
+                                        kFastPairPendingWritesPref);
   update->SetStringKey(mac_address, hex_model_id);
 }
 
@@ -77,7 +78,8 @@
     return;
   }
 
-  DictionaryPrefUpdate update(pref_service, kFastPairPendingWritesPref);
+  DictionaryPrefUpdateDeprecated update(pref_service,
+                                        kFastPairPendingWritesPref);
   update->RemoveKey(mac_address);
 }
 
@@ -88,7 +90,7 @@
     QP_LOG(WARNING) << __func__ << ": No user pref service available.";
     return;
   }
-  ListPrefUpdate update(pref_service, kFastPairPendingDeletesPref);
+  ListPrefUpdateDeprecated update(pref_service, kFastPairPendingDeletesPref);
   update->Append(hex_account_key);
 }
 
@@ -123,7 +125,7 @@
     return;
   }
 
-  ListPrefUpdate update(pref_service, kFastPairPendingDeletesPref);
+  ListPrefUpdateDeprecated update(pref_service, kFastPairPendingDeletesPref);
   update->EraseListValue(base::Value(hex_account_key));
 }
 
diff --git a/ash/quick_pair/repository/fast_pair/saved_device_registry.cc b/ash/quick_pair/repository/fast_pair/saved_device_registry.cc
index b659e2e6..55dc44d 100644
--- a/ash/quick_pair/repository/fast_pair/saved_device_registry.cc
+++ b/ash/quick_pair/repository/fast_pair/saved_device_registry.cc
@@ -37,7 +37,8 @@
     return;
   }
   std::string encoded = base::Base64Encode(account_key);
-  DictionaryPrefUpdate update(pref_service, kFastPairSavedDevicesPref);
+  DictionaryPrefUpdateDeprecated update(pref_service,
+                                        kFastPairSavedDevicesPref);
   update->SetStringKey(mac_address, encoded);
 }
 
diff --git a/ash/quick_pair/repository/fast_pair_repository.h b/ash/quick_pair/repository/fast_pair_repository.h
index c92b63b..3fc695d 100644
--- a/ash/quick_pair/repository/fast_pair_repository.h
+++ b/ash/quick_pair/repository/fast_pair_repository.h
@@ -82,7 +82,7 @@
   virtual bool EvictDeviceImages(const device::BluetoothDevice* device) = 0;
 
   // Returns device images belonging to |device_id|, if found.
-  virtual absl::optional<const chromeos::bluetooth_config::DeviceImageInfo>
+  virtual absl::optional<chromeos::bluetooth_config::DeviceImageInfo>
   GetImagesForDevice(const std::string& device_id) = 0;
 
  protected:
diff --git a/ash/quick_pair/repository/fast_pair_repository_impl.cc b/ash/quick_pair/repository/fast_pair_repository_impl.cc
index f8a4616..ca90331 100644
--- a/ash/quick_pair/repository/fast_pair_repository_impl.cc
+++ b/ash/quick_pair/repository/fast_pair_repository_impl.cc
@@ -289,7 +289,7 @@
   return device_image_store_->EvictDeviceImages(hex_model_id.value());
 }
 
-absl::optional<const chromeos::bluetooth_config::DeviceImageInfo>
+absl::optional<chromeos::bluetooth_config::DeviceImageInfo>
 FastPairRepositoryImpl::GetImagesForDevice(const std::string& device_id) {
   absl::optional<const std::string> hex_model_id =
       device_id_map_->GetModelIdForDeviceId(device_id);
diff --git a/ash/quick_pair/repository/fast_pair_repository_impl.h b/ash/quick_pair/repository/fast_pair_repository_impl.h
index b8ee590..8262c75 100644
--- a/ash/quick_pair/repository/fast_pair_repository_impl.h
+++ b/ash/quick_pair/repository/fast_pair_repository_impl.h
@@ -61,7 +61,7 @@
   void FetchDeviceImages(scoped_refptr<Device> device) override;
   bool PersistDeviceImages(scoped_refptr<Device> device) override;
   bool EvictDeviceImages(const device::BluetoothDevice* device) override;
-  absl::optional<const chromeos::bluetooth_config::DeviceImageInfo>
+  absl::optional<chromeos::bluetooth_config::DeviceImageInfo>
   GetImagesForDevice(const std::string& device_id) override;
 
  private:
diff --git a/ash/quick_pair/repository/mock_fast_pair_repository.h b/ash/quick_pair/repository/mock_fast_pair_repository.h
index 3c09cc1..ee218e6e 100644
--- a/ash/quick_pair/repository/mock_fast_pair_repository.h
+++ b/ash/quick_pair/repository/mock_fast_pair_repository.h
@@ -55,7 +55,7 @@
               EvictDeviceImages,
               (const device::BluetoothDevice* device),
               (override));
-  MOCK_METHOD(absl::optional<const chromeos::bluetooth_config::DeviceImageInfo>,
+  MOCK_METHOD(absl::optional<chromeos::bluetooth_config::DeviceImageInfo>,
               GetImagesForDevice,
               (const std::string& device_id),
               (override));
diff --git a/ash/quick_pair/scanning/fast_pair/fast_pair_scanner_impl.cc b/ash/quick_pair/scanning/fast_pair/fast_pair_scanner_impl.cc
index 841d3ef..9910b36 100644
--- a/ash/quick_pair/scanning/fast_pair/fast_pair_scanner_impl.cc
+++ b/ash/quick_pair/scanning/fast_pair/fast_pair_scanner_impl.cc
@@ -54,6 +54,8 @@
   auto filter = device::BluetoothLowEnergyScanFilter::Create(
       device::BluetoothLowEnergyScanFilter::Range::kNear,
       kFilterDeviceFoundTimeout, kFilterDeviceLostTimeout, {pattern});
+
+  RecordBluetoothLowEnergyScanFilterResult(/*success=*/filter != nullptr);
   if (!filter) {
     QP_LOG(ERROR) << "Bluetooth Low Energy Scan Session failed to start due to "
                      "failure to create filter.";
diff --git a/ash/quick_pair/ui/fast_pair/fast_pair_presenter.cc b/ash/quick_pair/ui/fast_pair/fast_pair_presenter.cc
index 1c941c1..5e1900fc 100644
--- a/ash/quick_pair/ui/fast_pair/fast_pair_presenter.cc
+++ b/ash/quick_pair/ui/fast_pair/fast_pair_presenter.cc
@@ -8,6 +8,7 @@
 
 #include "ash/public/cpp/system_tray_client.h"
 #include "ash/quick_pair/common/device.h"
+#include "ash/quick_pair/common/fast_pair/fast_pair_metrics.h"
 #include "ash/quick_pair/common/logging.h"
 #include "ash/quick_pair/common/quick_pair_browser_delegate.h"
 #include "ash/quick_pair/proto/fastpair.pb.h"
@@ -60,6 +61,9 @@
           .empty()) {
     device->SetAdditionalData(Device::AdditionalDataType::kFastPairVersion,
                               {1});
+    RecordFastPairDiscoveredVersion(FastPairVersion::kVersion1);
+  } else {
+    RecordFastPairDiscoveredVersion(FastPairVersion::kVersion2);
   }
 
   notification_controller_->ShowDiscoveryNotification(
@@ -131,9 +135,11 @@
 void FastPairPresenter::OnNavigateToSettings(PairingFailedCallback callback) {
   if (TrayPopupUtils::CanOpenWebUISettings()) {
     Shell::Get()->system_tray_model()->client()->ShowBluetoothSettings();
+    RecordNavigateToSettingsResult(/*success=*/true);
   } else {
     QP_LOG(WARNING) << "Cannot open Bluetooth Settings since it's not possible "
                        "to opening WebUI settings";
+    RecordNavigateToSettingsResult(/*success=*/false);
   }
 
   callback.Run(PairingFailedAction::kNavigateToSettings);
diff --git a/ash/services/BUILD.gn b/ash/services/BUILD.gn
index 9bab2a5..e0c6949 100644
--- a/ash/services/BUILD.gn
+++ b/ash/services/BUILD.gn
@@ -18,6 +18,7 @@
   deps = [
     "//ash/services/ime:services_unittests",
     "//ash/services/ime:unit_tests",
+    "//ash/services/nearby/public/cpp:unit_tests",
     "//ash/services/quick_pair:unit_tests",
     "//chromeos/services/machine_learning/public/cpp:ash_unit_tests",
   ]
diff --git a/ash/services/ime/public/mojom/input_engine.mojom b/ash/services/ime/public/mojom/input_engine.mojom
index a97be2d0..c1484db 100644
--- a/ash/services/ime/public/mojom/input_engine.mojom
+++ b/ash/services/ime/public/mojom/input_engine.mojom
@@ -15,7 +15,6 @@
 // the ProcessMessage() method. All other proto messages must be mirrored with
 // a respective mojom method here in this interface for security (see
 // ash/services/ime/decoder/system_engine.cc for an example of mirroring).
-// TODO(crbug/1194372): Investigate using libmojo within ime shared lib.
 interface InputChannel {
   // Returns a serialized protobuf result after processing a serialized
   // protobuf message.
diff --git a/ash/services/nearby/public/cpp/BUILD.gn b/ash/services/nearby/public/cpp/BUILD.gn
index d5c4bed..de3e6b1b 100644
--- a/ash/services/nearby/public/cpp/BUILD.gn
+++ b/ash/services/nearby/public/cpp/BUILD.gn
@@ -21,6 +21,17 @@
   ]
 }
 
+# Create separate build target to avoid circular dependencies with
+# //ash/services/nearby/public/mojom
+static_library("tcp_server_socket_port") {
+  sources = [
+    "tcp_server_socket_port.cc",
+    "tcp_server_socket_port.h",
+  ]
+
+  deps = [ "//base" ]
+}
+
 static_library("test_support") {
   testonly = true
 
@@ -43,3 +54,15 @@
     "//testing/gmock",
   ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [ "tcp_server_socket_port_unittest.cc" ]
+
+  deps = [
+    ":tcp_server_socket_port",
+    "//base",
+    "//testing/gtest",
+  ]
+}
diff --git a/ash/services/nearby/public/cpp/tcp_server_socket_port.cc b/ash/services/nearby/public/cpp/tcp_server_socket_port.cc
new file mode 100644
index 0000000..1d02083
--- /dev/null
+++ b/ash/services/nearby/public/cpp/tcp_server_socket_port.cc
@@ -0,0 +1,67 @@
+// 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/services/nearby/public/cpp/tcp_server_socket_port.h"
+
+#include "base/check.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/rand_util.h"
+
+namespace ash {
+namespace nearby {
+
+namespace {
+
+bool IsPortInRange(uint16_t port) {
+  return port >= TcpServerSocketPort::kMin && port <= TcpServerSocketPort::kMax;
+}
+
+// Generate random number in [kMin, kMax] inclusive. Note:
+// RandGenerator(range) uses the non-inclusive interval [0, range).
+uint16_t GenerateRandomPort() {
+  return static_cast<uint16_t>(TcpServerSocketPort::kMin +
+                               base::RandGenerator(TcpServerSocketPort::kMax -
+                                                   TcpServerSocketPort::kMin +
+                                                   1));
+}
+
+}  // namespace
+
+absl::optional<TcpServerSocketPort> TcpServerSocketPort::FromInt(int port) {
+  if (!base::IsValueInRangeForNumericType<uint16_t>(port)) {
+    LOG(ERROR) << "TcpServerSocketPort::" << __func__ << ": Port " << port
+               << " is not uint16.";
+    return absl::nullopt;
+  }
+
+  return TcpServerSocketPort::FromUInt16(static_cast<uint16_t>(port));
+}
+
+absl::optional<TcpServerSocketPort> TcpServerSocketPort::FromUInt16(
+    uint16_t port) {
+  if (!IsPortInRange(port)) {
+    LOG(ERROR) << "TcpServerSocketPort::" << __func__ << ": Port " << port
+               << " is not in the range [" << kMin << "," << kMax << "].";
+    return absl::nullopt;
+  }
+
+  return TcpServerSocketPort(port);
+}
+
+TcpServerSocketPort TcpServerSocketPort::Random() {
+  return TcpServerSocketPort(GenerateRandomPort());
+}
+
+TcpServerSocketPort::TcpServerSocketPort()
+    : TcpServerSocketPort(GenerateRandomPort()) {}
+
+TcpServerSocketPort::TcpServerSocketPort(uint16_t port) : port_(port) {
+  CHECK(IsPortInRange(port));
+}
+
+TcpServerSocketPort::~TcpServerSocketPort() = default;
+
+}  // namespace nearby
+}  // namespace ash
diff --git a/ash/services/nearby/public/cpp/tcp_server_socket_port.h b/ash/services/nearby/public/cpp/tcp_server_socket_port.h
new file mode 100644
index 0000000..184e8ba
--- /dev/null
+++ b/ash/services/nearby/public/cpp/tcp_server_socket_port.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 ASH_SERVICES_NEARBY_PUBLIC_CPP_TCP_SERVER_SOCKET_PORT_H_
+#define ASH_SERVICES_NEARBY_PUBLIC_CPP_TCP_SERVER_SOCKET_PORT_H_
+
+#include <cstdint>
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace ash {
+namespace nearby {
+
+// A TCP server socket port number used by the Nearby Connections WifiLan
+// medium. The port number is guaranteed to be in the interval [kMin, kMax]. We
+// restrict the range of port numbers so as not to interfere with the lower port
+// numbers used by core components.
+class TcpServerSocketPort {
+ public:
+  // This range agree with the Nearby Connections WifiLan implementation
+  // on GmsCore.
+  static constexpr uint16_t kMin = 49152;
+  static constexpr uint16_t kMax = 65535;
+
+  // Creates a TcpServerSocketPort from the input |port| value. Returns nullopt
+  // if |port| is not in the interval [kMin, kMax].
+  static absl::optional<TcpServerSocketPort> FromInt(int port);
+  static absl::optional<TcpServerSocketPort> FromUInt16(uint16_t port);
+
+  // Creates a TcpServerSocketPort with a random port number in the range [kMin,
+  // kMax].
+  static TcpServerSocketPort Random();
+
+  // Creates a TcpServerSocketPort with a random port number in the range [kMin,
+  // kMax]. Note: We need a public default constructor in order to support mojo
+  // type mapping. Do not use directly; prefer TcpServerSocketPort::Random().
+  TcpServerSocketPort();
+
+  ~TcpServerSocketPort();
+
+  uint16_t port() const { return port_; }
+
+ private:
+  explicit TcpServerSocketPort(uint16_t port);
+
+  uint16_t port_;
+};
+
+}  // namespace nearby
+}  // namespace ash
+
+#endif  // ASH_SERVICES_NEARBY_PUBLIC_CPP_TCP_SERVER_SOCKET_PORT_H_
diff --git a/ash/services/nearby/public/cpp/tcp_server_socket_port_unittest.cc b/ash/services/nearby/public/cpp/tcp_server_socket_port_unittest.cc
new file mode 100644
index 0000000..ed7faa6
--- /dev/null
+++ b/ash/services/nearby/public/cpp/tcp_server_socket_port_unittest.cc
@@ -0,0 +1,58 @@
+// 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/services/nearby/public/cpp/tcp_server_socket_port.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace ash {
+namespace nearby {
+namespace {
+
+TEST(TcpServerSocketPortTest, FromInt) {
+  // Can't convert input to uint16_t.
+  EXPECT_FALSE(TcpServerSocketPort::FromInt(-1));
+
+  // Outside of restricted range.
+  EXPECT_FALSE(TcpServerSocketPort::FromInt(
+      static_cast<int>(TcpServerSocketPort::kMin) - 1));
+  EXPECT_FALSE(TcpServerSocketPort::FromInt(
+      static_cast<int>(TcpServerSocketPort::kMax) + 1));
+
+  // Inside restricted range.
+  absl::optional<TcpServerSocketPort> port =
+      TcpServerSocketPort::FromInt(TcpServerSocketPort::kMin);
+  EXPECT_TRUE(port);
+  EXPECT_EQ(TcpServerSocketPort::kMin, port->port());
+  port = TcpServerSocketPort::FromInt(TcpServerSocketPort::kMax);
+  EXPECT_TRUE(port);
+  EXPECT_EQ(TcpServerSocketPort::kMax, port->port());
+}
+
+TEST(TcpServerSocketPortTest, FromUInt16) {
+  // Outside of restricted range. Note: kMax is currently the maximum uint16_t,
+  // so we can't test exceeding that bound.
+  EXPECT_FALSE(TcpServerSocketPort::FromUInt16(TcpServerSocketPort::kMin - 1));
+
+  // Inside restricted range.
+  absl::optional<TcpServerSocketPort> port =
+      TcpServerSocketPort::FromUInt16(TcpServerSocketPort::kMin);
+  EXPECT_TRUE(port);
+  EXPECT_EQ(TcpServerSocketPort::kMin, port->port());
+  port = TcpServerSocketPort::FromUInt16(TcpServerSocketPort::kMax);
+  EXPECT_TRUE(port);
+  EXPECT_EQ(TcpServerSocketPort::kMax, port->port());
+}
+
+TEST(TcpServerSocketPortTest, Random) {
+  // Random value is inside restricted range.
+  TcpServerSocketPort port = TcpServerSocketPort::Random();
+  EXPECT_GT(port.port(), TcpServerSocketPort::kMin);
+  EXPECT_LT(port.port(), TcpServerSocketPort::kMax);
+}
+
+}  // namespace
+}  // namespace nearby
+}  // namespace ash
diff --git a/ash/services/nearby/public/mojom/BUILD.gn b/ash/services/nearby/public/mojom/BUILD.gn
index 95a5cee..b1c83ac 100644
--- a/ash/services/nearby/public/mojom/BUILD.gn
+++ b/ash/services/nearby/public/mojom/BUILD.gn
@@ -18,6 +18,8 @@
     "nearby_decoder.mojom",
     "nearby_decoder_types.mojom",
     "sharing.mojom",
+    "tcp_server_socket_port.mojom",
+    "tcp_socket_factory.mojom",
     "webrtc.mojom",
     "webrtc_signaling_messenger.mojom",
   ]
@@ -43,5 +45,17 @@
       traits_sources = [ "nearby_connections_mojom_traits.cc" ]
       traits_deps = [ "//third_party/nearby:platform_api_types" ]
     },
+    {
+      types = [
+        {
+          mojom = "sharing.mojom.TcpServerSocketPort"
+          cpp = "::ash::nearby::TcpServerSocketPort"
+        },
+      ]
+      traits_headers = [ "tcp_server_socket_port_mojom_traits.h" ]
+      traits_sources = [ "tcp_server_socket_port_mojom_traits.cc" ]
+      traits_deps =
+          [ "//ash/services/nearby/public/cpp:tcp_server_socket_port" ]
+    },
   ]
 }
diff --git a/ash/services/nearby/public/mojom/tcp_server_socket_port.mojom b/ash/services/nearby/public/mojom/tcp_server_socket_port.mojom
new file mode 100644
index 0000000..9a6efe6307
--- /dev/null
+++ b/ash/services/nearby/public/mojom/tcp_server_socket_port.mojom
@@ -0,0 +1,10 @@
+// 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.
+
+module sharing.mojom;
+
+// See //ash/services/nearby/public/cpp/tcp_server_socket_port.h.
+struct TcpServerSocketPort {
+  uint16 port;
+};
diff --git a/ash/services/nearby/public/mojom/tcp_server_socket_port_mojom_traits.cc b/ash/services/nearby/public/mojom/tcp_server_socket_port_mojom_traits.cc
new file mode 100644
index 0000000..e69cbda
--- /dev/null
+++ b/ash/services/nearby/public/mojom/tcp_server_socket_port_mojom_traits.cc
@@ -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.
+
+#include "ash/services/nearby/public/mojom/tcp_server_socket_port_mojom_traits.h"
+
+namespace mojo {
+
+bool StructTraits<sharing::mojom::TcpServerSocketPortDataView,
+                  ash::nearby::TcpServerSocketPort>::
+    Read(sharing::mojom::TcpServerSocketPortDataView port,
+         ash::nearby::TcpServerSocketPort* out) {
+  // FromUInt16() validates the port range, returning nullopt if the port number
+  // is invalid.
+  absl::optional<ash::nearby::TcpServerSocketPort> p =
+      ash::nearby::TcpServerSocketPort::FromUInt16(port.port());
+  if (!p)
+    return false;
+
+  *out = *p;
+
+  return true;
+}
+
+}  // namespace mojo
diff --git a/ash/services/nearby/public/mojom/tcp_server_socket_port_mojom_traits.h b/ash/services/nearby/public/mojom/tcp_server_socket_port_mojom_traits.h
new file mode 100644
index 0000000..351f4e2b
--- /dev/null
+++ b/ash/services/nearby/public/mojom/tcp_server_socket_port_mojom_traits.h
@@ -0,0 +1,27 @@
+// 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_SERVICES_NEARBY_PUBLIC_MOJOM_TCP_SERVER_SOCKET_PORT_MOJOM_TRAITS_H_
+#define ASH_SERVICES_NEARBY_PUBLIC_MOJOM_TCP_SERVER_SOCKET_PORT_MOJOM_TRAITS_H_
+
+#include "ash/services/nearby/public/cpp/tcp_server_socket_port.h"
+#include "ash/services/nearby/public/mojom/tcp_server_socket_port.mojom-shared.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<sharing::mojom::TcpServerSocketPortDataView,
+                    ash::nearby::TcpServerSocketPort> {
+  static uint16_t port(const ash::nearby::TcpServerSocketPort& port) {
+    return port.port();
+  }
+
+  static bool Read(sharing::mojom::TcpServerSocketPortDataView port,
+                   ash::nearby::TcpServerSocketPort* out);
+};
+
+}  // namespace mojo
+
+#endif  // ASH_SERVICES_NEARBY_PUBLIC_MOJOM_TCP_SERVER_SOCKET_PORT_MOJOM_TRAITS_H_
diff --git a/ash/services/nearby/public/mojom/tcp_socket_factory.mojom b/ash/services/nearby/public/mojom/tcp_socket_factory.mojom
new file mode 100644
index 0000000..1ac67ccac
--- /dev/null
+++ b/ash/services/nearby/public/mojom/tcp_socket_factory.mojom
@@ -0,0 +1,44 @@
+// 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.
+
+module sharing.mojom;
+
+import "ash/services/nearby/public/mojom/tcp_server_socket_port.mojom";
+import "services/network/public/mojom/address_list.mojom";
+import "services/network/public/mojom/ip_address.mojom";
+import "services/network/public/mojom/ip_endpoint.mojom";
+import "services/network/public/mojom/mutable_network_traffic_annotation_tag.mojom";
+import "services/network/public/mojom/tcp_socket.mojom";
+
+// Wraps TCP socket creation functions from
+// //services/network/public/mojom/network_context.mojom. We need this wrapper
+// to create TCP sockets from the Nearby Connections utility process because
+// network::mojom::NetworkContext can only be used by the browser process.
+interface TcpSocketFactory {
+  // See //services/network/public/mojom/network_context.mojom. One slight
+  // modification is the use of sharing.mojom.TcpServerSocketPort; this
+  // restricts the range of allowed ports.
+  CreateTCPServerSocket(
+      network.mojom.IPAddress local_addr,
+      TcpServerSocketPort port,
+      uint32 backlog,
+      network.mojom.MutableNetworkTrafficAnnotationTag traffic_annotation,
+      pending_receiver<network.mojom.TCPServerSocket> socket)
+      => (int32 result, network.mojom.IPEndPoint? local_addr_out);
+
+  // See //services/network/public/mojom/network_context.mojom.
+  CreateTCPConnectedSocket(
+      network.mojom.IPEndPoint? local_addr,
+      network.mojom.AddressList remote_addr_list,
+      network.mojom.TCPConnectedSocketOptions? tcp_connected_socket_options,
+      network.mojom.MutableNetworkTrafficAnnotationTag traffic_annotation,
+      pending_receiver<network.mojom.TCPConnectedSocket> socket,
+      pending_remote<network.mojom.SocketObserver>? observer)
+      => (int32 result,
+          network.mojom.IPEndPoint? local_addr,
+          network.mojom.IPEndPoint? peer_addr,
+          handle<data_pipe_consumer>? receive_stream,
+          handle<data_pipe_producer>? send_stream);
+};
+
diff --git a/ash/shelf/contextual_tooltip.cc b/ash/shelf/contextual_tooltip.cc
index ea716d90..158bf7d2 100644
--- a/ash/shelf/contextual_tooltip.cc
+++ b/ash/shelf/contextual_tooltip.cc
@@ -228,7 +228,7 @@
 
 void HandleNudgeShown(PrefService* prefs, TooltipType type) {
   const int shown_count = GetShownCount(prefs, type);
-  DictionaryPrefUpdate update(prefs, prefs::kContextualTooltips);
+  DictionaryPrefUpdateDeprecated update(prefs, prefs::kContextualTooltips);
   update->SetIntPath(GetPath(type, kShownCount), shown_count + 1);
   update->SetPath(GetPath(type, kLastTimeShown), base::TimeToValue(GetTime()));
   GetStatusTracker(type)->HandleNudgeShown(base::TimeTicks::Now());
@@ -236,7 +236,7 @@
 
 void HandleGesturePerformed(PrefService* prefs, TooltipType type) {
   const int success_count = GetSuccessCount(prefs, type);
-  DictionaryPrefUpdate update(prefs, prefs::kContextualTooltips);
+  DictionaryPrefUpdateDeprecated update(prefs, prefs::kContextualTooltips);
   update->SetIntPath(GetPath(type, kSuccessCount), success_count + 1);
   GetStatusTracker(type)->HandleGesturePerformed(base::TimeTicks::Now());
 }
@@ -256,7 +256,7 @@
 
 void ClearPrefs() {
   DCHECK(Shell::Get()->session_controller()->GetLastActiveUserPrefService());
-  DictionaryPrefUpdate update(
+  DictionaryPrefUpdateDeprecated update(
       Shell::Get()->session_controller()->GetLastActiveUserPrefService(),
       prefs::kContextualTooltips);
   base::DictionaryValue* nudges_dict = update.Get();
diff --git a/ash/shelf/launcher_nudge_controller.cc b/ash/shelf/launcher_nudge_controller.cc
index 7e09aac..5f9a7635e 100644
--- a/ash/shelf/launcher_nudge_controller.cc
+++ b/ash/shelf/launcher_nudge_controller.cc
@@ -195,7 +195,7 @@
     return;
 
   const int shown_count = GetShownCount(prefs);
-  DictionaryPrefUpdate update(prefs, prefs::kShelfLauncherNudge);
+  DictionaryPrefUpdateDeprecated update(prefs, prefs::kShelfLauncherNudge);
   update->SetIntPath(kShownCount, shown_count + 1);
   update->SetPath(kLastShownTime, base::TimeToValue(GetNow()));
 }
@@ -249,7 +249,7 @@
   if (Shell::Get()->session_controller()->IsUserFirstLogin()) {
     // If the current logged in user is a new one, record the first login time
     // to know when to show the nudge.
-    DictionaryPrefUpdate update(prefs, prefs::kShelfLauncherNudge);
+    DictionaryPrefUpdateDeprecated update(prefs, prefs::kShelfLauncherNudge);
     update->SetPath(kFirstLoginTime, base::TimeToValue(GetNow()));
   } else if (GetFirstLoginTime(prefs).is_null()) {
     // For the users that has logged in before the nudge feature is landed, we
@@ -278,7 +278,7 @@
     return;
 
   if (!WasLauncherShownPreviously(prefs) && shown) {
-    DictionaryPrefUpdate update(prefs, prefs::kShelfLauncherNudge);
+    DictionaryPrefUpdateDeprecated update(prefs, prefs::kShelfLauncherNudge);
     update->SetBoolPath(kWasLauncherShown, true);
   }
 }
diff --git a/ash/shelf/scrollable_shelf_view_unittest.cc b/ash/shelf/scrollable_shelf_view_unittest.cc
index 5df6f0a..9ec6e0d 100644
--- a/ash/shelf/scrollable_shelf_view_unittest.cc
+++ b/ash/shelf/scrollable_shelf_view_unittest.cc
@@ -998,42 +998,29 @@
 
   // The app list's view is lazily loaded. Since this is the first time, and we
   // didn't scroll in the right spot, it shouldn't have been created yet.
-  EXPECT_EQ(nullptr,
-            Shell::Get()->app_list_controller()->presenter()->GetView());
+  auto* presenter = Shell::Get()->app_list_controller()->fullscreen_presenter();
+  EXPECT_EQ(nullptr, presenter->GetView());
 
   auto empty_shelf_point = scrollable_shelf_view_->GetBoundsInScreen().origin();
   empty_shelf_point.Offset(10, 10);
   GetEventGenerator()->MoveMouseTo(empty_shelf_point);
   GetEventGenerator()->MoveMouseWheel(0, shelf_scroll_threshold + 1);
-  EXPECT_EQ(AppListViewState::kPeeking, Shell::Get()
-                                            ->app_list_controller()
-                                            ->presenter()
-                                            ->GetView()
-                                            ->app_list_state());
+  auto* app_list_view = presenter->GetView();
+  EXPECT_EQ(AppListViewState::kPeeking, app_list_view->app_list_state());
 
   // Scrolling again should expand to all apps.
   GetEventGenerator()->MoveMouseWheel(0, shelf_scroll_threshold + 1);
-  EXPECT_EQ(AppListViewState::kFullscreenAllApps, Shell::Get()
-                                                      ->app_list_controller()
-                                                      ->presenter()
-                                                      ->GetView()
-                                                      ->app_list_state());
+  EXPECT_EQ(AppListViewState::kFullscreenAllApps,
+            app_list_view->app_list_state());
 
   // Scrolling up on fullscreen will do nothing.
   GetEventGenerator()->MoveMouseWheel(0, shelf_scroll_threshold + 1);
-  EXPECT_EQ(AppListViewState::kFullscreenAllApps, Shell::Get()
-                                                      ->app_list_controller()
-                                                      ->presenter()
-                                                      ->GetView()
-                                                      ->app_list_state());
+  EXPECT_EQ(AppListViewState::kFullscreenAllApps,
+            app_list_view->app_list_state());
 
   // Scrolling down will close the app list.
   GetEventGenerator()->MoveMouseWheel(0, -shelf_scroll_threshold - 1);
-  EXPECT_EQ(AppListViewState::kClosed, Shell::Get()
-                                           ->app_list_controller()
-                                           ->presenter()
-                                           ->GetView()
-                                           ->app_list_state());
+  EXPECT_EQ(AppListViewState::kClosed, app_list_view->app_list_state());
 }
 
 TEST_P(ScrollableShelfViewRTLTest, ScrollsByMouseWheelEvent) {
@@ -1090,7 +1077,7 @@
                                         scroll_steps, num_fingers);
     EXPECT_EQ(AppListViewState::kPeeking, Shell::Get()
                                               ->app_list_controller()
-                                              ->presenter()
+                                              ->fullscreen_presenter()
                                               ->GetView()
                                               ->app_list_state());
     EXPECT_EQ(default_strategy,
@@ -1130,41 +1117,29 @@
   GetEventGenerator()->ScrollSequence(start_point, base::TimeDelta(),
                                       -scroll_speed, /*y_offset*/ 0,
                                       scroll_steps, num_fingers);
-  EXPECT_EQ(AppListViewState::kFullscreenAllApps, Shell::Get()
-                                                      ->app_list_controller()
-                                                      ->presenter()
-                                                      ->GetView()
-                                                      ->app_list_state());
+  auto* app_list_view =
+      Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView();
+  EXPECT_EQ(AppListViewState::kFullscreenAllApps,
+            app_list_view->app_list_state());
 
   // Scrolling the same way again should do nothing.
   GetEventGenerator()->ScrollSequence(start_point, base::TimeDelta(),
                                       -scroll_speed, /*y_offset*/ 0,
                                       scroll_steps, num_fingers);
-  EXPECT_EQ(AppListViewState::kFullscreenAllApps, Shell::Get()
-                                                      ->app_list_controller()
-                                                      ->presenter()
-                                                      ->GetView()
-                                                      ->app_list_state());
+  EXPECT_EQ(AppListViewState::kFullscreenAllApps,
+            app_list_view->app_list_state());
 
   // Scrolling toward the side of the screen the shelf is on should collapse it.
   GetEventGenerator()->ScrollSequence(start_point, base::TimeDelta(),
                                       scroll_speed, /*y_offset*/ 0,
                                       scroll_steps, num_fingers);
-  EXPECT_EQ(AppListViewState::kClosed, Shell::Get()
-                                           ->app_list_controller()
-                                           ->presenter()
-                                           ->GetView()
-                                           ->app_list_state());
+  EXPECT_EQ(AppListViewState::kClosed, app_list_view->app_list_state());
 
   // Scrolling the same way again should do nothing.
   GetEventGenerator()->ScrollSequence(start_point, base::TimeDelta(),
                                       scroll_speed, /*y_offset*/ 0,
                                       scroll_steps, num_fingers);
-  EXPECT_EQ(AppListViewState::kClosed, Shell::Get()
-                                           ->app_list_controller()
-                                           ->presenter()
-                                           ->GetView()
-                                           ->app_list_state());
+  EXPECT_EQ(AppListViewState::kClosed, app_list_view->app_list_state());
 
   // Now we test the opposite side with the opposite scroll values.
   GetPrimaryShelf()->SetAlignment(ShelfAlignment::kRight);
@@ -1175,21 +1150,14 @@
   GetEventGenerator()->ScrollSequence(start_point, base::TimeDelta(),
                                       scroll_speed, /*y_offset*/ 0,
                                       scroll_steps, num_fingers);
-  EXPECT_EQ(AppListViewState::kFullscreenAllApps, Shell::Get()
-                                                      ->app_list_controller()
-                                                      ->presenter()
-                                                      ->GetView()
-                                                      ->app_list_state());
+  EXPECT_EQ(AppListViewState::kFullscreenAllApps,
+            app_list_view->app_list_state());
 
   // Scrolling toward the side of the screen the shelf is on should collapse it.
   GetEventGenerator()->ScrollSequence(start_point, base::TimeDelta(),
                                       -scroll_speed, /*y_offset*/ 0,
                                       scroll_steps, num_fingers);
-  EXPECT_EQ(AppListViewState::kClosed, Shell::Get()
-                                           ->app_list_controller()
-                                           ->presenter()
-                                           ->GetView()
-                                           ->app_list_state());
+  EXPECT_EQ(AppListViewState::kClosed, app_list_view->app_list_state());
 }
 
 // Verify that the ripple ring of the first/last app icon is fully shown
diff --git a/ash/shell_test_api.cc b/ash/shell_test_api.cc
index 19b0554..0632541 100644
--- a/ash/shell_test_api.cc
+++ b/ash/shell_test_api.cc
@@ -265,7 +265,7 @@
 
 PaginationModel* ShellTestApi::GetAppListPaginationModel() {
   AppListView* view =
-      Shell::Get()->app_list_controller()->presenter()->GetView();
+      Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView();
   if (!view)
     return nullptr;
   return view->GetAppsPaginationModel();
diff --git a/ash/shortcut_viewer/shortcut_viewer_strings.grd b/ash/shortcut_viewer/shortcut_viewer_strings.grd
index 48470f8..2ab9eb3 100644
--- a/ash/shortcut_viewer/shortcut_viewer_strings.grd
+++ b/ash/shortcut_viewer/shortcut_viewer_strings.grd
@@ -6,6 +6,7 @@
     <output filename="grit/shortcut_viewer_strings.h" type="rc_header">
       <emit emit_type='prepend'></emit>
     </output>
+    <output filename="shortcut_viewer_strings_af.pak" type="data_package" lang="af" />
     <output filename="shortcut_viewer_strings_am.pak" type="data_package" lang="am" />
     <output filename="shortcut_viewer_strings_ar.pak" type="data_package" lang="ar" />
     <output filename="shortcut_viewer_strings_bg.pak" type="data_package" lang="bg" />
@@ -62,6 +63,7 @@
     <output filename="shortcut_viewer_strings_vi.pak" type="data_package" lang="vi" />
     <output filename="shortcut_viewer_strings_zh-CN.pak" type="data_package" lang="zh-CN" />
     <output filename="shortcut_viewer_strings_zh-TW.pak" type="data_package" lang="zh-TW" />
+    <output filename="shortcut_viewer_strings_zu.pak" type="data_package" lang="zu" />
 
     <!-- Pseudolocales -->
     <output filename="shortcut_viewer_strings_ar-XB.pak" type="data_package" lang="ar-XB" />
diff --git a/ash/style/icon_button.cc b/ash/style/icon_button.cc
index 2ee2e748..67ad924 100644
--- a/ash/style/icon_button.cc
+++ b/ash/style/icon_button.cc
@@ -6,10 +6,13 @@
 
 #include "ash/style/ash_color_provider.h"
 #include "ash/style/style_util.h"
+#include "ash/wm/haptics_util.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/events/devices/haptic_touchpad_effects.h"
+#include "ui/events/event.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/size.h"
@@ -188,6 +191,14 @@
   SchedulePaint();
 }
 
+void IconButton::NotifyClick(const ui::Event& event) {
+  if (is_togglable_) {
+    haptics_util::PlayHapticToggleEffect(
+        !toggled_, ui::HapticTouchpadEffectStrength::kMedium);
+  }
+  views::Button::NotifyClick(event);
+}
+
 void IconButton::UpdateVectorIcon() {
   if (!icon_)
     return;
diff --git a/ash/style/icon_button.h b/ash/style/icon_button.h
index aeeeb8f0..29e2995 100644
--- a/ash/style/icon_button.h
+++ b/ash/style/icon_button.h
@@ -12,6 +12,10 @@
 struct VectorIcon;
 }  // namespace gfx
 
+namespace ui {
+class Event;
+}  // namespace ui
+
 namespace ash {
 
 // A circular ImageButton that can have small/medium/large different sizes. Each
@@ -83,6 +87,7 @@
   void PaintButtonContents(gfx::Canvas* canvas) override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   void OnThemeChanged() override;
+  void NotifyClick(const ui::Event& event) override;
 
  protected:
   void UpdateVectorIcon();
diff --git a/ash/style/pill_button.cc b/ash/style/pill_button.cc
index bc2816d..d1e11f5 100644
--- a/ash/style/pill_button.cc
+++ b/ash/style/pill_button.cc
@@ -54,26 +54,6 @@
   return AshColorProvider::Get()->GetControlsLayerColor(color_id);
 }
 
-SkColor GetPillButtonTextColor(PillButton::Type type) {
-  AshColorProvider::ContentLayerType color_id =
-      AshColorProvider::ContentLayerType::kButtonLabelColor;
-  switch (type) {
-    case PillButton::Type::kIcon:
-    case PillButton::Type::kIconless:
-    case PillButton::Type::kIconlessProminent:
-    case PillButton::Type::kIconlessFloating:
-      break;
-    case PillButton::Type::kIconlessAlert:
-      color_id = AshColorProvider::ContentLayerType::kButtonLabelColorPrimary;
-      break;
-    case PillButton::Type::kIconlessAccent:
-    case PillButton::Type::kIconlessAccentFloating:
-      color_id = AshColorProvider::ContentLayerType::kButtonLabelColorBlue;
-      break;
-  }
-  return AshColorProvider::Get()->GetContentLayerColor(color_id);
-}
-
 int GetPillButtonWidth(bool has_icon) {
   int button_width = 2 * kPillButtonHorizontalSpacing;
   if (has_icon)
@@ -177,6 +157,37 @@
                AshColorProvider::GetDisabledColor(enabled_text_color));
 }
 
+void PillButton::SetButtonTextColor(const SkColor text_color) {
+  if (text_color_ == text_color)
+    return;
+  text_color_ = text_color;
+  OnThemeChanged();
+}
+
+SkColor PillButton::GetPillButtonTextColor(Type type) {
+  // Use customized text color if it is set.
+  if (text_color_)
+    return text_color_.value();
+
+  AshColorProvider::ContentLayerType color_id =
+      AshColorProvider::ContentLayerType::kButtonLabelColor;
+  switch (type) {
+    case PillButton::Type::kIcon:
+    case PillButton::Type::kIconless:
+    case PillButton::Type::kIconlessProminent:
+    case PillButton::Type::kIconlessFloating:
+      break;
+    case PillButton::Type::kIconlessAlert:
+      color_id = AshColorProvider::ContentLayerType::kButtonLabelColorPrimary;
+      break;
+    case PillButton::Type::kIconlessAccent:
+    case PillButton::Type::kIconlessAccentFloating:
+      color_id = AshColorProvider::ContentLayerType::kButtonLabelColorBlue;
+      break;
+  }
+  return AshColorProvider::Get()->GetContentLayerColor(color_id);
+}
+
 BEGIN_METADATA(PillButton, views::LabelButton)
 END_METADATA
 
diff --git a/ash/style/pill_button.h b/ash/style/pill_button.h
index 54bbd94..e28d759 100644
--- a/ash/style/pill_button.h
+++ b/ash/style/pill_button.h
@@ -59,13 +59,24 @@
   int GetHeightForWidth(int width) const override;
   void OnThemeChanged() override;
 
+  // Sets the text's color for the button. Note, do this only when the button
+  // wants to have a different text color from the defined ones (E.g: the action
+  // buttons of notifications align their color with the app icon's color).
+  void SetButtonTextColor(const SkColor text_color);
+
  private:
+  // Get text's color depending on the type used.
+  SkColor GetPillButtonTextColor(Type type);
+
   const Type type_;
   const gfx::VectorIcon* const icon_;
 
   // True if the button wants to use light colors when the D/L mode feature is
   // not enabled. Note, can be removed when D/L mode feature is fully launched.
   bool use_light_colors_;
+
+  // Customized value for text's color.
+  absl::optional<SkColor> text_color_;
 };
 
 }  // namespace ash
diff --git a/ash/system/accessibility/dictation_bubble_controller.cc b/ash/system/accessibility/dictation_bubble_controller.cc
index 5d586c2..281f1b8d 100644
--- a/ash/system/accessibility/dictation_bubble_controller.cc
+++ b/ash/system/accessibility/dictation_bubble_controller.cc
@@ -62,6 +62,7 @@
   dictation_bubble_view_ = new DictationBubbleView();
   widget_ =
       views::BubbleDialogDelegateView::CreateBubble(dictation_bubble_view_);
+  widget_->SetZOrderLevel(ui::ZOrderLevel::kFloatingUIElement);
   CollisionDetectionUtils::MarkWindowPriorityForCollisionDetection(
       widget_->GetNativeWindow(),
       CollisionDetectionUtils::RelativePriority::kDictationBubble);
diff --git a/ash/system/accessibility/dictation_bubble_view.cc b/ash/system/accessibility/dictation_bubble_view.cc
index 2dca4d0..3198831 100644
--- a/ash/system/accessibility/dictation_bubble_view.cc
+++ b/ash/system/accessibility/dictation_bubble_view.cc
@@ -31,9 +31,12 @@
 DictationBubbleView::~DictationBubbleView() = default;
 
 void DictationBubbleView::Update(const absl::optional<std::u16string>& text) {
-  bool visible = text.has_value();
-  label_->SetVisible(visible);
-  label_->SetText(visible ? text.value() : std::u16string());
+  bool has_text = text.has_value();
+  // Show either the image view, which is a placeholder when there is no text
+  // to be displayed, or the label view. Never show both at the same time.
+  image_view_->SetVisible(!has_text);
+  label_->SetVisible(has_text);
+  label_->SetText(has_text ? text.value() : std::u16string());
   SizeToContents();
 }
 
diff --git a/ash/system/bluetooth/bluetooth_detailed_view_controller.cc b/ash/system/bluetooth/bluetooth_detailed_view_controller.cc
index 8939c1c4..66ced9a 100644
--- a/ash/system/bluetooth/bluetooth_detailed_view_controller.cc
+++ b/ash/system/bluetooth/bluetooth_detailed_view_controller.cc
@@ -21,6 +21,7 @@
 namespace ash {
 namespace {
 using chromeos::bluetooth_config::IsBluetoothEnabledOrEnabling;
+using chromeos::bluetooth_config::mojom::AudioOutputCapability;
 using chromeos::bluetooth_config::mojom::DeviceConnectionState;
 }  // namespace
 
@@ -107,6 +108,18 @@
   // When CloseBubble() is called |device| will be deleted so we need to make a
   // copy of the device ID that was selected.
   const std::string device_id = device->device_properties->id;
+
+  // Non-HID devices can be explicitly connected to, so we detect when this is
+  // the case and attempt to connect to the device instead of navigating to the
+  // Bluetooth Settings.
+  if (device->device_properties->audio_capability ==
+          AudioOutputCapability::kCapableOfAudioOutput &&
+      device->device_properties->connection_state ==
+          DeviceConnectionState::kNotConnected) {
+    remote_cros_bluetooth_config_->Connect(device_id,
+                                           /*callback=*/base::DoNothing());
+    return;
+  }
   tray_controller_->CloseBubble();  // Deletes |this|.
   Shell::Get()->system_tray_model()->client()->ShowBluetoothSettings(device_id);
 }
diff --git a/ash/system/bluetooth/bluetooth_detailed_view_controller_unittest.cc b/ash/system/bluetooth/bluetooth_detailed_view_controller_unittest.cc
index 5b0b06a..063ca88 100644
--- a/ash/system/bluetooth/bluetooth_detailed_view_controller_unittest.cc
+++ b/ash/system/bluetooth/bluetooth_detailed_view_controller_unittest.cc
@@ -23,6 +23,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chromeos/services/bluetooth_config/fake_adapter_state_controller.h"
 #include "chromeos/services/bluetooth_config/fake_device_cache.h"
+#include "chromeos/services/bluetooth_config/fake_device_operation_handler.h"
 #include "chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom.h"
 #include "chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.h"
 #include "mojo/public/cpp/bindings/clone_traits.h"
@@ -35,6 +36,8 @@
 const char kDeviceId[] = "/device/id";
 
 using chromeos::bluetooth_config::AdapterStateController;
+using chromeos::bluetooth_config::FakeDeviceOperationHandler;
+using chromeos::bluetooth_config::mojom::AudioOutputCapability;
 using chromeos::bluetooth_config::mojom::BluetoothDeviceProperties;
 using chromeos::bluetooth_config::mojom::BluetoothSystemState;
 using chromeos::bluetooth_config::mojom::DeviceConnectionState;
@@ -175,6 +178,10 @@
         .bluetooth_device_list_controller();
   }
 
+  FakeDeviceOperationHandler* fake_device_operation_handler() {
+    return scoped_bluetooth_config_test_helper_.fake_device_operation_handler();
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
   chromeos::bluetooth_config::ScopedBluetoothConfigTestHelper
@@ -236,7 +243,46 @@
 }
 
 TEST_F(BluetoothDetailedViewControllerTest,
-       OnDeviceListItemSelectedOpensBluetoothSettings) {
+       OnDeviceListAudioCapableItemSelected) {
+  PairedBluetoothDevicePropertiesPtr selected_device =
+      CreatePairedDevice(DeviceConnectionState::kNotConnected);
+  selected_device->device_properties->id = kDeviceId;
+  selected_device->device_properties->audio_capability =
+      AudioOutputCapability::kCapableOfAudioOutput;
+
+  std::vector<PairedBluetoothDevicePropertiesPtr> paired_devices;
+  paired_devices.push_back(mojo::Clone(selected_device));
+  SetPairedDevices(std::move(paired_devices));
+
+  EXPECT_EQ(0, GetSystemTrayClient()->show_bluetooth_settings_count());
+  EXPECT_TRUE(
+      GetSystemTrayClient()->last_bluetooth_settings_device_id().empty());
+  EXPECT_EQ(0u, fake_device_operation_handler()->perform_connect_call_count());
+
+  bluetooth_detailed_view_delegate()->OnDeviceListItemSelected(selected_device);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(0, GetSystemTrayClient()->show_bluetooth_settings_count());
+  EXPECT_TRUE(
+      GetSystemTrayClient()->last_bluetooth_settings_device_id().empty());
+  EXPECT_EQ(1u, fake_device_operation_handler()->perform_connect_call_count());
+  EXPECT_STREQ(kDeviceId, fake_device_operation_handler()
+                              ->last_perform_connect_device_id()
+                              .c_str());
+
+  selected_device->device_properties->connection_state =
+      DeviceConnectionState::kConnected;
+  bluetooth_detailed_view_delegate()->OnDeviceListItemSelected(selected_device);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1, GetSystemTrayClient()->show_bluetooth_settings_count());
+  EXPECT_STREQ(
+      kDeviceId,
+      GetSystemTrayClient()->last_bluetooth_settings_device_id().c_str());
+}
+
+TEST_F(BluetoothDetailedViewControllerTest,
+       OnDeviceListNonAudioCapableItemSelected) {
   PairedBluetoothDevicePropertiesPtr selected_device =
       CreatePairedDevice(DeviceConnectionState::kNotConnected);
   selected_device->device_properties->id = kDeviceId;
@@ -251,6 +297,7 @@
   EXPECT_STREQ(
       kDeviceId,
       GetSystemTrayClient()->last_bluetooth_settings_device_id().c_str());
+  EXPECT_EQ(0u, fake_device_operation_handler()->perform_connect_call_count());
 }
 
 TEST_F(BluetoothDetailedViewControllerTest,
diff --git a/ash/system/holding_space/holding_space_animation_registry.cc b/ash/system/holding_space/holding_space_animation_registry.cc
index 74de091..428e498 100644
--- a/ash/system/holding_space/holding_space_animation_registry.cc
+++ b/ash/system/holding_space/holding_space_animation_registry.cc
@@ -12,6 +12,7 @@
 #include "ash/public/cpp/holding_space/holding_space_model.h"
 #include "ash/public/cpp/holding_space/holding_space_model_observer.h"
 #include "ash/system/holding_space/holding_space_animation_registry.h"
+#include "ash/system/holding_space/holding_space_progress_icon_animation.h"
 #include "ash/system/holding_space/holding_space_progress_ring_animation.h"
 #include "base/containers/contains.h"
 #include "base/containers/cxx20_erase_map.h"
@@ -54,6 +55,32 @@
       const ProgressIndicatorAnimationDelegate&) = delete;
   ~ProgressIndicatorAnimationDelegate() override = default;
 
+  // Adds the specified `callback` to be notified of changes to the icon
+  // animation associated with the specified `key`. The `callback` will continue
+  // to receive events so long as both `this` and the returned subscription
+  // exist.
+  base::CallbackListSubscription AddIconAnimationChangedCallbackForKey(
+      const void* key,
+      ProgressIconAnimationChangedCallbackList::CallbackType callback) {
+    auto it = icon_animation_changed_callback_lists_by_key_.find(key);
+
+    // If this is the first time that an icon animation changed callback is
+    // being registered for the specified `key`, set a callback to destroy the
+    // created callback list when it becomes empty.
+    if (it == icon_animation_changed_callback_lists_by_key_.end()) {
+      it = icon_animation_changed_callback_lists_by_key_
+               .emplace(std::piecewise_construct, std::forward_as_tuple(key),
+                        std::forward_as_tuple())
+               .first;
+      it->second.set_removal_callback(base::BindRepeating(
+          &ProgressIndicatorAnimationDelegate::
+              EraseIconAnimationChangedCallbackListForKeyIfEmpty,
+          base::Unretained(this), base::Unretained(key)));
+    }
+
+    return it->second.Add(std::move(callback));
+  }
+
   // Adds the specified `callback` to be notified of changes to the ring
   // animation associated with the specified `key`. The `callback` will continue
   // to receive events so long as both `this` and the returned subscription
@@ -80,6 +107,13 @@
     return it->second.Add(std::move(callback));
   }
 
+  // Returns the registered icon animation for the specified `key`.
+  // NOTE: This may return `nullptr` if no such animation is registered.
+  HoldingSpaceProgressIconAnimation* GetIconAnimationForKey(const void* key) {
+    auto it = icon_animations_by_key_.find(key);
+    return it != icon_animations_by_key_.end() ? it->second.get() : nullptr;
+  }
+
   // Returns the registered ring animation for the specified `key`.
   // NOTE: This may return `nullptr` if no such animation is registered.
   HoldingSpaceProgressRingAnimation* GetRingAnimationForKey(const void* key) {
@@ -139,14 +173,55 @@
     UpdateAnimations(/*for_removal=*/false);
   }
 
-  // Erases all ring animations, notifying any animation changed callbacks.
-  void EraseAllRingAnimations() {
-    while (!ring_animations_by_key_.empty()) {
-      auto it = ring_animations_by_key_.begin();
-      const void* key = it->first;
-      ring_animations_by_key_.erase(it);
-      NotifyRingAnimationChangedForKey(key);
+  // Erases all animations, notifying any animation changed callbacks.
+  void EraseAllAnimations() {
+    EraseAllAnimationsForKeyIf(
+        base::BindRepeating([](const void* key) { return true; }));
+  }
+
+  // Erases all animations for the specified `key`, notifying any animation
+  // changed callbacks.
+  void EraseAllAnimationsForKey(const void* key) {
+    EraseIconAnimationForKey(key);
+    EraseRingAnimationForKey(key);
+  }
+
+  // Erases all animations for keys for which `predicate` returns `true`,
+  // notifying any animation changed callbacks.
+  void EraseAllAnimationsForKeyIf(
+      base::RepeatingCallback<bool(const void* key)> predicate) {
+    std::set<const void*> keys_to_erase;
+    for (const auto& icon_animation_by_key : icon_animations_by_key_) {
+      const void* key = icon_animation_by_key.first;
+      if (predicate.Run(key))
+        keys_to_erase.insert(key);
     }
+    for (const auto& ring_animation_by_key : ring_animations_by_key_) {
+      const void* key = ring_animation_by_key.first;
+      if (predicate.Run(key))
+        keys_to_erase.insert(key);
+    }
+    for (const void* key : keys_to_erase)
+      EraseAllAnimationsForKey(key);
+  }
+
+  // Erases the icon animation callback list for the specified `key` if empty.
+  void EraseIconAnimationChangedCallbackListForKeyIfEmpty(const void* key) {
+    auto it = icon_animation_changed_callback_lists_by_key_.find(key);
+    if (it == icon_animation_changed_callback_lists_by_key_.end())
+      return;
+    if (it->second.empty())
+      icon_animation_changed_callback_lists_by_key_.erase(it);
+  }
+
+  // Erases the icon animation for the specified `key`, notifying any animation
+  // changed callbacks.
+  void EraseIconAnimationForKey(const void* key) {
+    auto it = icon_animations_by_key_.find(key);
+    if (it == icon_animations_by_key_.end())
+      return;
+    icon_animations_by_key_.erase(it);
+    NotifyIconAnimationChangedForKey(key);
   }
 
   // Erases the ring animation for the specified `key`, notifying any animation
@@ -159,20 +234,6 @@
     NotifyRingAnimationChangedForKey(key);
   }
 
-  // Erases any ring animation for which `predicate` returns `true`, notifying
-  // any animation changed callbacks.
-  void EraseRingAnimationIf(
-      base::RepeatingCallback<bool(const void* key)> predicate) {
-    std::set<const void*> keys_to_erase;
-    for (const auto& ring_animation_by_key : ring_animations_by_key_) {
-      const void* key = ring_animation_by_key.first;
-      if (predicate.Run(key))
-        keys_to_erase.insert(key);
-    }
-    for (const void* key : keys_to_erase)
-      EraseRingAnimationForKey(key);
-  }
-
   // Erases the ring animation for the specified `key` if it is not of the
   // desired `type`, notifying any animation changed callbacks.
   void EraseRingAnimationIfNotOfTypeForKey(
@@ -194,6 +255,25 @@
       ring_animation_changed_callback_lists_by_key_.erase(it);
   }
 
+  // Ensures that the icon animation for the specified `key` exists. If
+  // necessary, a new animation is created and started, notifying any animation
+  // changed callbacks. NOTE: This method no-ops unless in-progress animations
+  // v2 is enabled.
+  void EnsureIconAnimationForKey(const void* key) {
+    if (!features::IsHoldingSpaceInProgressAnimationV2Enabled())
+      return;
+
+    auto it = icon_animations_by_key_.find(key);
+    if (it != icon_animations_by_key_.end())
+      return;
+
+    icon_animations_by_key_
+        .emplace(key, std::make_unique<HoldingSpaceProgressIconAnimation>())
+        .first->second->Start();
+
+    NotifyIconAnimationChangedForKey(key);
+  }
+
   // Ensures that the ring animation for the specified `key` is of the desired
   // `type`. If necessary, a new animation is created and started, notifying any
   // animation changed callbacks.
@@ -217,12 +297,29 @@
         .animation = std::move(animation),
         .subscription = std::move(subscription)};
 
-    ring_animations_by_key_.emplace(key, std::move(subscribed_animation))
-        .first->second.animation->Start();
+    if (it == ring_animations_by_key_.end()) {
+      ring_animations_by_key_.emplace(key, std::move(subscribed_animation))
+          .first->second.animation->Start();
+    } else {
+      it->second = std::move(subscribed_animation);
+      it->second.animation->Start();
+    }
 
     NotifyRingAnimationChangedForKey(key);
   }
 
+  // Notifies any icon animation changed callbacks registered for the specified
+  // `key` that the associated animation has changed.
+  void NotifyIconAnimationChangedForKey(const void* key) {
+    auto it = icon_animation_changed_callback_lists_by_key_.find(key);
+    if (it == icon_animation_changed_callback_lists_by_key_.end())
+      return;
+    auto animation_it = icon_animations_by_key_.find(key);
+    it->second.Notify(animation_it != icon_animations_by_key_.end()
+                          ? animation_it->second.get()
+                          : nullptr);
+  }
+
   // Notifies any ring animation changed callbacks registered for the specified
   // `key` that the associated animation has changed.
   void NotifyRingAnimationChangedForKey(const void* key) {
@@ -238,17 +335,17 @@
   // Updates animation state for the current `model_` state. If `for_removal` is
   // `true`, the update was triggered by holding space item removal.
   void UpdateAnimations(bool for_removal) {
-    // If no `model_` is currently attached, there should be no ring animations.
-    // Ring animations will be updated if and when a `model_` is attached.
+    // If no `model_` is currently attached, there should be no animations.
+    // Animations will be updated if and when a `model_` is attached.
     if (model_ == nullptr) {
       cumulative_progress_ = HoldingSpaceProgress();
-      EraseAllRingAnimations();
+      EraseAllAnimations();
       return;
     }
 
-    // Clean up any ring animations associated with holding space items that are
-    // no longer present in the attached `model_`.
-    EraseRingAnimationIf(base::BindRepeating(
+    // Clean up all animations associated with holding space items that are no
+    // longer present in the attached `model_`.
+    EraseAllAnimationsForKeyIf(base::BindRepeating(
         [](const std::vector<std::unique_ptr<HoldingSpaceItem>>& items,
            const void* controller, const void* key) {
           return key != controller &&
@@ -263,10 +360,9 @@
     // Iterate over each holding space item in the attached `model_`.
     for (const auto& item : model_->items()) {
       // If an `item` is not initialized or is not visibly in-progress, it
-      // shouldn't contribute to `cumulative_progress_` nor have a ring
-      // animation.
+      // shouldn't contribute to `cumulative_progress_` nor have an animation.
       if (!item->IsInitialized() || item->progress().IsHidden()) {
-        EraseRingAnimationForKey(item.get());
+        EraseAllAnimationsForKey(item.get());
         continue;
       }
 
@@ -274,9 +370,10 @@
       // animation if one was previously created and started. This would only
       // have happened in response to the `item` transitioning to completion at
       // runtime, as items that are already complete on creation are not
-      // animated. Any other type of ring animation should be cleared. Note that
-      // a completed `item` does not contribute to `cumulative_progress_`.
+      // animated. Any other type of animation should be cleared. Note that a
+      // completed `item` does not contribute to `cumulative_progress_`.
       if (item->progress().IsComplete()) {
+        EraseIconAnimationForKey(item.get());
         EraseRingAnimationIfNotOfTypeForKey(
             item.get(), HoldingSpaceProgressRingAnimation::Type::kPulse);
         continue;
@@ -284,6 +381,10 @@
 
       cumulative_progress_ += item->progress();
 
+      // Because the `item` is in-progress, an icon animation should be
+      // associated with it (if one does not already exist).
+      EnsureIconAnimationForKey(item.get());
+
       // If the `item` is in an indeterminate state, an indeterminate animation
       // should be associated with it (if one does not already exist).
       if (item->progress().IsIndeterminate()) {
@@ -299,6 +400,10 @@
     }
 
     if (cumulative_progress_.IsComplete()) {
+      // Because `cumulative_progress_` is complete, the `controller_` should
+      // not have an associated icon animation.
+      EraseIconAnimationForKey(controller_);
+
       if (!last_cumulative_progress.IsComplete()) {
         if (for_removal) {
           // If `cumulative_progress_` has just become complete as a result of
@@ -322,6 +427,10 @@
       return;
     }
 
+    // Because `cumulative_progress_` is in-progress, the `controller_` should
+    // have an associated icon animation.
+    EnsureIconAnimationForKey(controller_);
+
     // If `cumulative_progress_` is in an indeterminate state, an indeterminate
     // animation should be associated with the `controller_` (if one does not
     // already exist).
@@ -369,9 +478,22 @@
   // which time a pulse animation is created and started.
   HoldingSpaceProgress cumulative_progress_;
 
+  // Mapping of keys to their associated progress icon animations. For
+  // cumulative progress, the animation is keyed on a pointer to the holding
+  // space `controller_`. For individual item progress, the animation is keyed
+  // on a pointer to the holding space item itself.
+  std::map<const void*, std::unique_ptr<HoldingSpaceProgressIconAnimation>>
+      icon_animations_by_key_;
+
+  // Mapping of keys to their associated icon animation changed callback lists.
+  // Whenever an animation for a given key is changed, the callback list for
+  // that key will be notified.
+  std::map<const void*, ProgressIconAnimationChangedCallbackList>
+      icon_animation_changed_callback_lists_by_key_;
+
   struct SubscribedProgressRingAnimation {
     std::unique_ptr<HoldingSpaceProgressRingAnimation> animation;
-    base::RepeatingClosureList::Subscription subscription;
+    base::CallbackListSubscription subscription;
   };
 
   // Mapping of keys to their associated progress ring animations. For
@@ -418,6 +540,14 @@
 }
 
 base::CallbackListSubscription
+HoldingSpaceAnimationRegistry::AddProgressIconAnimationChangedCallbackForKey(
+    const void* key,
+    ProgressIconAnimationChangedCallbackList::CallbackType callback) {
+  return progress_indicator_animation_delegate_
+      ->AddIconAnimationChangedCallbackForKey(key, std::move(callback));
+}
+
+base::CallbackListSubscription
 HoldingSpaceAnimationRegistry::AddProgressRingAnimationChangedCallbackForKey(
     const void* key,
     ProgressRingAnimationChangedCallbackList::CallbackType callback) {
@@ -425,6 +555,11 @@
       ->AddRingAnimationChangedCallbackForKey(key, std::move(callback));
 }
 
+HoldingSpaceProgressIconAnimation*
+HoldingSpaceAnimationRegistry::GetProgressIconAnimationForKey(const void* key) {
+  return progress_indicator_animation_delegate_->GetIconAnimationForKey(key);
+}
+
 HoldingSpaceProgressRingAnimation*
 HoldingSpaceAnimationRegistry::GetProgressRingAnimationForKey(const void* key) {
   return progress_indicator_animation_delegate_->GetRingAnimationForKey(key);
diff --git a/ash/system/holding_space/holding_space_animation_registry.h b/ash/system/holding_space/holding_space_animation_registry.h
index cca9c4e..cf7979e 100644
--- a/ash/system/holding_space/holding_space_animation_registry.h
+++ b/ash/system/holding_space/holding_space_animation_registry.h
@@ -5,6 +5,7 @@
 #ifndef ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ANIMATION_REGISTRY_H_
 #define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_ANIMATION_REGISTRY_H_
 
+#include "ash/ash_export.h"
 #include "ash/shell.h"
 #include "ash/shell_observer.h"
 #include "base/callback.h"
@@ -13,6 +14,7 @@
 
 namespace ash {
 
+class HoldingSpaceProgressIconAnimation;
 class HoldingSpaceProgressRingAnimation;
 
 // A lazily initialized singleton registry for holding space animations. Since
@@ -20,7 +22,7 @@
 // different UI components as well have a lifetime which is decoupled from UI
 // component lifetime. Note that the singleton may only exist while `Shell` is
 // alive and will automatically delete itself when `Shell` is being destroyed.
-class HoldingSpaceAnimationRegistry : public ShellObserver {
+class ASH_EXPORT HoldingSpaceAnimationRegistry : public ShellObserver {
  public:
   HoldingSpaceAnimationRegistry(const HoldingSpaceAnimationRegistry&) = delete;
   HoldingSpaceAnimationRegistry& operator=(
@@ -32,22 +34,53 @@
   // when `Shell` is being destroyed.
   static HoldingSpaceAnimationRegistry* GetInstance();
 
+  using ProgressIconAnimationChangedCallbackList =
+      base::RepeatingCallbackList<void(HoldingSpaceProgressIconAnimation*)>;
+
+  // Adds the specified `callback` to be notified of changes to the progress
+  // icon animation associated with the specified `key`. Progress icon
+  // animations independently drive the animation of properties for a progress
+  // indicator's inner icon, as opposed to progress ring animations which
+  // independently drive the animation of properties for a progress indicator's
+  // outer ring. The `callback` will continue to receive events so long as both
+  // `this` and the returned subscription exist.
+  base::CallbackListSubscription AddProgressIconAnimationChangedCallbackForKey(
+      const void* key,
+      ProgressIconAnimationChangedCallbackList::CallbackType callback);
+
   using ProgressRingAnimationChangedCallbackList =
       base::RepeatingCallbackList<void(HoldingSpaceProgressRingAnimation*)>;
 
   // Adds the specified `callback` to be notified of changes to the progress
-  // ring animation associated with the specified `key`. The `callback` will
-  // continue to receive events so long as both `this` and the returned
-  // subscription exist.
+  // ring animation associated with the specified `key`. Progress ring
+  // animations independently drive the animation of properties for a progress
+  // indicator's outer ring, as opposed to progress icon animations which
+  // independently drive the animation of properties for a progress indicator's
+  // inner icon. The `callback` will continue to receive events so long as both
+  // `this` and the returned subscription exist.
   base::CallbackListSubscription AddProgressRingAnimationChangedCallbackForKey(
       const void* key,
       ProgressRingAnimationChangedCallbackList::CallbackType callback);
 
+  // Returns the progress icon animation registered for the specified `key`.
+  // Progress icon animations independently drive the animation of properties
+  // for a progress indicator's inner icon, as opposed to progress ring
+  // animations which independently drive the animation of properties for a
+  // progress indicator's outer ring. For cumulative progress, the animation is
+  // keyed on a pointer to the holding space controller. For individual item
+  // progress, the animation is keyed on a pointer to the holding space item
+  // itself. NOTE: This may return `nullptr` if no such animation is registered.
+  HoldingSpaceProgressIconAnimation* GetProgressIconAnimationForKey(
+      const void* key);
+
   // Returns the progress ring animation registered for the specified `key`.
-  // For cumulative progress, the animation is keyed on a pointer to the holding
-  // space controller. For individual item progress, the animation is keyed on a
-  // pointer to the holding space item itself.
-  // NOTE: This may return `nullptr` if no such animation is registered.
+  // Progress ring animations independently drive the animation of properties
+  // for a progress indicator's outer ring, as opposed to progress icon
+  // animations which independently drive the animation of properties for a
+  // progress indicator's inner icon. For cumulative progress, the animation is
+  // keyed on a pointer to the holding space controller. For individual item
+  // progress, the animation is keyed on a pointer to the holding space item
+  // itself. NOTE: This may return `nullptr` if no such animation is registered.
   HoldingSpaceProgressRingAnimation* GetProgressRingAnimationForKey(
       const void* key);
 
diff --git a/ash/system/holding_space/holding_space_animation_registry_unittest.cc b/ash/system/holding_space/holding_space_animation_registry_unittest.cc
new file mode 100644
index 0000000..74377bd
--- /dev/null
+++ b/ash/system/holding_space/holding_space_animation_registry_unittest.cc
@@ -0,0 +1,236 @@
+// Copyright 2022 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/system/holding_space/holding_space_animation_registry.h"
+
+#include <array>
+
+#include "ash/constants/ash_features.h"
+#include "ash/public/cpp/holding_space/holding_space_client.h"
+#include "ash/public/cpp/holding_space/holding_space_controller.h"
+#include "ash/public/cpp/holding_space/holding_space_image.h"
+#include "ash/public/cpp/holding_space/holding_space_item.h"
+#include "ash/public/cpp/holding_space/holding_space_model.h"
+#include "ash/public/cpp/holding_space/holding_space_prefs.h"
+#include "ash/public/cpp/holding_space/holding_space_util.h"
+#include "ash/public/cpp/holding_space/mock_holding_space_client.h"
+#include "ash/system/holding_space/holding_space_progress_icon_animation.h"
+#include "ash/system/holding_space/holding_space_progress_indicator_animation.h"
+#include "ash/system/holding_space/holding_space_progress_ring_animation.h"
+#include "ash/test/ash_test_base.h"
+#include "base/barrier_closure.h"
+#include "base/strings/strcat.h"
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace ash {
+namespace {
+
+constexpr char kTestUser[] = "user@test";
+
+// Helpers ---------------------------------------------------------------------
+
+template <typename... T>
+base::RepeatingCallback<void(T...)> IgnoreArgs(base::RepeatingClosure closure) {
+  return base::BindRepeating([](T...) {}).Then(std::move(closure));
+}
+
+// HoldingSpaceAnimationRegistryTest -------------------------------------------
+
+class HoldingSpaceAnimationRegistryTest : public AshTestBase,
+                                          public testing::WithParamInterface<
+                                              /*animation_v2_enabled=*/bool> {
+ public:
+  HoldingSpaceAnimationRegistryTest() {
+    scoped_feature_list_.InitWithFeatureState(
+        features::kHoldingSpaceInProgressAnimationV2, IsAnimationV2Enabled());
+  }
+
+  bool IsAnimationV2Enabled() const { return GetParam(); }
+
+  // AshTestBase:
+  void SetUp() override {
+    AshTestBase::SetUp();
+
+    // Initialize holding space for `kTestUser`.
+    AccountId user_account = AccountId::FromUserEmail(kTestUser);
+    HoldingSpaceController::Get()->RegisterClientAndModelForUser(
+        user_account, client(), model());
+    GetSessionControllerClient()->AddUserSession(kTestUser);
+    holding_space_prefs::MarkTimeOfFirstAvailability(
+        GetSessionControllerClient()->GetUserPrefService(user_account));
+  }
+
+  HoldingSpaceItem* AddItem(
+      HoldingSpaceItem::Type type,
+      const base::FilePath& path,
+      const HoldingSpaceProgress& progress = HoldingSpaceProgress()) {
+    GURL file_system_url(
+        base::StrCat({"filesystem:", path.BaseName().value()}));
+    std::unique_ptr<HoldingSpaceItem> item =
+        HoldingSpaceItem::CreateFileBackedItem(
+            type, path, file_system_url, progress,
+            base::BindOnce(
+                [](HoldingSpaceItem::Type type, const base::FilePath& path) {
+                  return std::make_unique<HoldingSpaceImage>(
+                      holding_space_util::GetMaxImageSizeForType(type), path,
+                      /*async_bitmap_resolver=*/base::DoNothing());
+                }));
+    HoldingSpaceItem* item_ptr = item.get();
+    model()->AddItem(std::move(item));
+    return item_ptr;
+  }
+
+  void ExpectProgressIconAnimationExistsForKey(const void* key, bool exists) {
+    auto* animation = registry()->GetProgressIconAnimationForKey(key);
+    EXPECT_EQ(!!animation, exists);
+  }
+
+  void ExpectProgressRingAnimationOfTypeForKey(
+      const void* key,
+      const absl::optional<HoldingSpaceProgressRingAnimation::Type>& type) {
+    auto* animation = registry()->GetProgressRingAnimationForKey(key);
+    EXPECT_EQ(!!animation, type.has_value());
+    if (animation && type.has_value())
+      EXPECT_EQ(animation->type(), type.value());
+  }
+
+  void StartSession() {
+    AccountId user_account = AccountId::FromUserEmail(kTestUser);
+    GetSessionControllerClient()->SwitchActiveUser(user_account);
+  }
+
+  HoldingSpaceController* controller() { return HoldingSpaceController::Get(); }
+
+  testing::NiceMock<MockHoldingSpaceClient>* client() {
+    return &holding_space_client_;
+  }
+
+  HoldingSpaceModel* model() { return &holding_space_model_; }
+
+  HoldingSpaceAnimationRegistry* registry() {
+    return HoldingSpaceAnimationRegistry::GetInstance();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  testing::NiceMock<MockHoldingSpaceClient> holding_space_client_;
+  HoldingSpaceModel holding_space_model_;
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         HoldingSpaceAnimationRegistryTest,
+                         /*in_progress_animation_v2_enabled=*/testing::Bool());
+
+}  // namespace
+
+// Tests -----------------------------------------------------------------------
+
+TEST_P(HoldingSpaceAnimationRegistryTest, ProgressIndicatorAnimations) {
+  using Type = HoldingSpaceProgressRingAnimation::Type;
+
+  StartSession();
+
+  // Verify initial animation `registry()` state.
+  ExpectProgressIconAnimationExistsForKey(controller(), false);
+  ExpectProgressRingAnimationOfTypeForKey(controller(), absl::nullopt);
+
+  // Add a completed item to the `model()`.
+  HoldingSpaceItem* item_0 =
+      AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/0"));
+
+  // Verify animation `registry()` state.
+  ExpectProgressIconAnimationExistsForKey(controller(), false);
+  ExpectProgressRingAnimationOfTypeForKey(controller(), absl::nullopt);
+  ExpectProgressIconAnimationExistsForKey(item_0, false);
+  ExpectProgressRingAnimationOfTypeForKey(item_0, absl::nullopt);
+
+  // Add an indeterminately in-progress item to the `model()`.
+  HoldingSpaceItem* item_1 =
+      AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/1"),
+              HoldingSpaceProgress(0, absl::nullopt));
+
+  // Verify animation `registry()` state.
+  ExpectProgressIconAnimationExistsForKey(controller(), IsAnimationV2Enabled());
+  ExpectProgressRingAnimationOfTypeForKey(controller(), Type::kIndeterminate);
+  ExpectProgressIconAnimationExistsForKey(item_0, false);
+  ExpectProgressRingAnimationOfTypeForKey(item_0, absl::nullopt);
+  ExpectProgressIconAnimationExistsForKey(item_1, IsAnimationV2Enabled());
+  ExpectProgressRingAnimationOfTypeForKey(item_1, Type::kIndeterminate);
+
+  // Add a determinately in-progress item to the `model()`.
+  HoldingSpaceItem* item_2 =
+      AddItem(HoldingSpaceItem::Type::kDownload, base::FilePath("/tmp/2"),
+              HoldingSpaceProgress(0, 10));
+
+  // Verify animation `registry()` state.
+  ExpectProgressIconAnimationExistsForKey(controller(), IsAnimationV2Enabled());
+  ExpectProgressRingAnimationOfTypeForKey(controller(), Type::kIndeterminate);
+  ExpectProgressIconAnimationExistsForKey(item_0, false);
+  ExpectProgressRingAnimationOfTypeForKey(item_0, absl::nullopt);
+  ExpectProgressIconAnimationExistsForKey(item_1, IsAnimationV2Enabled());
+  ExpectProgressRingAnimationOfTypeForKey(item_1, Type::kIndeterminate);
+  ExpectProgressIconAnimationExistsForKey(item_2, IsAnimationV2Enabled());
+  ExpectProgressRingAnimationOfTypeForKey(item_2, absl::nullopt);
+
+  // Complete the first in-progress item.
+  model()->UpdateItem(item_1->id())->SetProgress(HoldingSpaceProgress(10, 10));
+
+  // Verify animation `registry()` state.
+  ExpectProgressIconAnimationExistsForKey(controller(), IsAnimationV2Enabled());
+  ExpectProgressRingAnimationOfTypeForKey(controller(), absl::nullopt);
+  ExpectProgressIconAnimationExistsForKey(item_0, false);
+  ExpectProgressRingAnimationOfTypeForKey(item_0, absl::nullopt);
+  ExpectProgressIconAnimationExistsForKey(item_1, false);
+  ExpectProgressRingAnimationOfTypeForKey(item_1, Type::kPulse);
+  ExpectProgressIconAnimationExistsForKey(item_2, IsAnimationV2Enabled());
+  ExpectProgressRingAnimationOfTypeForKey(item_2, absl::nullopt);
+
+  // Complete the second in-progress item.
+  model()->UpdateItem(item_2->id())->SetProgress(HoldingSpaceProgress(10, 10));
+
+  // Verify animation `registry()` state.
+  ExpectProgressIconAnimationExistsForKey(controller(), false);
+  ExpectProgressRingAnimationOfTypeForKey(controller(), Type::kPulse);
+  ExpectProgressIconAnimationExistsForKey(item_0, false);
+  ExpectProgressRingAnimationOfTypeForKey(item_0, absl::nullopt);
+  ExpectProgressIconAnimationExistsForKey(item_1, false);
+  ExpectProgressRingAnimationOfTypeForKey(item_1, Type::kPulse);
+  ExpectProgressIconAnimationExistsForKey(item_2, false);
+  ExpectProgressRingAnimationOfTypeForKey(item_2, Type::kPulse);
+
+  {
+    // Wait for `kPulse` animations to complete.
+    base::RunLoop run_loop;
+    auto pulse_animation_complete = base::BarrierClosure(
+        3u, base::BindLambdaForTesting([&]() {
+          // Verify animation `registry()` state.
+          ExpectProgressIconAnimationExistsForKey(controller(), false);
+          ExpectProgressRingAnimationOfTypeForKey(controller(), absl::nullopt);
+          ExpectProgressIconAnimationExistsForKey(item_0, false);
+          ExpectProgressRingAnimationOfTypeForKey(item_0, absl::nullopt);
+          ExpectProgressIconAnimationExistsForKey(item_1, false);
+          ExpectProgressRingAnimationOfTypeForKey(item_1, absl::nullopt);
+          ExpectProgressIconAnimationExistsForKey(item_2, false);
+          ExpectProgressRingAnimationOfTypeForKey(item_2, absl::nullopt);
+          run_loop.Quit();
+        }));
+
+    std::array<base::CallbackListSubscription, 3u> subscriptions = {
+        registry()->AddProgressRingAnimationChangedCallbackForKey(
+            controller(), IgnoreArgs<HoldingSpaceProgressRingAnimation*>(
+                              pulse_animation_complete)),
+        registry()->AddProgressRingAnimationChangedCallbackForKey(
+            item_1, IgnoreArgs<HoldingSpaceProgressRingAnimation*>(
+                        pulse_animation_complete)),
+        registry()->AddProgressRingAnimationChangedCallbackForKey(
+            item_2, IgnoreArgs<HoldingSpaceProgressRingAnimation*>(
+                        pulse_animation_complete))};
+
+    run_loop.Run();
+  }
+}
+
+}  // namespace ash
diff --git a/ash/system/holding_space/holding_space_progress_icon_animation.cc b/ash/system/holding_space/holding_space_progress_icon_animation.cc
new file mode 100644
index 0000000..1cd2d18
--- /dev/null
+++ b/ash/system/holding_space/holding_space_progress_icon_animation.cc
@@ -0,0 +1,24 @@
+// Copyright 2022 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/system/holding_space/holding_space_progress_icon_animation.h"
+
+namespace ash {
+
+// Animation.
+constexpr base::TimeDelta kAnimationDuration = base::Milliseconds(200);
+
+HoldingSpaceProgressIconAnimation::HoldingSpaceProgressIconAnimation()
+    : HoldingSpaceProgressIndicatorAnimation(kAnimationDuration,
+                                             /*is_cyclic=*/true) {}
+
+HoldingSpaceProgressIconAnimation::~HoldingSpaceProgressIconAnimation() =
+    default;
+
+void HoldingSpaceProgressIconAnimation::UpdateAnimatableProperties(
+    double fraction) {
+  // TODO(dmblack): Implement.
+}
+
+}  // namespace ash
diff --git a/ash/system/holding_space/holding_space_progress_icon_animation.h b/ash/system/holding_space/holding_space_progress_icon_animation.h
new file mode 100644
index 0000000..2026405
--- /dev/null
+++ b/ash/system/holding_space/holding_space_progress_icon_animation.h
@@ -0,0 +1,30 @@
+// Copyright 2022 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_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_ICON_ANIMATION_H_
+#define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_ICON_ANIMATION_H_
+
+#include "ash/system/holding_space/holding_space_progress_indicator_animation.h"
+
+namespace ash {
+
+// An animation for a `HoldingSpaceProgressIndicator`'s icon.
+class HoldingSpaceProgressIconAnimation
+    : public HoldingSpaceProgressIndicatorAnimation {
+ public:
+  HoldingSpaceProgressIconAnimation();
+  HoldingSpaceProgressIconAnimation(const HoldingSpaceProgressIconAnimation&) =
+      delete;
+  HoldingSpaceProgressIconAnimation& operator=(
+      const HoldingSpaceProgressIconAnimation&) = delete;
+  ~HoldingSpaceProgressIconAnimation() override;
+
+ private:
+  // HoldingSpaceProgressIndicatorAnimation:
+  void UpdateAnimatableProperties(double fraction) override;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_PROGRESS_ICON_ANIMATION_H_
diff --git a/ash/system/holding_space/holding_space_progress_indicator.cc b/ash/system/holding_space/holding_space_progress_indicator.cc
index dd78604..d7a81f52 100644
--- a/ash/system/holding_space/holding_space_progress_indicator.cc
+++ b/ash/system/holding_space/holding_space_progress_indicator.cc
@@ -13,7 +13,8 @@
 #include "ash/public/cpp/holding_space/holding_space_progress.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/system/holding_space/holding_space_progress_ring_indeterminate_animation.h"
+#include "ash/system/holding_space/holding_space_progress_icon_animation.h"
+#include "ash/system/holding_space/holding_space_progress_ring_animation.h"
 #include "base/scoped_observation.h"
 #include "third_party/skia/include/core/SkPath.h"
 #include "third_party/skia/include/core/SkPathBuilder.h"
@@ -304,6 +305,23 @@
   HoldingSpaceAnimationRegistry* animation_registry =
       HoldingSpaceAnimationRegistry::GetInstance();
 
+  // Register to be notified of changes to the icon animation associated with
+  // this progress indicator's `animation_key_`. Note that it is safe to use a
+  // raw pointer here since `this` owns the subscription.
+  icon_animation_changed_subscription_ =
+      animation_registry->AddProgressIconAnimationChangedCallbackForKey(
+          animation_key_,
+          base::BindRepeating(
+              &HoldingSpaceProgressIndicator::OnProgressIconAnimationChanged,
+              base::Unretained(this)));
+
+  // If an `icon_animation` is already registered, perform additional
+  // initialization.
+  HoldingSpaceProgressIconAnimation* icon_animation =
+      animation_registry->GetProgressIconAnimationForKey(animation_key_);
+  if (icon_animation)
+    OnProgressIconAnimationChanged(icon_animation);
+
   // Register to be notified of changes to the ring animation associated with
   // this progress indicator's `animation_key_`. Note that it is safe to use a
   // raw pointer here since `this` owns the subscription.
@@ -314,11 +332,12 @@
               &HoldingSpaceProgressIndicator::OnProgressRingAnimationChanged,
               base::Unretained(this)));
 
-  // If an `animation` is already registered, perform additional initialization.
-  HoldingSpaceProgressRingAnimation* animation =
+  // If `ring_animation` is already registered, perform additional
+  // initialization.
+  HoldingSpaceProgressRingAnimation* ring_animation =
       animation_registry->GetProgressRingAnimationForKey(animation_key_);
-  if (animation)
-    OnProgressRingAnimationChanged(animation);
+  if (ring_animation)
+    OnProgressRingAnimationChanged(ring_animation);
 }
 
 HoldingSpaceProgressIndicator::~HoldingSpaceProgressIndicator() = default;
@@ -384,7 +403,7 @@
 
 void HoldingSpaceProgressIndicator::OnPaintLayer(
     const ui::PaintContext& context) {
-  // Look up the associated `animation` (if one exists).
+  // Look up the associated `ring_animation` (if one exists).
   HoldingSpaceProgressRingAnimation* ring_animation =
       HoldingSpaceAnimationRegistry::GetInstance()
           ->GetProgressRingAnimationForKey(animation_key_);
@@ -503,6 +522,20 @@
     progress_changed_callback_list_.Notify();
 }
 
+void HoldingSpaceProgressIndicator::OnProgressIconAnimationChanged(
+    HoldingSpaceProgressIconAnimation* animation) {
+  // Trigger repaint of this progress indicator on `animation` updates. Note
+  // that it is safe to use a raw pointer here since `this` owns the
+  // subscription.
+  if (animation) {
+    icon_animation_updated_subscription_ =
+        animation->AddAnimationUpdatedCallback(
+            base::BindRepeating(&HoldingSpaceProgressIndicator::InvalidateLayer,
+                                base::Unretained(this)));
+  }
+  InvalidateLayer();
+}
+
 void HoldingSpaceProgressIndicator::OnProgressRingAnimationChanged(
     HoldingSpaceProgressRingAnimation* animation) {
   // Trigger repaint of this progress indicator on `animation` updates. Note
diff --git a/ash/system/holding_space/holding_space_progress_indicator.h b/ash/system/holding_space/holding_space_progress_indicator.h
index 3a57b91..bb151a8 100644
--- a/ash/system/holding_space/holding_space_progress_indicator.h
+++ b/ash/system/holding_space/holding_space_progress_indicator.h
@@ -19,6 +19,7 @@
 
 class HoldingSpaceController;
 class HoldingSpaceItem;
+class HoldingSpaceProgressIconAnimation;
 class HoldingSpaceProgressRingAnimation;
 
 // A class owning a `ui::Layer` which paints indication of progress.
@@ -92,6 +93,12 @@
   void OnPaintLayer(const ui::PaintContext& context) override;
   void UpdateVisualState() override;
 
+  // Invoked when the icon `animation` associated with this progress indicator's
+  // `animation_key_` has changed in the `HoldingSpaceAnimationRegistry`.
+  // NOTE: The specified `animation` may be `nullptr`.
+  void OnProgressIconAnimationChanged(
+      HoldingSpaceProgressIconAnimation* animation);
+
   // Invoked when the ring `animation` associated with this progress indicator's
   // `animation_key_` has changed in the `HoldingSpaceAnimationRegistry`.
   // NOTE: The specified `animation` may be `nullptr`.
@@ -104,18 +111,29 @@
   // be painted for the cached `progress_`.
   const void* const animation_key_;
 
+  // A subscription to receive events when the icon animation associated with
+  // this progress indicator's `animation_key_` has changed in the
+  // `HoldingSpaceAnimationRegistry`.
+  base::CallbackListSubscription icon_animation_changed_subscription_;
+
+  // A subscription to receive events on updates to the icon animation owned by
+  // the `HoldingSpaceAnimationRegistry` which is associated with this progress
+  // indicator's `animation_key_`. On icon animation update, the progress
+  // indicator will `InvalidateLayer()` to trigger paint of the next animation
+  // frame.
+  base::CallbackListSubscription icon_animation_updated_subscription_;
+
   // A subscription to receive events when the ring animation associated with
   // this progress indicator's `animation_key_` has changed in the
   // `HoldingSpaceAnimationRegistry`.
-  HoldingSpaceAnimationRegistry::ProgressRingAnimationChangedCallbackList::
-      Subscription ring_animation_changed_subscription_;
+  base::CallbackListSubscription ring_animation_changed_subscription_;
 
   // A subscription to receive events on updates to the ring animation owned by
   // the `HoldingSpaceAnimationRegistry` which is associated with this progress
   // indicator's `animation_key_`. On ring animation update, the progress
   // indicator will `InvalidateLayer()` to trigger paint of the next animation
   // frame.
-  base::RepeatingClosureList::Subscription ring_animation_updated_subscription_;
+  base::CallbackListSubscription ring_animation_updated_subscription_;
 
   // Cached progress returned from `CalculateProgress()` just prior to painting.
   // NOTE: If absent, progress is indeterminate.
diff --git a/ash/system/message_center/ash_notification_view.cc b/ash/system/message_center/ash_notification_view.cc
index e0efd24..d3c98b5 100644
--- a/ash/system/message_center/ash_notification_view.cc
+++ b/ash/system/message_center/ash_notification_view.cc
@@ -10,6 +10,9 @@
 #include "ash/public/cpp/rounded_image_view.h"
 #include "ash/public/cpp/style/color_provider.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/root_window_controller.h"
+#include "ash/shelf/shelf.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/style/icon_button.h"
@@ -21,6 +24,7 @@
 #include "ash/system/message_center/message_center_style.h"
 #include "ash/system/message_center/message_center_utils.h"
 #include "ash/system/tray/tray_constants.h"
+#include "ash/wm/work_area_insets.h"
 #include "base/bind.h"
 #include "base/check.h"
 #include "base/time/time.h"
@@ -54,6 +58,7 @@
 #include "ui/views/controls/button/image_button_factory.h"
 #include "ui/views/controls/focus_ring.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/controls/scroll_view.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/box_layout_view.h"
 #include "ui/views/layout/flex_layout.h"
@@ -400,14 +405,23 @@
                    .CopyAddressTo(&collapsed_summary_view_)
                    .Build());
 
-  AddChildView(views::Builder<views::BoxLayoutView>()
-                   .CopyAddressTo(&grouped_notifications_container_)
-                   .SetOrientation(Orientation::kVertical)
-                   .SetInsideBorderInsets(kGroupedNotificationContainerInsets)
-                   .SetBetweenChildSpacing(
-                       IsExpanded() ? kGroupedNotificationsExpandedSpacing
-                                    : kGroupedNotificationsCollapsedSpacing)
-                   .Build());
+  if (!notification.group_child()) {
+    AddChildView(
+        views::Builder<views::ScrollView>()
+            .CopyAddressTo(&grouped_notifications_scroll_view_)
+            .SetBackgroundColor(absl::nullopt)
+            .SetDrawOverflowIndicator(false)
+            .ClipHeightTo(0, std::numeric_limits<int>::max())
+            .SetContents(
+                views::Builder<views::BoxLayoutView>()
+                    .CopyAddressTo(&grouped_notifications_container_)
+                    .SetOrientation(Orientation::kVertical)
+                    .SetInsideBorderInsets(kGroupedNotificationContainerInsets)
+                    .SetBetweenChildSpacing(
+                        IsExpanded() ? kGroupedNotificationsExpandedSpacing
+                                     : kGroupedNotificationsCollapsedSpacing))
+            .Build());
+  }
 
   AddChildView(CreateActionsRow(std::make_unique<views::FlexLayout>()));
 
@@ -656,24 +670,30 @@
 
   expand_button_->SetExpanded(expanded);
 
-  static_cast<views::BoxLayout*>(
-      grouped_notifications_container_->GetLayoutManager())
-      ->set_between_child_spacing(expanded
-                                      ? kGroupedNotificationsExpandedSpacing
-                                      : kGroupedNotificationsCollapsedSpacing);
+  if (is_grouped_parent_view_) {
+    if (shown_in_popup_) {
+      grouped_notifications_scroll_view_->ClipHeightTo(
+          0, CalculateMaxHeightForGroupedNotifications());
+    }
 
-  int notification_count = 0;
-  for (auto* child : grouped_notifications_container_->children()) {
-    auto* notification_view = static_cast<AshNotificationView*>(child);
-    notification_view->AnimateGroupedChildExpandedCollapse(expanded);
-    notification_view->SetGroupedChildExpanded(expanded);
-    notification_count++;
-    if (notification_count >
-        message_center_style::kMaxGroupedNotificationsInCollapsedState) {
-      notification_view->SetVisible(expanded);
+    static_cast<views::BoxLayout*>(
+        grouped_notifications_container_->GetLayoutManager())
+        ->set_between_child_spacing(
+            expanded ? kGroupedNotificationsExpandedSpacing
+                     : kGroupedNotificationsCollapsedSpacing);
+
+    int notification_count = 0;
+    for (auto* child : grouped_notifications_container_->children()) {
+      auto* notification_view = static_cast<AshNotificationView*>(child);
+      notification_view->AnimateGroupedChildExpandedCollapse(expanded);
+      notification_view->SetGroupedChildExpanded(expanded);
+      notification_count++;
+      if (notification_count >
+          message_center_style::kMaxGroupedNotificationsInCollapsedState) {
+        notification_view->SetVisible(expanded);
+      }
     }
   }
-
   NotificationViewBase::UpdateViewForExpandedState(expanded);
 }
 
@@ -682,13 +702,16 @@
   is_grouped_child_view_ = notification.group_child();
   is_grouped_parent_view_ = notification.group_parent();
 
-  grouped_notifications_container_->SetVisible(is_grouped_parent_view_);
+  if (!is_grouped_child_view_)
+    grouped_notifications_container_->SetVisible(is_grouped_parent_view_);
+
   header_row()->SetVisible(!is_grouped_child_view_);
   UpdateMessageViewInExpandedState(notification);
 
   NotificationViewBase::UpdateWithNotification(notification);
 
   CreateOrUpdateSnoozeButton(notification);
+  UpdateIconAndButtonsColor();
 }
 
 void AshNotificationView::CreateOrUpdateHeaderView(
@@ -822,8 +845,6 @@
   views::View::OnThemeChanged();
   UpdateBackground(top_radius_, bottom_radius_);
 
-  UpdateAppIconView();
-
   header_row()->SetColor(AshColorProvider::Get()->GetContentLayerColor(
       AshColorProvider::ContentLayerType::kTextColorSecondary));
 
@@ -831,14 +852,7 @@
       AshColorProvider::Get()->GetControlsLayerColor(
           AshColorProvider::ControlsLayerType::kFocusRingColor));
 
-  auto* notification =
-      message_center::MessageCenter::Get()->FindVisibleNotificationById(
-          notification_id());
-  SkColor button_color = notification->accent_color().value_or(
-      AshColorProvider::Get()->GetContentLayerColor(
-          AshColorProvider::ContentLayerType::kButtonLabelColorBlue));
-  if (snooze_button_)
-    snooze_button_->SetIconColor(button_color);
+  UpdateIconAndButtonsColor();
 }
 
 std::unique_ptr<message_center::NotificationInputContainer>
@@ -1021,23 +1035,9 @@
       (is_grouped_child_view_ && !notification->icon().IsEmpty()))
     return;
 
-  SkColor accent_color = notification->accent_color().value_or(
-      AshColorProvider::Get()->GetControlsLayerColor(
-          AshColorProvider::ControlsLayerType::kControlBackgroundColorActive));
-
   SkColor icon_color = AshColorProvider::Get()->GetBaseLayerColor(
       AshColorProvider::BaseLayerType::kTransparent80);
-
-  // To ensure the app icon looks distinct enough in the notification
-  // background, we change the lightness of the accent color to generate app
-  // icon's background color.
-  color_utils::HSL hsl;
-  color_utils::SkColorToHSL(accent_color, &hsl);
-  hsl.l = AshColorProvider::Get()->IsDarkModeEnabled()
-              ? kAppIconLightnessInDarkMode
-              : kAppIconLightnessInLightMode;
-  SkColor icon_background_color =
-      color_utils::HSLToSkColor(hsl, SkColorGetA(accent_color));
+  SkColor icon_background_color = CalculateIconAndButtonsColor();
 
   // TODO(crbug.com/768748): figure out if this has a performance impact and
   // cache images if so.
@@ -1055,6 +1055,43 @@
           kAppIconViewSize / 2, icon_background_color, app_icon));
 }
 
+SkColor AshNotificationView::CalculateIconAndButtonsColor() {
+  auto* notification =
+      message_center::MessageCenter::Get()->FindVisibleNotificationById(
+          notification_id());
+
+  SkColor default_color = AshColorProvider::Get()->GetControlsLayerColor(
+      AshColorProvider::ControlsLayerType::kControlBackgroundColorActive);
+
+  if (!notification)
+    return default_color;
+
+  SkColor accent_color = notification->accent_color().value_or(default_color);
+
+  // To ensure the icon and buttons look distinct enough in the notification
+  // background, we change the lightness of the accent color to generate the
+  // desired color.
+  color_utils::HSL hsl;
+  color_utils::SkColorToHSL(accent_color, &hsl);
+  hsl.l = AshColorProvider::Get()->IsDarkModeEnabled()
+              ? kAppIconLightnessInDarkMode
+              : kAppIconLightnessInLightMode;
+  return color_utils::HSLToSkColor(hsl, SkColorGetA(accent_color));
+}
+
+void AshNotificationView::UpdateIconAndButtonsColor() {
+  UpdateAppIconView();
+
+  SkColor button_color = CalculateIconAndButtonsColor();
+
+  for (auto* action_button : action_buttons()) {
+    static_cast<PillButton*>(action_button)->SetButtonTextColor(button_color);
+  }
+
+  if (snooze_button_)
+    snooze_button_->SetIconColor(button_color);
+}
+
 void AshNotificationView::PerformExpandCollapseAnimation() {
   if (title_row_)
     title_row_->PerformExpandCollapseAnimation();
@@ -1281,4 +1318,24 @@
                                    kToggleInlineSettingsFadeInDurationMs);
 }
 
+int AshNotificationView::CalculateMaxHeightForGroupedNotifications() {
+  auto* shelf = Shell::GetPrimaryRootWindowController()->shelf();
+  const WorkAreaInsets* work_area =
+      WorkAreaInsets::ForWindow(shelf->GetWindow()->GetRootWindow());
+
+  const int bottom = shelf->IsHorizontalAlignment()
+                         ? shelf->GetShelfBoundsInScreen().y()
+                         : work_area->user_work_area_bounds().bottom();
+
+  const int free_space_height_above_anchor =
+      bottom - work_area->user_work_area_bounds().y();
+
+  const int vertical_margin = 2 * message_center::kMarginBetweenPopups +
+                              kNotificationViewPadding.height();
+
+  return free_space_height_above_anchor -
+         control_buttons_container_->bounds().height() -
+         main_view_->bounds().height() - vertical_margin;
+}
+
 }  // namespace ash
diff --git a/ash/system/message_center/ash_notification_view.h b/ash/system/message_center/ash_notification_view.h
index 40b28d8..dab3584 100644
--- a/ash/system/message_center/ash_notification_view.h
+++ b/ash/system/message_center/ash_notification_view.h
@@ -160,6 +160,12 @@
   // Update the color and icon for `app_icon_view_`.
   void UpdateAppIconView();
 
+  // Calculate the color used for the app icon and action buttons.
+  SkColor CalculateIconAndButtonsColor();
+
+  // Update the color of icon and buttons.
+  void UpdateIconAndButtonsColor();
+
   // AshNotificationView will animate its expand/collapse in the parent's
   // ChildPreferredSizeChange(). Child views are animated here.
   void PerformExpandCollapseAnimation();
@@ -170,6 +176,10 @@
   // Animations when toggle inline settings.
   void PerformToggleInlineSettingsAnimation(bool should_show_inline_settings);
 
+  // Calculate vertical space available on screen for the
+  // grouped_notifications_scroll_view_
+  int CalculateMaxHeightForGroupedNotifications();
+
   // Owned by views hierarchy.
   views::View* main_view_ = nullptr;
   views::View* main_right_view_ = nullptr;
@@ -179,6 +189,7 @@
   views::View* control_buttons_container_ = nullptr;
   views::View* left_content_ = nullptr;
   views::Label* message_view_in_expanded_state_ = nullptr;
+  views::ScrollView* grouped_notifications_scroll_view_ = nullptr;
   views::View* grouped_notifications_container_ = nullptr;
   views::View* collapsed_summary_view_ = nullptr;
   views::View* control_buttons_view_ = nullptr;
diff --git a/ash/system/network/sms_observer_unittest.cc b/ash/system/network/sms_observer_unittest.cc
index 53dfcbe..28ee40b 100644
--- a/ash/system/network/sms_observer_unittest.cc
+++ b/ash/system/network/sms_observer_unittest.cc
@@ -28,11 +28,11 @@
   std::unique_ptr<base::DictionaryValue> sms =
       std::make_unique<base::DictionaryValue>();
   if (kDefaultNumber)
-    sms->SetString("number", kDefaultNumber);
+    sms->SetStringKey("number", kDefaultNumber);
   if (kDefaultMessage)
-    sms->SetString("text", kDefaultMessage);
+    sms->SetStringKey("text", kDefaultMessage);
   if (kDefaultTimestamp)
-    sms->SetString("timestamp", kDefaultMessage);
+    sms->SetStringKey("timestamp", kDefaultMessage);
   return sms;
 }
 
diff --git a/ash/system/power/power_button_controller.cc b/ash/system/power/power_button_controller.cc
index 26f66a9..ba72bcd 100644
--- a/ash/system/power/power_button_controller.cc
+++ b/ash/system/power/power_button_controller.cc
@@ -523,11 +523,11 @@
     return;
   }
 
-  std::string edge;
+  const std::string* edge = position_info->FindStringKey(kEdgeField);
   absl::optional<double> position =
       position_info->FindDoubleKey(kPositionField);
 
-  if (!position_info->GetString(kEdgeField, &edge) || !position) {
+  if (!edge || !position) {
     LOG(ERROR) << "Both " << kEdgeField << " field and " << kPositionField
                << " are always needed if " << switches::kAshPowerButtonPosition
                << " is set";
@@ -536,13 +536,13 @@
 
   power_button_offset_percentage_ = *position;
 
-  if (edge == kLeftEdge) {
+  if (*edge == kLeftEdge) {
     power_button_position_ = PowerButtonPosition::LEFT;
-  } else if (edge == kRightEdge) {
+  } else if (*edge == kRightEdge) {
     power_button_position_ = PowerButtonPosition::RIGHT;
-  } else if (edge == kTopEdge) {
+  } else if (*edge == kTopEdge) {
     power_button_position_ = PowerButtonPosition::TOP;
-  } else if (edge == kBottomEdge) {
+  } else if (*edge == kBottomEdge) {
     power_button_position_ = PowerButtonPosition::BOTTOM;
   } else {
     LOG(ERROR) << "Invalid " << kEdgeField << " field in "
diff --git a/ash/system/power/power_button_controller_unittest.cc b/ash/system/power/power_button_controller_unittest.cc
index acce672a..8004541 100644
--- a/ash/system/power/power_button_controller_unittest.cc
+++ b/ash/system/power/power_button_controller_unittest.cc
@@ -1088,20 +1088,20 @@
     base::DictionaryValue position_info;
     switch (power_button_position_) {
       case PowerButtonPosition::LEFT:
-        position_info.SetString(PowerButtonController::kEdgeField,
-                                PowerButtonController::kLeftEdge);
+        position_info.SetStringKey(PowerButtonController::kEdgeField,
+                                   PowerButtonController::kLeftEdge);
         break;
       case PowerButtonPosition::RIGHT:
-        position_info.SetString(PowerButtonController::kEdgeField,
-                                PowerButtonController::kRightEdge);
+        position_info.SetStringKey(PowerButtonController::kEdgeField,
+                                   PowerButtonController::kRightEdge);
         break;
       case PowerButtonPosition::TOP:
-        position_info.SetString(PowerButtonController::kEdgeField,
-                                PowerButtonController::kTopEdge);
+        position_info.SetStringKey(PowerButtonController::kEdgeField,
+                                   PowerButtonController::kTopEdge);
         break;
       case PowerButtonPosition::BOTTOM:
-        position_info.SetString(PowerButtonController::kEdgeField,
-                                PowerButtonController::kBottomEdge);
+        position_info.SetStringKey(PowerButtonController::kEdgeField,
+                                   PowerButtonController::kBottomEdge);
         break;
       default:
         return;
@@ -1363,9 +1363,10 @@
       power_button_test_api_->GetMenuBoundsInScreen()));
 }
 
+// Disabled due to consistent failures. http://crbug.com/1286199
 // Tests that a power button press before the menu is fully shown will not
 // create a new menu.
-TEST_F(PowerButtonControllerTest, LegacyPowerButtonIgnoreExtraPress) {
+TEST_F(PowerButtonControllerTest, DISABLED_LegacyPowerButtonIgnoreExtraPress) {
   Initialize(ButtonType::LEGACY, LoginStatus::USER);
 
   // Enable animations so that we can make sure that they occur.
diff --git a/ash/system/tray/tray_toggle_button.cc b/ash/system/tray/tray_toggle_button.cc
index 7b74506..4e4c6ae 100644
--- a/ash/system/tray/tray_toggle_button.cc
+++ b/ash/system/tray/tray_toggle_button.cc
@@ -6,7 +6,10 @@
 
 #include "ash/style/ash_color_provider.h"
 #include "ash/system/tray/tray_constants.h"
+#include "ash/wm/haptics_util.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/events/devices/haptic_touchpad_effects.h"
+#include "ui/events/event.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/focus_ring.h"
 
@@ -39,4 +42,10 @@
       AshColorProvider::ControlsLayerType::kFocusRingColor));
 }
 
+void TrayToggleButton::NotifyClick(const ui::Event& event) {
+  haptics_util::PlayHapticToggleEffect(
+      !GetIsOn(), ui::HapticTouchpadEffectStrength::kMedium);
+  views::ToggleButton::NotifyClick(event);
+}
+
 }  // namespace ash
diff --git a/ash/system/tray/tray_toggle_button.h b/ash/system/tray/tray_toggle_button.h
index ceabfe2..3d9947c 100644
--- a/ash/system/tray/tray_toggle_button.h
+++ b/ash/system/tray/tray_toggle_button.h
@@ -7,6 +7,10 @@
 
 #include "ui/views/controls/button/toggle_button.h"
 
+namespace ui {
+class Event;
+}  // namespace ui
+
 namespace ash {
 
 // A toggle button configured for the system tray menu's layout. Also gets the
@@ -20,6 +24,7 @@
 
   // views::ToggleButton:
   void OnThemeChanged() override;
+  void NotifyClick(const ui::Event& event) override;
 };
 
 }  // namespace ash
diff --git a/ash/wallpaper/wallpaper_controller_impl.cc b/ash/wallpaper/wallpaper_controller_impl.cc
index 387b1b0..d8aca2d 100644
--- a/ash/wallpaper/wallpaper_controller_impl.cc
+++ b/ash/wallpaper/wallpaper_controller_impl.cc
@@ -612,7 +612,7 @@
   if (!pref_service)
     return false;
 
-  DictionaryPrefUpdate wallpaper_update(pref_service, pref_name);
+  DictionaryPrefUpdateDeprecated wallpaper_update(pref_service, pref_name);
   base::Value wallpaper_info_dict(base::Value::Type::DICTIONARY);
   if (info.asset_id.has_value()) {
     wallpaper_info_dict.SetStringPath(
@@ -665,7 +665,8 @@
                          const std::string& pref_name) {
   if (!pref_service)
     return;
-  DictionaryPrefUpdate prefs_wallpapers_info_update(pref_service, pref_name);
+  DictionaryPrefUpdateDeprecated prefs_wallpapers_info_update(pref_service,
+                                                              pref_name);
   prefs_wallpapers_info_update->RemoveKey(account_id.GetUserEmail());
 }
 
@@ -995,8 +996,8 @@
   WallpaperInfo old_info;
   if (local_state_ && GetUserWallpaperInfo(account_id, &old_info)) {
     // Remove the color cache of the previous wallpaper if it exists.
-    DictionaryPrefUpdate wallpaper_colors_update(local_state_,
-                                                 prefs::kWallpaperColors);
+    DictionaryPrefUpdateDeprecated wallpaper_colors_update(
+        local_state_, prefs::kWallpaperColors);
     wallpaper_colors_update->RemoveKey(old_info.location);
   }
   bool success = SetLocalWallpaperInfo(account_id, info);
@@ -1883,8 +1884,8 @@
   if (!local_state_)
     return;
   // Remove the color cache of the previous wallpaper if it exists.
-  DictionaryPrefUpdate wallpaper_colors_update(local_state_,
-                                               prefs::kWallpaperColors);
+  DictionaryPrefUpdateDeprecated wallpaper_colors_update(
+      local_state_, prefs::kWallpaperColors);
   wallpaper_colors_update->RemoveKey(info.location);
 }
 
@@ -2417,8 +2418,8 @@
     const std::string& current_location) {
   if (!local_state_)
     return;
-  DictionaryPrefUpdate wallpaper_colors_update(local_state_,
-                                               prefs::kWallpaperColors);
+  DictionaryPrefUpdateDeprecated wallpaper_colors_update(
+      local_state_, prefs::kWallpaperColors);
   auto wallpaper_colors = std::make_unique<base::ListValue>();
   for (SkColor color : colors)
     wallpaper_colors->Append(static_cast<double>(color));
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index 0be0e06..c19fab7 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -263,22 +263,22 @@
     WallpaperInfo info) {
   auto wallpaper_info_dict = std::make_unique<base::DictionaryValue>();
   if (info.asset_id.has_value()) {
-    wallpaper_info_dict->SetString(
+    wallpaper_info_dict->SetStringKey(
         WallpaperControllerImpl::kNewWallpaperAssetIdNodeName,
         base::NumberToString(info.asset_id.value()));
   }
   if (info.unit_id.has_value()) {
-    wallpaper_info_dict->SetStringPath(
+    wallpaper_info_dict->SetStringKey(
         WallpaperControllerImpl::kNewWallpaperUnitIdNodeName,
         base::NumberToString(info.unit_id.value()));
   }
   base::Value online_wallpaper_variant_list(base::Value::Type::LIST);
   for (const auto& variant : info.variants) {
     base::Value online_wallpaper_variant_dict(base::Value::Type::DICTIONARY);
-    online_wallpaper_variant_dict.SetStringPath(
+    online_wallpaper_variant_dict.SetStringKey(
         WallpaperControllerImpl::kNewWallpaperAssetIdNodeName,
         base::NumberToString(variant.asset_id));
-    online_wallpaper_variant_dict.SetStringPath(
+    online_wallpaper_variant_dict.SetStringKey(
         WallpaperControllerImpl::kOnlineWallpaperUrlNodeName,
         variant.raw_url.spec());
     online_wallpaper_variant_dict.SetIntPath(
@@ -290,17 +290,17 @@
   wallpaper_info_dict->SetKey(
       WallpaperControllerImpl::kNewWallpaperVariantListNodeName,
       std::move(online_wallpaper_variant_list));
-  wallpaper_info_dict->SetString(
+  wallpaper_info_dict->SetStringKey(
       WallpaperControllerImpl::kNewWallpaperCollectionIdNodeName,
       info.collection_id);
-  wallpaper_info_dict->SetString(
+  wallpaper_info_dict->SetStringKey(
       WallpaperControllerImpl::kNewWallpaperDateNodeName,
       base::NumberToString(info.date.ToInternalValue()));
-  wallpaper_info_dict->SetString(
+  wallpaper_info_dict->SetStringKey(
       WallpaperControllerImpl::kNewWallpaperLocationNodeName, info.location);
-  wallpaper_info_dict->SetInteger(
+  wallpaper_info_dict->SetIntKey(
       WallpaperControllerImpl::kNewWallpaperLayoutNodeName, info.layout);
-  wallpaper_info_dict->SetInteger(
+  wallpaper_info_dict->SetIntKey(
       WallpaperControllerImpl::kNewWallpaperTypeNodeName,
       static_cast<int>(info.type));
   return wallpaper_info_dict;
@@ -319,7 +319,7 @@
                              WallpaperInfo info,
                              PrefService* pref_service,
                              const std::string& pref_name) {
-  DictionaryPrefUpdate wallpaper_update(pref_service, pref_name);
+  DictionaryPrefUpdateDeprecated wallpaper_update(pref_service, pref_name);
   auto wallpaper_info_dict = CreateWallpaperInfoDict(info);
   wallpaper_update->SetKey(
       account_id.GetUserEmail(),
@@ -3148,12 +3148,13 @@
  public:
   WallpaperControllerPrefTest() {
     base::DictionaryValue property;
-    property.SetInteger("rotation",
-                        static_cast<int>(display::Display::ROTATE_90));
-    property.SetInteger("width", 800);
-    property.SetInteger("height", 600);
+    property.SetIntKey("rotation",
+                       static_cast<int>(display::Display::ROTATE_90));
+    property.SetIntKey("width", 800);
+    property.SetIntKey("height", 600);
 
-    DictionaryPrefUpdate update(local_state(), prefs::kDisplayProperties);
+    DictionaryPrefUpdateDeprecated update(local_state(),
+                                          prefs::kDisplayProperties);
     update.Get()->SetKey("2200000000", std::move(property));
   }
 
diff --git a/ash/webui/camera_app_ui/resources/js/BUILD.gn b/ash/webui/camera_app_ui/resources/js/BUILD.gn
index 26abf8d..bc994bd4 100644
--- a/ash/webui/camera_app_ui/resources/js/BUILD.gn
+++ b/ash/webui/camera_app_ui/resources/js/BUILD.gn
@@ -105,7 +105,7 @@
     js_files += rebase_path(get_target_outputs(dep), root_dir, root_build_dir)
   }
 
-  extra_deps += [ "//ui/webui/resources/mojo:copy_bindings_dts" ]
+  extra_deps += [ "//ui/webui/resources/mojo:library" ]
 }
 
 ts_library("build_ts") {
diff --git a/ash/webui/camera_app_ui/resources/js/js.gni b/ash/webui/camera_app_ui/resources/js/js.gni
index f77e4e7..ab5c167 100644
--- a/ash/webui/camera_app_ui/resources/js/js.gni
+++ b/ash/webui/camera_app_ui/resources/js/js.gni
@@ -75,10 +75,10 @@
   "views/camera/mode/mode_base.ts",
   "views/camera/mode/photo.ts",
   "views/camera/mode/portrait.ts",
-  "views/camera/mode/record_time.js",
+  "views/camera/mode/record_time.ts",
   "views/camera/mode/scan.ts",
-  "views/camera/mode/square.js",
-  "views/camera/mode/video.js",
+  "views/camera/mode/square.ts",
+  "views/camera/mode/video.ts",
   "views/camera/document_corner_overlay.js",
   "views/camera/options.js",
   "views/camera/preview.js",
diff --git a/ash/webui/camera_app_ui/resources/js/metrics.ts b/ash/webui/camera_app_ui/resources/js/metrics.ts
index 22f6c1ba..f61c88f 100644
--- a/ash/webui/camera_app_ui/resources/js/metrics.ts
+++ b/ash/webui/camera_app_ui/resources/js/metrics.ts
@@ -71,7 +71,53 @@
   await (await gaHelper).setMetricsEnabled(GA_ID, enabled);
 }
 
-const SCHEMA_VERSION = 2;
+const SCHEMA_VERSION = 3;
+
+/**
+ * All dimensions for GA metrics.
+ *
+ * The following two documents should also be updated when the dimensions is
+ * updated.
+ *
+ * * Camera App PDD (Privacy Design Document): go/cca-metrics-pdd.
+ * * CCA GA Events & Dimensions sheet: go/cca-metrics-schema.
+ */
+enum MetricDimension {
+  BOARD = 1,
+  OS_VERSION = 2,
+  // Obsolete 'sound' state.
+  // SOUND = 3,
+  MIRROR = 4,
+  GRID = 5,
+  TIMER = 6,
+  MICROPHONE = 7,
+  MAXIMIZED = 8,
+  TALL_ORIENTATION = 9,
+  RESOLUTION = 10,
+  FPS = 11,
+  INTENT_RESULT = 12,
+  SHOULD_HANDLE_RESULT = 13,
+  SHOULD_DOWN_SCALE = 14,
+  IS_SECURE = 15,
+  ERROR_NAME = 16,
+  FILENAME = 17,
+  FUNC_NAME = 18,
+  LINE_NO = 19,
+  COL_NO = 20,
+  SHUTTER_TYPE = 21,
+  IS_VIDEO_SNAPSHOT = 22,
+  EVER_PAUSED = 23,
+  SUPPORT_PAN = 24,
+  SUPPORT_TILT = 25,
+  SUPPORT_ZOOM = 26,
+  DOC_RESULT = 27,
+  RECORD_TYPE = 28,
+  GIF_RESULT = 29,
+  DURATION = 30,
+  SCHEMA_VERSION = 31,
+  LAUNCH_TYPE = 32,
+  DOC_FIX_TYPE = 33,
+}
 
 /**
  * Initializes metrics with parameters.
@@ -81,10 +127,10 @@
   const boardName = /^(x86-)?(\w*)/.exec(board)[0];
   const match = navigator.appVersion.match(/CrOS\s+\S+\s+([\d.]+)/);
   const osVer = match ? match[1] : '';
-  baseDimen = new Map<number, string|number>([
-    [1, boardName],
-    [2, osVer],
-    [31, SCHEMA_VERSION],
+  baseDimen = new Map<MetricDimension, string|number>([
+    [MetricDimension.BOARD, boardName],
+    [MetricDimension.OS_VERSION, osVer],
+    [MetricDimension.SCHEMA_VERSION, SCHEMA_VERSION],
   ]);
 
   const GA_LOCAL_STORAGE_KEY = 'google-analytics.analytics.user-id';
@@ -125,7 +171,7 @@
         eventLabel: '',
       },
       new Map([
-        [32, launchType],
+        [MetricDimension.LAUNCH_TYPE, launchType],
       ]));
 }
 
@@ -249,31 +295,35 @@
         eventLabel: facing,
         eventValue: duration,
       },
-      new Map<number, unknown>([
+      new Map<MetricDimension, unknown>([
         // Skips 3rd dimension for obsolete 'sound' state.
-        [4, condState([State.MIRROR])],
+        [MetricDimension.MIRROR, condState([State.MIRROR])],
         [
-          5,
+          MetricDimension.GRID,
           condState(
               [State.GRID_3x3, State.GRID_4x4, State.GRID_GOLDEN], State.GRID),
         ],
-        [6, condState([State.TIMER_3SEC, State.TIMER_10SEC], State.TIMER)],
-        [7, condState([State.MIC], Mode.VIDEO, true)],
-        [8, condState([State.MAX_WND])],
-        [9, condState([State.TALL])],
-        [10, resolution.toString()],
-        [11, condState([State.FPS_30, State.FPS_60], Mode.VIDEO, true)],
-        [12, intentResult],
-        [21, shutterType],
-        [22, isVideoSnapshot],
-        [23, everPaused],
-        [27, docResult],
-        [28, recordType],
-        [29, gifResult],
-        [30, duration],
-        // This is included in baseDimen.
-        // [31, SCHEMA_VERSION]
-        [32, docFixType ?? ''],
+        [
+          MetricDimension.TIMER,
+          condState([State.TIMER_3SEC, State.TIMER_10SEC], State.TIMER),
+        ],
+        [MetricDimension.MICROPHONE, condState([State.MIC], Mode.VIDEO, true)],
+        [MetricDimension.MAXIMIZED, condState([State.MAX_WND])],
+        [MetricDimension.TALL_ORIENTATION, condState([State.TALL])],
+        [MetricDimension.RESOLUTION, resolution.toString()],
+        [
+          MetricDimension.FPS,
+          condState([State.FPS_30, State.FPS_60], Mode.VIDEO, true),
+        ],
+        [MetricDimension.INTENT_RESULT, intentResult],
+        [MetricDimension.SHUTTER_TYPE, shutterType],
+        [MetricDimension.IS_VIDEO_SNAPSHOT, isVideoSnapshot],
+        [MetricDimension.EVER_PAUSED, everPaused],
+        [MetricDimension.DOC_RESULT, docResult],
+        [MetricDimension.RECORD_TYPE, recordType],
+        [MetricDimension.GIF_RESULT, gifResult],
+        [MetricDimension.DURATION, duration],
+        [MetricDimension.DOC_FIX_TYPE, docFixType ?? ''],
       ]));
 }
 
@@ -308,7 +358,7 @@
         eventValue: Math.round(duration),
       },
       new Map([
-        [10, `${resolution}`],
+        [MetricDimension.RESOLUTION, `${resolution}`],
       ]));
 }
 
@@ -333,10 +383,13 @@
         eventLabel: result,
       },
       new Map([
-        [12, result],
-        [13, getBoolValue(shouldHandleResult)],
-        [14, getBoolValue(shouldDownScale)],
-        [15, getBoolValue(isSecure)],
+        [MetricDimension.INTENT_RESULT, result],
+        [
+          MetricDimension.SHOULD_HANDLE_RESULT,
+          getBoolValue(shouldHandleResult),
+        ],
+        [MetricDimension.SHOULD_DOWN_SCALE, getBoolValue(shouldDownScale)],
+        [MetricDimension.IS_SECURE, getBoolValue(isSecure)],
       ]));
 }
 
@@ -363,11 +416,11 @@
         eventLabel: level,
       },
       new Map([
-        [16, errorName],
-        [17, fileName],
-        [18, funcName],
-        [19, lineNo],
-        [20, colNo],
+        [MetricDimension.ERROR_NAME, errorName],
+        [MetricDimension.FILENAME, fileName],
+        [MetricDimension.FUNC_NAME, funcName],
+        [MetricDimension.LINE_NO, lineNo],
+        [MetricDimension.COL_NO, colNo],
       ]));
 }
 
@@ -416,8 +469,8 @@
         eventAction: 'open-panel',
       },
       new Map([
-        [24, capabilities.pan],
-        [25, capabilities.tilt],
-        [26, capabilities.zoom],
+        [MetricDimension.SUPPORT_PAN, capabilities.pan],
+        [MetricDimension.SUPPORT_TILT, capabilities.tilt],
+        [MetricDimension.SUPPORT_ZOOM, capabilities.zoom],
       ]));
 }
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/record_time.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/record_time.js
deleted file mode 100644
index 2096a02..0000000
--- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/record_time.js
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright 2018 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 {assertNotReached} from '../../../assert.js';
-import * as dom from '../../../dom.js';
-import {I18nString} from '../../../i18n_string.js';
-import * as loadTimeData from '../../../models/load_time_data.js';
-import {speak} from '../../../toast.js';
-
-/**
- * Maximal recording time in milliseconds and the function executed to notify
- * caller the tick reaches maximal time.
- * @typedef {{
- *   maxTime: number,
- *   onMaxTimeout: (function(): *),
- * }}
- */
-export let MaxTimeOption;
-
-/**
- * Controller for the record-time of Camera view.
- * @abstract
- */
-class RecordTimeBase {
-  /**
-   * @param {MaxTimeOption=} maxTimeOption
-   * @public
-   */
-  constructor(maxTimeOption) {
-    /**
-     * @type {!HTMLElement}
-     * @private
-     */
-    this.recordTime_ = dom.get('#record-time', HTMLElement);
-
-    /**
-     * @const {?MaxTimeOption}
-     * @protected
-     */
-    this.maxTimeOption_ = maxTimeOption || null;
-
-    /**
-     * Timeout to count every tick of elapsed recording time.
-     * @type {?number}
-     * @private
-     */
-    this.tickTimeout_ = null;
-
-    /**
-     * Tick count of elapsed recording time.
-     * @type {number}
-     * @private
-     */
-    this.ticks_ = 0;
-
-    /**
-     * The timestamp when the recording starts.
-     * @type {number}
-     * @private
-     */
-    this.startTimestamp_ = 0;
-
-    /**
-     * The total duration of the recording in milliseconds.
-     * @type {number}
-     * @private
-     */
-    this.totalDuration_ = 0;
-  }
-
-  /**
-   * @return {number} Time interval to update ticks in milliseconds.
-   * @protected
-   * @abstract
-   */
-  getTimeInterval_() {
-    assertNotReached();
-  }
-
-  /**
-   * @param {number} ticks Aggregated time ticks during the record time.
-   * @return {string} Message showing on record time area. Should already be
-   *     translated by i18n if necessary.
-   * @protected
-   * @abstract
-   */
-  getTimeMessage_(ticks) {
-    assertNotReached();
-  }
-
-  /**
-   * Updates UI by the elapsed recording time.
-   * @private
-   */
-  update_() {
-    dom.get('#record-time-msg', HTMLElement).textContent =
-        this.getTimeMessage_(this.ticks_);
-  }
-
-  /**
-   * Starts to count and show the elapsed recording time.
-   * @param {{resume: boolean}} params If the time count is resumed from paused
-   *     state.
-   */
-  start({resume}) {
-    if (!resume) {
-      this.ticks_ = 0;
-      this.totalDuration_ = 0;
-    }
-    this.update_();
-    this.recordTime_.hidden = false;
-
-    this.tickTimeout_ = setInterval(() => {
-      if (this.maxTimeOption_ === null ||
-          (this.ticks_ + 1) * this.getTimeInterval_() <=
-              this.maxTimeOption_.maxTime) {
-        this.ticks_++;
-      } else {
-        this.maxTimeOption_.onMaxTimeout();
-        clearInterval(this.tickTimeout_);
-        this.tickTimeout_ = null;
-      }
-      this.update_();
-    }, this.getTimeInterval_());
-
-    this.startTimestamp_ = performance.now();
-  }
-
-  /**
-   * Stops counting and showing the elapsed recording time.
-   * @param {{pause: boolean}} param If the time count is paused temporarily.
-   */
-  stop({pause}) {
-    speak(I18nString.STATUS_MSG_RECORDING_STOPPED);
-    if (this.tickTimeout_) {
-      clearInterval(this.tickTimeout_);
-      this.tickTimeout_ = null;
-    }
-    if (!pause) {
-      this.ticks_ = 0;
-      this.recordTime_.hidden = true;
-      this.update_();
-    }
-
-    this.totalDuration_ += performance.now() - this.startTimestamp_;
-    if (this.maxTimeOption_ !== null) {
-      this.totalDuration_ =
-          Math.min(this.totalDuration_, this.maxTimeOption_.maxTime);
-    }
-  }
-
-  /**
-   * Returns the recorded duration in milliseconds.
-   * @return {number}
-   */
-  inMilliseconds() {
-    return this.totalDuration_;
-  }
-}
-
-/**
- * Record time for normal record type.
- */
-export class RecordTime extends RecordTimeBase {
-  /**
-   * @override
-   */
-  getTimeInterval_() {
-    return 1000;
-  }
-
-  /**
-   * @override
-   */
-  getTimeMessage_(ticks) {
-    // Format time into HH:MM:SS or MM:SS.
-    const pad = (n) => {
-      return (n < 10 ? '0' : '') + n;
-    };
-    let hh = '';
-    if (ticks >= 3600) {
-      hh = pad(Math.floor(ticks / 3600)) + ':';
-    }
-    const mm = pad(Math.floor(ticks / 60) % 60) + ':';
-    return hh + mm + pad(ticks % 60);
-  }
-}
-
-/**
- * Record time for gif record type.
- */
-export class GifRecordTime extends RecordTimeBase {
-  /**
-   * @override
-   */
-  getTimeInterval_() {
-    return 100;
-  }
-
-  /**
-   * @override
-   */
-  getTimeMessage_(ticks) {
-    const maxTicks = this.maxTimeOption_.maxTime / this.getTimeInterval_();
-    /**
-     * Formats ticks to seconds with only first digit shown after floating
-     * point.
-     * @param {number} ticks
-     * @return {string} Formatted string.
-     */
-    const formatTick = (ticks) =>
-        (ticks / (1000 / this.getTimeInterval_())).toFixed(1);
-    return loadTimeData.getI18nMessage(
-        I18nString.LABEL_CURRENT_AND_MAXIMAL_RECORD_TIME, formatTick(ticks),
-        formatTick(maxTicks));
-  }
-}
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/record_time.ts b/ash/webui/camera_app_ui/resources/js/views/camera/mode/record_time.ts
new file mode 100644
index 0000000..d1cacfd94
--- /dev/null
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/record_time.ts
@@ -0,0 +1,171 @@
+// Copyright 2018 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 * as dom from '../../../dom.js';
+import {I18nString} from '../../../i18n_string.js';
+import * as loadTimeData from '../../../models/load_time_data.js';
+import {speak} from '../../../toast.js';
+
+/**
+ * Maximal recording time in milliseconds and the function executed to notify
+ * caller the tick reaches maximal time.
+ */
+export interface MaxTimeOption {
+  maxTime: number;
+  onMaxTimeout: () => void;
+}
+
+/**
+ * Controller for the record-time of Camera view.
+ */
+abstract class RecordTimeBase {
+  private readonly recordTime = dom.get('#record-time', HTMLElement);
+  protected readonly maxTimeOption: MaxTimeOption|null;
+
+  /**
+   * Timeout to count every tick of elapsed recording time.
+   */
+  private tickTimeout: number|null = null;
+
+  /**
+   * Tick count of elapsed recording time.
+   */
+  private ticks = 0;
+
+  /**
+   * The timestamp when the recording starts.
+   */
+  private startTimestamp = 0;
+
+  /**
+   * The total duration of the recording in milliseconds.
+   */
+  private totalDuration = 0;
+
+  constructor(maxTimeOption?: MaxTimeOption) {
+    this.maxTimeOption = maxTimeOption || null;
+  }
+
+  /**
+   * @return Time interval to update ticks in milliseconds.
+   */
+  protected abstract getTimeInterval(): number;
+
+  /**
+   * @param ticks Aggregated time ticks during the record time.
+   * @return Message showing on record time area. Should already be translated
+   *     by i18n if necessary.
+   */
+  protected abstract getTimeMessage(ticks: number): string;
+
+  /**
+   * Updates UI by the elapsed recording time.
+   */
+  private update() {
+    dom.get('#record-time-msg', HTMLElement).textContent =
+        this.getTimeMessage(this.ticks);
+  }
+
+  /**
+   * Starts to count and show the elapsed recording time.
+   * @param params If the time count is resumed from paused state.
+   */
+  start({resume}: {resume: boolean}): void {
+    if (!resume) {
+      this.ticks = 0;
+      this.totalDuration = 0;
+    }
+    this.update();
+    this.recordTime.hidden = false;
+
+    this.tickTimeout = setInterval(() => {
+      if (this.maxTimeOption === null ||
+          (this.ticks + 1) * this.getTimeInterval() <=
+              this.maxTimeOption.maxTime) {
+        this.ticks++;
+      } else {
+        this.maxTimeOption.onMaxTimeout();
+        clearInterval(this.tickTimeout);
+        this.tickTimeout = null;
+      }
+      this.update();
+    }, this.getTimeInterval());
+
+    this.startTimestamp = performance.now();
+  }
+
+  /**
+   * Stops counting and showing the elapsed recording time.
+   * @param param If the time count is paused temporarily.
+   */
+  stop({pause}: {pause: boolean}): void {
+    speak(I18nString.STATUS_MSG_RECORDING_STOPPED);
+    if (this.tickTimeout) {
+      clearInterval(this.tickTimeout);
+      this.tickTimeout = null;
+    }
+    if (!pause) {
+      this.ticks = 0;
+      this.recordTime.hidden = true;
+      this.update();
+    }
+
+    this.totalDuration += performance.now() - this.startTimestamp;
+    if (this.maxTimeOption !== null) {
+      this.totalDuration =
+          Math.min(this.totalDuration, this.maxTimeOption.maxTime);
+    }
+  }
+
+  /**
+   * Returns the recorded duration in milliseconds.
+   */
+  inMilliseconds(): number {
+    return this.totalDuration;
+  }
+}
+
+/**
+ * Record time for normal record type.
+ */
+export class RecordTime extends RecordTimeBase {
+  getTimeInterval(): number {
+    return 1000;
+  }
+
+  getTimeMessage(ticks: number): string {
+    // Format time into HH:MM:SS or MM:SS.
+    const pad = (n) => {
+      return (n < 10 ? '0' : '') + n;
+    };
+    let hh = '';
+    if (ticks >= 3600) {
+      hh = pad(Math.floor(ticks / 3600)) + ':';
+    }
+    const mm = pad(Math.floor(ticks / 60) % 60) + ':';
+    return hh + mm + pad(ticks % 60);
+  }
+}
+
+/**
+ * Record time for gif record type.
+ */
+export class GifRecordTime extends RecordTimeBase {
+  getTimeInterval(): number {
+    return 100;
+  }
+
+  getTimeMessage(ticks: number): string {
+    const maxTicks = this.maxTimeOption.maxTime / this.getTimeInterval();
+    /**
+     * Formats ticks to seconds with only first digit shown after floating
+     * point.
+     */
+    const formatTick = (ticks: number): string =>
+        (ticks / (1000 / this.getTimeInterval())).toFixed(1);
+    return loadTimeData.getI18nMessage(
+        I18nString.LABEL_CURRENT_AND_MAXIMAL_RECORD_TIME, formatTick(ticks),
+        formatTick(maxTicks));
+  }
+}
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/square.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/square.js
deleted file mode 100644
index 83064fb5..0000000
--- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/square.js
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (c) 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.
-
-import {
-  Facing,      // eslint-disable-line no-unused-vars
-  Resolution,  // eslint-disable-line no-unused-vars
-} from '../../../type.js';
-import * as util from '../../../util.js';
-
-import {
-  Photo,
-  PhotoFactory,
-  PhotoHandler,  // eslint-disable-line no-unused-vars
-} from './photo.js';
-
-/**
- * Crops out maximum possible centered square from the image blob.
- * @param {!Blob} blob
- * @return {!Promise<!Blob>} Promise with result cropped square image.
- */
-async function cropSquare(blob) {
-  const img = await util.blobToImage(blob);
-  try {
-    const side = Math.min(img.width, img.height);
-    const {canvas, ctx} = util.newDrawingCanvas({width: side, height: side});
-    ctx.drawImage(
-        img, Math.floor((img.width - side) / 2),
-        Math.floor((img.height - side) / 2), side, side, 0, 0, side, side);
-    const croppedBlob = await new Promise((resolve) => {
-      // TODO(b/174190121): Patch important exif entries from input blob to
-      // result blob.
-      canvas.toBlob(resolve, 'image/jpeg');
-    });
-    return croppedBlob;
-  } finally {
-    URL.revokeObjectURL(img.src);
-  }
-}
-
-/**
- * Cuts the returned photo into square and passed to underlying PhotoHandler.
- * @implements {PhotoHandler}
- */
-class SquarePhotoHandler {
-  /**
-   * @param {!PhotoHandler} handler
-   */
-  constructor(handler) {
-    /**
-     * @const {!PhotoHandler}
-     */
-    this.handler_ = handler;
-  }
-
-  /**
-   * @override
-   */
-  playShutterEffect() {
-    this.handler_.playShutterEffect();
-  }
-
-  /**
-   * @override
-   */
-  waitPreviewReady() {
-    return this.handler_.waitPreviewReady();
-  }
-
-  /**
-   * @override
-   */
-  onPhotoError() {
-    this.handler_.onPhotoError();
-  }
-
-  /**
-   * @override
-   */
-  async onPhotoCaptureDone(pendingPhotoResult) {
-    const pendingSquarePhotoResult = (async () => {
-      const photoResult = await pendingPhotoResult;
-      const croppedBlob = await cropSquare(photoResult.blob);
-      return {
-        ...photoResult,
-        blob: croppedBlob,
-      };
-    })();
-    await this.handler_.onPhotoCaptureDone(pendingSquarePhotoResult);
-  }
-}
-
-/**
- * Square mode capture controller.
- */
-export class Square extends Photo {
-  /**
-   * @param {!MediaStream} stream
-   * @param {!Facing} facing
-   * @param {!Resolution} captureResolution
-   * @param {!PhotoHandler} handler
-   */
-  constructor(stream, facing, captureResolution, handler) {
-    super(stream, facing, captureResolution, new SquarePhotoHandler(handler));
-  }
-}
-
-/**
- * Factory for creating square mode capture object.
- */
-export class SquareFactory extends PhotoFactory {
-  /**
-   * @override
-   */
-  produce() {
-    return new Square(
-        this.previewStream, this.facing, this.captureResolution, this.handler);
-  }
-}
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/square.ts b/ash/webui/camera_app_ui/resources/js/views/camera/mode/square.ts
new file mode 100644
index 0000000..212bf03e
--- /dev/null
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/square.ts
@@ -0,0 +1,96 @@
+// Copyright (c) 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.
+
+import {
+  Facing,
+  Resolution,
+} from '../../../type.js';
+import * as util from '../../../util.js';
+import {ModeBase} from './mode_base.js';
+
+import {
+  Photo,
+  PhotoFactory,
+  PhotoHandler,
+  PhotoResult,
+} from './photo.js';
+
+/**
+ * Crops out maximum possible centered square from the image blob.
+ * @return Promise with result cropped square image.
+ */
+async function cropSquare(blob: Blob): Promise<Blob> {
+  const img = await util.blobToImage(blob);
+  try {
+    const side = Math.min(img.width, img.height);
+    const {canvas, ctx} = util.newDrawingCanvas({width: side, height: side});
+    ctx.drawImage(
+        img, Math.floor((img.width - side) / 2),
+        Math.floor((img.height - side) / 2), side, side, 0, 0, side, side);
+    const croppedBlob = await new Promise<Blob>((resolve) => {
+      // TODO(b/174190121): Patch important exif entries from input blob to
+      // result blob.
+      canvas.toBlob(resolve, 'image/jpeg');
+    });
+    return croppedBlob;
+  } finally {
+    URL.revokeObjectURL(img.src);
+  }
+}
+
+/**
+ * Cuts the returned photo into square and passed to underlying PhotoHandler.
+ */
+class SquarePhotoHandler implements PhotoHandler {
+  constructor(private readonly handler: PhotoHandler) {}
+
+  playShutterEffect(): void {
+    this.handler.playShutterEffect();
+  }
+
+  waitPreviewReady(): Promise<void> {
+    return this.handler.waitPreviewReady();
+  }
+
+  onPhotoError(): void {
+    this.handler.onPhotoError();
+  }
+
+  async onPhotoCaptureDone(pendingPhotoResult: Promise<PhotoResult>):
+      Promise<void> {
+    const pendingSquarePhotoResult = (async () => {
+      const photoResult = await pendingPhotoResult;
+      const croppedBlob = await cropSquare(photoResult.blob);
+      return {
+        ...photoResult,
+        blob: croppedBlob,
+      };
+    })();
+    await this.handler.onPhotoCaptureDone(pendingSquarePhotoResult);
+  }
+}
+
+/**
+ * Square mode capture controller.
+ */
+export class Square extends Photo {
+  constructor(
+      stream: MediaStream,
+      facing: Facing,
+      captureResolution: Resolution,
+      handler: PhotoHandler,
+  ) {
+    super(stream, facing, captureResolution, new SquarePhotoHandler(handler));
+  }
+}
+
+/**
+ * Factory for creating square mode capture object.
+ */
+export class SquareFactory extends PhotoFactory {
+  produce(): ModeBase {
+    return new Square(
+        this.previewStream, this.facing, this.captureResolution, this.handler);
+  }
+}
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.js b/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.js
deleted file mode 100644
index fbf2ada..0000000
--- a/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.js
+++ /dev/null
@@ -1,777 +0,0 @@
-// Copyright (c) 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.
-
-import {
-  assert,
-  assertInstanceof,
-  assertNotReached,
-} from '../../../assert.js';
-// eslint-disable-next-line no-unused-vars
-import {StreamConstraints} from '../../../device/stream_constraints.js';
-import {
-  StreamManager,
-} from '../../../device/stream_manager.js';
-import * as dom from '../../../dom.js';
-import {reportError} from '../../../error.js';
-// eslint-disable-next-line no-unused-vars
-import * as h264 from '../../../h264.js';
-import {I18nString} from '../../../i18n_string.js';
-import {Filenamer} from '../../../models/file_namer.js';
-import * as loadTimeData from '../../../models/load_time_data.js';
-import {
-  GifSaver,
-  VideoSaver,  // eslint-disable-line no-unused-vars
-} from '../../../models/video_saver.js';
-import {DeviceOperator} from '../../../mojo/device_operator.js';
-import {CrosImageCapture} from '../../../mojo/image_capture.js';
-import * as sound from '../../../sound.js';
-import * as state from '../../../state.js';
-import * as toast from '../../../toast.js';
-import {
-  CanceledError,
-  ErrorLevel,
-  ErrorType,
-  Facing,  // eslint-disable-line no-unused-vars
-  NoChunkError,
-  Resolution,
-  VideoType,
-} from '../../../type.js';
-import {WaitableEvent} from '../../../waitable_event.js';
-
-import {ModeBase, ModeFactory} from './mode_base.js';
-import {PhotoResult} from './photo.js';  // eslint-disable-line no-unused-vars
-import {GifRecordTime, RecordTime} from './record_time.js';
-
-/**
- * Maps from board name to its default encoding profile and bitrate multiplier.
- * @const {!Map<string, {profile: h264.Profile, multiplier: number}>}
- */
-const encoderPreference = new Map([
-  ['strongbad', {profile: h264.Profile.HIGH, multiplier: 6}],
-  ['trogdor', {profile: h264.Profile.HIGH, multiplier: 6}],
-  ['dedede', {profile: h264.Profile.HIGH, multiplier: 8}],
-  ['volteer', {profile: h264.Profile.HIGH, multiplier: 8}],
-]);
-
-/**
- * @type {?h264.EncoderParameters}
- */
-let avc1Parameters = null;
-
-/**
- * The minimum duration of videos captured via cca.
- */
-const MINIMUM_VIDEO_DURATION_IN_MILLISECONDS = 500;
-
-/**
- * The maximal length of the longer side of gif width or height.
- */
-const GIF_MAX_SIDE = 640;
-
-/**
- * Maximum recording time for GIF animation mode.
- */
-const MAX_GIF_DURATION_MS = 5000;
-
-/**
- * Sample ratio of grabbing gif frame to be encoded.
- */
-const GRAB_GIF_FRAME_RATIO = 2;
-
-/**
- * Sets avc1 parameter used in video recording.
- * @param {?h264.EncoderParameters} params
- */
-export function setAvc1Parameters(params) {
-  avc1Parameters = params;
-}
-
-/**
- * Gets video recording MIME type. Mkv with AVC1 is the only preferred format.
- * @param {?h264.EncoderParameters} param
- * @return {string} Video recording MIME type.
- */
-function getVideoMimeType(param) {
-  let suffix = '';
-  if (param !== null) {
-    const {profile, level} = param;
-    suffix = '.' + profile.toString(16).padStart(2, '0') +
-        level.toString(16).padStart(4, '0');
-  }
-  return `video/x-matroska;codecs=avc1${suffix},pcm`;
-}
-
-/**
- * The 'beforeunload' listener which will show confirm dialog when trying to
- * close window.
- * @param {!BeforeUnloadEvent} event The 'beforeunload' event.
- */
-function beforeUnloadListener(event) {
-  event.preventDefault();
-  event.returnValue = '';
-}
-
-/**
- * @typedef {{
- *   blob: !Blob,
- *   resolution: !Resolution,
- *   timestamp: number,
- * }}
- */
-export let VideoSnapshotResult;
-
-/**
- * @typedef {{
- *   resolution: !Resolution,
- *   duration: number,
- *   videoSaver: !VideoSaver,
- *   everPaused: boolean,
- * }}
- */
-export let VideoResult;
-
-/**
- * @typedef {{
- *   name: string,
- *   gifSaver: !GifSaver,
- *   resolution: !Resolution,
- *   duration: number,
- * }}
- */
-export let GifResult;
-
-/**
- * Provides functions with external dependency used by video mode and handles
- * the captured result video.
- * @interface
- */
-export class VideoHandler {
-  /**
-   * Creates VideoSaver to save video capture result.
-   * @return {!Promise<!VideoSaver>}
-   * @abstract
-   */
-  createVideoSaver() {
-    assertNotReached();
-  }
-
-  /**
-   * Handles the result video snapshot.
-   * @param {!VideoSnapshotResult} videoSnapshotResult
-   * @return {!Promise}
-   * @abstract
-   */
-  handleVideoSnapshot(videoSnapshotResult) {
-    assertNotReached();
-  }
-
-  /**
-   * Plays UI effect when doing video snapshot.
-   */
-  playShutterEffect() {
-    assertNotReached();
-  }
-
-  /**
-   * Gets preview video element.
-   * @return {!HTMLVideoElement}
-   * @abstract
-   */
-  getPreviewVideo() {
-    assertNotReached();
-  }
-
-  /**
-   * @param {!GifResult} gifResult
-   * @return {!Promise<void>}
-   * @abstract
-   */
-  async onGifCaptureDone(gifResult) {
-    assertNotReached();
-  }
-
-  /**
-   * @param {!VideoResult} videoResult
-   * @return {!Promise<void>}
-   * @abstract
-   */
-  async onVideoCaptureDone(videoResult) {
-    assertNotReached();
-  }
-}
-
-/**
- * @enum {state.State}
- */
-const RecordType = {
-  NORMAL: state.State.RECORD_TYPE_NORMAL,
-  GIF: state.State.RECORD_TYPE_GIF,
-};
-
-/**
- * Video mode capture controller.
- */
-export class Video extends ModeBase {
-  /**
-   * @param {!MediaStream} stream Preview stream.
-   * @param {?StreamConstraints} captureConstraints
-   * @param {!Resolution} captureResolution
-   * @param {!Resolution} snapshotResolution
-   * @param {!Facing} facing
-   * @param {!VideoHandler} handler
-   */
-  constructor(
-      stream, captureConstraints, captureResolution, snapshotResolution, facing,
-      handler) {
-    super(stream, facing);
-
-    /**
-     * @const {?StreamConstraints}
-     * @private
-     */
-    this.captureConstraints_ = captureConstraints;
-
-    /**
-     * @const {!Resolution}
-     * @private
-     */
-    this.captureResolution_ = (() => {
-      if (captureResolution !== null) {
-        return captureResolution;
-      }
-      const {width, height} = stream.getVideoTracks()[0].getSettings();
-      return new Resolution(width, height);
-    })();
-
-    /**
-     * @const {!Resolution}
-     * @private
-     */
-    this.snapshotResolution_ = snapshotResolution;
-
-    /**
-     * @const {!VideoHandler}
-     * @private
-     */
-    this.handler_ = handler;
-
-    /**
-     * @type {?MediaStream}
-     * @private
-     */
-    this.captureStream_ = null;
-
-    /**
-     * MediaRecorder object to record motion pictures.
-     * @type {?MediaRecorder}
-     * @private
-     */
-    this.mediaRecorder_ = null;
-
-    /**
-     * @type {?CrosImageCapture}
-     * @private
-     */
-    this.crosImageCapture_ = null;
-
-    /**
-     * Record-time for the elapsed recording time.
-     * @type {!RecordTime}
-     * @private
-     */
-    this.recordTime_ = new RecordTime();
-
-    /**
-     * Record-time for the elapsed gif recording time.
-     * @type {!GifRecordTime}
-     * @private
-     */
-    this.gifRecordTime_ = new GifRecordTime(
-        {maxTime: MAX_GIF_DURATION_MS, onMaxTimeout: () => this.stop()});
-
-    /**
-     * Record type of ongoing recording.
-     * @type {!RecordType}
-     * @private
-     */
-    this.recordingType_ = RecordType.NORMAL;
-
-    /**
-     * The ongoing video snapshot.
-     * @type {?Promise<void>}
-     * @private
-     */
-    this.snapshotting_ = null;
-
-    /**
-     * Promise for process of toggling video pause/resume. Sets to null if CCA
-     * is already paused or resumed.
-     * @type {?Promise}
-     * @private
-     */
-    this.togglePaused_ = null;
-
-    /**
-     * Whether current recording ever paused/resumed before it ended.
-     * @type {boolean}
-     * @private
-     */
-    this.everPaused_ = false;
-  }
-
-  /**
-   * @override
-   */
-  async clear() {
-    await this.stopCapture();
-    if (this.captureStream_ !== null) {
-      await StreamManager.getInstance().closeCaptureStream(this.captureStream_);
-      this.captureStream_ = null;
-    }
-  }
-
-  /**
-   * @override
-   */
-  updatePreview(stream) {
-    assert(!state.get(state.State.RECORDING));
-    this.stream = stream;
-    this.crosImageCapture_ = new CrosImageCapture(this.getVideoTrack_());
-  }
-
-  /**
-   * @return {!RecordType} Returns record type of checked radio buttons in
-   *     record type option groups.
-   */
-  getToggledRecordOption_() {
-    if (state.get(state.State.SHOULD_HANDLE_INTENT_RESULT)) {
-      return RecordType.NORMAL;
-    }
-    return Object.values(RecordType).find((t) => state.get(t)) ||
-        RecordType.NORMAL;
-  }
-
-  /**
-   * @return {!Promise<boolean>} Returns whether taking video sanpshot via Blob
-   *     stream is enabled.
-   */
-  async isBlobVideoSnapshotEnabled() {
-    const deviceOperator = await DeviceOperator.getInstance();
-    const deviceId = this.stream.getVideoTracks()[0].getSettings().deviceId;
-    return deviceOperator !== null &&
-        (await deviceOperator.isBlobVideoSnapshotEnabled(deviceId));
-  }
-
-  /**
-   * Takes a video snapshot during recording.
-   * @return {!Promise} Promise resolved when video snapshot is finished.
-   */
-  async takeSnapshot() {
-    if (this.snapshotting_ !== null) {
-      return;
-    }
-    state.set(state.State.SNAPSHOTTING, true);
-    this.snapshotting_ = (async () => {
-      try {
-        const timestamp = Date.now();
-        let blob;
-        if (await this.isBlobVideoSnapshotEnabled()) {
-          const photoSettings = /** @type {!PhotoSettings} */ ({
-            imageWidth: this.snapshotResolution_.width,
-            imageHeight: this.snapshotResolution_.height,
-          });
-          const results = await this.crosImageCapture_.takePhoto(photoSettings);
-          blob = await results[0];
-        } else {
-          blob = await this.crosImageCapture_.grabJpegFrame();
-        }
-
-        this.handler_.playShutterEffect();
-        await this.handler_.handleVideoSnapshot({
-          blob,
-          resolution: this.captureResolution_,
-          timestamp,
-        });
-      } finally {
-        state.set(state.State.SNAPSHOTTING, false);
-        this.snapshotting_ = null;
-      }
-    })();
-    return this.snapshotting_;
-  }
-
-  /**
-   * Toggles pause/resume state of video recording.
-   * @return {!Promise} Promise resolved when recording is paused/resumed.
-   */
-  async togglePaused() {
-    if (!state.get(state.State.RECORDING)) {
-      return;
-    }
-    if (this.togglePaused_ !== null) {
-      return this.togglePaused_;
-    }
-    this.everPaused_ = true;
-    const waitable = new WaitableEvent();
-    this.togglePaused_ = waitable.wait();
-
-    assert(this.mediaRecorder_.state !== 'inactive');
-    const toBePaused = this.mediaRecorder_.state !== 'paused';
-    const toggledEvent = toBePaused ? 'pause' : 'resume';
-    const onToggled = () => {
-      this.mediaRecorder_.removeEventListener(toggledEvent, onToggled);
-      state.set(state.State.RECORDING_PAUSED, toBePaused);
-      this.togglePaused_ = null;
-      waitable.signal();
-    };
-    const playEffect = async () => {
-      state.set(state.State.RECORDING_UI_PAUSED, toBePaused);
-      await sound.play(dom.get(
-          toBePaused ? '#sound-rec-pause' : '#sound-rec-start',
-          HTMLAudioElement));
-    };
-
-    this.mediaRecorder_.addEventListener(toggledEvent, onToggled);
-    if (toBePaused) {
-      waitable.wait().then(playEffect);
-      this.recordTime_.stop({pause: true});
-      this.mediaRecorder_.pause();
-    } else {
-      await playEffect();
-      this.recordTime_.start({resume: true});
-      this.mediaRecorder_.resume();
-    }
-
-    return waitable.wait();
-  }
-
-  /**
-   * @return {?h264.EncoderParameters}
-   * @private
-   */
-  getEncoderParameters_() {
-    if (avc1Parameters !== null) {
-      return avc1Parameters;
-    }
-    const preference = encoderPreference.get(loadTimeData.getBoard()) ||
-        {profile: h264.Profile.HIGH, multiplier: 2};
-    const {profile, multiplier} = preference;
-    const {width, height, frameRate} = this.getVideoTrack_().getSettings();
-    const resolution = new Resolution(width, height);
-    const bitrate = resolution.area * multiplier;
-    const level = h264.getMinimalLevel(profile, bitrate, frameRate, resolution);
-    if (level === null) {
-      reportError(
-          ErrorType.NO_AVAILABLE_LEVEL, ErrorLevel.WARNING,
-          new Error(
-              `No valid level found for ` +
-              `profile: ${h264.getProfileName(profile)} bitrate: ${bitrate}`));
-      return null;
-    }
-    return {profile, level, bitrate};
-  }
-
-  /**
-   * @return {!MediaStream}
-   * @private
-   */
-  getRecordingStream_() {
-    if (this.captureStream_ !== null) {
-      return this.captureStream_;
-    }
-    return this.stream;
-  }
-
-  /**
-   * Gets video track of recording stream.
-   * @return {!MediaStreamTrack}
-   */
-  getVideoTrack_() {
-    return this.getRecordingStream_().getVideoTracks()[0];
-  }
-
-  /**
-   * @override
-   */
-  async start() {
-    assert(this.snapshotting_ === null);
-    this.togglePaused_ = null;
-    this.everPaused_ = false;
-
-    const isSoundEnded =
-        await sound.play(dom.get('#sound-rec-start', HTMLAudioElement));
-    if (!isSoundEnded) {
-      throw new CanceledError('Recording sound is canceled');
-    }
-
-    if (this.captureConstraints_ !== null && this.captureStream_ === null) {
-      this.captureStream_ = await StreamManager.getInstance().openCaptureStream(
-          this.captureConstraints_);
-    }
-    if (this.crosImageCapture_ === null) {
-      if (await this.isBlobVideoSnapshotEnabled()) {
-        // Blob stream is configured on the original device rather than the
-        // virtual one when multi-stream is enabled.
-        this.crosImageCapture_ =
-            new CrosImageCapture(this.stream.getVideoTracks()[0]);
-      } else {
-        this.crosImageCapture_ = new CrosImageCapture(this.getVideoTrack_());
-      }
-    }
-
-    try {
-      const param = this.getEncoderParameters_();
-      const mimeType = getVideoMimeType(param);
-      if (!MediaRecorder.isTypeSupported(mimeType)) {
-        throw new Error(
-            `The preferred mimeType "${mimeType}" is not supported.`);
-      }
-      const option = {mimeType};
-      if (param !== null) {
-        option.videoBitsPerSecond = param.bitrate;
-      }
-      this.mediaRecorder_ =
-          new MediaRecorder(this.getRecordingStream_(), option);
-    } catch (e) {
-      toast.show(I18nString.ERROR_MSG_RECORD_START_FAILED);
-      throw e;
-    }
-
-    this.recordingType_ = this.getToggledRecordOption_();
-    // TODO(b:191950622): Remove complex state logic bind with this enable flag
-    // after GIF recording move outside of expert mode and replace it with
-    // |RECORD_TYPE_GIF|.
-    state.set(
-        state.State.ENABLE_GIF_RECORDING,
-        this.recordingType_ === RecordType.GIF);
-    if (this.recordingType_ === RecordType.GIF) {
-      const gifName = (new Filenamer()).newVideoName(VideoType.GIF);
-      state.set(state.State.RECORDING, true);
-      this.gifRecordTime_.start({resume: false});
-
-      const gifSaver = await this.captureGif_();
-
-      state.set(state.State.RECORDING, false);
-      this.gifRecordTime_.stop({pause: false});
-
-      // TODO(b:191950622): Close capture stream before onGifCaptureDone()
-      // opening preview page when multi-stream recording enabled.
-      return () => this.handler_.onGifCaptureDone({
-        name: gifName,
-        gifSaver,
-        resolution: this.captureResolution_,
-        duration: this.gifRecordTime_.inMilliseconds(),
-      });
-    } else {
-      this.recordTime_.start({resume: false});
-      let /** ?VideoSaver */ videoSaver = null;
-
-      const isVideoTooShort = () => this.recordTime_.inMilliseconds() <
-          MINIMUM_VIDEO_DURATION_IN_MILLISECONDS;
-
-      try {
-        try {
-          videoSaver = await this.captureVideo_();
-        } finally {
-          this.recordTime_.stop({pause: false});
-          sound.play(dom.get('#sound-rec-end', HTMLAudioElement));
-          await this.snapshotting_;
-        }
-      } catch (e) {
-        // Tolerates the error if it is due to the very short duration. Reports
-        // for other errors.
-        if (!(e instanceof NoChunkError && isVideoTooShort())) {
-          toast.show(I18nString.ERROR_MSG_EMPTY_RECORDING);
-          throw e;
-        }
-      }
-
-      if (isVideoTooShort()) {
-        toast.show(I18nString.ERROR_MSG_VIDEO_TOO_SHORT);
-        await videoSaver.cancel();
-        return () => this.snapshotting_;
-      }
-
-      return async () => {
-        await this.handler_.onVideoCaptureDone({
-          resolution: this.captureResolution_,
-          duration: this.recordTime_.inMilliseconds(),
-          videoSaver,
-          everPaused: this.everPaused_,
-        });
-        await this.snapshotting_;
-      };
-    }
-  }
-
-  /**
-   * @override
-   */
-  stop() {
-    if (this.recordingType_ === RecordType.GIF) {
-      state.set(state.State.RECORDING, false);
-    } else {
-      sound.cancel(dom.get('#sound-rec-start', HTMLAudioElement));
-
-      if (this.mediaRecorder_ &&
-          (this.mediaRecorder_.state === 'recording' ||
-           this.mediaRecorder_.state === 'paused')) {
-        this.mediaRecorder_.stop();
-        window.removeEventListener('beforeunload', beforeUnloadListener);
-      }
-    }
-  }
-
-  /**
-   * Starts recording gif animation and waits for stop recording event triggered
-   * by stop shutter or time out over 5 seconds.
-   * @return {!Promise<!GifSaver>} Saves recorded video.
-   * @private
-   */
-  async captureGif_() {
-    // TODO(b:191950622): Grab frames from capture stream when multistream
-    // enabled.
-    const video = this.handler_.getPreviewVideo();
-    let {videoWidth: width, videoHeight: height} = video;
-    if (width > GIF_MAX_SIDE || height > GIF_MAX_SIDE) {
-      const ratio = GIF_MAX_SIDE / Math.max(width, height);
-      width = Math.round(width * ratio);
-      height = Math.round(height * ratio);
-    }
-    const gifSaver = await GifSaver.create(new Resolution(width, height));
-    const canvas = new OffscreenCanvas(width, height);
-    const context = assertInstanceof(
-        canvas.getContext('2d'), OffscreenCanvasRenderingContext2D);
-
-    await new Promise((resolve) => {
-      let encodedFrames = 0;
-      let start = 0.0;
-      const updateCanvas = (now) => {
-        if (start === 0.0) {
-          start = now;
-        }
-        if (!state.get(state.State.RECORDING)) {
-          resolve();
-          return;
-        }
-        encodedFrames++;
-        if (encodedFrames % GRAB_GIF_FRAME_RATIO === 0) {
-          context.drawImage(video, 0, 0, width, height);
-          gifSaver.write(context.getImageData(0, 0, width, height).data);
-        }
-        video.requestVideoFrameCallback(updateCanvas);
-      };
-      video.requestVideoFrameCallback(updateCanvas);
-    });
-    return gifSaver;
-  }
-
-  /**
-   * Starts recording and waits for stop recording event triggered by stop
-   * shutter.
-   * @return {!Promise<!VideoSaver>} Saves recorded video.
-   * @private
-   */
-  async captureVideo_() {
-    const saver = await this.handler_.createVideoSaver();
-
-    try {
-      await new Promise((resolve, reject) => {
-        let noChunk = true;
-
-        const ondataavailable = (event) => {
-          if (event.data && event.data.size > 0) {
-            noChunk = false;
-            saver.write(event.data);
-          }
-        };
-
-        const onstop = async (event) => {
-          state.set(state.State.RECORDING, false);
-          state.set(state.State.RECORDING_PAUSED, false);
-          state.set(state.State.RECORDING_UI_PAUSED, false);
-
-          this.mediaRecorder_.removeEventListener(
-              'dataavailable', ondataavailable);
-          this.mediaRecorder_.removeEventListener('stop', onstop);
-
-          if (noChunk) {
-            reject(new NoChunkError());
-          } else {
-            // TODO(yuli): Handle insufficient storage.
-            resolve(saver);
-          }
-        };
-        const onstart = () => {
-          state.set(state.State.RECORDING, true);
-          this.mediaRecorder_.removeEventListener('start', onstart);
-        };
-        this.mediaRecorder_.addEventListener('dataavailable', ondataavailable);
-        this.mediaRecorder_.addEventListener('stop', onstop);
-        this.mediaRecorder_.addEventListener('start', onstart);
-
-        window.addEventListener('beforeunload', beforeUnloadListener);
-
-        this.mediaRecorder_.start(100);
-        state.set(state.State.RECORDING_PAUSED, false);
-        state.set(state.State.RECORDING_UI_PAUSED, false);
-      });
-      return saver;
-    } catch (e) {
-      await saver.cancel();
-      throw e;
-    }
-  }
-}
-
-/**
- * Factory for creating video mode capture object.
- */
-export class VideoFactory extends ModeFactory {
-  /**
-   * @param {!StreamConstraints} constraints Constraints for preview
-   *     stream.
-   * @param {!Resolution} captureResolution
-   * @param {!Resolution} snapshotResolution
-   * @param {!VideoHandler} handler
-   */
-  constructor(constraints, captureResolution, snapshotResolution, handler) {
-    super(constraints, captureResolution);
-
-    /**
-     * @const {!Resolution}
-     * @private
-     */
-    this.snapshotResolution_ = snapshotResolution;
-
-    /**
-     * @const {!VideoHandler}
-     * @private
-     */
-    this.handler_ = handler;
-  }
-
-  /**
-   * @override
-   */
-  produce() {
-    let captureConstraints = null;
-    if (state.get(state.State.ENABLE_MULTISTREAM_RECORDING)) {
-      const {width, height} =
-          assertInstanceof(this.captureResolution, Resolution);
-      captureConstraints = {
-        deviceId: this.constraints.deviceId,
-        audio: this.constraints.audio,
-        video: {
-          frameRate: this.constraints.video.frameRate,
-          width,
-          height,
-        },
-      };
-    }
-    return new Video(
-        this.previewStream, captureConstraints, this.captureResolution,
-        this.snapshotResolution_, this.facing, this.handler_);
-  }
-}
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.ts b/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.ts
new file mode 100644
index 0000000..279aa5a
--- /dev/null
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/mode/video.ts
@@ -0,0 +1,646 @@
+// Copyright (c) 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.
+
+import {
+  assert,
+  assertInstanceof,
+} from '../../../assert.js';
+import {StreamConstraints} from '../../../device/stream_constraints.js';
+import {
+  StreamManager,
+} from '../../../device/stream_manager.js';
+import * as dom from '../../../dom.js';
+import {reportError} from '../../../error.js';
+import * as h264 from '../../../h264.js';
+import {I18nString} from '../../../i18n_string.js';
+import {Filenamer} from '../../../models/file_namer.js';
+import * as loadTimeData from '../../../models/load_time_data.js';
+import {
+  GifSaver,
+  VideoSaver,
+} from '../../../models/video_saver.js';
+import {DeviceOperator} from '../../../mojo/device_operator.js';
+import {CrosImageCapture} from '../../../mojo/image_capture.js';
+import * as sound from '../../../sound.js';
+import * as state from '../../../state.js';
+import * as toast from '../../../toast.js';
+import {
+  CanceledError,
+  ErrorLevel,
+  ErrorType,
+  Facing,
+  NoChunkError,
+  Resolution,
+  VideoType,
+} from '../../../type.js';
+import {WaitableEvent} from '../../../waitable_event.js';
+
+import {ModeBase, ModeFactory} from './mode_base.js';
+import {GifRecordTime, RecordTime} from './record_time.js';
+
+/**
+ * Maps from board name to its default encoding profile and bitrate multiplier.
+ */
+const encoderPreference = new Map([
+  ['strongbad', {profile: h264.Profile.HIGH, multiplier: 6}],
+  ['trogdor', {profile: h264.Profile.HIGH, multiplier: 6}],
+  ['dedede', {profile: h264.Profile.HIGH, multiplier: 8}],
+  ['volteer', {profile: h264.Profile.HIGH, multiplier: 8}],
+]);
+
+let avc1Parameters: h264.EncoderParameters|null = null;
+
+/**
+ * The minimum duration of videos captured via cca.
+ */
+const MINIMUM_VIDEO_DURATION_IN_MILLISECONDS = 500;
+
+/**
+ * The maximal length of the longer side of gif width or height.
+ */
+const GIF_MAX_SIDE = 640;
+
+/**
+ * Maximum recording time for GIF animation mode.
+ */
+const MAX_GIF_DURATION_MS = 5000;
+
+/**
+ * Sample ratio of grabbing gif frame to be encoded.
+ */
+const GRAB_GIF_FRAME_RATIO = 2;
+
+/**
+ * Sets avc1 parameter used in video recording.
+ */
+export function setAvc1Parameters(params: h264.EncoderParameters|null): void {
+  avc1Parameters = params;
+}
+
+/**
+ * Gets video recording MIME type. Mkv with AVC1 is the only preferred format.
+ * @return Video recording MIME type.
+ */
+function getVideoMimeType(param: h264.EncoderParameters|null): string {
+  let suffix = '';
+  if (param !== null) {
+    const {profile, level} = param;
+    suffix = '.' + profile.toString(16).padStart(2, '0') +
+        level.toString(16).padStart(4, '0');
+  }
+  return `video/x-matroska;codecs=avc1${suffix},pcm`;
+}
+
+/**
+ * The 'beforeunload' listener which will show confirm dialog when trying to
+ * close window.
+ */
+function beforeUnloadListener(event: BeforeUnloadEvent) {
+  event.preventDefault();
+  event.returnValue = '';
+}
+
+export interface VideoSnapshotResult {
+  blob: Blob;
+  resolution: Resolution;
+  timestamp: number;
+}
+
+export interface VideoResult {
+  resolution: Resolution;
+  duration: number;
+  videoSaver: VideoSaver;
+  everPaused: boolean;
+}
+
+export interface GifResult {
+  name: string;
+  gifSaver: GifSaver;
+  resolution: Resolution;
+  duration: number;
+}
+
+/**
+ * Provides functions with external dependency used by video mode and handles
+ * the captured result video.
+ */
+export interface VideoHandler {
+  /**
+   * Creates VideoSaver to save video capture result.
+   */
+  createVideoSaver(): Promise<VideoSaver>;
+
+  /**
+   * Handles the result video snapshot.
+   */
+  handleVideoSnapshot(videoSnapshotResult: VideoSnapshotResult): Promise<void>;
+
+  /**
+   * Plays UI effect when doing video snapshot.
+   */
+  playShutterEffect(): void;
+
+  /**
+   * Gets preview video element.
+   */
+  getPreviewVideo(): HTMLVideoElement;
+
+  onGifCaptureDone(gifResult: GifResult): Promise<void>;
+
+  onVideoCaptureDone(videoResult: VideoResult): Promise<void>;
+}
+
+const RecordType = {
+  NORMAL: state.State.RECORD_TYPE_NORMAL,
+  GIF: state.State.RECORD_TYPE_GIF,
+} as const;
+
+type RecordType = typeof RecordType[keyof typeof RecordType];
+
+/**
+ * Video mode capture controller.
+ */
+export class Video extends ModeBase {
+  private readonly captureResolution: Resolution;
+  private captureStream: MediaStream|null = null;
+
+  /**
+   * MediaRecorder object to record motion pictures.
+   */
+  private mediaRecorder: MediaRecorder|null = null;
+
+  private crosImageCapture: CrosImageCapture|null = null;
+
+  /**
+   * Record-time for the elapsed recording time.
+   */
+  private readonly recordTime = new RecordTime();
+
+  /**
+   * Record-time for the elapsed gif recording time.
+   */
+  private gifRecordTime: GifRecordTime;
+
+  /**
+   * Record type of ongoing recording.
+   */
+  private recordingType: RecordType = RecordType.NORMAL;
+
+  /**
+   * The ongoing video snapshot.
+   */
+  private snapshotting: Promise<void>|null = null;
+
+  /**
+   * Promise for process of toggling video pause/resume. Sets to null if CCA
+   * is already paused or resumed.
+   */
+  private togglePausedInternal: Promise<void>|null = null;
+
+  /**
+   * Whether current recording ever paused/resumed before it ended.
+   */
+  private everPaused = false;
+
+  /**
+   * @param stream Preview stream.
+   */
+  constructor(
+      stream: MediaStream,
+      private readonly captureConstraints: StreamConstraints|null,
+      captureResolution: Resolution,
+      private readonly snapshotResolution: Resolution,
+      facing: Facing,
+      private readonly handler: VideoHandler,
+  ) {
+    super(stream, facing);
+
+    this.captureResolution = (() => {
+      if (captureResolution !== null) {
+        return captureResolution;
+      }
+      const {width, height} = stream.getVideoTracks()[0].getSettings();
+      return new Resolution(width, height);
+    })();
+
+    this.gifRecordTime = new GifRecordTime(
+        {maxTime: MAX_GIF_DURATION_MS, onMaxTimeout: () => this.stop()});
+  }
+
+  async clear(): Promise<void> {
+    await this.stopCapture();
+    if (this.captureStream !== null) {
+      await StreamManager.getInstance().closeCaptureStream(this.captureStream);
+      this.captureStream = null;
+    }
+  }
+
+  updatePreview(stream: MediaStream): void {
+    assert(!state.get(state.State.RECORDING));
+    this.stream = stream;
+    this.crosImageCapture = new CrosImageCapture(this.getVideoTrack());
+  }
+
+  /**
+   * @return Returns record type of checked radio buttons in record type option
+   *     groups.
+   */
+  private getToggledRecordOption(): RecordType {
+    if (state.get(state.State.SHOULD_HANDLE_INTENT_RESULT)) {
+      return RecordType.NORMAL;
+    }
+    return Object.values(RecordType).find((t) => state.get(t)) ||
+        RecordType.NORMAL;
+  }
+
+  /**
+   * @return Returns whether taking video sanpshot via Blob stream is enabled.
+   */
+  async isBlobVideoSnapshotEnabled(): Promise<boolean> {
+    const deviceOperator = await DeviceOperator.getInstance();
+    const deviceId = this.stream.getVideoTracks()[0].getSettings().deviceId;
+    return deviceOperator !== null &&
+        (await deviceOperator.isBlobVideoSnapshotEnabled(deviceId));
+  }
+
+  /**
+   * Takes a video snapshot during recording.
+   * @return Promise resolved when video snapshot is finished.
+   */
+  async takeSnapshot(): Promise<void> {
+    if (this.snapshotting !== null) {
+      return;
+    }
+    state.set(state.State.SNAPSHOTTING, true);
+    this.snapshotting = (async () => {
+      try {
+        const timestamp = Date.now();
+        let blob: Blob;
+        if (await this.isBlobVideoSnapshotEnabled()) {
+          const photoSettings: PhotoSettings = {
+            imageWidth: this.snapshotResolution.width,
+            imageHeight: this.snapshotResolution.height,
+          };
+          const results = await this.crosImageCapture.takePhoto(photoSettings);
+          blob = await results[0];
+        } else {
+          blob = await this.crosImageCapture.grabJpegFrame();
+        }
+
+        this.handler.playShutterEffect();
+        await this.handler.handleVideoSnapshot({
+          blob,
+          resolution: this.captureResolution,
+          timestamp,
+        });
+      } finally {
+        state.set(state.State.SNAPSHOTTING, false);
+        this.snapshotting = null;
+      }
+    })();
+    return this.snapshotting;
+  }
+
+  /**
+   * Toggles pause/resume state of video recording.
+   * @return Promise resolved when recording is paused/resumed.
+   */
+  async togglePaused(): Promise<void> {
+    if (!state.get(state.State.RECORDING)) {
+      return;
+    }
+    if (this.togglePausedInternal !== null) {
+      return this.togglePausedInternal;
+    }
+    this.everPaused = true;
+    const waitable = new WaitableEvent();
+    this.togglePausedInternal = waitable.wait();
+
+    assert(this.mediaRecorder.state !== 'inactive');
+    const toBePaused = this.mediaRecorder.state !== 'paused';
+    const toggledEvent = toBePaused ? 'pause' : 'resume';
+    const onToggled = () => {
+      this.mediaRecorder.removeEventListener(toggledEvent, onToggled);
+      state.set(state.State.RECORDING_PAUSED, toBePaused);
+      this.togglePausedInternal = null;
+      waitable.signal();
+    };
+    const playEffect = async () => {
+      state.set(state.State.RECORDING_UI_PAUSED, toBePaused);
+      await sound.play(dom.get(
+          toBePaused ? '#sound-rec-pause' : '#sound-rec-start',
+          HTMLAudioElement));
+    };
+
+    this.mediaRecorder.addEventListener(toggledEvent, onToggled);
+    if (toBePaused) {
+      waitable.wait().then(playEffect);
+      this.recordTime.stop({pause: true});
+      this.mediaRecorder.pause();
+    } else {
+      await playEffect();
+      this.recordTime.start({resume: true});
+      this.mediaRecorder.resume();
+    }
+
+    return waitable.wait();
+  }
+
+  private getEncoderParameters(): h264.EncoderParameters|null {
+    if (avc1Parameters !== null) {
+      return avc1Parameters;
+    }
+    const preference = encoderPreference.get(loadTimeData.getBoard()) ||
+        {profile: h264.Profile.HIGH, multiplier: 2};
+    const {profile, multiplier} = preference;
+    const {width, height, frameRate} = this.getVideoTrack().getSettings();
+    const resolution = new Resolution(width, height);
+    const bitrate = resolution.area * multiplier;
+    const level = h264.getMinimalLevel(profile, bitrate, frameRate, resolution);
+    if (level === null) {
+      reportError(
+          ErrorType.NO_AVAILABLE_LEVEL, ErrorLevel.WARNING,
+          new Error(
+              `No valid level found for ` +
+              `profile: ${h264.getProfileName(profile)} bitrate: ${bitrate}`));
+      return null;
+    }
+    return {profile, level, bitrate};
+  }
+
+  private getRecordingStream(): MediaStream {
+    if (this.captureStream !== null) {
+      return this.captureStream;
+    }
+    return this.stream;
+  }
+
+  /**
+   * Gets video track of recording stream.
+   */
+  private getVideoTrack(): MediaStreamTrack {
+    return this.getRecordingStream().getVideoTracks()[0];
+  }
+
+  async start(): Promise<() => Promise<void>> {
+    assert(this.snapshotting === null);
+    this.togglePausedInternal = null;
+    this.everPaused = false;
+
+    const isSoundEnded =
+        await sound.play(dom.get('#sound-rec-start', HTMLAudioElement));
+    if (!isSoundEnded) {
+      throw new CanceledError('Recording sound is canceled');
+    }
+
+    if (this.captureConstraints !== null && this.captureStream === null) {
+      this.captureStream = await StreamManager.getInstance().openCaptureStream(
+          this.captureConstraints);
+    }
+    if (this.crosImageCapture === null) {
+      if (await this.isBlobVideoSnapshotEnabled()) {
+        // Blob stream is configured on the original device rather than the
+        // virtual one when multi-stream is enabled.
+        this.crosImageCapture =
+            new CrosImageCapture(this.stream.getVideoTracks()[0]);
+      } else {
+        this.crosImageCapture = new CrosImageCapture(this.getVideoTrack());
+      }
+    }
+
+    try {
+      const param = this.getEncoderParameters();
+      const mimeType = getVideoMimeType(param);
+      if (!MediaRecorder.isTypeSupported(mimeType)) {
+        throw new Error(
+            `The preferred mimeType "${mimeType}" is not supported.`);
+      }
+      const option: MediaRecorderOptions = {mimeType};
+      if (param !== null) {
+        option.videoBitsPerSecond = param.bitrate;
+      }
+      this.mediaRecorder = new MediaRecorder(this.getRecordingStream(), option);
+    } catch (e) {
+      toast.show(I18nString.ERROR_MSG_RECORD_START_FAILED);
+      throw e;
+    }
+
+    this.recordingType = this.getToggledRecordOption();
+    // TODO(b:191950622): Remove complex state logic bind with this enable flag
+    // after GIF recording move outside of expert mode and replace it with
+    // |RECORD_TYPE_GIF|.
+    state.set(
+        state.State.ENABLE_GIF_RECORDING,
+        this.recordingType === RecordType.GIF);
+    if (this.recordingType === RecordType.GIF) {
+      const gifName = (new Filenamer()).newVideoName(VideoType.GIF);
+      state.set(state.State.RECORDING, true);
+      this.gifRecordTime.start({resume: false});
+
+      const gifSaver = await this.captureGif();
+
+      state.set(state.State.RECORDING, false);
+      this.gifRecordTime.stop({pause: false});
+
+      // TODO(b:191950622): Close capture stream before onGifCaptureDone()
+      // opening preview page when multi-stream recording enabled.
+      return () => this.handler.onGifCaptureDone({
+        name: gifName,
+        gifSaver,
+        resolution: this.captureResolution,
+        duration: this.gifRecordTime.inMilliseconds(),
+      });
+    } else {
+      this.recordTime.start({resume: false});
+      let videoSaver: VideoSaver|null = null;
+
+      const isVideoTooShort = () => this.recordTime.inMilliseconds() <
+          MINIMUM_VIDEO_DURATION_IN_MILLISECONDS;
+
+      try {
+        try {
+          videoSaver = await this.captureVideo();
+        } finally {
+          this.recordTime.stop({pause: false});
+          sound.play(dom.get('#sound-rec-end', HTMLAudioElement));
+          await this.snapshotting;
+        }
+      } catch (e) {
+        // Tolerates the error if it is due to the very short duration. Reports
+        // for other errors.
+        if (!(e instanceof NoChunkError && isVideoTooShort())) {
+          toast.show(I18nString.ERROR_MSG_EMPTY_RECORDING);
+          throw e;
+        }
+      }
+
+      if (isVideoTooShort()) {
+        toast.show(I18nString.ERROR_MSG_VIDEO_TOO_SHORT);
+        await videoSaver.cancel();
+        return () => this.snapshotting;
+      }
+
+      return async () => {
+        await this.handler.onVideoCaptureDone({
+          resolution: this.captureResolution,
+          duration: this.recordTime.inMilliseconds(),
+          videoSaver,
+          everPaused: this.everPaused,
+        });
+        await this.snapshotting;
+      };
+    }
+  }
+
+  stop(): void {
+    if (this.recordingType === RecordType.GIF) {
+      state.set(state.State.RECORDING, false);
+    } else {
+      sound.cancel(dom.get('#sound-rec-start', HTMLAudioElement));
+
+      if (this.mediaRecorder &&
+          (this.mediaRecorder.state === 'recording' ||
+           this.mediaRecorder.state === 'paused')) {
+        this.mediaRecorder.stop();
+        window.removeEventListener('beforeunload', beforeUnloadListener);
+      }
+    }
+  }
+
+  /**
+   * Starts recording gif animation and waits for stop recording event triggered
+   * by stop shutter or time out over 5 seconds.
+   * @return Saves recorded video.
+   */
+  private async captureGif(): Promise<GifSaver> {
+    // TODO(b:191950622): Grab frames from capture stream when multistream
+    // enabled.
+    const video = this.handler.getPreviewVideo();
+    let {videoWidth: width, videoHeight: height} = video;
+    if (width > GIF_MAX_SIDE || height > GIF_MAX_SIDE) {
+      const ratio = GIF_MAX_SIDE / Math.max(width, height);
+      width = Math.round(width * ratio);
+      height = Math.round(height * ratio);
+    }
+    const gifSaver = await GifSaver.create(new Resolution(width, height));
+    const canvas = new OffscreenCanvas(width, height);
+    const context = assertInstanceof(
+        canvas.getContext('2d'), OffscreenCanvasRenderingContext2D);
+
+    await new Promise<void>((resolve) => {
+      let encodedFrames = 0;
+      let start = 0.0;
+      const updateCanvas = (now) => {
+        if (start === 0.0) {
+          start = now;
+        }
+        if (!state.get(state.State.RECORDING)) {
+          resolve();
+          return;
+        }
+        encodedFrames++;
+        if (encodedFrames % GRAB_GIF_FRAME_RATIO === 0) {
+          context.drawImage(video, 0, 0, width, height);
+          gifSaver.write(context.getImageData(0, 0, width, height).data);
+        }
+        video.requestVideoFrameCallback(updateCanvas);
+      };
+      video.requestVideoFrameCallback(updateCanvas);
+    });
+    return gifSaver;
+  }
+
+  /**
+   * Starts recording and waits for stop recording event triggered by stop
+   * shutter.
+   * @return Saves recorded video.
+   */
+  private async captureVideo(): Promise<VideoSaver> {
+    const saver = await this.handler.createVideoSaver();
+
+    try {
+      await new Promise((resolve, reject) => {
+        let noChunk = true;
+
+        const ondataavailable = (event) => {
+          if (event.data && event.data.size > 0) {
+            noChunk = false;
+            saver.write(event.data);
+          }
+        };
+
+        const onstop = async () => {
+          state.set(state.State.RECORDING, false);
+          state.set(state.State.RECORDING_PAUSED, false);
+          state.set(state.State.RECORDING_UI_PAUSED, false);
+
+          this.mediaRecorder.removeEventListener(
+              'dataavailable', ondataavailable);
+          this.mediaRecorder.removeEventListener('stop', onstop);
+
+          if (noChunk) {
+            reject(new NoChunkError());
+          } else {
+            // TODO(yuli): Handle insufficient storage.
+            resolve(saver);
+          }
+        };
+        const onstart = () => {
+          state.set(state.State.RECORDING, true);
+          this.mediaRecorder.removeEventListener('start', onstart);
+        };
+        this.mediaRecorder.addEventListener('dataavailable', ondataavailable);
+        this.mediaRecorder.addEventListener('stop', onstop);
+        this.mediaRecorder.addEventListener('start', onstart);
+
+        window.addEventListener('beforeunload', beforeUnloadListener);
+
+        this.mediaRecorder.start(100);
+        state.set(state.State.RECORDING_PAUSED, false);
+        state.set(state.State.RECORDING_UI_PAUSED, false);
+      });
+      return saver;
+    } catch (e) {
+      await saver.cancel();
+      throw e;
+    }
+  }
+}
+
+/**
+ * Factory for creating video mode capture object.
+ */
+export class VideoFactory extends ModeFactory {
+  /**
+   * @param constraints Constraints for preview
+   *     stream.
+   */
+  constructor(
+      constraints: StreamConstraints,
+      captureResolution: Resolution,
+      private readonly snapshotResolution: Resolution,
+      private readonly handler: VideoHandler,
+  ) {
+    super(constraints, captureResolution);
+  }
+
+  produce(): ModeBase {
+    let captureConstraints = null;
+    if (state.get(state.State.ENABLE_MULTISTREAM_RECORDING)) {
+      const {width, height} = this.captureResolution;
+      captureConstraints = {
+        deviceId: this.constraints.deviceId,
+        audio: this.constraints.audio,
+        video: {
+          frameRate: this.constraints.video.frameRate,
+          width,
+          height,
+        },
+      };
+    }
+    return new Video(
+        this.previewStream, captureConstraints, this.captureResolution,
+        this.snapshotResolution, this.facing, this.handler);
+  }
+}
diff --git a/ash/webui/camera_app_ui/resources/strings/camera_strings.grd b/ash/webui/camera_app_ui/resources/strings/camera_strings.grd
index de4bdac2..161ed516 100644
--- a/ash/webui/camera_app_ui/resources/strings/camera_strings.grd
+++ b/ash/webui/camera_app_ui/resources/strings/camera_strings.grd
@@ -6,6 +6,7 @@
     <output filename="grit/ash_camera_app_strings.h" type="rc_header">
       <emit emit_type='prepend'></emit>
     </output>
+    <output filename="ash_camera_app_strings_af.pak" type="data_package" lang="af" />
     <output filename="ash_camera_app_strings_am.pak" type="data_package" lang="am" />
     <output filename="ash_camera_app_strings_ar.pak" type="data_package" lang="ar" />
     <output filename="ash_camera_app_strings_bg.pak" type="data_package" lang="bg" />
@@ -62,6 +63,7 @@
     <output filename="ash_camera_app_strings_vi.pak" type="data_package" lang="vi" />
     <output filename="ash_camera_app_strings_zh-CN.pak" type="data_package" lang="zh-CN" />
     <output filename="ash_camera_app_strings_zh-TW.pak" type="data_package" lang="zh-TW" />
+    <output filename="ash_camera_app_strings_zu.pak" type="data_package" lang="zu" />
 
     <!-- Pseudolocales -->
     <output filename="ash_camera_app_strings_ar-XB.pak" type="data_package" lang="ar-XB" />
diff --git a/ash/webui/eche_app_ui/system_info_provider.cc b/ash/webui/eche_app_ui/system_info_provider.cc
index d978427..eba44de2 100644
--- a/ash/webui/eche_app_ui/system_info_provider.cc
+++ b/ash/webui/eche_app_ui/system_info_provider.cc
@@ -63,15 +63,16 @@
     base::OnceCallback<void(const std::string&)> callback) {
   PA_LOG(INFO) << "echeapi SystemInfoProvider GetSystemInfo";
   base::DictionaryValue json_dictionary;
-  json_dictionary.SetString(kJsonDeviceNameKey, system_info_->GetDeviceName());
-  json_dictionary.SetString(kJsonBoardNameKey, system_info_->GetBoardName());
+  json_dictionary.SetStringKey(kJsonDeviceNameKey,
+                               system_info_->GetDeviceName());
+  json_dictionary.SetStringKey(kJsonBoardNameKey, system_info_->GetBoardName());
   json_dictionary.SetBoolean(kJsonTabletModeKey,
                              TabletMode::Get()->InTabletMode());
   auto found_type = CONNECTION_STATE_TYPE.find(wifi_connection_state_);
   std::string connecton_state_string =
       found_type == CONNECTION_STATE_TYPE.end() ? "" : found_type->second;
-  json_dictionary.SetString(kJsonWifiConnectionStateKey,
-                            connecton_state_string);
+  json_dictionary.SetStringKey(kJsonWifiConnectionStateKey,
+                               connecton_state_string);
   json_dictionary.SetBoolean(
       kJsonDebugModeKey,
       base::FeatureList::IsEnabled(features::kEcheSWADebugMode));
diff --git a/ash/webui/eche_app_ui/system_info_provider_unittest.cc b/ash/webui/eche_app_ui/system_info_provider_unittest.cc
index c4e631c..366348d 100644
--- a/ash/webui/eche_app_ui/system_info_provider_unittest.cc
+++ b/ash/webui/eche_app_ui/system_info_provider_unittest.cc
@@ -37,14 +37,22 @@
       base::JSONReader::ReadDeprecated(json);
   base::DictionaryValue* message_dictionary;
   message_value->GetAsDictionary(&message_dictionary);
-  message_dictionary->GetString(kJsonDeviceNameKey, &device_name);
-  message_dictionary->GetString(kJsonBoardNameKey, &board_name);
+  const std::string* device_name_ptr =
+      message_dictionary->FindStringKey(kJsonDeviceNameKey);
+  if (device_name_ptr)
+    device_name = *device_name_ptr;
+  const std::string* board_name_ptr =
+      message_dictionary->FindStringKey(kJsonBoardNameKey);
+  if (board_name_ptr)
+    board_name = *board_name_ptr;
   absl::optional<bool> tablet_mode_opt =
       message_dictionary->FindBoolKey(kJsonTabletModeKey);
   if (tablet_mode_opt.has_value())
     tablet_mode = tablet_mode_opt.value();
-  message_dictionary->GetString(kJsonWifiConnectionStateKey,
-                                &wifi_connection_state);
+  const std::string* wifi_connection_state_ptr =
+      message_dictionary->FindStringKey(kJsonWifiConnectionStateKey);
+  if (wifi_connection_state_ptr)
+    wifi_connection_state = *wifi_connection_state_ptr;
   absl::optional<bool> debug_mode_opt =
       message_dictionary->FindBoolKey(kJsonDebugModeKey);
   if (debug_mode_opt.has_value())
diff --git a/ash/webui/firmware_update_ui/mojom/firmware_update.mojom b/ash/webui/firmware_update_ui/mojom/firmware_update.mojom
index a58af44..2a5edf2 100644
--- a/ash/webui/firmware_update_ui/mojom/firmware_update.mojom
+++ b/ash/webui/firmware_update_ui/mojom/firmware_update.mojom
@@ -5,6 +5,7 @@
 module ash.firmware_update.mojom;
 
 import "mojo/public/mojom/base/string16.mojom";
+import "mojo/public/mojom/base/file_path.mojom";
 
 // Represents the priority of an firmware update e.g. a security update may be
 // considered a critical priority.
@@ -32,6 +33,26 @@
 
   // Priority of the device's firmware update.
   UpdatePriority priority;
+
+  // Filepath of the device's firmware update.
+  mojo_base.mojom.FilePath filepath;
+};
+
+// Contains the completion percentage and state of an in-progress firmware
+// update.
+struct InstallationProgress {
+  uint32 percentage;
+  UpdateState state;
+};
+
+// Represents the state of an in-progress firmware update.
+enum UpdateState {
+  kUnknown,
+  kIdle,
+  kUpdating,
+  kRestarting,
+  kFailed,
+  kSuccess,
 };
 
 // Send update descriptions.
@@ -42,6 +63,14 @@
   OnUpdateListChanged(array<FirmwareUpdate> firmware_updates);
 };
 
+// Observer interface used to send remote updates about an in-progress
+// firmware update. Called when dbus::PropertiesChanged() is called for the
+// Fwupd interface.
+interface UpdateProgressObserver {
+  // Sends status updates for the in-progress firmware update.
+  OnStatusChanged(InstallationProgress update);
+};
+
 // Enables clients to receive a list of devices with pending updates.
 // Implemented in the browser process and called by the Firmware Update SWA
 // (a renderer process).
@@ -49,4 +78,21 @@
   // Registers an observer to be notified on changes to devices with pending
   // updates.
   ObservePeripheralUpdates(pending_remote<UpdateObserver> observer);
+
+  // TODO(jimmyxgong): Update to include the filename/path of the file used to
+  // update.
+  // Stores the |device_id| to be updated and returns a |InstallController|
+  // remote.
+  PrepareForUpdate(string device_id) =>
+    (pending_remote<InstallController>? controller);
+};
+
+// Enables clients to begin the install flow and receive progress updates.
+interface InstallController {
+  // Responsible for calling |FirmwareUpdateManager::StartInstall| to start
+  // the update.
+  BeginUpdate();
+  // Registers an observer that will be notified of changes to
+  // the inflight update's progress.
+  AddObserver(pending_remote<UpdateProgressObserver> observer);
 };
diff --git a/ash/webui/firmware_update_ui/resources/BUILD.gn b/ash/webui/firmware_update_ui/resources/BUILD.gn
index 45512a5..58aabf79 100644
--- a/ash/webui/firmware_update_ui/resources/BUILD.gn
+++ b/ash/webui/firmware_update_ui/resources/BUILD.gn
@@ -13,6 +13,7 @@
 
 preprocessed_dir = "preprocessed"
 preprocessed_gen_manifest = "preprocessed_gen_manifest.json"
+preprocess_external_mojo_manifest = "preprocessed_external_mojo_manifest.json"
 
 polymer_element_files = [
   "firmware_shared_css.js",
@@ -36,12 +37,17 @@
   ]
   input_files_base_dir = rebase_path(".", "//")
   deps = [
+    ":preprocess_external_mojo",
     ":preprocess_firmware_update_mojom",
     ":preprocess_generated",
   ]
   manifest_files = [
     "$target_gen_dir/$preprocessed_gen_manifest",
     "$target_gen_dir/manifest_preprocess_mojo.json",
+    "$target_gen_dir/$preprocess_external_mojo_manifest",
+  ]
+  resource_path_rewrites = [
+    "mojo/public/mojom/base/file_path.mojom-lite.js|file_path.mojom-lite.js",
   ]
   grd_prefix = "ash_firmware_update_app"
   out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
@@ -156,6 +162,14 @@
   in_files = [ "mojom/firmware_update.mojom-lite.js" ]
 }
 
+preprocess_if_expr("preprocess_external_mojo") {
+  deps = [ "//mojo/public/mojom/base:base_js__generator" ]
+  in_folder = "$root_gen_dir"
+  out_folder = "$target_gen_dir/preprocessed_external_mojo"
+  out_manifest = "$target_gen_dir/$preprocess_external_mojo_manifest"
+  in_files = [ "mojo/public/mojom/base/file_path.mojom-lite.js" ]
+}
+
 html_to_js("web_components") {
   js_files = polymer_element_files
 }
diff --git a/ash/webui/firmware_update_ui/resources/fake_data.js b/ash/webui/firmware_update_ui/resources/fake_data.js
index 2a597674..95ea08f 100644
--- a/ash/webui/firmware_update_ui/resources/fake_data.js
+++ b/ash/webui/firmware_update_ui/resources/fake_data.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {FirmwareUpdate, InstallationProgress, UpdatePriority} from './firmware_update_types.js';
+import {FirmwareUpdate, InstallationProgress, UpdatePriority, UpdateState} from './firmware_update_types.js';
 import {stringToMojoString16} from './mojo_utils.js';
 
 /** @type {!Array<!Array<!FirmwareUpdate>>} */
@@ -15,6 +15,7 @@
         `Update the firmware to the latest to enhance the security of your HP
          dock device`),
     priority: UpdatePriority.kCritical,
+    filepath: {'path': '1.cab'},
   },
   {
     deviceId: '2',
@@ -24,6 +25,7 @@
         `Updating your ColorHugALS device firmware improves performance and
          adds new features`),
     priority: UpdatePriority.kMedium,
+    filepath: {'path': '2.cab'},
   },
   {
     deviceId: '3',
@@ -32,23 +34,15 @@
     deviceDescription: stringToMojoString16(
         'Update firmware for Logitech keyboard to improve performance'),
     priority: UpdatePriority.kLow,
+    filepath: {'path': '3.cab'},
   },
 ]];
 
 /** @type {!Array<!InstallationProgress>} */
 export const fakeInstallationProgress = [
-  {
-    status: '',
-    percentage: 33,
-  },
-  {
-    status: '',
-    percentage: 66,
-  },
-  {
-    status: '',
-    percentage: 100,
-  },
+  {percentage: 33, state: UpdateState.kUpdating},
+  {percentage: 66, state: UpdateState.kUpdating},
+  {percentage: 100, state: UpdateState.kSuccess},
 ];
 
 /** @type {!FirmwareUpdate} */
@@ -59,6 +53,7 @@
   deviceDescription: stringToMojoString16(
       'Update firmware for Logitech keyboard to improve performance'),
   priority: UpdatePriority.kLow,
+  filepath: {'path': '1.cab'},
 };
 
 /** @type {!FirmwareUpdate} */
@@ -69,4 +64,5 @@
   deviceDescription: stringToMojoString16(
       'Update firmware for Logitech keyboard to improve performance'),
   priority: UpdatePriority.kCritical,
+  filepath: {'path': '2.cab'},
 };
diff --git a/ash/webui/firmware_update_ui/resources/fake_update_controller.js b/ash/webui/firmware_update_ui/resources/fake_update_controller.js
index 638b112..ddfcf378 100644
--- a/ash/webui/firmware_update_ui/resources/fake_update_controller.js
+++ b/ash/webui/firmware_update_ui/resources/fake_update_controller.js
@@ -7,26 +7,23 @@
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
 
 import {fakeFirmwareUpdates, fakeInstallationProgress} from './fake_data.js';
-import {FakeUpdateProviderInterface, FirmwareUpdate, InstallationProgress, UpdateControllerInterface, UpdateProgressObserver, UpdateProviderInterface} from './firmware_update_types.js';
+import {FakeUpdateProviderInterface, FirmwareUpdate, InstallationProgress, InstallControllerInterface, UpdateProgressObserver, UpdateProviderInterface, UpdateState} from './firmware_update_types.js';
 import {getUpdateProvider, setUseFakeProviders} from './mojo_interface_provider.js';
 
 // Method names.
-export const ON_PROGRESS_CHANGED = 'UpdateProgressObserver_onProgressChanged';
+export const ON_PROGRESS_CHANGED = 'UpdateProgressObserver_onStatusChanged';
 
 /**
  * @fileoverview
  * Implements a fake version of the UpdateController mojo interface.
  */
 
-/** @implements {UpdateControllerInterface} */
+/** @implements {InstallControllerInterface} */
 export class FakeUpdateController {
   constructor() {
     setUseFakeProviders(true);
     this.observables_ = new FakeObservables();
 
-    /** @private {UpdateProviderInterface|FakeUpdateProviderInterface} */
-    this.fakeUpdateProvider_ = getUpdateProvider();
-
     /** @private {!Set<string>} */
     this.completedFirmwareUpdates_ = new Set();
 
@@ -49,19 +46,16 @@
   }
 
   /*
-   * Implements UpdateControllerInterface.startUpdate.
-   * @param {string} deviceId
+   * Implements InstallControllerInterface.addObserver.
    * @param {!UpdateProgressObserver} remote
-   * @return {!Promise}
    */
-  startUpdate(deviceId, remote) {
-    this.deviceId_ = deviceId;
+  addObserver(remote) {
     this.isUpdateInProgress_ = true;
     this.updateCompletedPromise_ = new PromiseResolver();
-    this.startUpdatePromise_ = this.observeWithArg_(
-        ON_PROGRESS_CHANGED, deviceId, (installationProgress) => {
-          remote.onProgressChanged(installationProgress);
-          if (installationProgress.percentage === 100) {
+    this.startUpdatePromise_ =
+        this.observeWithArg_(ON_PROGRESS_CHANGED, this.deviceId_, (update) => {
+          remote.onStatusChanged(update);
+          if (update.state === UpdateState.kSuccess) {
             this.isUpdateInProgress_ = false;
             this.completedFirmwareUpdates_.add(this.deviceId_);
             this.updateDeviceList_();
@@ -70,7 +64,16 @@
             this.updateCompletedPromise_.resolve();
           }
         });
-    this.startUpdatePromise_.then(() => this.triggerProgressChangedObserver());
+  }
+
+  beginUpdate() {
+    assert(this.startUpdatePromise_);
+    this.triggerProgressChangedObserver();
+  }
+
+  /** @param {string} deviceId */
+  setDeviceIdForUpdateInProgress(deviceId) {
+    this.deviceId_ = deviceId;
   }
 
   /**
@@ -161,9 +164,11 @@
   updateDeviceList_() {
     const updatedFakeFirmwareUpdates = fakeFirmwareUpdates.flat().filter(
         u => !this.completedFirmwareUpdates_.has(u.deviceId));
-    this.fakeUpdateProvider_.setFakeFirmwareUpdates(
-        [updatedFakeFirmwareUpdates]);
-    this.fakeUpdateProvider_.triggerDeviceAddedObserver();
+
+    /** @type {UpdateProviderInterface|FakeUpdateProviderInterface} */
+    const provider = getUpdateProvider();
+    provider.setFakeFirmwareUpdates([updatedFakeFirmwareUpdates]);
+    provider.triggerDeviceAddedObserver();
   }
 
   /**
diff --git a/ash/webui/firmware_update_ui/resources/fake_update_provider.js b/ash/webui/firmware_update_ui/resources/fake_update_provider.js
index 31fab6e..cca8b1c 100644
--- a/ash/webui/firmware_update_ui/resources/fake_update_provider.js
+++ b/ash/webui/firmware_update_ui/resources/fake_update_provider.js
@@ -4,8 +4,8 @@
 
 import {FakeObservables} from 'chrome://resources/ash/common/fake_observables.js';
 
-import {FirmwareUpdate, UpdateObserver, UpdateProviderInterface} from './firmware_update_types.js';
-import {setUseFakeProviders} from './mojo_interface_provider.js';
+import {FakeInstallControllerInterface, FirmwareUpdate, InstallControllerInterface, UpdateObserver, UpdateProviderInterface} from './firmware_update_types.js';
+import {getUpdateController, getUpdateProvider, setUseFakeProviders} from './mojo_interface_provider.js';
 
 // Method names.
 export const ON_UPDATE_LIST_CHANGED = 'UpdateObserver_onUpdateListChanged';
@@ -40,6 +40,17 @@
   }
 
   /**
+   * @param {string} deviceId
+   * @return {!Promise}
+   */
+  prepareForUpdate(deviceId) {
+    /** @type {InstallControllerInterface|FakeInstallControllerInterface} */
+    const controller = getUpdateController();
+    controller.setDeviceIdForUpdateInProgress(deviceId);
+    return new Promise((resolve) => resolve({controller}));
+  }
+
+  /**
    * Sets the values that will be observed from observePeripheralUpdates.
    * @param {!Array<!Array<!FirmwareUpdate>>} firmwareUpdates
    */
diff --git a/ash/webui/firmware_update_ui/resources/firmware_update_dialog.js b/ash/webui/firmware_update_ui/resources/firmware_update_dialog.js
index d40ef52..6749573 100644
--- a/ash/webui/firmware_update_ui/resources/firmware_update_dialog.js
+++ b/ash/webui/firmware_update_ui/resources/firmware_update_dialog.js
@@ -5,6 +5,7 @@
 import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
 import 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js';
 import 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js';
+import '/file_path.mojom-lite.js';
 import './firmware_shared_css.js';
 import './firmware_shared_fonts.js';
 import './mojom/firmware_update.mojom-lite.js';
@@ -14,8 +15,8 @@
 import 'chrome://resources/polymer/v3_0/paper-progress/paper-progress.js';
 import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {FirmwareUpdate, InstallationProgress, UpdateControllerInterface} from './firmware_update_types.js';
-import {getUpdateController} from './mojo_interface_provider.js';
+import {FirmwareUpdate, InstallationProgress, InstallControllerRemote, UpdateProgressObserverInterface, UpdateProgressObserverReceiver, UpdateProviderInterface, UpdateState} from './firmware_update_types.js';
+import {getUpdateProvider} from './mojo_interface_provider.js';
 import {mojoString16ToString} from './mojo_utils.js';
 
 /** @enum {number} */
@@ -56,15 +57,16 @@
         type: Object,
       },
 
+      /** @type {?InstallationProgress} */
+      installationProgress: {
+        type: Object,
+      },
+
       /** @type {!DialogState} */
       dialogState: {
         type: Number,
         value: DialogState.CLOSED,
-      },
-
-      /** @type {?InstallationProgress} */
-      installationProgress: {
-        type: Object,
+        computed: 'onStateChanged_(installationProgress.state)'
       },
     };
   }
@@ -73,8 +75,11 @@
   constructor() {
     super();
 
-    /** @private {!UpdateControllerInterface} */
-    this.updateController_ = getUpdateController();
+    /** @private {!UpdateProviderInterface} */
+    this.updateProvider_ = getUpdateProvider();
+
+    /** @type {?InstallControllerRemote} */
+    this.installController_ = null;
 
     /**
      * Event callback for 'open-update-dialog'.
@@ -83,7 +88,7 @@
      */
     this.openUpdateDialog_ = (e) => {
       this.update = e.detail.update;
-      this.startUpdate_();
+      this.prepareForUpdate_();
     };
   }
 
@@ -96,13 +101,29 @@
   }
 
   /**
-   * Implements UpdateProgressObserver.onProgressChanged
-   * @param {!InstallationProgress} installationProgress
+   * Implements UpdateProgressObserver.onStatusChanged
+   * @param {!InstallationProgress} update
    */
-  onProgressChanged(installationProgress) {
-    this.installationProgress = installationProgress;
-    if (installationProgress.percentage === 100) {
-      this.dialogState = DialogState.UPDATE_DONE;
+  onStatusChanged(update) {
+    this.installationProgress = update;
+  }
+
+  /** @protected */
+  onStateChanged_() {
+    if (!this.installationProgress) {
+      return DialogState.CLOSED;
+    }
+    // TODO(michaelcheco): Handle restarting and failed states.
+    switch (this.installationProgress.state) {
+      case UpdateState.kUnknown:
+      case UpdateState.kIdle:
+        return DialogState.CLOSED;
+      case UpdateState.kUpdating:
+      case UpdateState.kRestarting:
+        return DialogState.UPDATING;
+      case UpdateState.kFailed:
+      case UpdateState.kSuccess:
+        return DialogState.UPDATE_DONE;
     }
   }
 
@@ -113,9 +134,30 @@
   }
 
   /** @protected */
-  startUpdate_() {
-    this.dialogState = DialogState.UPDATING;
-    this.updateController_.startUpdate(this.update.deviceId, this);
+  async prepareForUpdate_() {
+    const response =
+        await this.updateProvider_.prepareForUpdate(this.update.deviceId);
+    if (!response.controller) {
+      // TODO(michaelcheco): Handle |StartInstall| failed case.
+      return;
+    }
+    this.installController_ =
+        /**@type {InstallControllerRemote} */ (response.controller);
+    this.beginUpdate_();
+  }
+
+  /** @protected */
+  beginUpdate_() {
+    /** @protected {?UpdateProgressObserverReceiver} */
+    this.updateProgressObserverReceiver_ = new UpdateProgressObserverReceiver(
+        /**
+         * @type {!UpdateProgressObserverInterface}
+         */
+        (this));
+
+    this.installController_.addObserver(
+        this.updateProgressObserverReceiver_.$.bindNewPipeAndPassRemote());
+    this.installController_.beginUpdate();
   }
 
   /**
@@ -162,7 +204,7 @@
    * @return {string}
    */
   computeProgressText_() {
-    return this.i18n('installing', this.computePercentageValue_());
+    return this.i18n('installing', this.installationProgress.percentage);
   }
 
   /**
diff --git a/ash/webui/firmware_update_ui/resources/firmware_update_types.js b/ash/webui/firmware_update_ui/resources/firmware_update_types.js
index 3fb24c1..3c8f381 100644
--- a/ash/webui/firmware_update_ui/resources/firmware_update_types.js
+++ b/ash/webui/firmware_update_ui/resources/firmware_update_types.js
@@ -5,6 +5,7 @@
 import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
 import 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js';
 import 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js';
+import '/file_path.mojom-lite.js';
 import './mojom/firmware_update.mojom-lite.js';
 
 /**
@@ -51,20 +52,66 @@
     ash.firmwareUpdate.mojom.UpdateProviderInterface;
 
 /**
- * @typedef {{
- *   status: string,
- *   percentage: number,
- * }}
+ * Type alias for the UpdateState.
+ * @typedef {ash.firmwareUpdate.mojom.UpdateState}
  */
-export let InstallationProgress;
+export const UpdateState = ash.firmwareUpdate.mojom.UpdateState;
 
 /**
- * Type alias for UpdateProgressObserver.
+ * Type alias for the UpdateProgressObserver.
+ * @typedef {ash.firmwareUpdate.mojom.UpdateProgressObserver}
+ */
+export const UpdateProgressObserver =
+    ash.firmwareUpdate.mojom.UpdateProgressObserver;
+
+/**
+ * Type alias for UpdateProgressObserverRemote.
+ * @typedef {ash.firmwareUpdate.mojom.UpdateProgressObserverRemote}
+ */
+export const UpdateProgressObserverRemote =
+    ash.firmwareUpdate.mojom.UpdateProgressObserverRemote;
+
+/**
+ * Type alias for InstallController.
+ * @typedef {ash.firmwareUpdate.mojom.InstallController}
+ */
+export const InstallController = ash.firmwareUpdate.mojom.InstallController;
+
+/**
+ * Type alias for the InstallControllerInterface.
+ * @typedef {ash.firmwareUpdate.mojom.InstallControllerInterface}
+ */
+export const InstallControllerInterface =
+    ash.firmwareUpdate.mojom.InstallControllerInterface;
+
+/**
+ * Type alias for the UpdateProgressObserverInterface.
+ * @typedef {ash.firmwareUpdate.mojom.UpdateProgressObserverInterface}
+ */
+export const UpdateProgressObserverInterface =
+    ash.firmwareUpdate.mojom.UpdateProgressObserverInterface;
+
+/**
+ * Type for methods needed for the fake UpdateProvider implementation.
  * @typedef {{
- *   onProgressChanged: !function(!InstallationProgress)
+ *   setDeviceIdForUpdateInProgress: !function(string),
  * }}
  */
-export let UpdateProgressObserver;
+export let FakeInstallControllerInterface;
+
+/**
+ * Type alias for InstallControllerRemote.
+ * @typedef {ash.firmwareUpdate.mojom.InstallControllerRemote}
+ */
+export const InstallControllerRemote =
+    ash.firmwareUpdate.mojom.InstallControllerRemote;
+
+/**
+ * Type alias for UpdateProgressObserverReceiver.
+ * @typedef {ash.firmwareUpdate.mojom.UpdateProgressObserverReceiver}
+ */
+export const UpdateProgressObserverReceiver =
+    ash.firmwareUpdate.mojom.UpdateProgressObserverReceiver;
 
 /**
  * Type of UpdateControllerInterface.startUpdateFunction function.
@@ -73,15 +120,6 @@
 export let startUpdateFunction;
 
 /**
- * Type alias for the UpdateControllerInterface.
- * TODO(michaelcheco): Replace with a real mojo type when implemented.
- * @typedef {{
- *   startUpdate: !startUpdateFunction,
- * }}
- */
-export let UpdateControllerInterface;
-
-/**
  * Type alias for UpdateObserverReceiver.
  * @typedef {ash.firmwareUpdate.mojom.UpdateObserverReceiver}
  */
@@ -103,3 +141,10 @@
  * }}
  */
 export let FakeUpdateProviderInterface;
+
+/**
+ * Type alias for InstallationProgress.
+ * @typedef {ash.firmwareUpdate.mojom.InstallationProgress}
+ */
+export const InstallationProgress =
+    ash.firmwareUpdate.mojom.InstallationProgress;
diff --git a/ash/webui/firmware_update_ui/resources/mojo_interface_provider.js b/ash/webui/firmware_update_ui/resources/mojo_interface_provider.js
index 9aa4314c..9f01bab 100644
--- a/ash/webui/firmware_update_ui/resources/mojo_interface_provider.js
+++ b/ash/webui/firmware_update_ui/resources/mojo_interface_provider.js
@@ -6,8 +6,7 @@
 import {fakeFirmwareUpdates} from './fake_data.js';
 import {FakeUpdateController} from './fake_update_controller.js';
 import {FakeUpdateProvider} from './fake_update_provider.js';
-import {UpdateControllerInterface, UpdateProvider, UpdateProviderInterface} from './firmware_update_types.js';
-
+import {InstallController, InstallControllerInterface, UpdateProvider, UpdateProviderInterface} from './firmware_update_types.js';
 /**
  * @fileoverview
  * Provides singleton access to mojo interfaces with the ability
@@ -26,7 +25,7 @@
 let updateProvider = null;
 
 /**
- * @type {?UpdateControllerInterface}
+ * @type {?InstallControllerInterface}
  */
 let updateController = null;
 
@@ -45,7 +44,7 @@
 }
 
 /**
- * @param {!UpdateControllerInterface} testUpdateController
+ * @param {!InstallControllerInterface} testUpdateController
  */
 export function setUpdateControllerForTesting(testUpdateController) {
   updateController = testUpdateController;
@@ -86,13 +85,18 @@
   return updateProvider;
 }
 
-/** @return {!UpdateControllerInterface} */
+/**
+ * @return {!InstallControllerInterface}
+ */
 export function getUpdateController() {
   if (!updateController) {
-    // TODO(michaelcheco): Instantiate a real mojo interface here.
-    setupFakeUpdateController();
+    if (useFakeProviders) {
+      setupFakeUpdateController();
+    } else {
+      updateController = InstallController.getRemote();
+    }
   }
 
   assert(!!updateController);
   return updateController;
-}
+}
\ No newline at end of file
diff --git a/ash/webui/firmware_update_ui/resources/peripheral_updates_list.js b/ash/webui/firmware_update_ui/resources/peripheral_updates_list.js
index 20e0df1..0873cf4 100644
--- a/ash/webui/firmware_update_ui/resources/peripheral_updates_list.js
+++ b/ash/webui/firmware_update_ui/resources/peripheral_updates_list.js
@@ -4,6 +4,10 @@
 
 import './firmware_shared_css.js';
 import './firmware_shared_fonts.js';
+import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
+import 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js';
+import 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js';
+import '/file_path.mojom-lite.js';
 import './mojom/firmware_update.mojom-lite.js';
 import './update_card.js';
 
diff --git a/ash/webui/firmware_update_ui/resources/update_card.js b/ash/webui/firmware_update_ui/resources/update_card.js
index c5ef505..543ee13 100644
--- a/ash/webui/firmware_update_ui/resources/update_card.js
+++ b/ash/webui/firmware_update_ui/resources/update_card.js
@@ -6,6 +6,7 @@
 import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
 import 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js';
 import 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js';
+import '/file_path.mojom-lite.js';
 import './firmware_shared_css.js';
 import './firmware_shared_fonts.js';
 import './mojom/firmware_update.mojom-lite.js';
diff --git a/ash/webui/personalization_app/BUILD.gn b/ash/webui/personalization_app/BUILD.gn
index ad4948f..dd37e13b 100644
--- a/ash/webui/personalization_app/BUILD.gn
+++ b/ash/webui/personalization_app/BUILD.gn
@@ -14,6 +14,7 @@
     "personalization_app_ui.h",
     "personalization_app_url_constants.cc",
     "personalization_app_url_constants.h",
+    "personalization_app_user_provider.h",
     "personalization_app_wallpaper_provider.h",
     "untrusted_personalization_app_ui_config.cc",
     "untrusted_personalization_app_ui_config.h",
@@ -49,6 +50,8 @@
   sources = [
     "test/fake_personalization_app_theme_provider.cc",
     "test/fake_personalization_app_theme_provider.h",
+    "test/fake_personalization_app_user_provider.cc",
+    "test/fake_personalization_app_user_provider.h",
     "test/fake_personalization_app_wallpaper_provider.cc",
     "test/fake_personalization_app_wallpaper_provider.h",
     "test/personalization_app_browsertest_fixture.cc",
diff --git a/ash/webui/personalization_app/mojom/BUILD.gn b/ash/webui/personalization_app/mojom/BUILD.gn
index cbbae2bb..e2d4a17 100644
--- a/ash/webui/personalization_app/mojom/BUILD.gn
+++ b/ash/webui/personalization_app/mojom/BUILD.gn
@@ -40,6 +40,11 @@
           mojom = "ash.personalization_app.mojom.OnlineImageType"
           cpp = "backdrop::Image::ImageType"
         },
+        {
+          mojom = "ash.personalization_app.mojom.UserInfo"
+          cpp = "ash::personalization_app::UserDisplayInfo"
+          move_only = true
+        },
       ]
       traits_headers = [ "personalization_app_mojom_traits.h" ]
       traits_sources = [ "personalization_app_mojom_traits.cc" ]
diff --git a/ash/webui/personalization_app/mojom/personalization_app.mojom b/ash/webui/personalization_app/mojom/personalization_app.mojom
index 823ad4e8..d9690708 100644
--- a/ash/webui/personalization_app/mojom/personalization_app.mojom
+++ b/ash/webui/personalization_app/mojom/personalization_app.mojom
@@ -193,3 +193,18 @@
   // Disables or enables dark color mode.
   SetColorModePref(bool dark_mode_enabled);
 };
+
+// Contains information about the current user.
+struct UserInfo {
+  // The display email of the user.
+  string email;
+
+  // The display name of the user.
+  string name;
+};
+
+// Provides APIs to view information about the current user.
+interface UserProvider {
+  // Return information about the current user.
+  GetUserInfo() => (UserInfo user_info);
+};
diff --git a/ash/webui/personalization_app/mojom/personalization_app_mojom_traits.cc b/ash/webui/personalization_app/mojom/personalization_app_mojom_traits.cc
index 6d0c288..8367a9d89 100644
--- a/ash/webui/personalization_app/mojom/personalization_app_mojom_traits.cc
+++ b/ash/webui/personalization_app/mojom/personalization_app_mojom_traits.cc
@@ -4,6 +4,9 @@
 
 #include "ash/webui/personalization_app/mojom/personalization_app_mojom_traits.h"
 
+#include <string>
+
+#include "ash/public/cpp/personalization_app/user_display_info.h"
 #include "ash/public/cpp/wallpaper/wallpaper_types.h"
 #include "ash/webui/personalization_app/mojom/personalization_app.mojom-shared.h"
 #include "ash/webui/personalization_app/proto/backdrop_wallpaper.pb.h"
@@ -233,4 +236,25 @@
   return false;
 }
 
+const std::string&
+StructTraits<ash::personalization_app::mojom::UserInfoDataView,
+             ash::personalization_app::UserDisplayInfo>::
+    email(const ash::personalization_app::UserDisplayInfo& user_display_info) {
+  return user_display_info.email;
+}
+
+const std::string&
+StructTraits<ash::personalization_app::mojom::UserInfoDataView,
+             ash::personalization_app::UserDisplayInfo>::
+    name(const ash::personalization_app::UserDisplayInfo& user_display_info) {
+  return user_display_info.name;
+}
+
+bool StructTraits<ash::personalization_app::mojom::UserInfoDataView,
+                  ash::personalization_app::UserDisplayInfo>::
+    Read(ash::personalization_app::mojom::UserInfoDataView data,
+         ash::personalization_app::UserDisplayInfo* out) {
+  return data.ReadEmail(&out->email) && data.ReadName(&out->name);
+}
+
 }  // namespace mojo
diff --git a/ash/webui/personalization_app/mojom/personalization_app_mojom_traits.h b/ash/webui/personalization_app/mojom/personalization_app_mojom_traits.h
index 9a53271cc..1f3b5a53e 100644
--- a/ash/webui/personalization_app/mojom/personalization_app_mojom_traits.h
+++ b/ash/webui/personalization_app/mojom/personalization_app_mojom_traits.h
@@ -5,6 +5,9 @@
 #ifndef ASH_WEBUI_PERSONALIZATION_APP_MOJOM_PERSONALIZATION_APP_MOJOM_TRAITS_H_
 #define ASH_WEBUI_PERSONALIZATION_APP_MOJOM_PERSONALIZATION_APP_MOJOM_TRAITS_H_
 
+#include <string>
+
+#include "ash/public/cpp/personalization_app/user_display_info.h"
 #include "ash/public/cpp/wallpaper/wallpaper_types.h"
 #include "ash/webui/personalization_app/mojom/personalization_app.mojom-shared.h"
 #include "ash/webui/personalization_app/proto/backdrop_wallpaper.pb.h"
@@ -72,6 +75,17 @@
   static bool IsNull(const backdrop::Image& image);
 };
 
+template <>
+struct StructTraits<ash::personalization_app::mojom::UserInfoDataView,
+                    ash::personalization_app::UserDisplayInfo> {
+  static const std::string& email(
+      const ash::personalization_app::UserDisplayInfo& user_display_info);
+  static const std::string& name(
+      const ash::personalization_app::UserDisplayInfo& user_display_info);
+  static bool Read(ash::personalization_app::mojom::UserInfoDataView data,
+                   ash::personalization_app::UserDisplayInfo* out);
+};
+
 }  // namespace mojo
 
 #endif  // ASH_WEBUI_PERSONALIZATION_APP_MOJOM_PERSONALIZATION_APP_MOJOM_TRAITS_H_
diff --git a/ash/webui/personalization_app/personalization_app_ui.cc b/ash/webui/personalization_app/personalization_app_ui.cc
index fe32c0ce..3b1c9810 100644
--- a/ash/webui/personalization_app/personalization_app_ui.cc
+++ b/ash/webui/personalization_app/personalization_app_ui.cc
@@ -9,6 +9,7 @@
 #include "ash/grit/ash_personalization_app_resources_map.h"
 #include "ash/webui/personalization_app/personalization_app_theme_provider.h"
 #include "ash/webui/personalization_app/personalization_app_url_constants.h"
+#include "ash/webui/personalization_app/personalization_app_user_provider.h"
 #include "ash/webui/personalization_app/personalization_app_wallpaper_provider.h"
 #include "base/strings/strcat.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
@@ -118,9 +119,11 @@
 PersonalizationAppUI::PersonalizationAppUI(
     content::WebUI* web_ui,
     std::unique_ptr<PersonalizationAppThemeProvider> theme_provider,
+    std::unique_ptr<PersonalizationAppUserProvider> user_provider,
     std::unique_ptr<PersonalizationAppWallpaperProvider> wallpaper_provider)
     : ui::MojoWebUIController(web_ui),
       theme_provider_(std::move(theme_provider)),
+      user_provider_(std::move(user_provider)),
       wallpaper_provider_(std::move(wallpaper_provider)) {
   DCHECK(wallpaper_provider_);
 
@@ -164,6 +167,11 @@
   wallpaper_provider_->BindInterface(std::move(receiver));
 }
 
+void PersonalizationAppUI::BindInterface(
+    mojo::PendingReceiver<personalization_app::mojom::UserProvider> receiver) {
+  user_provider_->BindInterface(std::move(receiver));
+}
+
 WEB_UI_CONTROLLER_TYPE_IMPL(PersonalizationAppUI)
 
 }  // namespace ash
diff --git a/ash/webui/personalization_app/personalization_app_ui.h b/ash/webui/personalization_app/personalization_app_ui.h
index df962c5..5893744 100644
--- a/ash/webui/personalization_app/personalization_app_ui.h
+++ b/ash/webui/personalization_app/personalization_app_ui.h
@@ -15,12 +15,14 @@
 
 class PersonalizationAppThemeProvider;
 class PersonalizationAppWallpaperProvider;
+class PersonalizationAppUserProvider;
 
 class PersonalizationAppUI : public ui::MojoWebUIController {
  public:
   PersonalizationAppUI(
       content::WebUI* web_ui,
       std::unique_ptr<PersonalizationAppThemeProvider> theme_provider,
+      std::unique_ptr<PersonalizationAppUserProvider> user_provider,
       std::unique_ptr<PersonalizationAppWallpaperProvider> wallpaper_provider);
 
   PersonalizationAppUI(const PersonalizationAppUI&) = delete;
@@ -33,11 +35,15 @@
           receiver);
 
   void BindInterface(
+      mojo::PendingReceiver<personalization_app::mojom::UserProvider> receiver);
+
+  void BindInterface(
       mojo::PendingReceiver<personalization_app::mojom::WallpaperProvider>
           receiver);
 
  private:
   std::unique_ptr<PersonalizationAppThemeProvider> theme_provider_;
+  std::unique_ptr<PersonalizationAppUserProvider> user_provider_;
   std::unique_ptr<PersonalizationAppWallpaperProvider> wallpaper_provider_;
 
   WEB_UI_CONTROLLER_TYPE_DECL();
diff --git a/ash/webui/personalization_app/personalization_app_user_provider.h b/ash/webui/personalization_app/personalization_app_user_provider.h
new file mode 100644
index 0000000..105fbcf
--- /dev/null
+++ b/ash/webui/personalization_app/personalization_app_user_provider.h
@@ -0,0 +1,23 @@
+// 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_WEBUI_PERSONALIZATION_APP_PERSONALIZATION_APP_USER_PROVIDER_H_
+#define ASH_WEBUI_PERSONALIZATION_APP_PERSONALIZATION_APP_USER_PROVIDER_H_
+
+#include "ash/webui/personalization_app/mojom/personalization_app.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+
+namespace ash {
+
+class PersonalizationAppUserProvider
+    : public personalization_app::mojom::UserProvider {
+ public:
+  virtual void BindInterface(
+      mojo::PendingReceiver<personalization_app::mojom::UserProvider>
+          receiver) = 0;
+};
+
+}  // namespace ash
+
+#endif  // ASH_WEBUI_PERSONALIZATION_APP_PERSONALIZATION_APP_USER_PROVIDER_H_
diff --git a/ash/webui/personalization_app/resources/BUILD.gn b/ash/webui/personalization_app/resources/BUILD.gn
index fd6fb87a..029d5ddc 100644
--- a/ash/webui/personalization_app/resources/BUILD.gn
+++ b/ash/webui/personalization_app/resources/BUILD.gn
@@ -33,6 +33,11 @@
   "trusted/theme/theme_interface_provider.ts",
   "trusted/theme/theme_reducers.ts",
   "trusted/theme/theme_state.ts",
+  "trusted/user/user_actions.ts",
+  "trusted/user/user_controller.ts",
+  "trusted/user/user_interface_provider.ts",
+  "trusted/user/user_reducers.ts",
+  "trusted/user/user_state.ts",
   "trusted/utils.ts",
   "trusted/wallpaper/wallpaper_actions.ts",
   "trusted/wallpaper/untrusted_message_handler.ts",
@@ -57,6 +62,7 @@
   "trusted/personalization_theme_element.ts",
   "trusted/personalization_toast_element.ts",
   "trusted/personalization_breadcrumb_element.ts",
+  "trusted/user_preview_element.ts",
   "trusted/user/user_subpage_element.ts",
   "trusted/wallpaper/google_photos_albums_element.ts",
   "trusted/wallpaper/google_photos_collection_element.ts",
@@ -103,13 +109,10 @@
 }
 
 preprocess_if_expr("preprocess_generated") {
-  deps = [
-    ":web_components",
-    "untrusted:shared_vars_css_module",
-  ]
+  deps = [ ":web_components" ]
   in_folder = target_gen_dir
   out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = polymer_js_files + [ "untrusted/untrusted_shared_vars_css.js" ]
+  in_files = polymer_js_files
 }
 
 copy("copy_mojo_to_trusted") {
@@ -119,26 +122,14 @@
       [ "$target_gen_dir/$preprocess_folder/trusted/{{source_file_part}}" ]
 }
 
-# This is to work around the problem untrusted/ cannot access shared resources
-# in chrome://. In the future when we merge trusted/ and untrusted/ we should
-# remove this.
-copy("copy_js_to_common") {
-  deps = [ "//ui/webui/resources/js:modulize_local" ]
-  sources = [ "$root_gen_dir/ui/webui/resources/js/assert.m.js" ]
-  outputs = [ "$target_gen_dir/$preprocess_folder/common/{{source_file_part}}" ]
-}
-
 ts_library("build_ts") {
   composite = true
   root_dir = "$target_gen_dir/$preprocess_folder"
   out_dir = "$target_gen_dir/tsc"
   tsconfig_base = "tsconfig_base.json"
 
-  in_files = static_js_files + polymer_js_files + [
-               "common/assert.m.js",
-               "trusted/personalization_app.mojom-webui.js",
-               "untrusted/untrusted_shared_vars_css.js",
-             ]
+  in_files = static_js_files + polymer_js_files +
+             [ "trusted/personalization_app.mojom-webui.js" ]
 
   deps = [
     "//third_party/polymer/v3_0:library",
@@ -146,19 +137,7 @@
     "//ui/webui/resources/mojo:library",
   ]
 
-  definitions = []
-
-  path_mappings = [
-    "chrome-untrusted://personalization/polymer/v3_0/polymer/polymer_bundled.min.js|" + rebase_path(
-            "//third_party/polymer/v3_0/components-chromium/polymer/polymer.d.ts",
-            target_gen_dir),
-    "chrome-untrusted://resources/js/load_time_data.m.js|" + rebase_path(
-            "$root_gen_dir/ui/webui/resources/preprocessed/js/load_time_data.m.d.ts",
-            target_gen_dir),
-  ]
-
   extra_deps = [
-    ":copy_js_to_common",
     ":copy_mojo_to_trusted",
     ":preprocess",
     ":preprocess_generated",
diff --git a/ash/webui/personalization_app/resources/trusted/iframe_api.ts b/ash/webui/personalization_app/resources/trusted/iframe_api.ts
index cb04c24..60c1fda 100644
--- a/ash/webui/personalization_app/resources/trusted/iframe_api.ts
+++ b/ash/webui/personalization_app/resources/trusted/iframe_api.ts
@@ -17,168 +17,175 @@
 
 import {WallpaperCollection, WallpaperImage} from './personalization_app.mojom-webui.js';
 
-
-/**
- * Send an array of wallpaper collections to untrusted.
- */
-export function sendCollections(
-    target: Window, collections: Array<WallpaperCollection>) {
-  const event: constants.SendCollectionsEvent = {
-    type: constants.EventType.SEND_COLLECTIONS,
-    collections
-  };
-  target.postMessage(event, constants.untrustedOrigin);
-}
-
-/**
- * Sends the count of Google Photos photos to untrusted.
- */
-export function sendGooglePhotosCount(target: Window, count: number|null) {
-  const event: constants.SendGooglePhotosCountEvent = {
-    type: constants.EventType.SEND_GOOGLE_PHOTOS_COUNT,
-    count
-  };
-  target.postMessage(event, constants.untrustedOrigin);
-}
-
-/**
- * Sends the list of Google Photos photos to untrusted.
- */
-export function sendGooglePhotosPhotos(
-    target: Window, photos: Array<Url>|null) {
-  const event: constants.SendGooglePhotosPhotosEvent = {
-    type: constants.EventType.SEND_GOOGLE_PHOTOS_PHOTOS,
-    photos
-  };
-  target.postMessage(event, constants.untrustedOrigin);
-}
-
-/**
- * Send a mapping of collectionId to the number of images in that collection.
- * A value of null for a given collection id represents that the collection
- * failed to load.
- */
-export function sendImageCounts(
-    target: Window, counts: {[key: string]: number|null}) {
-  const event: constants.SendImageCountsEvent = {
-    type: constants.EventType.SEND_IMAGE_COUNTS,
-    counts
-  };
-  target.postMessage(event, constants.untrustedOrigin);
-}
-
-/**
- * Send visibility status to a target iframe. Currently used to trigger a
- * resize event on iron-list when an iframe becomes visible again so that
- * iron-list will run layout with the current size.
- */
-export function sendVisible(target: Window, visible: boolean) {
-  const event: constants.SendVisibleEvent = {
-    type: constants.EventType.SEND_VISIBLE,
-    visible
-  };
-  target.postMessage(event, constants.untrustedOrigin);
-}
-
-/**
- * Send an array of wallpaper images to chrome-untrusted://.
- * Will clear the page if images is empty array.
- */
-export function sendImageTiles(target: Window, tiles: constants.ImageTile[]) {
-  const event: constants.SendImageTilesEvent = {
-    type: constants.EventType.SEND_IMAGE_TILES,
-    tiles
-  };
-  target.postMessage(event, constants.untrustedOrigin);
-}
-
-/**
- * Send an array of local images to chrome-untrusted://.
- */
-export function sendLocalImages(target: Window, images: FilePath[]) {
-  const event: constants.SendLocalImagesEvent = {
-    type: constants.EventType.SEND_LOCAL_IMAGES,
-    images
-  };
-  target.postMessage(event, constants.untrustedOrigin);
-}
-
-/**
- * Sends image data keyed by stringified image id.
- */
-export function sendLocalImageData(
-    target: Window, data: Record<string, string>) {
-  const event: constants.SendLocalImageDataEvent = {
-    type: constants.EventType.SEND_LOCAL_IMAGE_DATA,
-    data
-  };
-  target.postMessage(event, constants.untrustedOrigin);
-}
-
-/**
- * Send the |assetId| of the currently selected wallpaper to |target| iframe
- * window. Sending null indicates that no image is selected.
- */
-export function sendCurrentWallpaperAssetId(
-    target: Window, assetId: bigint|undefined) {
-  const event: constants.SendCurrentWallpaperAssetIdEvent = {
-    type: constants.EventType.SEND_CURRENT_WALLPAPER_ASSET_ID,
-    assetId
-  };
-  target.postMessage(event, constants.untrustedOrigin);
-}
-
-/**
- * Send the |assetId| to the |target| iframe when the user clicks on online
- * wallpaper image.
- */
-export function sendPendingWallpaperAssetId(
-    target: Window, assetId: bigint|undefined) {
-  const event: constants.SendPendingWallpaperAssetIdEvent = {
-    type: constants.EventType.SEND_PENDING_WALLPAPER_ASSET_ID,
-    assetId
-  };
-  target.postMessage(event, constants.untrustedOrigin);
-}
-
-
-/**
- * Called from trusted code to validate that a received postMessage event
- * contains valid data. Ignores messages that are not of the expected type.
- */
-export function validateReceivedSelection(
-    event: MessageEvent,
-    choices: WallpaperCollection[]|null): WallpaperCollection;
-export function validateReceivedSelection(
-    event: MessageEvent, choices: WallpaperImage[]|null): WallpaperImage;
-export function validateReceivedSelection(
-    event: MessageEvent, choices: (WallpaperCollection|WallpaperImage)[]|null):
-    WallpaperCollection|WallpaperImage {
-  assert(isNonEmptyArray(choices), 'choices must be a non-empty array');
-
-  const data: constants.Events = event.data;
-  let selected: WallpaperCollection|WallpaperImage|undefined = undefined;
-  switch (data.type) {
-    case constants.EventType.SELECT_COLLECTION: {
-      assert(!!data.collectionId, 'Expected a collection id parameter');
-      selected = (choices as WallpaperCollection[])
-                     .find(choice => choice.id === data.collectionId);
-      break;
-    }
-    case constants.EventType.SELECT_IMAGE: {
-      assert(
-          data.hasOwnProperty('assetId'),
-          'Expected an image assetId parameter');
-      assert(
-          typeof data.assetId === 'bigint', 'assetId parameter must be bigint');
-      selected = (choices as WallpaperImage[])
-                     .find(choice => choice.assetId === data.assetId);
-      break;
-    }
-    default:
-      assertNotReached('Unknown event type');
+export class IFrameApi {
+  /**
+   * Send an array of wallpaper collections to untrusted.
+   */
+  sendCollections(target: Window, collections: Array<WallpaperCollection>) {
+    const event: constants.SendCollectionsEvent = {
+      type: constants.EventType.SEND_COLLECTIONS,
+      collections
+    };
+    target.postMessage(event, constants.untrustedOrigin);
   }
 
-  assert(!!selected, 'No valid selection found in choices');
-  return selected!;
+  /**
+   * Sends the count of Google Photos photos to untrusted.
+   */
+  sendGooglePhotosCount(target: Window, count: number|null) {
+    const event: constants.SendGooglePhotosCountEvent = {
+      type: constants.EventType.SEND_GOOGLE_PHOTOS_COUNT,
+      count
+    };
+    target.postMessage(event, constants.untrustedOrigin);
+  }
+
+  /**
+   * Sends the list of Google Photos photos to untrusted.
+   */
+  sendGooglePhotosPhotos(target: Window, photos: Array<Url>|null) {
+    const event: constants.SendGooglePhotosPhotosEvent = {
+      type: constants.EventType.SEND_GOOGLE_PHOTOS_PHOTOS,
+      photos
+    };
+    target.postMessage(event, constants.untrustedOrigin);
+  }
+
+  /**
+   * Send a mapping of collectionId to the number of images in that collection.
+   * A value of null for a given collection id represents that the collection
+   * failed to load.
+   */
+  sendImageCounts(target: Window, counts: {[key: string]: number|null}) {
+    const event: constants.SendImageCountsEvent = {
+      type: constants.EventType.SEND_IMAGE_COUNTS,
+      counts
+    };
+    target.postMessage(event, constants.untrustedOrigin);
+  }
+
+  /**
+   * Send visibility status to a target iframe. Currently used to trigger a
+   * resize event on iron-list when an iframe becomes visible again so that
+   * iron-list will run layout with the current size.
+   */
+  sendVisible(target: Window, visible: boolean) {
+    const event: constants.SendVisibleEvent = {
+      type: constants.EventType.SEND_VISIBLE,
+      visible
+    };
+    target.postMessage(event, constants.untrustedOrigin);
+  }
+
+  /**
+   * Send an array of wallpaper images to chrome-untrusted://.
+   * Will clear the page if images is empty array.
+   */
+  sendImageTiles(target: Window, tiles: constants.ImageTile[]) {
+    const event: constants.SendImageTilesEvent = {
+      type: constants.EventType.SEND_IMAGE_TILES,
+      tiles
+    };
+    target.postMessage(event, constants.untrustedOrigin);
+  }
+
+  /**
+   * Send an array of local images to chrome-untrusted://.
+   */
+  sendLocalImages(target: Window, images: FilePath[]) {
+    const event: constants.SendLocalImagesEvent = {
+      type: constants.EventType.SEND_LOCAL_IMAGES,
+      images
+    };
+    target.postMessage(event, constants.untrustedOrigin);
+  }
+
+  /**
+   * Sends image data keyed by stringified image id.
+   */
+  sendLocalImageData(target: Window, data: Record<string, string>) {
+    const event: constants.SendLocalImageDataEvent = {
+      type: constants.EventType.SEND_LOCAL_IMAGE_DATA,
+      data
+    };
+    target.postMessage(event, constants.untrustedOrigin);
+  }
+
+  /**
+   * Send the |assetId| of the currently selected wallpaper to |target| iframe
+   * window. Sending null indicates that no image is selected.
+   */
+  sendCurrentWallpaperAssetId(target: Window, assetId: bigint|undefined) {
+    const event: constants.SendCurrentWallpaperAssetIdEvent = {
+      type: constants.EventType.SEND_CURRENT_WALLPAPER_ASSET_ID,
+      assetId
+    };
+    target.postMessage(event, constants.untrustedOrigin);
+  }
+
+  /**
+   * Send the |assetId| to the |target| iframe when the user clicks on online
+   * wallpaper image.
+   */
+  sendPendingWallpaperAssetId(target: Window, assetId: bigint|undefined) {
+    const event: constants.SendPendingWallpaperAssetIdEvent = {
+      type: constants.EventType.SEND_PENDING_WALLPAPER_ASSET_ID,
+      assetId
+    };
+    target.postMessage(event, constants.untrustedOrigin);
+  }
+
+
+  /**
+   * Called from trusted code to validate that a received postMessage event
+   * contains valid data. Ignores messages that are not of the expected type.
+   */
+  validateReceivedSelection(
+      event: MessageEvent,
+      choices: WallpaperCollection[]|null): WallpaperCollection;
+  validateReceivedSelection(
+      event: MessageEvent, choices: WallpaperImage[]|null): WallpaperImage;
+  validateReceivedSelection(
+      event: MessageEvent,
+      choices: (WallpaperCollection|WallpaperImage)[]|null): WallpaperCollection
+      |WallpaperImage {
+    assert(isNonEmptyArray(choices), 'choices must be a non-empty array');
+
+    const data: constants.Events = event.data;
+    let selected: WallpaperCollection|WallpaperImage|undefined = undefined;
+    switch (data.type) {
+      case constants.EventType.SELECT_COLLECTION: {
+        assert(!!data.collectionId, 'Expected a collection id parameter');
+        selected = (choices as WallpaperCollection[])
+                       .find(choice => choice.id === data.collectionId);
+        break;
+      }
+      case constants.EventType.SELECT_IMAGE: {
+        assert(
+            data.hasOwnProperty('assetId'),
+            'Expected an image assetId parameter');
+        assert(
+            typeof data.assetId === 'bigint',
+            'assetId parameter must be bigint');
+        selected = (choices as WallpaperImage[])
+                       .find(choice => choice.assetId === data.assetId);
+        break;
+      }
+      default:
+        assertNotReached('Unknown event type');
+    }
+
+    assert(!!selected, 'No valid selection found in choices');
+    return selected!;
+  }
+
+  static getInstance(): IFrameApi {
+    return instance || (instance = new IFrameApi());
+  }
+
+  static setInstance(obj: IFrameApi) {
+    instance = obj;
+  }
 }
+
+let instance: IFrameApi|null = null;
diff --git a/ash/webui/personalization_app/resources/trusted/personalization_actions.ts b/ash/webui/personalization_app/resources/trusted/personalization_actions.ts
index 2b20806..1ad25fc 100644
--- a/ash/webui/personalization_app/resources/trusted/personalization_actions.ts
+++ b/ash/webui/personalization_app/resources/trusted/personalization_actions.ts
@@ -5,6 +5,7 @@
 import {Action} from 'chrome://resources/js/cr/ui/store.js';
 
 import {ThemeActions} from './theme/theme_actions.js';
+import {UserActions} from './user/user_actions.js';
 import {WallpaperActions} from './wallpaper/wallpaper_actions.js';
 
 /**
@@ -25,4 +26,5 @@
   return {name: PersonalizationActionName.DISMISS_ERROR};
 }
 
-export type Actions = ThemeActions|WallpaperActions|DismissErrorAction;
+export type Actions =
+    ThemeActions|UserActions|WallpaperActions|DismissErrorAction;
diff --git a/ash/webui/personalization_app/resources/trusted/personalization_app.ts b/ash/webui/personalization_app/resources/trusted/personalization_app.ts
index 47f7209..2fe5006 100644
--- a/ash/webui/personalization_app/resources/trusted/personalization_app.ts
+++ b/ash/webui/personalization_app/resources/trusted/personalization_app.ts
@@ -16,6 +16,7 @@
 import './personalization_breadcrumb_element.js';
 import './personalization_main_element.js';
 import './personalization_theme_element.js';
+import './user_preview_element.js';
 import './user/user_subpage_element.js';
 import './wallpaper/wallpaper_subpage.js';
 import {emptyState} from './personalization_state.js';
diff --git a/ash/webui/personalization_app/resources/trusted/personalization_main_element.html b/ash/webui/personalization_app/resources/trusted/personalization_main_element.html
index 407ed982..28534181 100644
--- a/ash/webui/personalization_app/resources/trusted/personalization_main_element.html
+++ b/ash/webui/personalization_app/resources/trusted/personalization_main_element.html
@@ -1,6 +1,7 @@
 <style></style>
 <div id="container">
   <h1>Personalization</h1>
+  <user-preview></user-preview>
   <wallpaper-preview></wallpaper-preview>
   <template is="dom-if" if="[[isDarkLightModeEnabled_()]]">
     <personalization-theme></personalization-theme>
diff --git a/ash/webui/personalization_app/resources/trusted/personalization_reducers.ts b/ash/webui/personalization_app/resources/trusted/personalization_reducers.ts
index aff2da7..87d6d6c0 100644
--- a/ash/webui/personalization_app/resources/trusted/personalization_reducers.ts
+++ b/ash/webui/personalization_app/resources/trusted/personalization_reducers.ts
@@ -18,6 +18,8 @@
 import {PersonalizationState} from './personalization_state.js';
 import {themeReducers} from './theme/theme_reducers.js';
 import {ThemeState} from './theme/theme_state.js';
+import {userReducers} from './user/user_reducers.js';
+import {UserState} from './user/user_state.js';
 import {WallpaperActionName} from './wallpaper/wallpaper_actions.js';
 import {wallpaperReducers} from './wallpaper/wallpaper_reducers.js';
 import {WallpaperState} from './wallpaper/wallpaper_state.js';
@@ -96,9 +98,10 @@
 }
 
 const root = combineReducers<PersonalizationState>({
-  theme: combineReducers<ThemeState>(themeReducers),
-  wallpaper: combineReducers<WallpaperState>(wallpaperReducers),
   error: errorReducer,
+  theme: combineReducers<ThemeState>(themeReducers),
+  user: combineReducers<UserState>(userReducers),
+  wallpaper: combineReducers<WallpaperState>(wallpaperReducers),
 });
 
 export function reduce(
diff --git a/ash/webui/personalization_app/resources/trusted/personalization_state.ts b/ash/webui/personalization_app/resources/trusted/personalization_state.ts
index 6fb0278..f8a2ac8 100644
--- a/ash/webui/personalization_app/resources/trusted/personalization_state.ts
+++ b/ash/webui/personalization_app/resources/trusted/personalization_state.ts
@@ -3,18 +3,21 @@
 // found in the LICENSE file.
 
 import {emptyState as emptyThemeState, ThemeState} from './theme/theme_state.js';
+import {emptyState as emptyUserState, UserState} from './user/user_state.js';
 import {emptyState as emptyWallpaperState, WallpaperState} from './wallpaper/wallpaper_state.js';
 
 export interface PersonalizationState {
-  wallpaper: WallpaperState;
   error: string|null;
   theme: ThemeState;
+  user: UserState;
+  wallpaper: WallpaperState;
 }
 
 export function emptyState(): PersonalizationState {
   return {
-    wallpaper: emptyWallpaperState(),
     error: null,
     theme: emptyThemeState(),
+    user: emptyUserState(),
+    wallpaper: emptyWallpaperState(),
   };
 }
diff --git a/ash/webui/personalization_app/resources/trusted/user/user_actions.ts b/ash/webui/personalization_app/resources/trusted/user/user_actions.ts
new file mode 100644
index 0000000..c2fc89b
--- /dev/null
+++ b/ash/webui/personalization_app/resources/trusted/user/user_actions.ts
@@ -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.
+
+import {Action} from 'chrome://resources/js/cr/ui/store.js';
+
+import {UserInfo} from '../personalization_app.mojom-webui.js';
+
+/**
+ * @fileoverview Defines the actions to change user state.
+ */
+
+export enum UserActionName {
+  SET_USER_INFO = 'set_user_info',
+}
+
+export type UserActions = SetUserInfoAction;
+
+export type SetUserInfoAction = Action&{
+  name: UserActionName.SET_USER_INFO;
+  user_info: UserInfo;
+};
+
+/**
+ * Notify that the app has finished loading user info. Will be called with null
+ * on error.
+ */
+export function setUserInfoAction(user_info: UserInfo): SetUserInfoAction {
+  return {
+    name: UserActionName.SET_USER_INFO,
+    user_info,
+  };
+}
\ No newline at end of file
diff --git a/ash/webui/personalization_app/resources/trusted/user/user_controller.ts b/ash/webui/personalization_app/resources/trusted/user/user_controller.ts
new file mode 100644
index 0000000..05e985a
--- /dev/null
+++ b/ash/webui/personalization_app/resources/trusted/user/user_controller.ts
@@ -0,0 +1,17 @@
+// 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 {UserProviderInterface} from '../personalization_app.mojom-webui';
+import {PersonalizationStore} from '../personalization_store.js';
+import {setUserInfoAction} from './user_actions.js';
+
+/**
+ * @fileoverview provides functions to fetch and set user info.
+ */
+
+export async function initializeUserData(
+    provider: UserProviderInterface, store: PersonalizationStore) {
+  const {userInfo} = await provider.getUserInfo();
+  store.dispatch(setUserInfoAction(userInfo));
+}
diff --git a/ash/webui/personalization_app/resources/trusted/user/user_interface_provider.ts b/ash/webui/personalization_app/resources/trusted/user/user_interface_provider.ts
new file mode 100644
index 0000000..2226858
--- /dev/null
+++ b/ash/webui/personalization_app/resources/trusted/user/user_interface_provider.ts
@@ -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.
+
+/**
+ * @fileoverview a singleton getter for the user mojom interface used in
+ * the Personalization SWA. Also contains utility function for mocking out the
+ * implementation for testing.
+ */
+
+import 'chrome://resources/mojo/mojo/public/js/bindings.js';
+
+import {UserProvider, UserProviderInterface} from '../personalization_app.mojom-webui.js';
+
+let userProvider: UserProviderInterface|null = null;
+
+export function setUserProviderForTesting(testProvider: UserProviderInterface):
+    void {
+  userProvider = testProvider;
+}
+
+/** Returns a singleton for the UserProvider mojom interface. */
+export function getUserProvider(): UserProviderInterface {
+  if (!userProvider) {
+    userProvider = UserProvider.getRemote();
+  }
+  return userProvider;
+}
diff --git a/ash/webui/personalization_app/resources/trusted/user/user_reducers.ts b/ash/webui/personalization_app/resources/trusted/user/user_reducers.ts
new file mode 100644
index 0000000..0b197a0b
--- /dev/null
+++ b/ash/webui/personalization_app/resources/trusted/user/user_reducers.ts
@@ -0,0 +1,26 @@
+// 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 {Actions} from '../personalization_actions.js';
+import {ReducerFunction} from '../personalization_reducers.js';
+import {PersonalizationState} from '../personalization_state.js';
+
+import {UserActionName} from './user_actions.js';
+import {UserState} from './user_state.js';
+
+export function infoReducer(
+    state: UserState['info'], action: Actions,
+    _: PersonalizationState): UserState['info'] {
+  switch (action.name) {
+    case UserActionName.SET_USER_INFO:
+      return action.user_info;
+    default:
+      return state;
+  }
+}
+
+export const userReducers:
+    {[K in keyof UserState]: ReducerFunction<UserState[K]>} = {
+      info: infoReducer,
+    };
diff --git a/ash/webui/personalization_app/resources/trusted/user/user_state.ts b/ash/webui/personalization_app/resources/trusted/user/user_state.ts
new file mode 100644
index 0000000..22107d1
--- /dev/null
+++ b/ash/webui/personalization_app/resources/trusted/user/user_state.ts
@@ -0,0 +1,15 @@
+// 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 {UserInfo} from '../personalization_app.mojom-webui.js';
+
+export interface UserState {
+  info: UserInfo|null;
+}
+
+export function emptyState(): UserState {
+  return {
+    info: null,
+  };
+}
diff --git a/ash/webui/personalization_app/resources/trusted/user_preview_element.html b/ash/webui/personalization_app/resources/trusted/user_preview_element.html
new file mode 100644
index 0000000..474c027
--- /dev/null
+++ b/ash/webui/personalization_app/resources/trusted/user_preview_element.html
@@ -0,0 +1,7 @@
+<style></style>
+<div id="container">
+  <template is="dom-if" if="[[info_]]">
+    <p id="name">[[info_.name]]</p>
+    <p id="email">[[info_.email]]</p>
+  </template>
+</div>
diff --git a/ash/webui/personalization_app/resources/trusted/user_preview_element.ts b/ash/webui/personalization_app/resources/trusted/user_preview_element.ts
new file mode 100644
index 0000000..0c1b30b
--- /dev/null
+++ b/ash/webui/personalization_app/resources/trusted/user_preview_element.ts
@@ -0,0 +1,42 @@
+// 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.
+
+/**
+ * @fileoverview The user-preview component displays information about the
+ * current user.
+ */
+
+import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {UserInfo} from './personalization_app.mojom-webui.js';
+import {WithPersonalizationStore} from './personalization_store.js';
+import {initializeUserData} from './user/user_controller.js';
+import {getUserProvider} from './user/user_interface_provider.js';
+
+export class UserPreview extends WithPersonalizationStore {
+  static get is() {
+    return 'user-preview';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      info_: Object,
+    };
+  }
+
+  private info_: UserInfo|null;
+
+  connectedCallback() {
+    super.connectedCallback();
+    this.watch<UserPreview['info_']>('info_', state => state.user.info);
+    this.updateFromStore();
+    initializeUserData(getUserProvider(), this.getStore());
+  }
+}
+
+customElements.define(UserPreview.is, UserPreview);
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/untrusted_message_handler.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/untrusted_message_handler.ts
index d982f0e..57301a4 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/untrusted_message_handler.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/untrusted_message_handler.ts
@@ -5,7 +5,7 @@
 import {assert} from 'chrome://resources/js/assert.m.js';
 
 import {Events, EventType, untrustedOrigin} from '../../common/constants.js';
-import {validateReceivedSelection} from '../iframe_api.js';
+import {IFrameApi} from '../iframe_api.js';
 import {PersonalizationRouter} from '../personalization_router_element.js';
 import {PersonalizationStore} from '../personalization_store.js';
 
@@ -27,7 +27,8 @@
     case EventType.SELECT_COLLECTION:
       const collections = store.data.wallpaper.backdrop.collections;
 
-      const selectedCollection = validateReceivedSelection(event, collections);
+      const selectedCollection =
+          IFrameApi.getInstance().validateReceivedSelection(event, collections);
       PersonalizationRouter.instance().selectCollection(selectedCollection);
       break;
     case EventType.SELECT_GOOGLE_PHOTOS_COLLECTION:
@@ -43,7 +44,8 @@
         return;
       }
       const images = store.data.wallpaper.backdrop.images[collectionId];
-      const selectedImage = validateReceivedSelection(event, images);
+      const selectedImage =
+          IFrameApi.getInstance().validateReceivedSelection(event, images);
       selectWallpaper(selectedImage, getWallpaperProvider(), store);
       break;
   }
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.ts
index af08807..e557254 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.ts
@@ -16,61 +16,13 @@
 
 import {kMaximumGooglePhotosPreviews, kMaximumLocalImagePreviews} from '../../common/constants.js';
 import {isNonEmptyArray, isNullOrArray, isNullOrNumber, promisifyOnload} from '../../common/utils.js';
-import {sendCollections, sendGooglePhotosCount, sendGooglePhotosPhotos, sendImageCounts, sendLocalImageData, sendLocalImages, sendVisible} from '../iframe_api.js';
+import {IFrameApi} from '../iframe_api.js';
 import {WallpaperCollection, WallpaperImage, WallpaperProviderInterface} from '../personalization_app.mojom-webui.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
 
 import {initializeBackdropData} from './wallpaper_controller.js';
 import {getWallpaperProvider} from './wallpaper_interface_provider.js';
 
-let sendCollectionsFunction = sendCollections;
-let sendGooglePhotosCountFunction = sendGooglePhotosCount;
-let sendGooglePhotosPhotosFunction = sendGooglePhotosPhotos;
-let sendImageCountsFunction = sendImageCounts;
-let sendLocalImagesFunction = sendLocalImages;
-let sendLocalImageDataFunction = sendLocalImageData;
-
-/**
- * Mock out the iframe api functions for testing. Return promises that are
- * resolved when the function is called by |WallpaperCollectionsElement|.
- */
-interface PromisifyResult {
-  sendCollections: Promise<unknown>;
-  sendGooglePhotosCount: Promise<unknown>;
-  sendGooglePhotosPhotos: Promise<unknown>;
-  sendImageCounts: Promise<unknown>;
-  sendLocalImages: Promise<unknown>;
-  sendLocalImageData: Promise<unknown>;
-}
-
-export function promisifyIframeFunctionsForTesting(): PromisifyResult {
-  const resolvers = {} as
-      {[key in keyof PromisifyResult]: (_: unknown) => void};
-  const promises = ([
-                     'sendCollections',
-                     'sendGooglePhotosCount',
-                     'sendGooglePhotosPhotos',
-                     'sendImageCounts',
-                     'sendLocalImages',
-                     'sendLocalImageData',
-                   ] as (keyof PromisifyResult)[])
-                       .reduce((result, next) => {
-                         result[next] =
-                             new Promise(resolve => resolvers[next] = resolve);
-                         return result;
-                       }, {} as PromisifyResult);
-  sendCollectionsFunction = (...args) => resolvers['sendCollections'](args);
-  sendGooglePhotosCountFunction = (...args) =>
-      resolvers['sendGooglePhotosCount'](args);
-  sendGooglePhotosPhotosFunction = (...args) =>
-      resolvers['sendGooglePhotosPhotos'](args);
-  sendImageCountsFunction = (...args) => resolvers['sendImageCounts'](args);
-  sendLocalImagesFunction = (...args) => resolvers['sendLocalImages'](args);
-  sendLocalImageDataFunction = (...args) =>
-      resolvers['sendLocalImageData'](args);
-  return promises;
-}
-
 export class WallpaperCollections extends WithPersonalizationStore {
   static get is() {
     return 'wallpaper-collections';
@@ -227,7 +179,7 @@
       document.title = this.i18n('title');
     }
     const iframe = await this.iframePromise_;
-    sendVisible(iframe.contentWindow!, !hidden);
+    IFrameApi.getInstance().sendVisible(iframe.contentWindow!, !hidden);
   }
 
   private computeHasError_(
@@ -254,7 +206,8 @@
     // the iframe. Collections could be null/empty array.
     if (!collectionsLoading) {
       const iframe = await this.iframePromise_;
-      sendCollectionsFunction(iframe.contentWindow!, collections);
+      IFrameApi.getInstance().sendCollections(
+          iframe.contentWindow!, collections);
     }
   }
 
@@ -291,7 +244,7 @@
                          return result;
                        }, {} as Record<string, number|null>);
     const iframe = await this.iframePromise_;
-    sendImageCountsFunction(iframe.contentWindow!, counts);
+    IFrameApi.getInstance().sendImageCounts(iframe.contentWindow!, counts);
   }
 
   /** Invoked on changes to the list of Google Photos photos. */
@@ -301,7 +254,7 @@
       return;
     }
     const iframe = await this.iframePromise_;
-    sendGooglePhotosPhotosFunction(
+    IFrameApi.getInstance().sendGooglePhotosPhotos(
         iframe.contentWindow!,
         googlePhotos?.slice(0, kMaximumGooglePhotosPreviews) ?? null);
   }
@@ -313,7 +266,8 @@
       return;
     }
     const iframe = await this.iframePromise_;
-    sendGooglePhotosCountFunction(iframe.contentWindow!, googlePhotosCount);
+    IFrameApi.getInstance().sendGooglePhotosCount(
+        iframe.contentWindow!, googlePhotosCount);
   }
 
   /**
@@ -324,7 +278,8 @@
     this.didSendLocalImageData_ = false;
     if (!localImagesLoading && Array.isArray(localImages)) {
       const iframe = await this.iframePromise_;
-      sendLocalImagesFunction(iframe.contentWindow!, localImages);
+      IFrameApi.getInstance().sendLocalImages(
+          iframe.contentWindow!, localImages);
     }
   }
 
@@ -389,7 +344,7 @@
       this.didSendLocalImageData_ = true;
 
       const iframe = await this.iframePromise_;
-      sendLocalImageDataFunction(iframe.contentWindow!, data);
+      IFrameApi.getInstance().sendLocalImageData(iframe.contentWindow!, data);
     }
   }
 }
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_images_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_images_element.ts
index 8139be8..ccf4a675 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_images_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_images_element.ts
@@ -17,41 +17,13 @@
 
 import {ImageTile} from '../../common/constants.js';
 import {isNonEmptyArray, promisifyOnload} from '../../common/utils.js';
-import {sendCurrentWallpaperAssetId, sendImageTiles, sendPendingWallpaperAssetId, sendVisible} from '../iframe_api.js';
+import {IFrameApi} from '../iframe_api.js';
 import {CurrentWallpaper, OnlineImageType, WallpaperCollection, WallpaperImage, WallpaperType} from '../personalization_app.mojom-webui.js';
 import {DisplayableImage} from '../personalization_reducers.js';
 import {PersonalizationRouter} from '../personalization_router_element.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
 import {isWallpaperImage} from '../utils.js';
 
-let sendCurrentWallpaperAssetIdFunction = sendCurrentWallpaperAssetId;
-let sendImageTilesFunction = sendImageTiles;
-
-/**
- * Mock out the images iframe api functions for testing. Return promises that
- * are resolved when the function is called by |WallpaperImagesElement|.
- */
-interface PromisifyResult {
-  sendCurrentWallpaperAssetId: Promise<unknown>;
-  sendImageTiles: Promise<unknown>;
-}
-
-export function promisifyImagesIframeFunctionsForTesting(): PromisifyResult {
-  const resolvers = {} as
-      {[key in keyof PromisifyResult]: (_: unknown) => void};
-  const promises = (['sendCurrentWallpaperAssetId', 'sendImageTiles'] as
-                    (keyof PromisifyResult)[])
-                       .reduce((result, next) => {
-                         result[next] =
-                             new Promise(resolve => resolvers[next] = resolve);
-                         return result;
-                       }, {} as PromisifyResult);
-  sendCurrentWallpaperAssetIdFunction = (...args) =>
-      resolvers['sendCurrentWallpaperAssetId'](args);
-  sendImageTilesFunction = (...args) => resolvers['sendImageTiles'](args);
-  return promises;
-}
-
 /**
  * If |current| is set and is an online wallpaper (include daily refresh
  * wallpaper), return the assetId of that image. Otherwise returns null.
@@ -249,19 +221,20 @@
       this.shadowRoot!.getElementById('main')!.focus();
     }
     const iframe = await this.iframePromise_;
-    sendVisible(iframe.contentWindow!, !hidden);
+    IFrameApi.getInstance().sendVisible(iframe.contentWindow!, !hidden);
   }
 
   private async onCurrentSelectedChanged_(selected: CurrentWallpaper|null) {
     const assetId = getAssetId(selected);
     const iframe = await this.iframePromise_;
-    sendCurrentWallpaperAssetIdFunction(iframe.contentWindow!, assetId);
+    IFrameApi.getInstance().sendCurrentWallpaperAssetId(
+        iframe.contentWindow!, assetId);
   }
 
   private async onPendingSelectedChanged_(pendingSelected: DisplayableImage|
                                           null) {
     const iframe = await this.iframePromise_;
-    sendPendingWallpaperAssetId(
+    IFrameApi.getInstance().sendPendingWallpaperAssetId(
         iframe.contentWindow!,
         isWallpaperImage(pendingSelected) ? pendingSelected.assetId :
                                             undefined);
@@ -323,11 +296,11 @@
       const isDarkLightModeEnabled =
           loadTimeData.getBoolean('isDarkLightModeEnabled');
       if (isDarkLightModeEnabled) {
-        sendImageTilesFunction(
+        IFrameApi.getInstance().sendImageTiles(
             iframe.contentWindow!,
             getDarkLightImageTiles(isDarkModeActive, imageArr!));
       } else {
-        sendImageTilesFunction(
+        IFrameApi.getInstance().sendImageTiles(
             iframe.contentWindow!, getRegularImageTiles(imageArr!));
       }
     }
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_interface_provider.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_interface_provider.ts
index d7035ab..c5dfb6a 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_interface_provider.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_interface_provider.ts
@@ -3,9 +3,9 @@
 // found in the LICENSE file.
 
 /**
- * @fileoverview a singleton getter for the mojom interface used in
- * the Personalization SWA. Also contains utility functions around fetching
- * mojom data and mocking out the implementation for testing.
+ * @fileoverview a singleton getter for the wallpaper mojom interface used in
+ * the Personalization SWA. Also contains utility function for mocking out the
+ * implementation for testing.
  */
 
 import 'chrome://resources/mojo/mojo/public/js/bindings.js';
diff --git a/ash/webui/personalization_app/resources/untrusted/BUILD.gn b/ash/webui/personalization_app/resources/untrusted/BUILD.gn
deleted file mode 100644
index 985c61b..0000000
--- a/ash/webui/personalization_app/resources/untrusted/BUILD.gn
+++ /dev/null
@@ -1,24 +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.
-
-import("//build/config/chromeos/ui_mode.gni")
-import("//tools/polymer/polymer.gni")
-
-assert(is_chromeos_ash)
-
-# TODO(cowmoo) remove this hack when there is a better way to access //resources
-# from untrusted. This is necessary for now to block unresolvable imports inside
-# the generated shared_vars_css.m.js file. The output
-# untrusted_shared_vars_css.js must be imported after polymer. For the full
-# range of color declarations, also manually import paper-styles/color.js.
-polymer_modulizer("shared_vars_css") {
-  js_file = "untrusted_shared_vars_css.js"
-  html_file =
-      "../../../../../ui/webui/resources/cr_elements/shared_vars_css.html"
-  html_type = "custom-style"
-  ignore_imports = [
-    "ui/webui/resources/html/polymer.html",
-    "third_party/polymer/v1_0/components-chromium/paper-styles/color.html",
-  ]
-}
diff --git a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
index dee9140..4a2e433 100644
--- a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
+++ b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome-untrusted://personalization/polymer/v3_0/iron-list/iron-list.js';
+import '//resources/polymer/v3_0/iron-list/iron-list.js';
 import './setup.js';
 import './styles.js';
 
-import {afterNextRender, html, PolymerElement} from 'chrome-untrusted://personalization/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {loadTimeData} from 'chrome-untrusted://resources/js/load_time_data.m.js';
+import {loadTimeData} from '//resources/js/load_time_data.m.js';
+import {afterNextRender, html, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {Events, EventType, kMaximumGooglePhotosPreviews, kMaximumLocalImagePreviews} from '../common/constants.js';
 import {getLoadingPlaceholderAnimationDelay, getNumberOfGridItemsPerRow, isNullOrArray, isNullOrNumber, isSelectionEvent} from '../common/utils.js';
diff --git a/ash/webui/personalization_app/resources/untrusted/iframe_api.ts b/ash/webui/personalization_app/resources/untrusted/iframe_api.ts
index 5e643cb..b60b139 100644
--- a/ash/webui/personalization_app/resources/untrusted/iframe_api.ts
+++ b/ash/webui/personalization_app/resources/untrusted/iframe_api.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {assert} from '../common/assert.m.js';
+import {assert} from '//resources/js/assert.m.js';
 import * as constants from '../common/constants.js';
 import {isNonEmptyArray, isNullOrArray, isNullOrNumber} from '../common/utils.js';
 
diff --git a/ash/webui/personalization_app/resources/untrusted/images_grid.ts b/ash/webui/personalization_app/resources/untrusted/images_grid.ts
index d123905..19f39f75 100644
--- a/ash/webui/personalization_app/resources/untrusted/images_grid.ts
+++ b/ash/webui/personalization_app/resources/untrusted/images_grid.ts
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome-untrusted://personalization/polymer/v3_0/iron-list/iron-list.js';
+import '//resources/polymer/v3_0/iron-list/iron-list.js';
 import './setup.js';
 import './styles.js';
 
-import {html, PolymerElement} from 'chrome-untrusted://personalization/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assert, assertNotReached} from '//resources/js/assert.m.js';
+import {html, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {assert, assertNotReached} from '../common/assert.m.js';
 import {Events, EventType, ImageTile} from '../common/constants.js';
 import {isSelectionEvent} from '../common/utils.js';
 import {selectImage, validateReceivedData} from '../untrusted/iframe_api.js';
diff --git a/ash/webui/personalization_app/resources/untrusted/setup.ts b/ash/webui/personalization_app/resources/untrusted/setup.ts
index 7043c96..513177a 100644
--- a/ash/webui/personalization_app/resources/untrusted/setup.ts
+++ b/ash/webui/personalization_app/resources/untrusted/setup.ts
@@ -9,10 +9,9 @@
 
 // Import necessary built in modules before ../common files. This will
 // guarantee that polymer and certain polymer elements are loaded first.
-import 'chrome-untrusted://personalization/polymer/v3_0/iron-icon/iron-icon.js';
-import 'chrome-untrusted://personalization/polymer/v3_0/iron-iconset-svg/iron-iconset-svg.js';
-import 'chrome-untrusted://personalization/polymer/v3_0/paper-styles/color.js';
+import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import '//resources/polymer/v3_0/iron-iconset-svg/iron-iconset-svg.js';
+import '//resources/cr_elements/shared_vars_css.m.js';
 import '../common/icons.js';
 import '../common/styles.js';
-import '../untrusted/untrusted_shared_vars_css.js';
 import '/strings.m.js';
diff --git a/ash/webui/personalization_app/resources/untrusted/styles.ts b/ash/webui/personalization_app/resources/untrusted/styles.ts
index 5912faa..2943681 100644
--- a/ash/webui/personalization_app/resources/untrusted/styles.ts
+++ b/ash/webui/personalization_app/resources/untrusted/styles.ts
@@ -6,7 +6,7 @@
  * @fileoverview styles for polymer components in untrusted code.
  */
 
-import 'chrome-untrusted://personalization/polymer/v3_0/polymer/polymer_bundled.min.js';
+import '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 const template = document.createElement('dom-module');
 template.innerHTML = `{__html_template__}`;
diff --git a/ash/webui/personalization_app/test/fake_personalization_app_user_provider.cc b/ash/webui/personalization_app/test/fake_personalization_app_user_provider.cc
new file mode 100644
index 0000000..67ea992
--- /dev/null
+++ b/ash/webui/personalization_app/test/fake_personalization_app_user_provider.cc
@@ -0,0 +1,36 @@
+// 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/webui/personalization_app/test/fake_personalization_app_user_provider.h"
+
+#include "ash/public/cpp/personalization_app/user_display_info.h"
+#include "ash/webui/personalization_app/mojom/personalization_app.mojom-forward.h"
+#include "content/public/browser/web_ui.h"
+
+namespace ash {
+
+FakePersonalizationAppUserProvider::FakePersonalizationAppUserProvider(
+    content::WebUI* web_ui) {}
+
+FakePersonalizationAppUserProvider::~FakePersonalizationAppUserProvider() =
+    default;
+
+void FakePersonalizationAppUserProvider::BindInterface(
+    mojo::PendingReceiver<personalization_app::mojom::UserProvider> receiver) {
+  user_receiver_.reset();
+  user_receiver_.Bind(std::move(receiver));
+}
+
+void FakePersonalizationAppUserProvider::GetUserInfo(
+    GetUserInfoCallback callback) {
+  // auto user_info_ptr = ash::personalization_app::mojom::UserInfo::New();
+  // user_info_ptr->email = "fake-email";
+  // user_info_ptr->name = "Fake Name";
+  ash::personalization_app::UserDisplayInfo display_info;
+  display_info.email = "fake-email";
+  display_info.name = "Fake Name";
+  std::move(callback).Run(std::move(display_info));
+}
+
+}  // namespace ash
diff --git a/ash/webui/personalization_app/test/fake_personalization_app_user_provider.h b/ash/webui/personalization_app/test/fake_personalization_app_user_provider.h
new file mode 100644
index 0000000..810b748d
--- /dev/null
+++ b/ash/webui/personalization_app/test/fake_personalization_app_user_provider.h
@@ -0,0 +1,51 @@
+// 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_WEBUI_PERSONALIZATION_APP_TEST_FAKE_PERSONALIZATION_APP_USER_PROVIDER_H_
+#define ASH_WEBUI_PERSONALIZATION_APP_TEST_FAKE_PERSONALIZATION_APP_USER_PROVIDER_H_
+
+#include "ash/webui/personalization_app/personalization_app_user_provider.h"
+
+#include <stdint.h>
+
+#include "ash/public/cpp/wallpaper/wallpaper_types.h"
+#include "ash/webui/personalization_app/mojom/personalization_app.mojom.h"
+#include "base/unguessable_token.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+
+namespace content {
+class WebUI;
+}  // namespace content
+
+namespace ash {
+
+class FakePersonalizationAppUserProvider
+    : public PersonalizationAppUserProvider {
+ public:
+  explicit FakePersonalizationAppUserProvider(content::WebUI* web_ui);
+
+  FakePersonalizationAppUserProvider(
+      const FakePersonalizationAppUserProvider&) = delete;
+  FakePersonalizationAppUserProvider& operator=(
+      const FakePersonalizationAppUserProvider&) = delete;
+
+  ~FakePersonalizationAppUserProvider() override;
+
+  // PersonalizationAppUserProvider:
+  void BindInterface(
+      mojo::PendingReceiver<personalization_app::mojom::UserProvider> receiver)
+      override;
+
+  // personalization_app::mojom::UserProvider
+  void GetUserInfo(GetUserInfoCallback callback) override;
+
+ private:
+  mojo::Receiver<ash::personalization_app::mojom::UserProvider> user_receiver_{
+      this};
+};
+
+}  // namespace ash
+
+#endif  // ASH_WEBUI_PERSONALIZATION_APP_TEST_FAKE_PERSONALIZATION_APP_USER_PROVIDER_H_
diff --git a/ash/webui/personalization_app/test/personalization_app_browsertest.js b/ash/webui/personalization_app/test/personalization_app_browsertest.js
index 9035c54..42f0565 100644
--- a/ash/webui/personalization_app/test/personalization_app_browsertest.js
+++ b/ash/webui/personalization_app/test/personalization_app_browsertest.js
@@ -48,6 +48,39 @@
   }
 }
 
+/**
+ * Wait until |func| returns true.
+ * If |timeoutMs| milliseconds elapse, will reject with |message|.
+ * Polls every |intervalMs| milliseconds.
+ */
+function waitUntil(func, message, intervalMs = 50, timeoutMs = 1001) {
+  let rejectTimer = null;
+  let pollTimer = null;
+
+  function cleanup() {
+    if (rejectTimer) {
+      window.clearTimeout(rejectTimer);
+    }
+    if (pollTimer) {
+      window.clearInterval(pollTimer);
+    }
+  }
+
+  return new Promise((resolve, reject) => {
+    rejectTimer = window.setTimeout(() => {
+      cleanup();
+      reject(message);
+    }, timeoutMs);
+
+    pollTimer = window.setInterval(() => {
+      if (func()) {
+        cleanup();
+        resolve();
+      }
+    }, intervalMs);
+  });
+}
+
 // TODO(crbug/1262025) revisit this workaround for js2gtest requiring "var"
 // declarations.
 this[PersonalizationAppBrowserTest.name] = PersonalizationAppBrowserTest;
@@ -79,6 +112,21 @@
   testDone();
 });
 
+TEST_F('PersonalizationAppBrowserTest', 'ShowsUserInfo', async () => {
+  const preview = document.querySelector('personalization-router')
+                      .shadowRoot.querySelector('personalization-main')
+                      .shadowRoot.querySelector('user-preview');
+
+  await waitUntil(
+      () => preview.shadowRoot.getElementById('email'),
+      'failed to find user email');
+  assertEquals(
+      'fake-email', preview.shadowRoot.getElementById('email').innerText);
+  assertEquals(
+      'Fake Name', preview.shadowRoot.getElementById('name').innerText);
+  testDone();
+});
+
 class WallpaperSubpageBrowserTest extends PersonalizationAppBrowserTest {
   /** @override */
   get browsePreload() {
diff --git a/ash/webui/personalization_app/test/personalization_app_browsertest_fixture.cc b/ash/webui/personalization_app/test/personalization_app_browsertest_fixture.cc
index 81ff842..c0daa36 100644
--- a/ash/webui/personalization_app/test/personalization_app_browsertest_fixture.cc
+++ b/ash/webui/personalization_app/test/personalization_app_browsertest_fixture.cc
@@ -9,6 +9,7 @@
 #include "ash/webui/personalization_app/personalization_app_ui.h"
 #include "ash/webui/personalization_app/personalization_app_url_constants.h"
 #include "ash/webui/personalization_app/test/fake_personalization_app_theme_provider.h"
+#include "ash/webui/personalization_app/test/fake_personalization_app_user_provider.h"
 #include "ash/webui/personalization_app/test/fake_personalization_app_wallpaper_provider.h"
 #include "chrome/test/base/mojo_web_ui_browser_test.h"
 
@@ -19,8 +20,11 @@
       std::make_unique<FakePersonalizationAppThemeProvider>(web_ui);
   auto wallpaper_provider =
       std::make_unique<FakePersonalizationAppWallpaperProvider>(web_ui);
+  auto user_provider =
+      std::make_unique<ash::FakePersonalizationAppUserProvider>(web_ui);
   return std::make_unique<ash::PersonalizationAppUI>(
-      web_ui, std::move(theme_provider), std::move(wallpaper_provider));
+      web_ui, std::move(theme_provider), std::move(user_provider),
+      std::move(wallpaper_provider));
 }
 
 void PersonalizationAppBrowserTestFixture::SetUpOnMainThread() {
diff --git a/ash/webui/personalization_app/untrusted_personalization_app_ui_config.cc b/ash/webui/personalization_app/untrusted_personalization_app_ui_config.cc
index 8a60b99..07c10773 100644
--- a/ash/webui/personalization_app/untrusted_personalization_app_ui_config.cc
+++ b/ash/webui/personalization_app/untrusted_personalization_app_ui_config.cc
@@ -16,10 +16,6 @@
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/common/url_constants.h"
 #include "services/network/public/mojom/content_security_policy.mojom-shared.h"
-#include "ui/chromeos/styles/cros_styles.h"
-#include "ui/resources/grit/webui_generated_resources.h"
-#include "ui/resources/grit/webui_generated_resources_map.h"
-#include "ui/resources/grit/webui_resources.h"
 #include "url/gurl.h"
 
 namespace ash {
@@ -48,17 +44,6 @@
                      features::IsWallpaperGooglePhotosIntegrationEnabled());
 }
 
-void AddCrosColors(content::WebUIDataSource* source) {
-  source->AddResourcePath("chromeos/colors/cros_styles.css",
-                          IDR_WEBUI_CROS_COLORS_CSS);
-
-  source->AddString(
-      "crosColorsDebugOverrides",
-      base::FeatureList::IsEnabled(features::kSemanticColorsDebugOverride)
-          ? cros_styles::kDebugOverrideCssString
-          : std::string());
-}
-
 class UntrustedPersonalizationAppUI : public ui::UntrustedWebUIController {
  public:
   explicit UntrustedPersonalizationAppUI(content::WebUI* web_ui)
@@ -78,12 +63,6 @@
           base::StartsWith(resource.path, "common"))
         source->AddResourcePath(resource.path, resource.id);
     }
-    // Add WebUI resources like polymer and iron-list so that it is accessible
-    // inside untrusted iframe.
-    source->AddResourcePaths(base::make_span(kWebuiGeneratedResources,
-                                             kWebuiGeneratedResourcesSize));
-
-    AddCrosColors(source.get());
 
     source->AddFrameAncestor(GURL(kChromeUIPersonalizationAppURL));
 
diff --git a/ash/webui/projector_app/resources/BUILD.gn b/ash/webui/projector_app/resources/BUILD.gn
index ea55192a..17bcd92 100644
--- a/ash/webui/projector_app/resources/BUILD.gn
+++ b/ash/webui/projector_app/resources/BUILD.gn
@@ -10,10 +10,6 @@
 
 js_type_check("closure_compile") {
   is_polymer3 = true
-  closure_flags = default_closure_args + [
-                    "browser_resolver_prefix_replacements=\"chrome-untrusted://projector/js/post_message_api_client.m.js=./js/post_message_api_client.m.js\"",
-                    "browser_resolver_prefix_replacements=\"chrome-untrusted://projector/js/post_message_api_request_handler.m.js=./js/post_message_api_request_handler.m.js\"",
-                  ]
   deps = [
     ":message_types",
     ":projector_browser_proxy",
diff --git a/ash/webui/projector_app/resources/annotator/untrusted/untrusted_annotator_comm_factory.js b/ash/webui/projector_app/resources/annotator/untrusted/untrusted_annotator_comm_factory.js
index bfa2df7..645a9f32 100644
--- a/ash/webui/projector_app/resources/annotator/untrusted/untrusted_annotator_comm_factory.js
+++ b/ash/webui/projector_app/resources/annotator/untrusted/untrusted_annotator_comm_factory.js
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PostMessageAPIClient} from 'chrome-untrusted://projector/js/post_message_api_client.m.js';
-import {RequestHandler} from 'chrome-untrusted://projector/js/post_message_api_request_handler.m.js';
+import {PostMessageAPIClient} from '//resources/js/post_message_api_client.m.js';
+import {RequestHandler} from '//resources/js/post_message_api_request_handler.m.js';
 
 const TARGET_URL = 'chrome://projector/';
 
diff --git a/ash/webui/projector_app/resources/app/embedder.js b/ash/webui/projector_app/resources/app/embedder.js
index 0a890bb..8d7b3b3 100644
--- a/ash/webui/projector_app/resources/app/embedder.js
+++ b/ash/webui/projector_app/resources/app/embedder.js
@@ -52,6 +52,10 @@
       client.onSodaInstallProgressUpdated(progress);
     });
 
+    this.addWebUIListener('onSodaInstalled', (args) => {
+      client.onSodaInstalled();
+    });
+
     this.addWebUIListener('onSodaInstallError', (args) => {
       client.onSodaInstallError();
     });
diff --git a/ash/webui/projector_app/resources/app/trusted/trusted_app_comm_factory.js b/ash/webui/projector_app/resources/app/trusted/trusted_app_comm_factory.js
index b666304..87a2a39 100644
--- a/ash/webui/projector_app/resources/app/trusted/trusted_app_comm_factory.js
+++ b/ash/webui/projector_app/resources/app/trusted/trusted_app_comm_factory.js
@@ -39,6 +39,13 @@
   }
 
   /**
+   * Notifies the Projector App when SODA download and installation is complete.
+   */
+  onSodaInstalled() {
+    return this.callApiFn('onSodaInstalled', []);
+  }
+
+  /**
    * Notifies the Projector App when there is a SODA installation error.
    */
   onSodaInstallError() {
diff --git a/ash/webui/projector_app/resources/app/untrusted/untrusted_app_comm_factory.js b/ash/webui/projector_app/resources/app/untrusted/untrusted_app_comm_factory.js
index 088342fc..16fbe44d 100644
--- a/ash/webui/projector_app/resources/app/untrusted/untrusted_app_comm_factory.js
+++ b/ash/webui/projector_app/resources/app/untrusted/untrusted_app_comm_factory.js
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PostMessageAPIClient} from 'chrome-untrusted://projector/js/post_message_api_client.m.js';
-import {RequestHandler} from 'chrome-untrusted://projector/js/post_message_api_request_handler.m.js';
+import {PostMessageAPIClient} from '//resources/js/post_message_api_client.m.js';
+import {RequestHandler} from '//resources/js/post_message_api_request_handler.m.js';
 
 import {ProjectorError} from '../../communication/message_types.js';
 
@@ -184,6 +184,9 @@
 
       getAppElement().onSodaInstallProgressUpdated(args[0]);
     });
+    this.registerMethod('onSodaInstalled', (args) => {
+      getAppElement().onSodaInstalled();
+    });
     this.registerMethod('onSodaInstallError', (args) => {
       getAppElement().onSodaInstallError();
     });
diff --git a/ash/webui/projector_app/resources/communication/projector_app.externs.js b/ash/webui/projector_app/resources/communication/projector_app.externs.js
index 4cae94a..da1cf8d 100644
--- a/ash/webui/projector_app/resources/communication/projector_app.externs.js
+++ b/ash/webui/projector_app/resources/communication/projector_app.externs.js
@@ -355,6 +355,11 @@
     progress) {};
 
 /**
+ * Notifies the Projector App when SODA download and installation is complete.
+ */
+projectorApp.AppApi.prototype.onSodaInstalled = function() {};
+
+/**
  * Notifies the Projector App when there is a SODA installation error.
  */
 projectorApp.AppApi.prototype.onSodaInstallError = function() {};
\ No newline at end of file
diff --git a/ash/webui/projector_app/test/projector_message_handler_unittest.cc b/ash/webui/projector_app/test/projector_message_handler_unittest.cc
index c58a2be..adfa353 100644
--- a/ash/webui/projector_app/test/projector_message_handler_unittest.cc
+++ b/ash/webui/projector_app/test/projector_message_handler_unittest.cc
@@ -45,6 +45,7 @@
 const char kOnNewScreencastPreconditionChanged[] =
     "onNewScreencastPreconditionChanged";
 const char kOnSodaInstallProgressUpdated[] = "onSodaInstallProgressUpdated";
+const char kOnSodaInstalled[] = "onSodaInstalled";
 const char kOnSodaInstallError[] = "onSodaInstallError";
 
 const char kShouldDownloadSodaCallback[] = "shouldDownloadSodaCallbck";
@@ -295,6 +296,14 @@
   EXPECT_EQ(call_data.arg2()->GetInt(), 50);
 }
 
+TEST_F(ProjectorMessageHandlerUnitTest, OnSodaInstalled) {
+  static_cast<ProjectorAppClient::Observer*>(message_handler())
+      ->OnSodaInstalled();
+  const content::TestWebUI::CallData& call_data = FetchCallData(0);
+  EXPECT_EQ(call_data.function_name(), kWebUIListenerCall);
+  EXPECT_EQ(call_data.arg1()->GetString(), kOnSodaInstalled);
+}
+
 TEST_F(ProjectorMessageHandlerUnitTest, OnSodaError) {
   static_cast<ProjectorAppClient::Observer*>(message_handler())->OnSodaError();
   const content::TestWebUI::CallData& call_data = FetchCallData(0);
diff --git a/ash/webui/projector_app/untrusted_projector_ui.cc b/ash/webui/projector_app/untrusted_projector_ui.cc
index 5271e97..adc95fd 100644
--- a/ash/webui/projector_app/untrusted_projector_ui.cc
+++ b/ash/webui/projector_app/untrusted_projector_ui.cc
@@ -14,8 +14,6 @@
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/common/url_constants.h"
-#include "ui/resources/grit/webui_generated_resources.h"
-#include "ui/resources/grit/webui_generated_resources_map.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(ENABLE_CROS_MEDIA_APP)
@@ -39,11 +37,6 @@
                       kChromeosProjectorAppBundleResourcesSize));
   source->AddResourcePath("", IDR_ASH_PROJECTOR_APP_UNTRUSTED_APP_INDEX_HTML);
 
-  // Allows WebUI resources like Polymer and PostMessageAPI to be accessible
-  // inside the untrusted iframe.
-  source->AddResourcePaths(
-      base::make_span(kWebuiGeneratedResources, kWebuiGeneratedResourcesSize));
-
 #if BUILDFLAG(ENABLE_CROS_MEDIA_APP)
   // Loads WASM resources shipped to Chromium by chrome://media-app.
   source->AddResourcePath("annotator/ink_engine_ink.worker.js",
diff --git a/ash/webui/shimless_rma/resources/onboarding_select_components_page.html b/ash/webui/shimless_rma/resources/onboarding_select_components_page.html
index da38241..d4e2c75 100644
--- a/ash/webui/shimless_rma/resources/onboarding_select_components_page.html
+++ b/ash/webui/shimless_rma/resources/onboarding_select_components_page.html
@@ -13,7 +13,8 @@
       <template is="dom-repeat" items="{{componentCheckboxes_}}" as="component">
         <repair-component-chip id="[[component.id]]"
           checked="{{component.checked}}"
-          disabled$="[[component.disabled]]"
+          disabled$="[[isComponentDisabled_(component.disabled,
+              allButtonsDisabled)]]"
           component-name="[[component.name]]"
           component-id="[[component.uniqueId]]">
         </repair-component-chip>
diff --git a/ash/webui/shimless_rma/resources/onboarding_select_components_page.js b/ash/webui/shimless_rma/resources/onboarding_select_components_page.js
index 75176b1..b31a92fb 100644
--- a/ash/webui/shimless_rma/resources/onboarding_select_components_page.js
+++ b/ash/webui/shimless_rma/resources/onboarding_select_components_page.js
@@ -53,6 +53,12 @@
 
   static get properties() {
     return {
+      /**
+       * Set by shimless_rma.js.
+       * @type {boolean}
+       */
+      allButtonsDisabled: Boolean,
+
       /** @protected {!Array<!ComponentCheckbox>} */
       componentCheckboxes_: {
         type: Array,
@@ -149,8 +155,22 @@
         this.i18nAdvanced('reworkFlowLinkText', {attrs: ['id']});
     const linkElement = this.shadowRoot.querySelector('#reworkFlowLink');
     linkElement.setAttribute('href', '#');
-    linkElement.addEventListener(
-        'click', e => this.onReworkFlowLinkClicked_(e));
+    linkElement.addEventListener('click', e => {
+      if (this.allButtonsDisabled) {
+        return;
+      }
+
+      this.onReworkFlowLinkClicked_(e);
+    });
+  }
+
+  /**
+   * @param {boolean} componentDisabled
+   * @return {boolean}
+   * @protected
+   */
+  isComponentDisabled_(componentDisabled) {
+    return this.allButtonsDisabled || componentDisabled;
   }
 }
 
diff --git a/ash/wm/desks/desks_restore_util.cc b/ash/wm/desks/desks_restore_util.cc
index 20bccfb..9a9a863e 100644
--- a/ash/wm/desks/desks_restore_util.cc
+++ b/ash/wm/desks/desks_restore_util.cc
@@ -260,7 +260,8 @@
     return;
   }
 
-  ListPrefUpdate name_update(primary_user_prefs, prefs::kDesksNamesList);
+  ListPrefUpdateDeprecated name_update(primary_user_prefs,
+                                       prefs::kDesksNamesList);
   base::ListValue* name_pref_data = name_update.Get();
   name_pref_data->ClearList();
 
@@ -293,7 +294,8 @@
   }
 
   // Save per-desk metrics.
-  ListPrefUpdate metrics_update(primary_user_prefs, prefs::kDesksMetricsList);
+  ListPrefUpdateDeprecated metrics_update(primary_user_prefs,
+                                          prefs::kDesksMetricsList);
   base::ListValue* metrics_pref_data = metrics_update.Get();
   metrics_pref_data->ClearList();
 
@@ -301,11 +303,11 @@
   const auto& desks = desks_controller->desks();
   for (const auto& desk : desks) {
     base::DictionaryValue metrics_dict;
-    metrics_dict.SetInteger(
+    metrics_dict.SetIntKey(
         kCreationTimeKey,
         desk->creation_time().ToDeltaSinceWindowsEpoch().InMinutes());
-    metrics_dict.SetInteger(kFirstDayVisitedKey, desk->first_day_visited());
-    metrics_dict.SetInteger(kLastDayVisitedKey, desk->last_day_visited());
+    metrics_dict.SetIntKey(kFirstDayVisitedKey, desk->first_day_visited());
+    metrics_dict.SetIntKey(kLastDayVisitedKey, desk->last_day_visited());
     metrics_dict.SetBoolean(kInteractedWithThisWeekKey,
                             desk->interacted_with_this_week());
     metrics_pref_data->Append(std::move(metrics_dict));
@@ -314,7 +316,7 @@
   DCHECK_EQ(metrics_pref_data->GetList().size(), desks.size());
 
   // Save weekly active report time.
-  DictionaryPrefUpdate weekly_active_desks_update(
+  DictionaryPrefUpdateDeprecated weekly_active_desks_update(
       primary_user_prefs, prefs::kDesksWeeklyActiveDesksMetrics);
   weekly_active_desks_update->SetIntPath(
       kReportTimeKey, desks_controller->GetWeeklyActiveReportTime()
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index de5c2cad..327db5d 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -3255,7 +3255,7 @@
   void InitPrefsWithDesksRestoreData(PrefService* prefs,
                                      std::vector<std::string> desk_names) {
     DCHECK(prefs);
-    ListPrefUpdate update(prefs, prefs::kDesksNamesList);
+    ListPrefUpdateDeprecated update(prefs, prefs::kDesksNamesList);
     base::ListValue* pref_data = update.Get();
     ASSERT_TRUE(pref_data->GetList().empty());
     for (auto desk_name : desk_names)
@@ -6199,11 +6199,16 @@
 }
 
 // Tests that the bar will only be created when the app list is not in
-// fullscreen mode.
+// fullscreen mode. This test can be deleted when ProductivityLauncher is the
+// default.
 TEST_F(PersistentDesksBarTest, AppListFullscreen) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(features::kProductivityLauncher);
+
   AppListControllerImpl* app_list_controller =
       Shell::Get()->app_list_controller();
-  AppListView* app_list_view = app_list_controller->presenter()->GetView();
+  AppListView* app_list_view =
+      app_list_controller->fullscreen_presenter()->GetView();
 
   // The bar should be created when the app list view remains null.
   NewDesk();
@@ -6214,7 +6219,7 @@
   // The bar should be created when the app list view is created and
   // showing in kPeeking mode.
   app_list_controller->ShowAppList();
-  app_list_view = app_list_controller->presenter()->GetView();
+  app_list_view = app_list_controller->fullscreen_presenter()->GetView();
   ASSERT_TRUE(app_list_view);
   EXPECT_TRUE(app_list_view->app_list_state() == AppListViewState::kPeeking);
   EXPECT_TRUE(GetBarWidget());
@@ -6239,13 +6244,38 @@
               AppListViewState::kFullscreenAllApps);
   EXPECT_FALSE(GetBarWidget());
 
-  // The bar should be created when the app list is in kClosed mode.
-  app_list_view->SetState(AppListViewState::kClosed);
+  // The bar should be created when the app list is dismissed.
+  app_list_controller->DismissAppList();
   EXPECT_TRUE(app_list_view->app_list_state() == AppListViewState::kClosed);
   EXPECT_TRUE(GetBarWidget());
   EXPECT_TRUE(IsWidgetVisible());
 }
 
+// Tests that the bar is not affected by ProductivityLauncher.
+TEST_F(PersistentDesksBarTest, ProductivityLauncher) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kProductivityLauncher);
+
+  AppListControllerImpl* app_list_controller =
+      Shell::Get()->app_list_controller();
+
+  // The bar should be created when the app list is closed.
+  NewDesk();
+  EXPECT_EQ(2u, DesksController::Get()->desks().size());
+  EXPECT_TRUE(GetBarWidget());
+  EXPECT_TRUE(IsWidgetVisible());
+
+  // The bar should still exist when the app list is opened.
+  app_list_controller->ShowAppList();
+  EXPECT_TRUE(GetBarWidget());
+  EXPECT_TRUE(IsWidgetVisible());
+
+  // The bar should still exist when the app list is closed again.
+  app_list_controller->DismissAppList();
+  EXPECT_TRUE(GetBarWidget());
+  EXPECT_TRUE(IsWidgetVisible());
+}
+
 // Tests that the bar will not be created if Docked Magnifier is on.
 TEST_F(PersistentDesksBarTest, NoPersistentDesksBarWithDockedMagnifierOn) {
   AccessibilityControllerImpl* accessibility_controller =
diff --git a/ash/wm/desks/templates/desks_templates_dialog_controller.cc b/ash/wm/desks/templates/desks_templates_dialog_controller.cc
index a2c88f6..6560e01 100644
--- a/ash/wm/desks/templates/desks_templates_dialog_controller.cc
+++ b/ash/wm/desks/templates/desks_templates_dialog_controller.cc
@@ -10,6 +10,7 @@
 #include "ash/wm/desks/templates/desks_templates_grid_view.h"
 #include "ash/wm/desks/templates/desks_templates_icon_container.h"
 #include "ash/wm/desks/templates/desks_templates_item_view.h"
+#include "ash/wm/desks/templates/desks_templates_metrics_util.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_grid.h"
 #include "base/bind.h"
@@ -184,6 +185,7 @@
           .Build();
   icon_container->PopulateIconContainerFromWindows(unsupported_apps);
   CreateDialogWidget(std::move(dialog), root_window);
+  RecordUnsupportedAppDialogShowHistogram();
 }
 
 void DesksTemplatesDialogController::ShowReplaceDialog(
diff --git a/ash/wm/desks/templates/desks_templates_grid_view.cc b/ash/wm/desks/templates/desks_templates_grid_view.cc
index 283b520..0583ff4 100644
--- a/ash/wm/desks/templates/desks_templates_grid_view.cc
+++ b/ash/wm/desks/templates/desks_templates_grid_view.cc
@@ -122,13 +122,13 @@
       for (DesksTemplatesItemView* template_view : grid_items_) {
         if (template_view->IsViewHighlighted()) {
           highlight_controller->OnViewDestroyingOrDisabling(template_view);
-          return;
+          break;
         }
 
         if (template_view->name_view()->IsViewHighlighted()) {
           highlight_controller->OnViewDestroyingOrDisabling(
               template_view->name_view());
-          return;
+          break;
         }
       }
     }
diff --git a/ash/wm/desks/templates/desks_templates_item_view.cc b/ash/wm/desks/templates/desks_templates_item_view.cc
index 94f4707..8e3d146 100644
--- a/ash/wm/desks/templates/desks_templates_item_view.cc
+++ b/ash/wm/desks/templates/desks_templates_item_view.cc
@@ -14,7 +14,6 @@
 #include "ash/style/close_button.h"
 #include "ash/style/pill_button.h"
 #include "ash/style/style_util.h"
-#include "ash/wm/desks/desk_name_view.h"
 #include "ash/wm/desks/desks_textfield.h"
 #include "ash/wm/desks/templates/desks_templates_dialog_controller.h"
 #include "ash/wm/desks/templates/desks_templates_icon_container.h"
@@ -24,7 +23,6 @@
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_highlight_controller.h"
 #include "ash/wm/overview/overview_session.h"
-#include "base/notreached.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chromeos/ui/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -40,7 +38,6 @@
 #include "ui/views/layout/box_layout_view.h"
 #include "ui/views/metadata/view_factory_internal.h"
 #include "ui/views/view.h"
-#include "ui/views/view_targeter_delegate.h"
 
 namespace ash {
 namespace {
@@ -58,15 +55,15 @@
 // The margin for the delete button.
 constexpr int kDeleteButtonMargin = 8;
 
-// The minimum template name view width.
-constexpr int kMinTemplateNameViewWidth = 56;
+// The preferred width of the container that houses the template name textfield
+// and managed status indicator and the time label.
+constexpr int kTemplateNameAndTimePreferredWidth =
+    kPreferredSize.width() - kHorizontalPaddingDp * 2;
 
-// The margin between the grid item contents and the card container.
-constexpr int kGridItemMargin = 24;
 constexpr int kTimeViewHeight = 20;
 
-// The margin for the managed status icon.
-constexpr int kManagedStatusIndicatorMargin = 8;
+// The spacing between the textfield and the managed status icon.
+constexpr int kManagedStatusIndicatorSpacing = 8;
 constexpr int kManagedStatusIndicatorSize = 20;
 
 constexpr char kAmPmTimeDateFmtStr[] = "%d:%02d%s, %d-%02d-%02d";
@@ -97,16 +94,17 @@
       &DesksTemplatesItemView::OnGridItemPressed, base::Unretained(this));
 
   const std::u16string template_name = desk_template_->template_name();
+  auto* color_provider = AshColorProvider::Get();
 
-  views::View* spacer;
   views::BoxLayoutView* card_container;
+  views::View* spacer;
   views::Builder<DesksTemplatesItemView>(this)
       .SetPreferredSize(kPreferredSize)
       .SetUseDefaultFillLayout(true)
       .SetAccessibleName(template_name)
       .SetCallback(std::move(launch_template_callback))
       .SetBackground(views::CreateRoundedRectBackground(
-          AshColorProvider::Get()->GetControlsLayerColor(
+          color_provider->GetControlsLayerColor(
               AshColorProvider::ControlsLayerType::
                   kControlBackgroundColorInactive),
           kCornerRadius))
@@ -118,31 +116,42 @@
                   views::BoxLayout::CrossAxisAlignment::kStart)
               .SetInsideBorderInsets(
                   gfx::Insets(kVerticalPaddingDp, kHorizontalPaddingDp))
+              // TODO(richui): Consider splitting some of the children into
+              // different files and/or classes.
               .AddChildren(
-                  views::Builder<DesksTemplatesNameView>()
-                      .CopyAddressTo(&name_view_)
-                      .SetText(template_name)
-                      .SetAccessibleName(template_name),
+                  views::Builder<views::BoxLayoutView>()
+                      .SetOrientation(
+                          views::BoxLayout::Orientation::kHorizontal)
+                      .SetBetweenChildSpacing(kManagedStatusIndicatorSpacing)
+                      .SetPreferredSize(gfx::Size(
+                          kTemplateNameAndTimePreferredWidth,
+                          DesksTemplatesNameView::kTemplateNameViewHeight))
+                      .AddChildren(
+                          views::Builder<DesksTemplatesNameView>()
+                              .CopyAddressTo(&name_view_)
+                              .SetText(template_name)
+                              .SetAccessibleName(template_name),
+                          views::Builder<views::ImageView>()
+                              .SetPreferredSize(
+                                  gfx::Size(kManagedStatusIndicatorSize,
+                                            kManagedStatusIndicatorSize))
+                              .SetImage(gfx::CreateVectorIcon(
+                                  chromeos::kEnterpriseIcon,
+                                  kManagedStatusIndicatorSize,
+                                  color_provider->GetContentLayerColor(
+                                      AshColorProvider::ContentLayerType::
+                                          kIconColorSecondary)))
+                              .SetVisible(desk_template->source() ==
+                                          DeskTemplateSource::kPolicy)),
                   views::Builder<views::Label>()
                       .CopyAddressTo(&time_view_)
                       .SetHorizontalAlignment(gfx::ALIGN_LEFT)
                       .SetText(GetTimeStr(desk_template_->created_time()))
                       .SetPreferredSize(gfx::Size(
-                          kPreferredSize.width() - kGridItemMargin * 2,
-                          kTimeViewHeight)),
+                          kTemplateNameAndTimePreferredWidth, kTimeViewHeight)),
                   views::Builder<views::View>().CopyAddressTo(&spacer),
                   views::Builder<DesksTemplatesIconContainer>().CopyAddressTo(
                       &icon_container_view_)),
-          views::Builder<views::ImageView>()
-              .CopyAddressTo(&managed_status_indicator_)
-              .SetPreferredSize(gfx::Size(kManagedStatusIndicatorSize,
-                                          kManagedStatusIndicatorSize))
-              .SetImage(gfx::CreateVectorIcon(
-                  chromeos::kEnterpriseIcon, kManagedStatusIndicatorSize,
-                  AshColorProvider::Get()->GetContentLayerColor(
-                      AshColorProvider::ContentLayerType::kIconColorSecondary)))
-              .SetVisible(desk_template->source() ==
-                          DeskTemplateSource::kPolicy),
           views::Builder<views::View>().CopyAddressTo(&hover_container_))
       .BuildChildren();
 
@@ -158,7 +167,6 @@
                           base::Unretained(this)),
       CloseButton::Type::kMedium));
 
-  name_view_->SetTextAndElideIfNeeded(template_name);
   name_view_->set_controller(this);
   name_view_observation_.Observe(name_view_);
 
@@ -210,15 +218,14 @@
 }
 
 void DesksTemplatesItemView::Layout() {
+  const int previous_name_view_width = name_view_->width();
+
   views::View::Layout();
 
-  LayoutTemplateNameView();
-
-  managed_status_indicator_->SetBoundsRect(
-      gfx::Rect(name_view_->bounds().width() + kHorizontalPaddingDp +
-                    kManagedStatusIndicatorMargin,
-                name_view_->y(), kManagedStatusIndicatorSize,
-                kManagedStatusIndicatorSize));
+  // A change in the `name_view_`'s width might mean the need to elide the text
+  // differently.
+  if (previous_name_view_width != name_view_->width())
+    OnTemplateNameChanged(desk_template_->template_name());
 
   const gfx::Size delete_button_size = delete_button_->GetPreferredSize();
   DCHECK_EQ(delete_button_size.width(), delete_button_size.height());
@@ -371,7 +378,7 @@
     name_view_->SetText(trimmed_new_contents);
   }
 
-  Layout();
+  name_view_->OnContentsChanged();
 }
 
 bool DesksTemplatesItemView::HandleKeyEvent(views::Textfield* sender,
@@ -438,12 +445,16 @@
 
 views::View* DesksTemplatesItemView::TargetForRect(views::View* root,
                                                    const gfx::Rect& rect) {
+  gfx::RectF name_view_bounds(name_view_->GetMirroredBounds());
+  views::View::ConvertRectToTarget(name_view_->parent(), this,
+                                   &name_view_bounds);
+
   // With the design of the template card having the textfield within a
   // clickable button, as well as having the grid view be a `PreTargetHandler`,
   // we needed to make `this` a `ViewTargeterDelegate` for the view event
   // targeter in order to allow the `name_view_` to be specifically targeted and
   // focused.
-  if (root == this && name_view_->GetMirroredBounds().Contains(rect))
+  if (root == this && gfx::ToRoundedRect(name_view_bounds).Contains(rect))
     return name_view_;
   return views::ViewTargeterDelegate::TargetForRect(root, rect);
 }
@@ -507,35 +518,6 @@
   Layout();
 }
 
-void DesksTemplatesItemView::LayoutTemplateNameView() {
-  const int previous_width = name_view_->width();
-  const gfx::Size name_view_size = name_view_->GetPreferredSize();
-  // The item view's width is supposed to be larger than
-  // `kMinTemplateNameViewWidth`, but it might be not the truth for tests with
-  // extreme abnormal size of display.
-  const int min_width =
-      std::min(kPreferredSize.width(), kMinTemplateNameViewWidth);
-  // TODO(crbug.com/1264174): Investigate the best way to get this to work with
-  // the enterprise indicator. Possibly wrap both in a `BoxLayoutView`.
-  const int max_width = std::max(
-      kPreferredSize.width() - (kHorizontalPaddingDp * 2) -
-          (managed_status_indicator_->GetVisible()
-               ? (kManagedStatusIndicatorMargin + kManagedStatusIndicatorSize)
-               : 0),
-      kMinTemplateNameViewWidth);
-  const int text_width =
-      base::clamp(name_view_size.width(), min_width, max_width);
-  gfx::Rect name_view_bounds{name_view_->bounds()};
-  name_view_bounds.set_width(text_width);
-
-  name_view_->SetBoundsRect(name_view_bounds);
-
-  // A change in the `name_view_`'s width might mean the need to elide the text
-  // differently.
-  if (previous_width != name_view_bounds.width())
-    OnTemplateNameChanged(desk_template_->template_name());
-}
-
 views::View* DesksTemplatesItemView::GetView() {
   return this;
 }
diff --git a/ash/wm/desks/templates/desks_templates_item_view.h b/ash/wm/desks/templates/desks_templates_item_view.h
index 8c3611c..3d7e4852 100644
--- a/ash/wm/desks/templates/desks_templates_item_view.h
+++ b/ash/wm/desks/templates/desks_templates_item_view.h
@@ -7,7 +7,6 @@
 
 #include "ash/ash_export.h"
 #include "ash/wm/overview/overview_highlightable_view.h"
-#include "base/guid.h"
 #include "base/scoped_observation.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/controls/button/button.h"
@@ -18,7 +17,6 @@
 namespace views {
 class Label;
 class Textfield;
-class ImageView;
 }  // namespace views
 
 namespace ash {
@@ -89,10 +87,6 @@
   // changes.
   void OnTemplateNameChanged(const std::u16string& new_name);
 
-  // Layout `name_view_` given the current bounds of `this` as well as the
-  // contents of the textfield.
-  void LayoutTemplateNameView();
-
   // OverviewHighlightableView:
   views::View* GetView() override;
   void MaybeActivateHighlightedView() override;
@@ -113,9 +107,6 @@
   // Container used for holding all the views that appear on hover.
   views::View* hover_container_ = nullptr;
 
-  // The indicator to show if template is managed by admin.
-  views::ImageView* managed_status_indicator_ = nullptr;
-
   // When the `name_view_` is focused, we select all its text. However, if it is
   // focused via a mouse press event, on mouse release will clear the selection.
   // Therefore, we defer selecting all text until we receive that mouse release.
diff --git a/ash/wm/desks/templates/desks_templates_metrics_util.cc b/ash/wm/desks/templates/desks_templates_metrics_util.cc
index 2af2ce4..fa23d0b 100644
--- a/ash/wm/desks/templates/desks_templates_metrics_util.cc
+++ b/ash/wm/desks/templates/desks_templates_metrics_util.cc
@@ -74,4 +74,8 @@
   base::UmaHistogramCounts100(kWindowAndTabCountHistogramName, total_count);
 }
 
+void RecordUnsupportedAppDialogShowHistogram() {
+  base::UmaHistogramBoolean(kUnsupportedAppDialogShowHistogramName, true);
+}
+
 }  // namespace ash
diff --git a/ash/wm/desks/templates/desks_templates_metrics_util.h b/ash/wm/desks/templates/desks_templates_metrics_util.h
index b314bdf0..eb5f6b9 100644
--- a/ash/wm/desks/templates/desks_templates_metrics_util.h
+++ b/ash/wm/desks/templates/desks_templates_metrics_util.h
@@ -28,6 +28,8 @@
     "Ash.DeskTemplate.LaunchFromTemplate";
 constexpr char kUserTemplateCountHistogramName[] =
     "Ash.DeskTemplate.UserTemplateCount";
+constexpr char kUnsupportedAppDialogShowHistogramName[] =
+    "Ash.DeskTemplate.UnsupportedAppDialogShow";
 
 // Wrappers calls base::uma with correct histogram name.
 void RecordLoadTemplateHistogram();
@@ -39,6 +41,7 @@
 void RecordUserTemplateCountHistogram(size_t entry_count,
                                       size_t max_entry_count);
 void RecordWindowAndTabCountHistogram(DeskTemplate* desk_template);
+void RecordUnsupportedAppDialogShowHistogram();
 
 }  // namespace ash
 
diff --git a/ash/wm/desks/templates/desks_templates_name_view.cc b/ash/wm/desks/templates/desks_templates_name_view.cc
index 4066fbdc..14f4d19 100644
--- a/ash/wm/desks/templates/desks_templates_name_view.cc
+++ b/ash/wm/desks/templates/desks_templates_name_view.cc
@@ -63,20 +63,43 @@
   focus_manager->SetStoredFocusView(nullptr);
 }
 
+void DesksTemplatesNameView::OnContentsChanged() {
+  PreferredSizeChanged();
+}
+
 void DesksTemplatesNameView::SetTextAndElideIfNeeded(
     const std::u16string& text) {
-  // Calculates the max width taking into account the insets and padding of the
-  // parent.
-  auto* parent_view = static_cast<views::BoxLayoutView*>(parent());
-  SetText(gfx::ElideText(text, GetFontList(),
-                         parent_view->width() -
-                             parent_view->GetInsideBorderInsets().width() -
-                             GetInsets().width(),
+  SetText(gfx::ElideText(text, GetFontList(), GetAvailableWidth(),
                          gfx::ELIDE_TAIL));
-
   full_text_ = text;
 }
 
+gfx::Size DesksTemplatesNameView::CalculatePreferredSize() const {
+  const gfx::Size preferred_size = DesksTextfield::CalculatePreferredSize();
+  // Use the available width if it is larger than the preferred width.
+  const int preferred_width =
+      std::clamp(preferred_size.width(), 1, GetAvailableWidth());
+  return gfx::Size(preferred_width, kTemplateNameViewHeight);
+}
+
+int DesksTemplatesNameView::GetAvailableWidth() const {
+  auto* parent_view = static_cast<const views::BoxLayoutView*>(parent());
+  int available_width = parent_view->width() -
+                        parent_view->GetInsideBorderInsets().width() -
+                        GetInsets().width();
+  const int between_child_spacing = parent_view->GetBetweenChildSpacing();
+  for (auto* child : parent_view->children()) {
+    if (child == this || !child->GetVisible())
+      continue;
+    // The width of `child` may be 0 if it is offscreen, so use the preferred
+    // width instead.
+    available_width -=
+        (child->GetPreferredSize().width() + between_child_spacing);
+  }
+
+  return std::max(1, available_width);
+}
+
 BEGIN_METADATA(DesksTemplatesNameView, DesksTextfield)
 END_METADATA
 
diff --git a/ash/wm/desks/templates/desks_templates_name_view.h b/ash/wm/desks/templates/desks_templates_name_view.h
index 45ec150..0304166 100644
--- a/ash/wm/desks/templates/desks_templates_name_view.h
+++ b/ash/wm/desks/templates/desks_templates_name_view.h
@@ -21,16 +21,28 @@
   DesksTemplatesNameView& operator=(const DesksTemplatesNameView&) = delete;
   ~DesksTemplatesNameView() override;
 
+  static constexpr int kTemplateNameViewHeight = 24;
+
   // Commits an on-going template name change (if any) by bluring the focus away
   // from any view on `widget`, where `widget` should be the desks templates
   // grid widget.
   static void CommitChanges(views::Widget* widget);
 
+  // Called when the contents in the textfield change. Updates the preferred
+  // size of `this`, which invalidates the layout.
+  void OnContentsChanged();
+
   // DesksTextfield:
   void SetTextAndElideIfNeeded(const std::u16string& text) override;
+  gfx::Size CalculatePreferredSize() const override;
 
  private:
   friend class DesksTemplatesNameViewTestApi;
+
+  // Gets the available width for `this`. It is the largest width `this` can be
+  // based on the parent's and visible sibling's preferred sizes. Will always
+  // return a value greater than or equal to one.
+  int GetAvailableWidth() const;
 };
 
 BEGIN_VIEW_BUILDER(/* no export */, DesksTemplatesNameView, DesksTextfield)
diff --git a/ash/wm/desks/templates/desks_templates_unittest.cc b/ash/wm/desks/templates/desks_templates_unittest.cc
index 3d7ba956..989381d1 100644
--- a/ash/wm/desks/templates/desks_templates_unittest.cc
+++ b/ash/wm/desks/templates/desks_templates_unittest.cc
@@ -1606,6 +1606,12 @@
   WaitForDesksTemplatesUI();
   name_view = GetItemViewFromTemplatesGrid(0)->name_view();
   EXPECT_EQ(u"abc", name_view->GetText());
+
+  // There was a bug where a relayout could cause a revert of the name changes,
+  // and lead to a crash if the name view had highlight focus. This is a
+  // regression test for that. See https://crbug.com/1285113 for more details.
+  GetItemViewFromTemplatesGrid(0)->SetBoundsRect(gfx::Rect(150, 40));
+  EXPECT_EQ(u"abc", GetItemViewFromTemplatesGrid(0)->name_view()->GetText());
 }
 
 // Tests for checking that certain conditions will revert the template name to
@@ -1813,6 +1819,36 @@
       kExpectedNewTemplates);
 }
 
+// Tests that UnsupportedAppDialogShow metric is recorded when the unsupported
+// app dialog is shown.
+TEST_F(DesksTemplatesTest, UnsupportedAppDialogRecordsMetric) {
+  // For asserting histogram was captured.
+  base::HistogramTester histogram_tester;
+
+  // Create a crostini window.
+  auto crostini_window = CreateAppWindow();
+  crostini_window->SetProperty(aura::client::kAppType,
+                               static_cast<int>(AppType::CROSTINI_APP));
+
+  // Create a normal window.
+  auto test_window = CreateAppWindow();
+
+  // Open overview and click on the save template button. The unsupported apps
+  // dialog should show up.
+  auto* root = Shell::Get()->GetPrimaryRootWindow();
+  ToggleOverview();
+  WaitForDesksTemplatesUI();
+  views::Widget* save_template = GetSaveDeskAsTemplateButtonForRoot(root);
+  ASSERT_TRUE(save_template->IsVisible());
+  ClickOnView(save_template->GetContentsView());
+  EXPECT_TRUE(Shell::IsSystemModalWindowOpen());
+
+  // Now we assert that we've recorded the metric.
+  constexpr int kExpectedDialogShows = 1;
+  histogram_tester.ExpectTotalCount(kUnsupportedAppDialogShowHistogramName,
+                                    kExpectedDialogShows);
+}
+
 // Tests to verify that clicking the spacebar doesn't cause the name view to
 // lose focus (since it's within a button), and that whitespaces are handled
 // correctly.
diff --git a/ash/wm/haptics_util.cc b/ash/wm/haptics_util.cc
index df7ce04..fb21f6e 100644
--- a/ash/wm/haptics_util.cc
+++ b/ash/wm/haptics_util.cc
@@ -31,5 +31,12 @@
   input_controller->PlayHapticTouchpadEffect(effect, strength);
 }
 
+void PlayHapticToggleEffect(bool on,
+                            ui::HapticTouchpadEffectStrength strength) {
+  PlayHapticTouchpadEffect(on ? ui::HapticTouchpadEffect::kToggleOn
+                              : ui::HapticTouchpadEffect::kToggleOff,
+                           strength);
+}
+
 }  // namespace haptics_util
 }  // namespace ash
diff --git a/ash/wm/haptics_util.h b/ash/wm/haptics_util.h
index 5c999fd3..e23bbf2 100644
--- a/ash/wm/haptics_util.h
+++ b/ash/wm/haptics_util.h
@@ -30,6 +30,11 @@
     ui::HapticTouchpadEffect effect,
     ui::HapticTouchpadEffectStrength strength);
 
+// Plays a `ToggleOn` or `ToggleOff` haptic effect based on the `on` bool value.
+ASH_EXPORT void PlayHapticToggleEffect(
+    bool on,
+    ui::HapticTouchpadEffectStrength strength);
+
 }  // namespace haptics_util
 }  // namespace ash
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index ae6d122b..d977585 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -842,6 +842,7 @@
     "threading/simple_thread.h",
     "threading/thread.cc",
     "threading/thread.h",
+    "threading/thread_checker.cc",
     "threading/thread_checker.h",
     "threading/thread_checker_impl.cc",
     "threading/thread_checker_impl.h",
diff --git a/base/allocator/partition_alloc_support.cc b/base/allocator/partition_alloc_support.cc
index 41c4699..8dbff6d 100644
--- a/base/allocator/partition_alloc_support.cc
+++ b/base/allocator/partition_alloc_support.cc
@@ -137,43 +137,45 @@
 
 namespace {
 
-bool g_memory_reclaimer_running = false;
-
-void DelayedPurgeActionForThreadCache(OnceClosure task, base::TimeDelta delay) {
+void RunThreadCachePeriodicPurge() {
+  TRACE_EVENT0("memory", "PeriodicPurge");
+  auto& instance = internal::ThreadCacheRegistry::Instance();
+  instance.RunPeriodicPurge();
+  TimeDelta delay =
+      Microseconds(instance.GetPeriodicPurgeNextIntervalInMicroseconds());
   ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE,
-      BindOnce(
-          [](OnceClosure task) {
-            TRACE_EVENT0("memory", "PeriodicPurge");
-            std::move(task).Run();
-          },
-          std::move(task)),
-      delay);
+      FROM_HERE, BindOnce(RunThreadCachePeriodicPurge), delay);
 }
 
-base::RepeatingTimer& GetTimer() {
-  static base::NoDestructor<base::RepeatingTimer> timer;
-  return *timer.get();
-}
-
-void ReclaimPeriodically() {
+void RunPartitionAllocMemoryReclaimer(
+    scoped_refptr<SequencedTaskRunner> task_runner) {
   TRACE_EVENT0("base", "PartitionAllocMemoryReclaimer::Reclaim()");
-  PartitionAllocMemoryReclaimer::Instance()->ReclaimNormal();
+  auto* instance = PartitionAllocMemoryReclaimer::Instance();
+  instance->ReclaimNormal();
+  TimeDelta delay =
+      Microseconds(instance->GetRecommendedReclaimIntervalInMicroseconds());
+  task_runner->PostDelayedTask(
+      FROM_HERE, BindOnce(RunPartitionAllocMemoryReclaimer, task_runner),
+      delay);
 }
 
 }  // namespace
 
 void StartThreadCachePeriodicPurge() {
-  internal::ThreadCacheRegistry::Instance().StartPeriodicPurge(
-      DelayedPurgeActionForThreadCache);
+  auto& instance = internal::ThreadCacheRegistry::Instance();
+  TimeDelta delay =
+      Microseconds(instance.GetPeriodicPurgeNextIntervalInMicroseconds());
+  ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, BindOnce(RunThreadCachePeriodicPurge), delay);
 }
 
 void StartMemoryReclaimer(scoped_refptr<SequencedTaskRunner> task_runner) {
   // Can be called several times.
-  if (g_memory_reclaimer_running)
+  static bool is_memory_reclaimer_running = false;
+  if (is_memory_reclaimer_running)
     return;
+  is_memory_reclaimer_running = true;
 
-  g_memory_reclaimer_running = true;
   // The caller of the API fully controls where running the reclaim.
   // However there are a few reasons to recommend that the caller runs
   // it on the main thread:
@@ -189,11 +191,12 @@
   // seconds is useful. Since this is meant to run during idle time only, it is
   // a reasonable starting point balancing effectivenes vs cost. See
   // crbug.com/942512 for details and experimental results.
-  GetTimer().SetTaskRunner(task_runner);
-  GetTimer().Start(FROM_HERE,
-                   PartitionAllocMemoryReclaimer::Instance()
-                       ->GetRecommendedReclaimInterval(),
-                   BindRepeating(&ReclaimPeriodically));
+  auto* instance = PartitionAllocMemoryReclaimer::Instance();
+  TimeDelta delay =
+      Microseconds(instance->GetRecommendedReclaimIntervalInMicroseconds());
+  task_runner->PostDelayedTask(
+      FROM_HERE, BindOnce(RunPartitionAllocMemoryReclaimer, task_runner),
+      delay);
 }
 
 std::map<std::string, std::string> ProposeSyntheticFinchTrials(
diff --git a/base/allocator/partition_allocator/memory_reclaimer.h b/base/allocator/partition_allocator/memory_reclaimer.h
index dde12a3..9816668 100644
--- a/base/allocator/partition_allocator/memory_reclaimer.h
+++ b/base/allocator/partition_allocator/memory_reclaimer.h
@@ -42,12 +42,12 @@
   // Triggers an explicit reclaim now to reclaim as much free memory as
   // possible. The API callers need to invoke this method periodically
   // if they want to use memory reclaimer.
-  // c.f. See also GetRecommendedReclaimInterval()'s comment.
+  // See also GetRecommendedReclaimIntervalInMicroseconds()'s comment.
   void ReclaimNormal();
 
   // Returns a recommended interval to invoke ReclaimNormal.
-  static constexpr base::TimeDelta GetRecommendedReclaimInterval() {
-    return Seconds(4);
+  int64_t GetRecommendedReclaimIntervalInMicroseconds() {
+    return Seconds(4).InMicroseconds();
   }
 
   // Triggers an explicit reclaim now reclaiming all free memory
diff --git a/base/allocator/partition_allocator/thread_cache.cc b/base/allocator/partition_allocator/thread_cache.cc
index 4b914949..fb40233e4 100644
--- a/base/allocator/partition_allocator/thread_cache.cc
+++ b/base/allocator/partition_allocator/thread_cache.cc
@@ -187,20 +187,6 @@
   }
 }
 
-void ThreadCacheRegistry::StartPeriodicPurge(
-    RunAfterDelayCallback run_after_delay_callback) {
-  ThreadCache::EnsureThreadSpecificDataInitialized();
-  PA_DCHECK(run_after_delay_callback);
-
-  // Can be called several times, don't post multiple tasks.
-  if (periodic_purge_running_)
-    return;
-
-  run_after_delay_callback_ = run_after_delay_callback;
-  periodic_purge_running_ = true;
-  PostDelayedPurgeTask();
-}
-
 void ThreadCacheRegistry::SetLargestActiveBucketIndex(
     uint8_t largest_active_bucket_index) {
   largest_active_bucket_index_ = largest_active_bucket_index;
@@ -237,16 +223,11 @@
   }
 }
 
-void ThreadCacheRegistry::PostDelayedPurgeTask() {
-  run_after_delay_callback_(base::BindOnce(&ThreadCacheRegistry::PeriodicPurge,
-                                           base::Unretained(this)),
-                            purge_interval_);
-}
-
-void ThreadCacheRegistry::PeriodicPurge() {
-  // To stop periodic purge for testing.
-  if (!periodic_purge_running_)
-    return;
+void ThreadCacheRegistry::RunPeriodicPurge() {
+  if (!periodic_purge_is_initialized_) {
+    ThreadCache::EnsureThreadSpecificDataInitialized();
+    periodic_purge_is_initialized_ = true;
+  }
 
   // Summing across all threads can be slow, but is necessary. Otherwise we rely
   // on the assumption that the current thread is a good proxy for overall
@@ -282,21 +263,26 @@
   // of a renderer moving to foreground. To mitigate that, if cached memory
   // jumps is very large, make a greater leap to faster purging.
   if (cached_memory_approx > 10 * kMinCachedMemoryForPurging) {
-    purge_interval_ = std::min(kDefaultPurgeInterval, purge_interval_ / 2);
+    periodic_purge_next_interval_ =
+        std::min(kDefaultPurgeInterval, periodic_purge_next_interval_ / 2);
   } else if (cached_memory_approx > 2 * kMinCachedMemoryForPurging) {
-    purge_interval_ = std::max(kMinPurgeInterval, purge_interval_ / 2);
+    periodic_purge_next_interval_ =
+        std::max(kMinPurgeInterval, periodic_purge_next_interval_ / 2);
   } else if (cached_memory_approx < kMinCachedMemoryForPurging) {
-    purge_interval_ = std::min(kMaxPurgeInterval, purge_interval_ * 2);
+    periodic_purge_next_interval_ =
+        std::min(kMaxPurgeInterval, periodic_purge_next_interval_ * 2);
   }
 
   PurgeAll();
+}
 
-  PostDelayedPurgeTask();
+int64_t ThreadCacheRegistry::GetPeriodicPurgeNextIntervalInMicroseconds()
+    const {
+  return periodic_purge_next_interval_.InMicroseconds();
 }
 
 void ThreadCacheRegistry::ResetForTesting() {
-  purge_interval_ = kDefaultPurgeInterval;
-  periodic_purge_running_ = false;
+  periodic_purge_next_interval_ = kDefaultPurgeInterval;
 }
 
 // static
diff --git a/base/allocator/partition_allocator/thread_cache.h b/base/allocator/partition_allocator/thread_cache.h
index c397314..86cbbde 100644
--- a/base/allocator/partition_allocator/thread_cache.h
+++ b/base/allocator/partition_allocator/thread_cache.h
@@ -118,15 +118,15 @@
   // a later point (during a deallocation).
   void PurgeAll();
 
-  typedef void (*RunAfterDelayCallback)(OnceClosure task,
-                                        base::TimeDelta delay);
-
-  // Starts purging all thread caches with the parameter:
-  // "run_after_delay_callback". The parameter: "run_after_delay_callback" takes
-  // 2 parameters: "purge_action" and "delay". ThreadCacheRegistry invokes
-  // "run_after_delay_clalback" to request the caller to execute "purge_action"
-  // after "delay" passes.
-  void StartPeriodicPurge(RunAfterDelayCallback run_afer_delay_callback);
+  // Runs `PurgeAll` and updates the next interval which
+  // `GetPeriodicPurgeNextIntervalInMicroseconds` returns.
+  //
+  // Note that it's a caller's responsibility to invoke this member function
+  // periodically with an appropriate interval. This function does not schedule
+  // any task nor timer.
+  void RunPeriodicPurge();
+  // Returns the appropriate interval to invoke `RunPeriodicPurge` next time.
+  int64_t GetPeriodicPurgeNextIntervalInMicroseconds() const;
 
   // Controls the thread cache size, by setting the multiplier to a value above
   // or below |ThreadCache::kDefaultMultiplier|.
@@ -138,8 +138,6 @@
   // should only be called in a post-fork() handler.
   void ForcePurgeAllThreadAfterForkUnsafe();
 
-  base::TimeDelta purge_interval_for_testing() const { return purge_interval_; }
-
   void ResetForTesting();
 
   static constexpr TimeDelta kMinPurgeInterval = Seconds(1);
@@ -149,16 +147,12 @@
 
  private:
   friend class tools::ThreadCacheInspector;
-
-  void PeriodicPurge();
-  void PostDelayedPurgeTask();
   friend class NoDestructor<ThreadCacheRegistry>;
   // Not using base::Lock as the object's constructor must be constexpr.
   PartitionLock lock_;
   ThreadCache* list_head_ GUARDED_BY(GetLock()) = nullptr;
-  base::TimeDelta purge_interval_ = kDefaultPurgeInterval;
-  bool periodic_purge_running_ = false;
-  RunAfterDelayCallback run_after_delay_callback_ = nullptr;
+  bool periodic_purge_is_initialized_ = false;
+  base::TimeDelta periodic_purge_next_interval_ = kDefaultPurgeInterval;
 
 #if defined(OS_NACL)
   // The thread cache is never used with NaCl, but its compiler doesn't
diff --git a/base/allocator/partition_allocator/thread_cache_unittest.cc b/base/allocator/partition_allocator/thread_cache_unittest.cc
index 716a5ec6..c500967 100644
--- a/base/allocator/partition_allocator/thread_cache_unittest.cc
+++ b/base/allocator/partition_allocator/thread_cache_unittest.cc
@@ -92,17 +92,6 @@
   return root;
 }
 
-OnceClosure g_purge_task;
-
-void DelayedAction(OnceClosure task, base::TimeDelta delay) {
-  // Need to invoke purge_action manually.
-  g_purge_task = std::move(task);
-}
-
-void PurgeManually() {
-  std::move(g_purge_task).Run();
-}
-
 }  // namespace
 
 class PartitionAllocThreadCacheTest : public ::testing::Test {
@@ -143,7 +132,6 @@
 
     ThreadCacheRegistry::Instance().ResetForTesting();
     tcache->ResetForTesting();
-    g_purge_task = base::OnceClosure();
   }
 
   size_t FillThreadCacheAndReturnIndex(size_t size, size_t count = 1) {
@@ -560,51 +548,46 @@
 
 TEST_F(PartitionAllocThreadCacheTest, PeriodicPurge) {
   auto& registry = ThreadCacheRegistry::Instance();
-  registry.StartPeriodicPurge(DelayedAction);
-  EXPECT_EQ(ThreadCacheRegistry::kDefaultPurgeInterval,
-            registry.purge_interval_for_testing());
+  auto NextInterval = [&registry]() {
+    return Microseconds(registry.GetPeriodicPurgeNextIntervalInMicroseconds());
+  };
+
+  EXPECT_EQ(NextInterval(), ThreadCacheRegistry::kDefaultPurgeInterval);
 
   // Small amount of memory, the period gets longer.
   auto* tcache = ThreadCache::Get();
   ASSERT_LT(tcache->CachedMemory(),
             ThreadCacheRegistry::kMinCachedMemoryForPurging);
-  PurgeManually();
-  EXPECT_EQ(2 * ThreadCacheRegistry::kDefaultPurgeInterval,
-            registry.purge_interval_for_testing());
-  PurgeManually();
-  EXPECT_EQ(4 * ThreadCacheRegistry::kDefaultPurgeInterval,
-            registry.purge_interval_for_testing());
+  registry.RunPeriodicPurge();
+  EXPECT_EQ(NextInterval(), 2 * ThreadCacheRegistry::kDefaultPurgeInterval);
+  registry.RunPeriodicPurge();
+  EXPECT_EQ(NextInterval(), 4 * ThreadCacheRegistry::kDefaultPurgeInterval);
 
   // Check that the purge interval is clamped at the maximum value.
-  while (registry.purge_interval_for_testing() <
-         ThreadCacheRegistry::kMaxPurgeInterval) {
-    PurgeManually();
+  while (NextInterval() < ThreadCacheRegistry::kMaxPurgeInterval) {
+    registry.RunPeriodicPurge();
   }
-  PurgeManually();
+  registry.RunPeriodicPurge();
 
   // Not enough memory to decrease the interval.
   FillThreadCacheWithMemory(ThreadCacheRegistry::kMinCachedMemoryForPurging +
                             1);
-  PurgeManually();
-  EXPECT_EQ(ThreadCacheRegistry::kMaxPurgeInterval,
-            registry.purge_interval_for_testing());
+  registry.RunPeriodicPurge();
+  EXPECT_EQ(NextInterval(), ThreadCacheRegistry::kMaxPurgeInterval);
 
   FillThreadCacheWithMemory(
       2 * ThreadCacheRegistry::kMinCachedMemoryForPurging + 1);
-  PurgeManually();
-  EXPECT_EQ(ThreadCacheRegistry::kMaxPurgeInterval / 2,
-            registry.purge_interval_for_testing());
+  registry.RunPeriodicPurge();
+  EXPECT_EQ(NextInterval(), ThreadCacheRegistry::kMaxPurgeInterval / 2);
 
   // Enough memory, interval doesn't change.
   FillThreadCacheWithMemory(ThreadCacheRegistry::kMinCachedMemoryForPurging);
-  PurgeManually();
-  EXPECT_EQ(ThreadCacheRegistry::kMaxPurgeInterval / 2,
-            registry.purge_interval_for_testing());
+  registry.RunPeriodicPurge();
+  EXPECT_EQ(NextInterval(), ThreadCacheRegistry::kMaxPurgeInterval / 2);
 
   // No cached memory, increase the interval.
-  PurgeManually();
-  EXPECT_EQ(ThreadCacheRegistry::kMaxPurgeInterval,
-            registry.purge_interval_for_testing());
+  registry.RunPeriodicPurge();
+  EXPECT_EQ(NextInterval(), ThreadCacheRegistry::kMaxPurgeInterval);
 
   // Cannot test the very large size with only one thread, this is tested below
   // in the multiple threads test.
@@ -614,34 +597,31 @@
 TEST_F(PartitionAllocThreadCacheTest,
        DISABLED_PeriodicPurgeSumsOverAllThreads) {
   auto& registry = ThreadCacheRegistry::Instance();
-  registry.StartPeriodicPurge(DelayedAction);
-  EXPECT_EQ(ThreadCacheRegistry::kDefaultPurgeInterval,
-            registry.purge_interval_for_testing());
+  auto NextInterval = [&registry]() {
+    return Microseconds(registry.GetPeriodicPurgeNextIntervalInMicroseconds());
+  };
+  EXPECT_EQ(NextInterval(), ThreadCacheRegistry::kDefaultPurgeInterval);
 
   // Small amount of memory, the period gets longer.
   auto* tcache = ThreadCache::Get();
   ASSERT_LT(tcache->CachedMemory(),
             ThreadCacheRegistry::kMinCachedMemoryForPurging);
-  PurgeManually();
-  EXPECT_EQ(2 * ThreadCacheRegistry::kDefaultPurgeInterval,
-            registry.purge_interval_for_testing());
-  PurgeManually();
-  EXPECT_EQ(4 * ThreadCacheRegistry::kDefaultPurgeInterval,
-            registry.purge_interval_for_testing());
+  registry.RunPeriodicPurge();
+  EXPECT_EQ(NextInterval(), 2 * ThreadCacheRegistry::kDefaultPurgeInterval);
+  registry.RunPeriodicPurge();
+  EXPECT_EQ(NextInterval(), 4 * ThreadCacheRegistry::kDefaultPurgeInterval);
 
   // Check that the purge interval is clamped at the maximum value.
-  while (registry.purge_interval_for_testing() <
-         ThreadCacheRegistry::kMaxPurgeInterval) {
-    PurgeManually();
+  while (NextInterval() < ThreadCacheRegistry::kMaxPurgeInterval) {
+    registry.RunPeriodicPurge();
   }
-  PurgeManually();
+  registry.RunPeriodicPurge();
 
   // Not enough memory on this thread to decrease the interval.
   FillThreadCacheWithMemory(ThreadCacheRegistry::kMinCachedMemoryForPurging /
                             2);
-  PurgeManually();
-  EXPECT_EQ(ThreadCacheRegistry::kMaxPurgeInterval,
-            registry.purge_interval_for_testing());
+  registry.RunPeriodicPurge();
+  EXPECT_EQ(NextInterval(), ThreadCacheRegistry::kMaxPurgeInterval);
 
   std::atomic<int> allocations_done{0};
   std::atomic<bool> can_finish{false};
@@ -661,12 +641,12 @@
   PlatformThread::Create(0, &delegate, &thread_handle_2);
 
   while (allocations_done.load(std::memory_order_acquire) != 2) {
+    PlatformThread::YieldCurrentThread();
   }
 
   // Many allocations on the other thread.
-  PurgeManually();
-  EXPECT_EQ(ThreadCacheRegistry::kDefaultPurgeInterval,
-            registry.purge_interval_for_testing());
+  registry.RunPeriodicPurge();
+  EXPECT_EQ(NextInterval(), ThreadCacheRegistry::kDefaultPurgeInterval);
 
   can_finish.store(true, std::memory_order_release);
   PlatformThread::Join(thread_handle);
diff --git a/base/containers/flat_tree.h b/base/containers/flat_tree.h
index ced0d22..0e6616f 100644
--- a/base/containers/flat_tree.h
+++ b/base/containers/flat_tree.h
@@ -6,12 +6,14 @@
 #define BASE_CONTAINERS_FLAT_TREE_H_
 
 #include <algorithm>
+#include <array>
+#include <initializer_list>
 #include <iterator>
 #include <type_traits>
 #include <utility>
-#include <vector>
 
 #include "base/as_const.h"
+#include "base/check.h"
 #include "base/compiler_specific.h"
 #include "base/cxx17_backports.h"
 #include "base/functional/not_fn.h"
diff --git a/base/containers/small_map.h b/base/containers/small_map.h
index 2dbb174..e8bf0771 100644
--- a/base/containers/small_map.h
+++ b/base/containers/small_map.h
@@ -10,7 +10,6 @@
 #include <limits>
 #include <map>
 #include <new>
-#include <unordered_map>
 #include <utility>
 
 #include "base/check_op.h"
diff --git a/base/memory/raw_ptr.h b/base/memory/raw_ptr.h
index 7777f44..de6259e 100644
--- a/base/memory/raw_ptr.h
+++ b/base/memory/raw_ptr.h
@@ -543,50 +543,11 @@
   }
   RAW_PTR_FUNC_ATTRIBUTES T* operator->() const { return GetForDereference(); }
 
-  // PendingMemberFunctionCall implements a temporary object returned by
-  // operator->*.  PendingMemberFunctionCall::operator() runs the member
-  // function taking arguments.
-  template <typename PMF,  // PMF = pointer to member function
-            typename ReturnType,
-            typename... ArgTypes>
-  class PendingMemberFunctionCall {
-   public:
-    explicit PendingMemberFunctionCall(T* receiver, PMF pmf)
-        : receiver_(receiver), pmf_(pmf) {}
-    // It's possible to support copy and move semantics, but we don't need them
-    // at this moment.
-    PendingMemberFunctionCall(const PendingMemberFunctionCall&) = delete;
-    PendingMemberFunctionCall& operator=(const PendingMemberFunctionCall&) =
-        delete;
-
-    ReturnType operator()(ArgTypes... args) const {
-      return (receiver_->*pmf_)(std::forward<ArgTypes>(args)...);
-    }
-
-   private:
-    T* receiver_;
-    PMF pmf_;
-  };
-
-  // Implements `(my_raw_ptr->*pmf)(arg1, arg2, ...)` as a workaround for
+  // Disables `(my_raw_ptr->*pmf)(...)` as a workaround for
+  // the ICE in GCC parsing the code, reported at
   // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103455
-  template <typename ReceiverType, typename ReturnType, typename... ArgTypes>
-  RAW_PTR_FUNC_ATTRIBUTES auto operator->*(
-      ReturnType (ReceiverType::*pmf)(ArgTypes...) const) const -> const
-      PendingMemberFunctionCall<decltype(pmf), ReturnType, ArgTypes...> {
-    Impl::IncrementPointerToMemberOperatorCountForTest();
-    return PendingMemberFunctionCall<decltype(pmf), ReturnType, ArgTypes...>(
-        GetForDereference(), pmf);
-  }
-  template <typename ReceiverType, typename ReturnType, typename... ArgTypes>
-  RAW_PTR_FUNC_ATTRIBUTES auto operator->*(
-      ReturnType (ReceiverType::*pmf)(ArgTypes...)) const -> const
-      PendingMemberFunctionCall<decltype(pmf), ReturnType, ArgTypes...> {
-    Impl::IncrementPointerToMemberOperatorCountForTest();
-    return PendingMemberFunctionCall<decltype(pmf), ReturnType, ArgTypes...>(
-        GetForDereference(), pmf);
-  }
-  // Ref-qualified variants should be added on demand.
+  template <typename PMF>
+  void operator->*(PMF) const = delete;
 
   // Deliberately implicit, because raw_ptr is supposed to resemble raw ptr.
   // NOLINTNEXTLINE(runtime/explicit)
diff --git a/base/memory/raw_ptr_unittest.cc b/base/memory/raw_ptr_unittest.cc
index 28e24c0..370ae46 100644
--- a/base/memory/raw_ptr_unittest.cc
+++ b/base/memory/raw_ptr_unittest.cc
@@ -892,90 +892,6 @@
   int MemFunc(float, double) { return 22; }
 };
 
-TEST_F(RawPtrTest, PointerToMemberFunction) {
-  PmfTestDerived object;
-  int (PmfTestBase::*pmf_base_base)(char, double) const = &PmfTestBase::MemFunc;
-  int (PmfTestDerived::*pmf_derived_base)(char, double) const =
-      &PmfTestDerived::MemFunc;
-  int (PmfTestDerived::*pmf_derived_derived)(float, double) =
-      &PmfTestDerived::MemFunc;
-  int base_count = g_pointer_to_member_operator_cnt;
-  EXPECT_EQ(base_count, 0);
-
-  // Test for `derived_ptr`
-  CountingRawPtr<PmfTestDerived> derived_ptr = &object;
-
-  static_assert(
-      std::is_same<decltype(derived_ptr->*pmf_base_base),
-                   const decltype(derived_ptr)::PendingMemberFunctionCall<
-                       decltype(pmf_base_base), int, char, double>>::value);
-  static_assert(
-      std::is_same<decltype(derived_ptr->*pmf_derived_base),
-                   const decltype(derived_ptr)::PendingMemberFunctionCall<
-                       decltype(pmf_derived_base), int, char, double>>::value);
-  static_assert(std::is_same<
-                decltype(derived_ptr->*pmf_derived_derived),
-                const decltype(derived_ptr)::PendingMemberFunctionCall<
-                    decltype(pmf_derived_derived), int, float, double>>::value);
-
-  base_count = g_pointer_to_member_operator_cnt;
-  EXPECT_EQ((derived_ptr->*pmf_base_base)(0, 0), 11);
-  EXPECT_EQ(g_pointer_to_member_operator_cnt, base_count + 1);
-  EXPECT_EQ((derived_ptr->*pmf_derived_base)(0, 0), 11);
-  EXPECT_EQ(g_pointer_to_member_operator_cnt, base_count + 2);
-  EXPECT_EQ((derived_ptr->*pmf_derived_derived)(0, 0), 22);
-  EXPECT_EQ(g_pointer_to_member_operator_cnt, base_count + 3);
-
-  // Test for `derived_ptr_const`
-  const CountingRawPtr<PmfTestDerived> derived_ptr_const = &object;
-
-  static_assert(
-      std::is_same<decltype(derived_ptr_const->*pmf_base_base),
-                   const decltype(derived_ptr_const)::PendingMemberFunctionCall<
-                       decltype(pmf_base_base), int, char, double>>::value);
-  static_assert(
-      std::is_same<decltype(derived_ptr_const->*pmf_derived_base),
-                   const decltype(derived_ptr_const)::PendingMemberFunctionCall<
-                       decltype(pmf_derived_base), int, char, double>>::value);
-  static_assert(std::is_same<
-                decltype(derived_ptr_const->*pmf_derived_derived),
-                const decltype(derived_ptr_const)::PendingMemberFunctionCall<
-                    decltype(pmf_derived_derived), int, float, double>>::value);
-
-  base_count = g_pointer_to_member_operator_cnt;
-  EXPECT_EQ((derived_ptr_const->*pmf_base_base)(0, 0), 11);
-  EXPECT_EQ(g_pointer_to_member_operator_cnt, base_count + 1);
-  EXPECT_EQ((derived_ptr_const->*pmf_derived_base)(0, 0), 11);
-  EXPECT_EQ(g_pointer_to_member_operator_cnt, base_count + 2);
-  EXPECT_EQ((derived_ptr_const->*pmf_derived_derived)(0, 0), 22);
-  EXPECT_EQ(g_pointer_to_member_operator_cnt, base_count + 3);
-
-  // Test for `const_derived_ptr`
-  CountingRawPtr<const PmfTestDerived> const_derived_ptr = &object;
-
-  static_assert(
-      std::is_same<decltype(const_derived_ptr->*pmf_base_base),
-                   const decltype(const_derived_ptr)::PendingMemberFunctionCall<
-                       decltype(pmf_base_base), int, char, double>>::value);
-  static_assert(
-      std::is_same<decltype(const_derived_ptr->*pmf_derived_base),
-                   const decltype(const_derived_ptr)::PendingMemberFunctionCall<
-                       decltype(pmf_derived_base), int, char, double>>::value);
-  static_assert(std::is_same<
-                decltype(const_derived_ptr->*pmf_derived_derived),
-                const decltype(const_derived_ptr)::PendingMemberFunctionCall<
-                    decltype(pmf_derived_derived), int, float, double>>::value);
-
-  base_count = g_pointer_to_member_operator_cnt;
-  EXPECT_EQ((const_derived_ptr->*pmf_base_base)(0, 0), 11);
-  EXPECT_EQ(g_pointer_to_member_operator_cnt, base_count + 1);
-  EXPECT_EQ((const_derived_ptr->*pmf_derived_base)(0, 0), 11);
-  EXPECT_EQ(g_pointer_to_member_operator_cnt, base_count + 2);
-  // Despite that it's possible to make a temporary PendingMemberFunctionCall
-  // object for the case of `const_derived_ptr->*pmf_derived_derived`, it's not
-  // possible to invoke the member function due to constness.
-}
-
 }  // namespace
 
 namespace base {
diff --git a/base/memory/raw_ptr_unittest.nc b/base/memory/raw_ptr_unittest.nc
index 48475c33..617bf27 100644
--- a/base/memory/raw_ptr_unittest.nc
+++ b/base/memory/raw_ptr_unittest.nc
@@ -17,6 +17,10 @@
 struct OtherDerivedProducer : Producer {};
 struct Unrelated {};
 struct DerivedUnrelated : Unrelated {};
+struct PmfTest {
+ public:
+  int Func(char, double) const { return 11; }
+};
 
 #if defined(NCTEST_AUTO_DOWNCAST)  // [r"no viable conversion from 'raw_ptr<\(anonymous namespace\)::Producer>' to 'raw_ptr<\(anonymous namespace\)::DerivedProducer>'"]
 
@@ -89,6 +93,16 @@
   std::ignore = raw_ptr_var.get();
 }
 
+#elif defined(NCTEST_POINTER_TO_MEMBER) // [r"overload resolution selected deleted operator '->\*'"]
+
+void WontCompile() {
+  PmfTest object;
+  int (PmfTest::*pmf_func)(char, double) const = &PmfTest::Func;
+
+  raw_ptr<PmfTest> object_ptr = &object;
+  std::ignore = object_ptr->*pmf_func;
+}
+
 #endif
 
 }  // namespace
diff --git a/base/memory/values_equivalent.h b/base/memory/values_equivalent.h
index c63c058..c5205d0 100644
--- a/base/memory/values_equivalent.h
+++ b/base/memory/values_equivalent.h
@@ -29,7 +29,7 @@
 // Example usage:
 //   struct Example {
 //     std::unique_ptr<Child> child;
-//     bool operator(const Example& other) {
+//     bool operator==(const Example& other) const {
 //       return base::ValuesEquivalent(child, other.child);
 //     }
 //   };
@@ -46,7 +46,7 @@
 //   namespace blink {
 //   struct Example : public GarbageCollected<Example> {
 //     Member<Child> child;
-//     bool operator(const Example& other) {
+//     bool operator==(const Example& other) const {
 //       return base::ValuesEquivalent(child, other.child);
 //     }
 //     void Trace(Visitor*) const;
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index b8d9eb3..8fdf017d 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -771,11 +771,7 @@
     bool result = CreateTrialsFromSwitchValue(switch_value, fd_key);
     UMA_HISTOGRAM_BOOLEAN("ChildProcess.FieldTrials.CreateFromShmemSuccess",
                           result);
-#if !defined(OS_WIN)
-    // TODO(https://crbug.com/1262370): This check is triggered in a utility
-    // process when running XR tests on Windows.
     DCHECK(result);
-#endif
   }
 #endif  // !defined(OS_NACL) && !defined(OS_IOS)
 
@@ -1139,12 +1135,20 @@
 
   std::stringstream ss;
 #if defined(OS_WIN)
+  // Elevated process might not need this, although it is harmless.
   launch_options->handles_to_inherit.push_back(shm.GetPlatformHandle());
 
   // Tell the child process the name of the inherited HANDLE.
   uintptr_t uintptr_handle =
       reinterpret_cast<uintptr_t>(shm.GetPlatformHandle());
   ss << uintptr_handle << ",";
+  if (launch_options->elevated) {
+    // Tell the child that it must open its parent and grab the handle.
+    ss << "p,";
+  } else {
+    // Tell the child that it inherited the handle.
+    ss << "i,";
+  }
 #elif defined(OS_MAC)
   launch_options->mach_ports_for_rendezvous.emplace(
       kFieldTrialRendezvousKey,
@@ -1153,6 +1157,8 @@
   // The handle on Mac is looked up directly by the child, rather than being
   // transferred to the child over the command line.
   ss << kFieldTrialRendezvousKey << ",";
+  // Tell the child that the handle is looked up.
+  ss << "r,";
 #elif defined(OS_FUCHSIA)
   zx::vmo transfer_vmo;
   zx_status_t status = shm.GetPlatformHandle()->duplicate(
@@ -1165,10 +1171,12 @@
   uint32_t handle_id = LaunchOptions::AddHandleToTransfer(
       &launch_options->handles_to_transfer, transfer_vmo.release());
   ss << handle_id << ",";
+  // Tell the child that the handle is inherited.
+  ss << "i,";
 #elif defined(OS_POSIX)
   // This is actually unused in the child process, but allows non-Mac Posix
   // platforms to have the same format as the others.
-  ss << "0,";
+  ss << "0,i,";
 #else
 #error Unsupported OS
 #endif
@@ -1184,18 +1192,24 @@
 FieldTrialList::DeserializeSharedMemoryRegionMetadata(
     const std::string& switch_value,
     int fd) {
+  // Format: "handle,[irp],guid-high,guid-low,size".
   std::vector<StringPiece> tokens =
       SplitStringPiece(switch_value, ",", KEEP_WHITESPACE, SPLIT_WANT_ALL);
 
-  if (tokens.size() != 4)
+  if (tokens.size() != 5)
     return ReadOnlySharedMemoryRegion();
 
   int field_trial_handle = 0;
   if (!StringToInt(tokens[0], &field_trial_handle))
     return ReadOnlySharedMemoryRegion();
+
+    // token[1] has a fixed value but is ignored on all platforms except
+    // Windows, where it can be 'i' or 'p' to indicate that the handle is
+    // inherited or must be obtained from the parent.
 #if defined(OS_WIN)
   HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle);
-  if (IsCurrentProcessElevated()) {
+  if (tokens[1] == "p") {
+    DCHECK(IsCurrentProcessElevated());
     // LaunchElevatedProcess doesn't have a way to duplicate the handle,
     // but this process can since by definition it's not sandboxed.
     ProcessId parent_pid = GetParentProcessId(GetCurrentProcess());
@@ -1206,6 +1220,8 @@
     DuplicateHandle(parent_handle, handle, GetCurrentProcess(), &handle, 0,
                     FALSE, DUPLICATE_SAME_ACCESS);
     CloseHandle(parent_handle);
+  } else if (tokens[1] != "i") {
+    return ReadOnlySharedMemoryRegion();
   }
   win::ScopedHandle scoped_handle(handle);
 #elif defined(OS_MAC)
@@ -1232,11 +1248,11 @@
 #endif
 
   UnguessableToken guid;
-  if (!DeserializeGUIDFromStringPieces(tokens[1], tokens[2], &guid))
+  if (!DeserializeGUIDFromStringPieces(tokens[2], tokens[3], &guid))
     return ReadOnlySharedMemoryRegion();
 
   int size;
-  if (!StringToInt(tokens[3], &size))
+  if (!StringToInt(tokens[4], &size))
     return ReadOnlySharedMemoryRegion();
 
   auto platform_handle = subtle::PlatformSharedMemoryRegion::Take(
diff --git a/base/numerics/checked_math_impl.h b/base/numerics/checked_math_impl.h
index 8249540..540bce5c2 100644
--- a/base/numerics/checked_math_impl.h
+++ b/base/numerics/checked_math_impl.h
@@ -51,8 +51,7 @@
   using result_type = typename MaxExponentPromotion<T, U>::type;
   template <typename V>
   static constexpr bool Do(T x, U y, V* result) {
-    // TODO(jschuh) Make this "constexpr if" once we're C++17.
-    if (CheckedAddFastOp<T, U>::is_supported)
+    if constexpr (CheckedAddFastOp<T, U>::is_supported)
       return CheckedAddFastOp<T, U>::Do(x, y, result);
 
     // Double the underlying type up to a full machine word.
@@ -115,8 +114,7 @@
   using result_type = typename MaxExponentPromotion<T, U>::type;
   template <typename V>
   static constexpr bool Do(T x, U y, V* result) {
-    // TODO(jschuh) Make this "constexpr if" once we're C++17.
-    if (CheckedSubFastOp<T, U>::is_supported)
+    if constexpr (CheckedSubFastOp<T, U>::is_supported)
       return CheckedSubFastOp<T, U>::Do(x, y, result);
 
     // Double the underlying type up to a full machine word.
@@ -181,8 +179,7 @@
   using result_type = typename MaxExponentPromotion<T, U>::type;
   template <typename V>
   static constexpr bool Do(T x, U y, V* result) {
-    // TODO(jschuh) Make this "constexpr if" once we're C++17.
-    if (CheckedMulFastOp<T, U>::is_supported)
+    if constexpr (CheckedMulFastOp<T, U>::is_supported)
       return CheckedMulFastOp<T, U>::Do(x, y, result);
 
     using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
diff --git a/base/numerics/clamped_math_impl.h b/base/numerics/clamped_math_impl.h
index 303a7e9..7af8407 100644
--- a/base/numerics/clamped_math_impl.h
+++ b/base/numerics/clamped_math_impl.h
@@ -105,8 +105,7 @@
   using result_type = typename MaxExponentPromotion<T, U>::type;
   template <typename V = result_type>
   static constexpr V Do(T x, U y) {
-    // TODO(jschuh) Make this "constexpr if" once we're C++17.
-    if (ClampedSubFastOp<T, U>::is_supported)
+    if constexpr (ClampedSubFastOp<T, U>::is_supported)
       return ClampedSubFastOp<T, U>::template Do<V>(x, y);
 
     static_assert(std::is_same<V, result_type>::value ||
@@ -132,8 +131,7 @@
   using result_type = typename MaxExponentPromotion<T, U>::type;
   template <typename V = result_type>
   static constexpr V Do(T x, U y) {
-    // TODO(jschuh) Make this "constexpr if" once we're C++17.
-    if (ClampedMulFastOp<T, U>::is_supported)
+    if constexpr (ClampedMulFastOp<T, U>::is_supported)
       return ClampedMulFastOp<T, U>::template Do<V>(x, y);
 
     V result = {};
diff --git a/base/process/launch.h b/base/process/launch.h
index 7695b64..9324befd 100644
--- a/base/process/launch.h
+++ b/base/process/launch.h
@@ -94,6 +94,9 @@
 #if defined(OS_WIN)
   bool start_hidden = false;
 
+  // Process will be started using a shell helper so that it is elevated.
+  bool elevated = false;
+
   // Sets STARTF_FORCEOFFFEEDBACK so that the feedback cursor is forced off
   // while the process is starting.
   bool feedback_cursor_off = false;
diff --git a/base/process/launch_win.cc b/base/process/launch_win.cc
index 4f1e44d7..d9ce154 100644
--- a/base/process/launch_win.cc
+++ b/base/process/launch_win.cc
@@ -214,6 +214,7 @@
 
 Process LaunchProcess(const CommandLine::StringType& cmdline,
                       const LaunchOptions& options) {
+  CHECK(!options.elevated);
   // Mitigate the issues caused by loading DLLs on a background thread
   // (http://crbug/973868).
   SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
@@ -409,6 +410,7 @@
 
 Process LaunchElevatedProcess(const CommandLine& cmdline,
                               const LaunchOptions& options) {
+  CHECK(options.elevated);
   const FilePath::StringType file = cmdline.GetProgram().value();
   const CommandLine::StringType arguments = cmdline.GetArgumentsString();
 
diff --git a/base/process/process_info.h b/base/process/process_info.h
index b392e88..c91a467 100644
--- a/base/process/process_info.h
+++ b/base/process/process_info.h
@@ -23,7 +23,9 @@
 // case of an underlying system failure.
 BASE_EXPORT IntegrityLevel GetCurrentProcessIntegrityLevel();
 
-// Determines whether the current process is elevated.
+// Determines whether the current process is elevated. Note: in some
+// configurations this may be true for processes launched without using
+// base::LaunchElevatedProcess().
 BASE_EXPORT bool IsCurrentProcessElevated();
 
 // Determines whether the current process is running within an App Container.
diff --git a/base/task/sequence_manager/task_queue_impl.cc b/base/task/sequence_manager/task_queue_impl.cc
index 4b1ec0e0..025cff1 100644
--- a/base/task/sequence_manager/task_queue_impl.cc
+++ b/base/task/sequence_manager/task_queue_impl.cc
@@ -163,12 +163,13 @@
 }
 
 DelayedTaskHandle TaskQueueImpl::TaskRunner::PostCancelableDelayedTask(
+    subtle::PostDelayedTaskPassKey pass_key,
     const Location& location,
     OnceClosure callback,
     TimeDelta delay) {
   if (!g_is_remove_canceled_tasks_in_task_queue_enabled) {
     return SequencedTaskRunner::PostCancelableDelayedTask(
-        location, std::move(callback), delay);
+        pass_key, location, std::move(callback), delay);
   }
 
   return task_poster_->PostCancelableTask(
diff --git a/base/task/sequence_manager/task_queue_impl.h b/base/task/sequence_manager/task_queue_impl.h
index 451dc0b..bb1d8ae 100644
--- a/base/task/sequence_manager/task_queue_impl.h
+++ b/base/task/sequence_manager/task_queue_impl.h
@@ -346,7 +346,8 @@
         OnceClosure callback,
         TimeTicks delayed_run_time,
         base::subtle::DelayPolicy delay_policy) final;
-    DelayedTaskHandle PostCancelableDelayedTask(const Location& location,
+    DelayedTaskHandle PostCancelableDelayedTask(subtle::PostDelayedTaskPassKey,
+                                                const Location& location,
                                                 OnceClosure callback,
                                                 TimeDelta delay) final;
     bool PostNonNestableDelayedTask(const Location& location,
diff --git a/base/task/sequence_manager/task_queue_unittest.cc b/base/task/sequence_manager/task_queue_unittest.cc
index 8e90d659..d1c6bc6 100644
--- a/base/task/sequence_manager/task_queue_unittest.cc
+++ b/base/task/sequence_manager/task_queue_unittest.cc
@@ -131,7 +131,8 @@
     bool task_ran = false;
     DelayedTaskHandle delayed_task_handle =
         task_runner->PostCancelableDelayedTask(
-            FROM_HERE, BindLambdaForTesting([&task_ran]() { task_ran = true; }),
+            subtle::PostDelayedTaskPassKeyForTesting(), FROM_HERE,
+            BindLambdaForTesting([&task_ran]() { task_ran = true; }),
             Seconds(20));
     EXPECT_EQ(queue->GetNumberOfPendingTasks(), 1u);
 
diff --git a/base/task/sequenced_task_runner.cc b/base/task/sequenced_task_runner.cc
index 307c2a9..41a1cf4 100644
--- a/base/task/sequenced_task_runner.cc
+++ b/base/task/sequenced_task_runner.cc
@@ -18,6 +18,7 @@
 }
 
 DelayedTaskHandle SequencedTaskRunner::PostCancelableDelayedTask(
+    subtle::PostDelayedTaskPassKey,
     const Location& from_here,
     OnceClosure task,
     TimeDelta delay) {
diff --git a/base/task/sequenced_task_runner.h b/base/task/sequenced_task_runner.h
index 0230fd6..0eb2788b 100644
--- a/base/task/sequenced_task_runner.h
+++ b/base/task/sequenced_task_runner.h
@@ -16,8 +16,20 @@
 #include "base/task/task_runner.h"
 #include "base/types/pass_key.h"
 
+namespace ash {
+class PSIMemoryMetrics;
+}
+
+namespace blink {
+class TimerBase;
+}
+
 namespace base {
 
+namespace internal {
+class TimerBase;
+}
+
 namespace subtle {
 
 // Used to restrict access to PostCancelableDelayedTaskAt() to authorize
@@ -27,6 +39,11 @@
   // Avoid =default to disallow creation by uniform initialization.
   PostDelayedTaskPassKey() {}
 
+  friend class base::internal::TimerBase;
+  friend class blink::TimerBase;
+  // TODO(pmonette): Remove this once PSIMemoryMetrics no longer uses
+  // PostCancelableDelayedTask.
+  friend class ash::PSIMemoryMetrics;
   friend class PostDelayedTaskPassKeyForTesting;
 };
 
@@ -138,7 +155,9 @@
                                           base::TimeDelta delay) = 0;
 
   // Posts the given |task| to be run only after |delay| has passed. Returns a
-  // handle that can be used to cancel the task.
+  // handle that can be used to cancel the task. This should not be used
+  // directly. Consider using higher level timer primitives in
+  // base/timer/timer.h.
   //
   // The handle is only valid while the task is pending execution. This means
   // that it will be invalid if the posting failed, and will be invalid while
@@ -147,14 +166,16 @@
   //
   // This method and the handle it returns are not thread-safe and can only be
   // used from the sequence this task runner runs its tasks on.
-  virtual DelayedTaskHandle PostCancelableDelayedTask(const Location& from_here,
-                                                      OnceClosure task,
-                                                      TimeDelta delay);
+  virtual DelayedTaskHandle PostCancelableDelayedTask(
+      subtle::PostDelayedTaskPassKey,
+      const Location& from_here,
+      OnceClosure task,
+      TimeDelta delay);
 
   // Posts the given |task| to be run at |delayed_run_time|, following
   // |delay_policy|. Returns a handle that can be used to cancel the task.
-  // This should not be used directly, consider using higher level base::Timer
-  // primitives.
+  // This should not be used directly. Consider using higher level timer
+  // primitives in base/timer/timer.h.
   WARN_UNUSED_RESULT virtual DelayedTaskHandle PostCancelableDelayedTaskAt(
       subtle::PostDelayedTaskPassKey,
       const Location& from_here,
diff --git a/base/task/sequenced_task_runner_unittest.cc b/base/task/sequenced_task_runner_unittest.cc
index 3d89c00..206c86a 100644
--- a/base/task/sequenced_task_runner_unittest.cc
+++ b/base/task/sequenced_task_runner_unittest.cc
@@ -112,8 +112,8 @@
   bool task_ran = false;
   DelayedTaskHandle delayed_task_handle =
       task_runner->PostCancelableDelayedTask(
-          FROM_HERE, BindLambdaForTesting([&task_ran]() { task_ran = true; }),
-          Seconds(1));
+          subtle::PostDelayedTaskPassKeyForTesting(), FROM_HERE,
+          BindLambdaForTesting([&task_ran]() { task_ran = true; }), Seconds(1));
   EXPECT_TRUE(delayed_task_handle.IsValid());
   EXPECT_TRUE(task_runner->HasPendingTask());
 
@@ -131,8 +131,8 @@
   bool task_ran = false;
   DelayedTaskHandle delayed_task_handle =
       task_runner->PostCancelableDelayedTask(
-          FROM_HERE, BindLambdaForTesting([&task_ran]() { task_ran = true; }),
-          Seconds(1));
+          subtle::PostDelayedTaskPassKeyForTesting(), FROM_HERE,
+          BindLambdaForTesting([&task_ran]() { task_ran = true; }), Seconds(1));
   EXPECT_TRUE(delayed_task_handle.IsValid());
   EXPECT_TRUE(task_runner->HasPendingTask());
 
@@ -150,8 +150,8 @@
   bool task_ran = false;
   DelayedTaskHandle delayed_task_handle =
       task_runner->PostCancelableDelayedTask(
-          FROM_HERE, BindLambdaForTesting([&task_ran]() { task_ran = true; }),
-          Seconds(1));
+          subtle::PostDelayedTaskPassKeyForTesting(), FROM_HERE,
+          BindLambdaForTesting([&task_ran]() { task_ran = true; }), Seconds(1));
   EXPECT_TRUE(delayed_task_handle.IsValid());
   EXPECT_TRUE(task_runner->HasPendingTask());
 
@@ -171,8 +171,8 @@
   bool task_ran = false;
   DelayedTaskHandle delayed_task_handle =
       task_runner->PostCancelableDelayedTask(
-          FROM_HERE, BindLambdaForTesting([&task_ran]() { task_ran = true; }),
-          Seconds(1));
+          subtle::PostDelayedTaskPassKeyForTesting(), FROM_HERE,
+          BindLambdaForTesting([&task_ran]() { task_ran = true; }), Seconds(1));
   EXPECT_FALSE(delayed_task_handle.IsValid());
   EXPECT_FALSE(task_ran);
 }
diff --git a/base/threading/thread_checker.cc b/base/threading/thread_checker.cc
new file mode 100644
index 0000000..8350e80
--- /dev/null
+++ b/base/threading/thread_checker.cc
@@ -0,0 +1,39 @@
+// Copyright 2022 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 "base/threading/thread_checker.h"
+
+#if DCHECK_IS_ON()
+#include <memory>
+#include <ostream>
+
+#include "base/check.h"
+#include "base/debug/stack_trace.h"
+#endif
+
+namespace base {
+
+#if DCHECK_IS_ON()
+ScopedValidateThreadChecker::ScopedValidateThreadChecker(
+    const ThreadChecker& checker) {
+  std::unique_ptr<debug::StackTrace> bound_at;
+  DCHECK(checker.CalledOnValidThread(&bound_at))
+      << (bound_at ? "\nWas attached to thread at:\n" + bound_at->ToString()
+                   : "");
+}
+
+ScopedValidateThreadChecker::ScopedValidateThreadChecker(
+    const ThreadChecker& checker,
+    const StringPiece& msg) {
+  std::unique_ptr<debug::StackTrace> bound_at;
+  DCHECK(checker.CalledOnValidThread(&bound_at))
+      << msg
+      << (bound_at ? "\nWas attached to thread at:\n" + bound_at->ToString()
+                   : "");
+}
+
+ScopedValidateThreadChecker::~ScopedValidateThreadChecker() = default;
+#endif  // DCHECK_IS_ON()
+
+}  // namespace base
diff --git a/base/threading/thread_checker.h b/base/threading/thread_checker.h
index b029b04..b3f91a47 100644
--- a/base/threading/thread_checker.h
+++ b/base/threading/thread_checker.h
@@ -5,16 +5,13 @@
 #ifndef BASE_THREADING_THREAD_CHECKER_H_
 #define BASE_THREADING_THREAD_CHECKER_H_
 
-#include "base/check.h"
+#include "base/base_export.h"
 #include "base/compiler_specific.h"
+#include "base/dcheck_is_on.h"
 #include "base/strings/string_piece.h"
 #include "base/thread_annotations.h"
 #include "base/threading/thread_checker_impl.h"
 
-#if DCHECK_IS_ON()
-#include "base/debug/stack_trace.h"
-#endif
-
 // ThreadChecker is a helper class used to help verify that some methods of a
 // class are called from the same thread (for thread-affinity).  It supports
 // thread safety annotations (see base/thread_annotations.h).
@@ -140,31 +137,19 @@
 #endif  // DCHECK_IS_ON()
 
 #if DCHECK_IS_ON()
-class SCOPED_LOCKABLE ScopedValidateThreadChecker {
+class BASE_EXPORT SCOPED_LOCKABLE ScopedValidateThreadChecker {
  public:
   explicit ScopedValidateThreadChecker(const ThreadChecker& checker)
-      EXCLUSIVE_LOCK_FUNCTION(checker) {
-    std::unique_ptr<debug::StackTrace> bound_at;
-    DCHECK(checker.CalledOnValidThread(&bound_at))
-        << (bound_at ? "\nWas attached to thread at:\n" + bound_at->ToString()
-                     : "");
-  }
-
-  explicit ScopedValidateThreadChecker(const ThreadChecker& checker,
-                                       const StringPiece& msg)
-      EXCLUSIVE_LOCK_FUNCTION(checker) {
-    std::unique_ptr<debug::StackTrace> bound_at;
-    DCHECK(checker.CalledOnValidThread(&bound_at))
-        << msg
-        << (bound_at ? "\nWas attached to thread at:\n" + bound_at->ToString()
-                     : "");
-  }
+      EXCLUSIVE_LOCK_FUNCTION(checker);
+  ScopedValidateThreadChecker(const ThreadChecker& checker,
+                              const StringPiece& msg)
+      EXCLUSIVE_LOCK_FUNCTION(checker);
 
   ScopedValidateThreadChecker(const ScopedValidateThreadChecker&) = delete;
   ScopedValidateThreadChecker& operator=(const ScopedValidateThreadChecker&) =
       delete;
 
-  ~ScopedValidateThreadChecker() UNLOCK_FUNCTION() {}
+  ~ScopedValidateThreadChecker() UNLOCK_FUNCTION();
 };
 #endif
 
diff --git a/base/timer/timer.cc b/base/timer/timer.cc
index 2172252..e5df43a 100644
--- a/base/timer/timer.cc
+++ b/base/timer/timer.cc
@@ -203,7 +203,7 @@
     delay = TimeDelta();
 
   delayed_task_handle_ = GetTaskRunner()->PostCancelableDelayedTask(
-      posted_from_,
+      base::subtle::PostDelayedTaskPassKey(), posted_from_,
       BindOnce(&TimerBase::OnScheduledTaskInvoked, Unretained(this),
                std::move(task_destruction_detector)),
       delay);
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h
index 928b4a9..41b0d05 100644
--- a/base/trace_event/builtin_categories.h
+++ b/base/trace_event/builtin_categories.h
@@ -346,7 +346,9 @@
       "viz.quads") "," TRACE_DISABLED_BY_DEFAULT("devtools.timeline.layers")) \
   X(TRACE_DISABLED_BY_DEFAULT("cc.debug.display_items") "," \
       TRACE_DISABLED_BY_DEFAULT("cc.debug.picture") "," \
-      TRACE_DISABLED_BY_DEFAULT("devtools.timeline.picture"))
+      TRACE_DISABLED_BY_DEFAULT("devtools.timeline.picture"))                 \
+  X(TRACE_DISABLED_BY_DEFAULT("v8.inspector") "," TRACE_DISABLED_BY_DEFAULT(  \
+      "v8.stack_trace"))
 
 #define INTERNAL_TRACE_INIT_CATEGORY_NAME(name) name,
 
diff --git a/build/config/fuchsia/generate_runner_scripts.gni b/build/config/fuchsia/generate_runner_scripts.gni
index 0c1fdc44..5e1e430 100644
--- a/build/config/fuchsia/generate_runner_scripts.gni
+++ b/build/config/fuchsia/generate_runner_scripts.gni
@@ -30,6 +30,10 @@
 
   # A list of additional Fuchsia boot images to include in the test isolates.
   fuchsia_additional_boot_images = []
+
+  # This variable controls the browser included in the Telemetry based test
+  # targets.
+  fuchsia_browser_type = "web_engine_shell"
 }
 
 # Generates a wrapper script under root_build_dir/bin that performs an
@@ -230,7 +234,14 @@
     }
 
     if (defined(invoker.use_test_server) && invoker.use_test_server) {
-      executable_args += [ "--enable-test-server" ]
+      # TODO(crbug.com/1254563): Tests that needs the test server need access
+      # to the network and run-test-component doesn't support it. Provide the
+      # necessary access via capability routing once the tests are running via
+      # CFv2.
+      executable_args += [
+        "--enable-test-server",
+        "--use-run",
+      ]
     }
 
     if (defined(invoker.use_cfv2) && invoker.use_cfv2) {
diff --git a/build/config/locales.gni b/build/config/locales.gni
index 1d57b877..b1d41fe 100644
--- a/build/config/locales.gni
+++ b/build/config/locales.gni
@@ -162,7 +162,11 @@
 ]
 
 # New locales added to ChromeOS builds.
-chromeos_only_locales = [ "is" ]
+chromeos_only_locales = [
+  "af",
+  "is",
+  "zu",
+]
 
 if (is_android) {
   locales = all_chrome_locales
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index eac3c75..f017fdc6 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-7.20220110.2.1
+7.20220111.2.2
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index fdbcbf9..96c8be9 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-7.20220110.2.3
+7.20220111.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index eac3c75..d22b94c3 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7.20220110.2.1
+7.20220111.2.3
diff --git a/build/fuchsia/test_runner.py b/build/fuchsia/test_runner.py
index 66fb65dc8..bcede99e 100755
--- a/build/fuchsia/test_runner.py
+++ b/build/fuchsia/test_runner.py
@@ -222,8 +222,15 @@
     assert not args.code_coverage
 
   if args.code_coverage and not args.use_run_test_component:
-    raise ValueError('Collecting code coverage info requires using '
-                     'run-test-component.')
+    if args.enable_test_server:
+      # TODO(1254563): Tests that need access to the test server cannot be run
+      # as test component under CFv1. Because code coverage requires it, force
+      # the test to run as a test component. It is expected that test that tries
+      # to use the external test server will fail.
+      args.use_run_test_component = False
+    else:
+      raise ValueError('Collecting code coverage info requires using '
+                       'run-test-component.')
 
   ConfigureLogging(args)
 
diff --git a/cc/base/features.cc b/cc/base/features.cc
index 5092d787..3a52f7b 100644
--- a/cc/base/features.cc
+++ b/cc/base/features.cc
@@ -33,7 +33,7 @@
 // submitting a frame.
 const base::Feature kSynchronizedScrolling = {
     "SynchronizedScrolling",
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     base::FEATURE_DISABLED_BY_DEFAULT};
 #else
     base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/cc/input/snap_fling_curve.cc b/cc/input/snap_fling_curve.cc
index 8ca9c17..e4b9353 100644
--- a/cc/input/snap_fling_curve.cc
+++ b/cc/input/snap_fling_curve.cc
@@ -11,7 +11,7 @@
 namespace cc {
 namespace {
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 constexpr double kDistanceEstimatorScalar = 40;
 // The delta to be scrolled in next frame is 0.9 of the delta in last frame.
 constexpr double kRatio = 0.9;
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index d90e33e..1a39316 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -102,7 +102,7 @@
   return stream.str();
 }
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 struct MetricsDrawSizes {
   const int kTopPadding = 35;
   const int kPadding = 15;
diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc
index c4671a9..cad4c15b 100644
--- a/cc/layers/texture_layer_unittest.cc
+++ b/cc/layers/texture_layer_unittest.cc
@@ -25,6 +25,7 @@
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "cc/animation/animation_host.h"
 #include "cc/layers/solid_color_layer.h"
 #include "cc/layers/texture_layer_client.h"
@@ -1110,7 +1111,7 @@
 };
 
 // TODO(crbug.com/1197350): Test fails on chromeos-amd64-generic-rel.
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
 #define MAYBE_SINGLE_AND_MULTI_THREAD_TEST_F MULTI_THREAD_TEST_F
 #else
 #define MAYBE_SINGLE_AND_MULTI_THREAD_TEST_F SINGLE_AND_MULTI_THREAD_TEST_F
diff --git a/cc/metrics/dropped_frame_counter.cc b/cc/metrics/dropped_frame_counter.cc
index 6a8ae92..a2d0a15 100644
--- a/cc/metrics/dropped_frame_counter.cc
+++ b/cc/metrics/dropped_frame_counter.cc
@@ -75,6 +75,11 @@
   for (size_t i = 0; i < bin_count; ++i) {
     sum += histogram_bins_[i] * i;
   }
+
+  // Don't calculate if count is 1 or less. Avoid divide by zero.
+  if (total_count_ <= 1)
+    return 0;
+
   double average = sum / total_count_;
   sum = 0;  // Sum is reset to be used for variance calculation
 
@@ -85,8 +90,6 @@
     // histogram_bins_[i] times.
   }
 
-  if (total_count_ <= 1)
-    return 0;
   return sum / (total_count_ - 1);
 }
 
diff --git a/cc/metrics/frame_info.cc b/cc/metrics/frame_info.cc
index 6d03d2f..00d12e8 100644
--- a/cc/metrics/frame_info.cc
+++ b/cc/metrics/frame_info.cc
@@ -57,7 +57,7 @@
 }
 
 void FrameInfo::MergeWith(const FrameInfo& other) {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // TODO(1278168): on android-webview, multiple frames can be submitted against
   // the same BeginFrameArgs. This can trip the DCHECK()s in this function.
   if (was_merged)
diff --git a/cc/mojo_embedder/async_layer_tree_frame_sink.cc b/cc/mojo_embedder/async_layer_tree_frame_sink.cc
index 5bebaf72..522c1a6 100644
--- a/cc/mojo_embedder/async_layer_tree_frame_sink.cc
+++ b/cc/mojo_embedder/async_layer_tree_frame_sink.cc
@@ -51,7 +51,7 @@
                          params->gpu_memory_buffer_manager),
       synthetic_begin_frame_source_(
           std::move(params->synthetic_begin_frame_source)),
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
       io_thread_id_(params->io_thread_id),
 #endif
       pipes_(std::move(params->pipes)),
@@ -100,7 +100,7 @@
   compositor_frame_sink_ptr_->InitializeCompositorFrameSinkType(
       viz::mojom::CompositorFrameSinkType::kLayerTree);
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   std::vector<int32_t> thread_ids;
   thread_ids.push_back(base::PlatformThread::CurrentId());
   if (io_thread_id_ != base::kInvalidThreadId)
diff --git a/cc/mojo_embedder/async_layer_tree_frame_sink.h b/cc/mojo_embedder/async_layer_tree_frame_sink.h
index 332df8f..53a1e93 100644
--- a/cc/mojo_embedder/async_layer_tree_frame_sink.h
+++ b/cc/mojo_embedder/async_layer_tree_frame_sink.h
@@ -124,7 +124,7 @@
   viz::LocalSurfaceId local_surface_id_;
   std::unique_ptr<viz::ExternalBeginFrameSource> begin_frame_source_;
   std::unique_ptr<viz::SyntheticBeginFrameSource> synthetic_begin_frame_source_;
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   base::PlatformThreadId io_thread_id_;
 #endif
 
diff --git a/cc/mojom/render_frame_metadata_mojom_traits.cc b/cc/mojom/render_frame_metadata_mojom_traits.cc
index 0bcadca..17ce686 100644
--- a/cc/mojom/render_frame_metadata_mojom_traits.cc
+++ b/cc/mojom/render_frame_metadata_mojom_traits.cc
@@ -35,7 +35,7 @@
   out->external_page_scale_factor = data.external_page_scale_factor();
   out->top_controls_height = data.top_controls_height();
   out->top_controls_shown_ratio = data.top_controls_shown_ratio();
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   out->bottom_controls_height = data.bottom_controls_height();
   out->bottom_controls_shown_ratio = data.bottom_controls_shown_ratio();
   out->top_controls_min_height_offset = data.top_controls_min_height_offset();
@@ -49,7 +49,7 @@
   return data.ReadRootScrollOffset(&out->root_scroll_offset) &&
          data.ReadSelection(&out->selection) &&
          data.ReadDelegatedInkMetadata(&out->delegated_ink_metadata) &&
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
          data.ReadScrollableViewportSize(&out->scrollable_viewport_size) &&
          data.ReadRootLayerSize(&out->root_layer_size) &&
 #endif
diff --git a/cc/mojom/render_frame_metadata_mojom_traits.h b/cc/mojom/render_frame_metadata_mojom_traits.h
index ffacfc9..a695c72 100644
--- a/cc/mojom/render_frame_metadata_mojom_traits.h
+++ b/cc/mojom/render_frame_metadata_mojom_traits.h
@@ -101,7 +101,7 @@
     return metadata.visual_properties_update_duration;
   }
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   static float bottom_controls_height(const cc::RenderFrameMetadata& metadata) {
     return metadata.bottom_controls_height;
   }
diff --git a/cc/paint/oop_pixeltest.cc b/cc/paint/oop_pixeltest.cc
index 08df0a2..ef52ee3 100644
--- a/cc/paint/oop_pixeltest.cc
+++ b/cc/paint/oop_pixeltest.cc
@@ -50,7 +50,7 @@
 #include "ui/gfx/geometry/skia_conversions.h"
 #include "ui/gl/gl_implementation.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include "base/android/build_info.h"
 #endif
 
@@ -1831,7 +1831,7 @@
     // and distinctly different from using the wrong glyph or text params.
     float error_pixels_percentage = 0.f;
     int max_abs_error = 0;
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     // The nexus5 and nexus5x bots are particularly susceptible to small changes
     // when bilerping an image (not visible).
     const int sdk = base::android::BuildInfo::GetInstance()->sdk_int();
@@ -2314,7 +2314,7 @@
 // A workaround on Android that forces the use of GLES 2.0 instead of 3.0
 // prevents the use of the GL_RG textures required for NV12 format. This
 // test will be reactiviated on Android once the workaround is removed.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 TEST_F(OopPixelTest, ConvertNV12ToRGB) {
   RasterOptions options(gfx::Size(16, 16));
   RasterOptions uv_options(gfx::Size(options.resource_size.width() / 2,
@@ -2388,7 +2388,7 @@
   sii->DestroySharedImage(sync_token, y_uv_mailboxes[0]);
   sii->DestroySharedImage(sync_token, y_uv_mailboxes[1]);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 class OopPathPixelTest : public OopPixelTest,
                          public ::testing::WithParamInterface<bool> {
diff --git a/cc/paint/paint_filter.cc b/cc/paint/paint_filter.cc
index 5ebd057..8dd7b218 100644
--- a/cc/paint/paint_filter.cc
+++ b/cc/paint/paint_filter.cc
@@ -30,7 +30,7 @@
 namespace {
 const bool kHasNoDiscardableImages = false;
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 struct StretchShaderUniforms {
   // multiplier to apply to scale effect
   float uMaxStretchIntensity;
@@ -1578,7 +1578,7 @@
       width_(width),
       height_(height),
       input_(std::move(input)) {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   float normOverScrollDistX = stretch_x_;
   float normOverScrollDistY = stretch_y_;
   float distanceStretchedX =
@@ -1609,10 +1609,10 @@
   sk_sp<SkData> uniformVals = SkData::MakeWithCopy(&uniforms, sizeof(uniforms));
   cached_sk_filter_ = SkMakeRuntimeImageFilter(getStretchEffect(), uniformVals,
                                                GetSkFilter(input_.get()));
-#else   // defined(OS_ANDROID)
+#else   // BUILDFLAG(IS_ANDROID)
   // Stretch filter is only used on android and removed from other platforms
   // to reduce size. See https://crbug.com/1226170.
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 }
 
 StretchPaintFilter::~StretchPaintFilter() = default;
diff --git a/cc/paint/solid_color_analyzer.cc b/cc/paint/solid_color_analyzer.cc
index f230bd9..02d23cb 100644
--- a/cc/paint/solid_color_analyzer.cc
+++ b/cc/paint/solid_color_analyzer.cc
@@ -81,12 +81,12 @@
       !flags.getLooper() && !flags.getMaskFilter() && !flags.getColorFilter() &&
       !flags.getImageFilter() && flags.getStyle() == PaintFlags::kFill_Style;
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   // Additionally, on Mac, we require that the color is opaque due to
   // https://crbug.com/922899.
   // TODO(andrescj): remove this condition once that bug is fixed.
   is_solid_color = (is_solid_color && SkColorGetA(flags.getColor()) == 255);
-#endif  // OS_MAC
+#endif  // BUILDFLAG(IS_MAC)
 
   return is_solid_color;
 }
@@ -174,12 +174,12 @@
   bool solid_color_candidate =
       does_cover_canvas && IsSolidColorBlendMode(blendmode);
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   // Additionally, on Mac, we require that the color is opaque due to
   // https://crbug.com/922899.
   // TODO(andrescj): remove this condition once that bug is fixed.
   solid_color_candidate = (solid_color_candidate && alpha == 255);
-#endif  // OS_MAC
+#endif  // BUILDFLAG(IS_MAC)
 
   if (solid_color_candidate) {
     CalculateSolidColor(color /* src_color */, blendmode,
diff --git a/cc/paint/solid_color_analyzer_unittest.cc b/cc/paint/solid_color_analyzer_unittest.cc
index 217f915f..a2bec2e 100644
--- a/cc/paint/solid_color_analyzer_unittest.cc
+++ b/cc/paint/solid_color_analyzer_unittest.cc
@@ -97,13 +97,13 @@
   Initialize();
   SkColor color = SkColorSetARGB(128, 11, 22, 33);
   canvas()->clear(color);
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   // TODO(andrescj): remove the special treatment of OS_MAC once
   // https://crbug.com/922899 is fixed.
   EXPECT_FALSE(IsSolidColor());
 #else
   EXPECT_EQ(color, GetColor());
-#endif  // OS_MAC
+#endif  // BUILDFLAG(IS_MAC)
 }
 
 TEST_F(SolidColorAnalyzerTest, DrawColor) {
@@ -295,13 +295,13 @@
   flags.setColor(color);
   SkRect rect = SkRect::MakeWH(100, 100);
   canvas()->drawRect(rect, flags);
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   // TODO(andrescj): remove the special treatment of OS_MAC once
   // https://crbug.com/922899 is fixed.
   EXPECT_FALSE(IsSolidColor());
 #else
   EXPECT_EQ(color, GetColor());
-#endif  // OS_MAC
+#endif  // BUILDFLAG(IS_MAC)
 }
 
 TEST_F(SolidColorAnalyzerTest, DrawRectTranslucentOverNonSolid) {
@@ -343,14 +343,14 @@
   flags.setColor(color);
   rect = SkRect::MakeWH(100, 100);
   canvas()->drawRect(rect, flags);
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   // TODO(andrescj): remove the special treatment of OS_MAC once
   // https://crbug.com/922899 is fixed.
   EXPECT_FALSE(IsSolidColor());
 #else
   EXPECT_EQ(SkColorSetARGB(159, 15, 25, 35),
             GetColor(2 /* max_ops_to_analyze */));
-#endif  // OS_MAC
+#endif  // BUILDFLAG(IS_MAC)
 }
 
 TEST_F(SolidColorAnalyzerTest, SaveLayer) {
diff --git a/cc/raster/scoped_gpu_raster.cc b/cc/raster/scoped_gpu_raster.cc
index 2d8a5f9..c02a29ec 100644
--- a/cc/raster/scoped_gpu_raster.cc
+++ b/cc/raster/scoped_gpu_raster.cc
@@ -33,7 +33,7 @@
   // arguments even when tracing is disabled.
   gl->TraceBeginCHROMIUM("ScopedGpuRaster", "GpuRasterization");
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // TODO(crbug.com/832810): The following reset should not be necessary.
   GrDirectContext* gr_context = context_provider_->GrContext();
   gr_context->resetContext();
diff --git a/cc/resources/ui_resource_bitmap.cc b/cc/resources/ui_resource_bitmap.cc
index b4982df..ca32943d 100644
--- a/cc/resources/ui_resource_bitmap.cc
+++ b/cc/resources/ui_resource_bitmap.cc
@@ -75,7 +75,7 @@
   DCHECK(skbitmap.isImmutable());
 
   const SkBitmap* target = &skbitmap;
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   SkBitmap copy;
   if (features::IsDrDcEnabled()) {
     // TODO(vikassoni): Forcing everything to N32 while android backing cannot
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index 4b9ddbc..6f02e20 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -661,7 +661,7 @@
     // SwiftShader is a multi-threaded renderer and TSAN takes a lot longer to
     // run tests when using SwiftShader
     timeout_seconds_ = 35;
-#elif defined(OS_WIN) && defined(_DEBUG)
+#elif BUILDFLAG(IS_WIN) && defined(_DEBUG)
     // Debug builds on Windows are much slower than on other platforms, possibly
     // because Windows uses separate debug versions of the C Run-Time Library
     // for debug builds, whereas other platforms use the same system libraries
@@ -693,9 +693,9 @@
     init_vulkan = true;
   } else if (renderer_type_ == viz::RendererType::kSkiaDawn) {
     scoped_feature_list_.InitAndEnableFeature(features::kSkiaDawn);
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     init_vulkan = true;
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
     // TODO(rivr): Initialize D3D12 for Windows.
 #else
     NOTREACHED();
diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h
index 07d5acd6..b15a738 100644
--- a/cc/test/layer_tree_test.h
+++ b/cc/test/layer_tree_test.h
@@ -225,7 +225,7 @@
     return renderer_type_ == viz::RendererType::kSkiaVk;
   }
   bool use_d3d12() const {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     return renderer_type_ == viz::RendererType::kSkiaDawn;
 #else
     return false;
diff --git a/cc/test/pixel_test.cc b/cc/test/pixel_test.cc
index 62dedeef..3b86a15 100644
--- a/cc/test/pixel_test.cc
+++ b/cc/test/pixel_test.cc
@@ -71,9 +71,9 @@
     init_vulkan = true;
   } else if (backend == kSkiaDawn) {
     scoped_feature_list_.InitAndEnableFeature(features::kSkiaDawn);
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     init_vulkan = true;
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
     // TODO(rivr): Initialize D3D12 for Windows.
 #else
     NOTREACHED();
diff --git a/cc/tiles/image_decode_cache_utils.h b/cc/tiles/image_decode_cache_utils.h
index c8adfd1..6bfac7492 100644
--- a/cc/tiles/image_decode_cache_utils.h
+++ b/cc/tiles/image_decode_cache_utils.h
@@ -9,7 +9,7 @@
 #include "cc/paint/paint_flags.h"
 #include "third_party/skia/include/core/SkPixmap.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include "base/android/build_info.h"
 #endif
 
@@ -18,7 +18,7 @@
 class ImageDecodeCacheUtils {
  public:
   static bool CanResizeF16Image(PaintFlags::FilterQuality filter_quality) {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     // Return false on Android KitKat or lower if filter quality is medium or
     // high (hence, mipmaps are used), return true otherwise. This is because
     // of skia:8410 which causes a crash when trying to scale a f16 image on
diff --git a/cc/tiles/picture_layer_tiling_set_unittest.cc b/cc/tiles/picture_layer_tiling_set_unittest.cc
index 01ff1e3..7172b4a 100644
--- a/cc/tiles/picture_layer_tiling_set_unittest.cc
+++ b/cc/tiles/picture_layer_tiling_set_unittest.cc
@@ -319,7 +319,7 @@
 }
 
 // Test is flaky: https://crbug.com/1056828.
-#if defined(OS_LINUX) && defined(THREAD_SANITIZER)
+#if BUILDFLAG(IS_LINUX) && defined(THREAD_SANITIZER)
 #define MAYBE_ManyTilings_Equal DISABLED_ManyTilings_Equal
 #else
 #define MAYBE_ManyTilings_Equal ManyTilings_Equal
@@ -329,7 +329,7 @@
 }
 
 // Test is flaky: https://crbug.com/1056828.
-#if defined(OS_LINUX) && defined(THREAD_SANITIZER)
+#if BUILDFLAG(IS_LINUX) && defined(THREAD_SANITIZER)
 #define MAYBE_ManyTilings_NotEqual DISABLED_ManyTilings_NotEqual
 #else
 #define MAYBE_ManyTilings_NotEqual ManyTilings_NotEqual
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index 2b8ad609..f59be36 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -1126,7 +1126,7 @@
     ElementId overscroll_elasticity_effect_element_id,
     const gfx::Vector2dF& elastic_overscroll,
     const ScrollNode* inner_viewport) {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // On android, elastic overscroll is implemented by stretching the content
   // from the overscrolled edge.
   if (!overscroll_elasticity_effect_element_id &&
@@ -1184,7 +1184,7 @@
   }
   overscroll_elasticity_transform_node->needs_local_transform_update = true;
   property_trees->transform_tree.set_needs_update(true);
-#else  // defined(OS_ANDROID)
+#else  // BUILDFLAG(IS_ANDROID)
   if (!overscroll_elasticity_transform_node) {
     DCHECK(elastic_overscroll.IsZero());
     return;
@@ -1202,7 +1202,7 @@
   overscroll_elasticity_transform_node->needs_local_transform_update = true;
   property_trees->transform_tree.set_needs_update(true);
 
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 }
 
 void ComputeDrawPropertiesOfVisibleLayers(const LayerImplList* layer_list,
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index b012b3b8..8d6c612c 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2391,7 +2391,7 @@
       browser_controls_offset_manager_->TopControlsHeight();
   metadata.top_controls_shown_ratio =
       browser_controls_offset_manager_->TopControlsShownRatio();
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   metadata.bottom_controls_height =
       browser_controls_offset_manager_->BottomControlsHeight();
   metadata.bottom_controls_shown_ratio =
@@ -2442,7 +2442,7 @@
   }
 
   bool allocate_new_local_surface_id =
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
       last_draw_render_frame_metadata_ &&
       (last_draw_render_frame_metadata_->top_controls_height !=
            metadata.top_controls_height ||
@@ -3480,10 +3480,10 @@
 
     // TODO(crbug.com/1189208): Unlocking decoded-image-tracker images causes
     // flickering in visible trees if Out-Of-Process rasterization is enabled.
-#if defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_FUCHSIA)
   if (use_oop_rasterization() && visible())
     return;
-#endif  // defined(OS_FUCHSIA)
+#endif  // BUILDFLAG(IS_FUCHSIA)
 
   ReleaseTileResources();
   active_tree_->OnPurgeMemory();
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 2e66cb2..05f9cf4 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -11890,7 +11890,7 @@
       ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr));
 }
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 TEST_P(ScrollUnifiedLayerTreeHostImplTest,
        SelectionBoundsPassedToCompositorFrameMetadata) {
   LayerImpl* root = SetupRootLayer<SolidColorLayerImpl>(
@@ -11955,7 +11955,7 @@
   EXPECT_FALSE(selection_after.start.visible());
   EXPECT_FALSE(selection_after.end.visible());
 }
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
 TEST_P(ScrollUnifiedLayerTreeHostImplTest, SimpleSwapPromiseMonitor) {
   {
@@ -16549,7 +16549,7 @@
     EXPECT_EQ(gfx::PointF(), metadata.root_scroll_offset);
     EXPECT_EQ(1, metadata.page_scale_factor);
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     EXPECT_EQ(gfx::SizeF(50, 50), metadata.scrollable_viewport_size);
     EXPECT_EQ(0.5f, metadata.min_page_scale_factor);
     EXPECT_EQ(4, metadata.max_page_scale_factor);
@@ -16579,7 +16579,7 @@
     EXPECT_EQ(gfx::PointF(0, 10), metadata.root_scroll_offset);
   }
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // Root "overflow: hidden" properties should be reflected on the outer
   // viewport scroll layer.
   {
@@ -16644,7 +16644,7 @@
     EXPECT_EQ(gfx::PointF(0, 10), metadata.root_scroll_offset);
     EXPECT_EQ(2, metadata.page_scale_factor);
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     EXPECT_EQ(gfx::SizeF(25, 25), metadata.scrollable_viewport_size);
     EXPECT_EQ(0.5f, metadata.min_page_scale_factor);
     EXPECT_EQ(4, metadata.max_page_scale_factor);
@@ -16662,7 +16662,7 @@
     EXPECT_EQ(gfx::PointF(0, 10), metadata.root_scroll_offset);
     EXPECT_EQ(4, metadata.page_scale_factor);
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     EXPECT_EQ(gfx::SizeF(12.5f, 12.5f), metadata.scrollable_viewport_size);
     EXPECT_EQ(0.5f, metadata.min_page_scale_factor);
     EXPECT_EQ(4, metadata.max_page_scale_factor);
diff --git a/cc/trees/layer_tree_host_perftest.cc b/cc/trees/layer_tree_host_perftest.cc
index 963e4ffd..4f500ac 100644
--- a/cc/trees/layer_tree_host_perftest.cc
+++ b/cc/trees/layer_tree_host_perftest.cc
@@ -166,7 +166,7 @@
 
 // Simulates a tab switcher scene with two stacks of 10 tabs each.
 // Timed out on Android: http://crbug.com/723821
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #define MAYBE_TenTenSingleThread DISABLED_TenTenSingleThread
 #else
 #define MAYBE_TenTenSingleThread TenTenSingleThread
@@ -178,7 +178,7 @@
 }
 
 // Timed out on Android: http://crbug.com/723821
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #define MAYBE_TenTenThreaded DISABLED_TenTenThreaded
 #else
 #define MAYBE_TenTenThreaded TenTenThreaded
@@ -272,7 +272,7 @@
 };
 
 // Timed out on Android: http://crbug.com/723821
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #define MAYBE_LongScrollablePageSingleThread \
     DISABLED_LongScrollablePageSingleThread
 #else
@@ -285,7 +285,7 @@
 }
 
 // Timed out on Android: http://crbug.com/723821
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #define MAYBE_LongScrollablePageThreaded DISABLED_LongScrollablePageThreaded
 #else
 #define MAYBE_LongScrollablePageThreaded LongScrollablePageThreaded
@@ -389,7 +389,7 @@
 
 // Simulates a page with several large, transformed and animated layers.
 // Timed out on Android: http://crbug.com/723821
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #define MAYBE_HeavyPageThreaded DISABLED_HeavyPageThreaded
 #else
 #define MAYBE_HeavyPageThreaded HeavyPageThreaded
diff --git a/cc/trees/layer_tree_host_pixeltest_blending.cc b/cc/trees/layer_tree_host_pixeltest_blending.cc
index 66259f84..14f0041 100644
--- a/cc/trees/layer_tree_host_pixeltest_blending.cc
+++ b/cc/trees/layer_tree_host_pixeltest_blending.cc
@@ -20,7 +20,7 @@
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkSurface.h"
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 
 namespace cc {
 namespace {
diff --git a/cc/trees/layer_tree_host_pixeltest_filters.cc b/cc/trees/layer_tree_host_pixeltest_filters.cc
index 2e2bd96..27ac7d65 100644
--- a/cc/trees/layer_tree_host_pixeltest_filters.cc
+++ b/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -15,7 +15,7 @@
 #include "cc/test/pixel_comparator.h"
 #include "cc/test/solid_color_content_layer_client.h"
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 
 namespace cc {
 namespace {
@@ -111,7 +111,7 @@
   gfx::RRectF backdrop_filter_bounds(gfx::RectF(gfx::SizeF(blur->bounds())), 0);
   blur->SetBackdropFilterBounds(backdrop_filter_bounds);
 
-#if defined(OS_WIN) || defined(ARCH_CPU_ARM64)
+#if BUILDFLAG(IS_WIN) || defined(ARCH_CPU_ARM64)
   // Windows and ARM64 have 436 pixels off by 1: crbug.com/259915
   float percentage_pixels_large_error = 1.09f;  // 436px / (200*200)
   float percentage_pixels_small_error = 0.0f;
@@ -172,7 +172,7 @@
   gfx::RRectF backdrop_filter_bounds(gfx::RectF(gfx::SizeF(blur->bounds())), 0);
   blur->SetBackdropFilterBounds(backdrop_filter_bounds);
 
-#if defined(OS_WIN) || defined(ARCH_CPU_ARM64)
+#if BUILDFLAG(IS_WIN) || defined(ARCH_CPU_ARM64)
   // Windows and ARM64 have 436 pixels off by 1: crbug.com/259915
   float percentage_pixels_large_error = 1.09f;  // 436px / (200*200)
   float percentage_pixels_small_error = 0.0f;
@@ -260,8 +260,8 @@
   gfx::RRectF backdrop_filter_bounds(gfx::RectF(gfx::SizeF(blur->bounds())), 0);
   blur->SetBackdropFilterBounds(backdrop_filter_bounds);
 
-#if defined(OS_WIN) || defined(_MIPS_ARCH_LOONGSON) || defined(ARCH_CPU_ARM64)
-#if defined(OS_WIN) || defined(ARCH_CPU_ARM64)
+#if BUILDFLAG(IS_WIN) || defined(_MIPS_ARCH_LOONGSON) || defined(ARCH_CPU_ARM64)
+#if BUILDFLAG(IS_WIN) || defined(ARCH_CPU_ARM64)
   // Windows has 5.9325% pixels by at most 2: crbug.com/259922
   float percentage_pixels_large_error = 6.0f;
 #else
@@ -370,8 +370,8 @@
 // See skbug.com/9545
 TEST_P(LayerTreeHostBlurFiltersPixelTestGPULayerList,
        DISABLED_BackdropFilterBlurOffAxis) {
-#if defined(OS_WIN) || defined(ARCH_CPU_ARM64)
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN) || defined(ARCH_CPU_ARM64)
+#if BUILDFLAG(IS_WIN)
   // Windows has 116 pixels off by at most 2: crbug.com/225027
   float percentage_pixels_large_error = 0.3f;  // 116px / (200*200), rounded up
   int large_error_allowed = 2;
@@ -583,9 +583,9 @@
   filter->SetBackdropFilters(filters);
   filter->ClearBackdropFilterBounds();
 
-#if defined(OS_WIN) || defined(OS_MAC) || defined(_MIPS_ARCH_LOONGSON) || \
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || defined(_MIPS_ARCH_LOONGSON) || \
     defined(ARCH_CPU_ARM64)
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // Windows has 153 pixels off by at most 2: crbug.com/225027
   float percentage_pixels_large_error = 0.3825f;  // 153px / (200*200)
   int large_error_allowed = 2;
@@ -594,7 +594,7 @@
     percentage_pixels_large_error = 0.415f;  // 166px / (200*200)
     large_error_allowed = 1;
   }
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   // There's a 1 pixel error on MacOS
   float percentage_pixels_large_error = 0.0025f;  // 1px / (200*200)
   int large_error_allowed = 1;
@@ -795,7 +795,7 @@
   contained_zoom->SetBackdropFilterBounds(gfx::RRectF(mid_filter_bounds, 0));
   root->AddChild(contained_zoom);
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // Windows has 1 pixel off by 1: crbug.com/259915
   float percentage_pixels_large_error = 0.00111112f;  // 1px / (300*300)
   float percentage_pixels_small_error = 0.0f;
@@ -836,14 +836,14 @@
 
   background->AddChild(child);
 
-#if defined(OS_WIN) || defined(OS_FUCHSIA) || defined(OS_MAC)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_MAC)
 #if defined(ARCH_CPU_ARM64)
   // Windows, macOS, and Fuchsia on ARM64 has some pixels difference
   // crbug.com/1029728, crbug.com/1048249, crbug.com/1128443
   float percentage_pixels_large_error = 1.f;
   float average_error_allowed_in_bad_pixels = 2.f;
   int large_error_allowed = 3;
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   // Windows has 1 pixel off by 1: crbug.com/259915
   float percentage_pixels_large_error = 0.00111112f;  // 1px / (300*300)
   float average_error_allowed_in_bad_pixels = 1.f;
@@ -897,16 +897,16 @@
 
   background->AddChild(child);
 
-#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_CHROMEOS) || \
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS) || \
     defined(ARCH_CPU_ARM64) || defined(USE_OZONE)
 #if defined(ARCH_CPU_ARM64) && \
-    (defined(OS_WIN) || defined(OS_FUCHSIA) || defined(OS_MAC))
+    (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_MAC))
   // Windows, macOS, and Fuchsia on ARM64 has some pixels difference.
   // crbug.com/1029728, crbug.com/1128443
   float percentage_pixels_large_error = 0.89f;
   float average_error_allowed_in_bad_pixels = 5.f;
   int large_error_allowed = 17;
-#elif defined(OS_MAC) || defined(OS_CHROMEOS) || defined(USE_OZONE)
+#elif BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS) || defined(USE_OZONE)
   // There's a 1 pixel error on MacOS and ChromeOS
   float percentage_pixels_large_error = 0.00111112f;  // 1px / (300*300)
   float average_error_allowed_in_bad_pixels = 1.f;
@@ -1081,8 +1081,8 @@
   // Force the allocation a larger textures.
   set_enlarge_texture_amount(gfx::Size(50, 50));
 
-#if defined(OS_WIN) || defined(ARCH_CPU_ARM64)
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN) || defined(ARCH_CPU_ARM64)
+#if BUILDFLAG(IS_WIN)
   // Windows has 1880 pixels off by 1: crbug.com/259915
   float percentage_pixels_large_error = 4.7f;  // 1880px / (200*200)
 #else
diff --git a/cc/trees/layer_tree_host_pixeltest_masks.cc b/cc/trees/layer_tree_host_pixeltest_masks.cc
index fa044b3..bc1f41e 100644
--- a/cc/trees/layer_tree_host_pixeltest_masks.cc
+++ b/cc/trees/layer_tree_host_pixeltest_masks.cc
@@ -22,7 +22,7 @@
 #include "components/viz/test/buildflags.h"
 #include "third_party/skia/include/core/SkImage.h"
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 
 namespace cc {
 namespace {
@@ -762,7 +762,7 @@
       small_error_allowed = 1;
     } else {
 #if defined(ARCH_CPU_ARM64)
-#if defined(OS_WIN) || defined(OS_FUCHSIA) || defined(OS_MAC)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_MAC)
       // ARM Windows, macOS, and Fuchsia has some pixels difference
       // Affected tests: RotatedClippedCircle, RotatedClippedCircleUnderflow
       // crbug.com/1030244, crbug.com/1048249, crbug.com/1128443
@@ -1162,4 +1162,4 @@
 }  // namespace
 }  // namespace cc
 
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
diff --git a/cc/trees/layer_tree_host_pixeltest_mirror.cc b/cc/trees/layer_tree_host_pixeltest_mirror.cc
index a41605e..835fac7 100644
--- a/cc/trees/layer_tree_host_pixeltest_mirror.cc
+++ b/cc/trees/layer_tree_host_pixeltest_mirror.cc
@@ -10,7 +10,7 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/transform_util.h"
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 
 namespace cc {
 namespace {
@@ -72,4 +72,4 @@
 }  // namespace
 }  // namespace cc
 
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
diff --git a/cc/trees/layer_tree_host_pixeltest_readback.cc b/cc/trees/layer_tree_host_pixeltest_readback.cc
index 277b3e2..a17ed67 100644
--- a/cc/trees/layer_tree_host_pixeltest_readback.cc
+++ b/cc/trees/layer_tree_host_pixeltest_readback.cc
@@ -17,7 +17,7 @@
 #include "components/viz/test/buildflags.h"
 #include "components/viz/test/paths.h"
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 
 namespace cc {
 namespace {
diff --git a/cc/trees/layer_tree_host_pixeltest_scrollbars.cc b/cc/trees/layer_tree_host_pixeltest_scrollbars.cc
index d7321c4..61b1722 100644
--- a/cc/trees/layer_tree_host_pixeltest_scrollbars.cc
+++ b/cc/trees/layer_tree_host_pixeltest_scrollbars.cc
@@ -18,7 +18,7 @@
 #include "components/viz/test/test_in_process_context_provider.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 
 namespace cc {
 namespace {
diff --git a/cc/trees/layer_tree_host_pixeltest_synchronous.cc b/cc/trees/layer_tree_host_pixeltest_synchronous.cc
index 5e19dad..b8ccd64e 100644
--- a/cc/trees/layer_tree_host_pixeltest_synchronous.cc
+++ b/cc/trees/layer_tree_host_pixeltest_synchronous.cc
@@ -11,7 +11,7 @@
 #include "cc/test/pixel_comparator.h"
 #include "components/viz/test/test_types.h"
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 
 namespace cc {
 namespace {
diff --git a/cc/trees/layer_tree_host_pixeltest_tiles.cc b/cc/trees/layer_tree_host_pixeltest_tiles.cc
index 49b97d0..7b46d19 100644
--- a/cc/trees/layer_tree_host_pixeltest_tiles.cc
+++ b/cc/trees/layer_tree_host_pixeltest_tiles.cc
@@ -17,7 +17,7 @@
 #include "components/viz/test/buildflags.h"
 #include "gpu/command_buffer/client/raster_interface.h"
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 
 namespace cc {
 namespace {
@@ -243,7 +243,7 @@
                          ::testing::PrintToStringParamName());
 
 #if BUILDFLAG(IS_CHROMEOS_ASH) || defined(MEMORY_SANITIZER) || \
-    defined(ADDRESS_SANITIZER) || defined(OS_FUCHSIA)
+    defined(ADDRESS_SANITIZER) || BUILDFLAG(IS_FUCHSIA)
 // TODO(crbug.com/1045521): Flakes on all slower bots.
 #define MAYBE_PartialRaster DISABLED_PartialRaster
 #else
@@ -292,11 +292,11 @@
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
     LayerTreeHostTilesTestPartialInvalidationMultiThread);
 
-#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(THREAD_SANITIZER)
+#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && defined(THREAD_SANITIZER)
 // Flaky on Linux TSAN. https://crbug.com/707711
 #define MAYBE_PartialRaster DISABLED_PartialRaster
 #elif BUILDFLAG(IS_CHROMEOS_ASH) || defined(MEMORY_SANITIZER) || \
-    defined(ADDRESS_SANITIZER) || defined(OS_FUCHSIA)
+    defined(ADDRESS_SANITIZER) || BUILDFLAG(IS_FUCHSIA)
 // TODO(crbug.com/1045521): Flakes on all slower bots.
 #define MAYBE_PartialRaster DISABLED_PartialRaster
 #else
@@ -399,4 +399,4 @@
 }  // namespace
 }  // namespace cc
 
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index e3f4bcab9..4d05596 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -2969,7 +2969,7 @@
 };
 
 // TODO(crbug.com/1223226): Disabled on Chrome OS due to flakiness.
-#if !defined(OS_CHROMEOS)
+#if !BUILDFLAG(IS_CHROMEOS)
 SINGLE_AND_MULTI_THREAD_TEST_F(
     LayerTreeHostTestViewportRectChangeBlockedMainThread);
 #endif
@@ -6096,7 +6096,7 @@
 
   void VerifyOverscroll(const gfx::Vector2dF& stretch_amount,
                         const gfx::Transform& transform) {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     gfx::Vector2dF scale = transform.Scale2d();
     // On android, overscroll stretches the content. We don't assert the amount
     // of stretch but there should be some stretch for overscroll and no stretch
@@ -6109,11 +6109,11 @@
       EXPECT_EQ(1.f, scale.y());
     else
       EXPECT_GT(scale.y(), 1.f);
-#else   // defined(OS_ANDROID)
+#else   // BUILDFLAG(IS_ANDROID)
     gfx::Transform expected_draw_transform;
     expected_draw_transform.Translate(-stretch_amount);
     EXPECT_EQ(expected_draw_transform, transform);
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
   }
 
   void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
@@ -7591,7 +7591,7 @@
     viz::TestGLES2Interface* gl =
         display_context_provider->UnboundTestContextGL();
     gl->set_support_sync_query(true);
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
     gl->set_support_texture_rectangle(true);
 #endif
     display_context_provider->BindToCurrentThread();
diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc
index c020634..b25173b 100644
--- a/cc/trees/layer_tree_host_unittest_animation.cc
+++ b/cc/trees/layer_tree_host_unittest_animation.cc
@@ -1067,7 +1067,7 @@
 };
 
 // Disabled on ChromeOS due to test flakiness. See https://crbug.com/1246422
-#if !defined(OS_CHROMEOS)
+#if !BUILDFLAG(IS_CHROMEOS)
 MULTI_THREAD_TEST_F(LayerTreeHostPresentationDuringAnimation);
 #endif
 
@@ -1756,7 +1756,7 @@
 };
 
 // Disabled on ChromeOS due to test flakiness. See https://crbug.com/1246422
-#if !defined(OS_CHROMEOS)
+#if !BUILDFLAG(IS_CHROMEOS)
 SINGLE_THREAD_TEST_F(LayerTreeHostAnimationTestRemoveKeyframeModel);
 #endif
 
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index 594b129..6298e5f 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -1057,7 +1057,7 @@
 
 // TODO(crbug.com/574283): Mac currently doesn't support smooth scrolling wheel
 // events.
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
 // This test simulates scrolling on the impl thread such that it starts a scroll
 // animation. It ensures that RequestScrollAnimationEndNotification() correctly
 // notifies the callback after the animation ends.
@@ -1163,7 +1163,7 @@
 };
 
 MULTI_THREAD_TEST_F(SmoothScrollAnimationEndNotification);
-#endif  // !defined(OS_MAC)
+#endif  // !BUILDFLAG(IS_MAC)
 
 void DoGestureScroll(LayerTreeHostImpl* host_impl,
                      const scoped_refptr<Layer>& scroller,
@@ -1308,7 +1308,7 @@
 };
 
 // TODO(crbug.com/1201662): Flaky on Fuchsia, ChromeOS, and Linux.
-#if !defined(OS_FUCHSIA) && !defined(OS_CHROMEOS) && !defined(OS_LINUX)
+#if !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_LINUX)
 MULTI_THREAD_TEST_F(LayerTreeHostScrollTestImplOnlyScrollSnap);
 #endif
 
@@ -1457,7 +1457,7 @@
 };
 
 // TODO(crbug.com/1243814): Test is flaky on Chrome OS (both Ash and Lacros).
-#if !defined(OS_CHROMEOS)
+#if !BUILDFLAG(IS_CHROMEOS)
 MULTI_THREAD_TEST_F(LayerTreeHostScrollTestImplOnlyMultipleScrollSnap);
 #endif
 
@@ -1725,7 +1725,7 @@
 
 // This test is flaky in the single threaded configuration, only on the
 // chromeos-amd64-generic-rel bot. https://crbug.com/1093078.
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
 // SINGLE_THREAD_TEST_F(
 //    LayerTreeHostScrollTestImplScrollUnderMainThreadScrollingParent);
 MULTI_THREAD_TEST_F(
diff --git a/cc/trees/render_frame_metadata.cc b/cc/trees/render_frame_metadata.cc
index 0efa349..9e9e03d 100644
--- a/cc/trees/render_frame_metadata.cc
+++ b/cc/trees/render_frame_metadata.cc
@@ -36,7 +36,7 @@
          external_page_scale_factor == other.external_page_scale_factor &&
          top_controls_height == other.top_controls_height &&
          top_controls_shown_ratio == other.top_controls_shown_ratio &&
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
          bottom_controls_height == other.bottom_controls_height &&
          bottom_controls_shown_ratio == other.bottom_controls_shown_ratio &&
          top_controls_min_height_offset ==
diff --git a/cc/trees/render_frame_metadata.h b/cc/trees/render_frame_metadata.h
index 98b371bd..8c7813fa 100644
--- a/cc/trees/render_frame_metadata.h
+++ b/cc/trees/render_frame_metadata.h
@@ -119,7 +119,7 @@
   // set of VisualProperties arriving. See WidgetBase::UpdateVisualProperties.
   base::TimeDelta visual_properties_update_duration;
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // Used to position Android bottom bar, whose position is computed by the
   // renderer compositor.
   float bottom_controls_height = 0.f;
diff --git a/chrome/VERSION b/chrome/VERSION
index 793bccb3..d36fc56 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=99
 MINOR=0
-BUILD=4820
+BUILD=4824
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 79a3e66..ff9a132 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -3908,7 +3908,6 @@
     "java/src/org/chromium/chrome/browser/infobar/NearOomInfoBar.java",
     "java/src/org/chromium/chrome/browser/infobar/NearOomReductionInfoBar.java",
     "java/src/org/chromium/chrome/browser/infobar/PermissionInfoBar.java",
-    "java/src/org/chromium/chrome/browser/infobar/PreviewsLitePageInfoBar.java",
     "java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java",
     "java/src/org/chromium/chrome/browser/infobar/SafetyTipInfoBar.java",
     "java/src/org/chromium/chrome/browser/infobar/SavePasswordInfoBar.java",
@@ -3971,7 +3970,6 @@
     "java/src/org/chromium/chrome/browser/permissions/PermissionSettingsBridge.java",
     "java/src/org/chromium/chrome/browser/permissions/PermissionUpdateRequester.java",
     "java/src/org/chromium/chrome/browser/policy/PolicyAuditor.java",
-    "java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java",
     "java/src/org/chromium/chrome/browser/printing/TabPrinter.java",
     "java/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManagerImpl.java",
     "java/src/org/chromium/chrome/browser/push_messaging/PushMessagingServiceObserver.java",
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index 2eeaac5..d68119ad 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -618,7 +618,6 @@
   "java/res/layout/custom_tabs_toolbar_button.xml",
   "java/res/layout/custom_tabs_topbar.xml",
   "java/res/layout/custom_view_menu_item.xml",
-  "java/res/layout/data_reduction_main_menu_item.xml",
   "java/res/layout/data_reduction_stats_layout.xml",
   "java/res/layout/data_usage_breakdown.xml",
   "java/res/layout/data_usage_breakdown_row.xml",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 5954fd79..418eef00 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -182,6 +182,7 @@
   "java/src/org/chromium/chrome/browser/bookmarks/BookmarkUIState.java",
   "java/src/org/chromium/chrome/browser/bookmarks/BookmarkUndoController.java",
   "java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java",
+  "java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkMetrics.java",
   "java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRow.java",
   "java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkTagChipList.java",
   "java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtils.java",
@@ -388,6 +389,7 @@
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateHandler.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagementDelegate.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java",
+  "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerSupplier.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchNetworkCommunicator.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchObserver.java",
   "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java",
@@ -491,7 +493,6 @@
   "java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarAnimationDelegate.java",
   "java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarColorController.java",
   "java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarCoordinator.java",
-  "java/src/org/chromium/chrome/browser/datareduction/DataReductionMainMenuItem.java",
   "java/src/org/chromium/chrome/browser/datareduction/DataReductionPromoScreen.java",
   "java/src/org/chromium/chrome/browser/datareduction/DataReductionPromoUtils.java",
   "java/src/org/chromium/chrome/browser/datareduction/DataReductionProxyUma.java",
@@ -710,7 +711,6 @@
   "java/src/org/chromium/chrome/browser/infobar/NearOomReductionInfoBar.java",
   "java/src/org/chromium/chrome/browser/infobar/PasswordInfoBarUtils.java",
   "java/src/org/chromium/chrome/browser/infobar/PermissionInfoBar.java",
-  "java/src/org/chromium/chrome/browser/infobar/PreviewsLitePageInfoBar.java",
   "java/src/org/chromium/chrome/browser/infobar/ReaderModeInfoBar.java",
   "java/src/org/chromium/chrome/browser/infobar/SafetyTipInfoBar.java",
   "java/src/org/chromium/chrome/browser/infobar/SavePasswordInfoBar.java",
@@ -982,9 +982,6 @@
   "java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceImpl.java",
   "java/src/org/chromium/chrome/browser/policy/PolicyAuditor.java",
   "java/src/org/chromium/chrome/browser/prerender/ChromePrerenderServiceImpl.java",
-  "java/src/org/chromium/chrome/browser/previews/HttpsImageCompressionUtils.java",
-  "java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java",
-  "java/src/org/chromium/chrome/browser/previews/PreviewsUma.java",
   "java/src/org/chromium/chrome/browser/printing/PrintShareActivity.java",
   "java/src/org/chromium/chrome/browser/printing/TabPrinter.java",
   "java/src/org/chromium/chrome/browser/privacy/settings/DoNotTrackSettings.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 56b4fd9..1eeec99 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -51,7 +51,6 @@
   "javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java",
   "javatests/src/org/chromium/chrome/browser/accessibility/FontSizePrefsTest.java",
   "javatests/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettingsTest.java",
-  "javatests/src/org/chromium/chrome/browser/app/appmenu/DataSaverAppMenuTest.java",
   "javatests/src/org/chromium/chrome/browser/app/appmenu/OverviewAppMenuTest.java",
   "javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java",
   "javatests/src/org/chromium/chrome/browser/app/metrics/LaunchCauseMetricsTest.java",
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 730ebff..5aa82b80 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -94,6 +94,7 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantSnackbarFactoryChrome.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependenciesChrome.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabObscuringUtilChrome.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabObserverChrome.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabUtilChrome.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTagsForTesting.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTextUtils.java",
@@ -171,8 +172,6 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataNativeDelegate.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataSection.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantContactDetailsSection.java",
-    "java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantDateChoiceOptions.java",
-    "java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantDateSection.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantDateTime.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantInfoSection.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantLoginChoice.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependenciesChrome.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependenciesChrome.java
index 32868f2..a7dd312 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependenciesChrome.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependenciesChrome.java
@@ -9,12 +9,12 @@
 
 import androidx.annotation.Nullable;
 
+import org.chromium.base.lifetime.Destroyable;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.ActivityUtils;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
-import org.chromium.chrome.browser.ui.TabObscuringHandler;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider;
 import org.chromium.content_public.browser.WebContents;
@@ -34,7 +34,6 @@
     private KeyboardVisibilityDelegate mKeyboardVisibilityDelegate;
     private ApplicationViewportInsetSupplier mBottomInsetProvider;
     private ActivityTabProvider mActivityTabProvider;
-    private TabObscuringHandler mTabObscuringHandler;
     private View mRootView;
     private AssistantSnackbarFactory mSnackbarFactory;
 
@@ -57,7 +56,6 @@
         mKeyboardVisibilityDelegate = mWindowAndroid.getKeyboardDelegate();
         mBottomInsetProvider = mWindowAndroid.getApplicationBottomInsetProvider();
         mActivityTabProvider = chromeActivity.getActivityTabProvider();
-        mTabObscuringHandler = chromeActivity.getTabObscuringHandler();
         mRootView = rootView.get();
         mSnackbarFactory =
                 new AssistantSnackbarFactoryChrome(mActivity, chromeActivity.getSnackbarManager());
@@ -103,16 +101,6 @@
     }
 
     @Override
-    public ActivityTabProvider getActivityTabProvider() {
-        return mActivityTabProvider;
-    }
-
-    @Override
-    public TabObscuringHandler getTabObscuringHandler() {
-        return mTabObscuringHandler;
-    }
-
-    @Override
     public View getRootView() {
         return mRootView;
     }
@@ -121,4 +109,9 @@
     public AssistantSnackbarFactory getSnackbarFactory() {
         return mSnackbarFactory;
     }
+
+    @Override
+    public Destroyable observeTabChanges(AssistantTabObserver tabObserver) {
+        return new AssistantTabObserverChrome(mActivityTabProvider, tabObserver);
+    }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingHelperImpl.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingHelperImpl.java
index 0e6af29..5f21161e 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingHelperImpl.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingHelperImpl.java
@@ -38,7 +38,7 @@
         mOnboardingCoordinatorFactory = new OnboardingCoordinatorFactory(dependencies.getActivity(),
                 dependencies.getBottomSheetController(), dependencies.getBrowserControls(),
                 dependencies.getRootView(), dependencies.getAccessibilityUtil(),
-                dependencies.getInfoPageUtil());
+                dependencies.createInfoPageUtil());
         mTriggerScriptBridge = new AssistantTriggerScriptBridge(webContents, dependencies);
     }
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependenciesChrome.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependenciesChrome.java
index 5ff7399..041a151 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependenciesChrome.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependenciesChrome.java
@@ -55,22 +55,22 @@
     }
 
     @Override
-    public AssistantInfoPageUtil getInfoPageUtil() {
+    public AssistantInfoPageUtil createInfoPageUtil() {
         return new AssistantInfoPageUtilChrome();
     }
 
     @Override
-    public AssistantFeedbackUtil getFeedbackUtil() {
+    public AssistantFeedbackUtil createFeedbackUtil() {
         return new AssistantFeedbackUtilChrome();
     }
 
     @Override
-    public AssistantTabUtil getTabUtil() {
+    public AssistantTabUtil createTabUtil() {
         return new AssistantTabUtilChrome();
     }
 
     @Override
-    public AssistantAccessTokenUtil getAccessTokenUtil() {
+    public AssistantAccessTokenUtil createAccessTokenUtil() {
         return new AssistantAccessTokenUtilChrome();
     }
 
@@ -85,7 +85,7 @@
 
     @Override
     @Nullable
-    public AssistantProfileImageUtil getProfileImageUtilOrNull(Context context) {
+    public AssistantProfileImageUtil createProfileImageUtilOrNull(Context context) {
         String signedInAccountEmail = getSignedInAccountEmailOrNull();
         if (signedInAccountEmail == null) return null;
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabObserverChrome.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabObserverChrome.java
new file mode 100644
index 0000000..c84f9e4
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabObserverChrome.java
@@ -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.
+
+package org.chromium.chrome.browser.autofill_assistant;
+
+import androidx.annotation.Nullable;
+
+import org.chromium.base.lifetime.Destroyable;
+import org.chromium.chrome.browser.ActivityTabProvider;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * Adapter hiding Chrome's ActivityTabTabObserver.
+ */
+public class AssistantTabObserverChrome implements Destroyable {
+    private final ActivityTabProvider.ActivityTabTabObserver mActivityTabObserver;
+
+    public AssistantTabObserverChrome(
+            ActivityTabProvider activityTabProvider, AssistantTabObserver tabObserver) {
+        mActivityTabObserver = new ActivityTabProvider.ActivityTabTabObserver(
+                activityTabProvider, /* shouldTrigger = */ true) {
+            @Override
+            public void onObservingDifferentTab(Tab tab, boolean hint) {
+                tabObserver.onObservingDifferentTab(
+                        tab == null, tab != null ? tab.getWebContents() : null, hint);
+            }
+
+            @Override
+            public void onActivityAttachmentChanged(
+                    Tab tab, @Nullable WindowAndroid windowAndroid) {
+                tabObserver.onActivityAttachmentChanged(
+                        tab != null ? tab.getWebContents() : null, windowAndroid);
+            }
+        };
+    }
+
+    @Override
+    public void destroy() {
+        mActivityTabObserver.destroy();
+    }
+}
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTagsForTesting.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTagsForTesting.java
index 0afe5aa..442850a 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTagsForTesting.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTagsForTesting.java
@@ -16,8 +16,6 @@
     public static final String COLLECT_USER_DATA_CONTACT_DETAILS_SECTION_TAG = "contact";
     public static final String COLLECT_USER_DATA_PAYMENT_METHOD_SECTION_TAG = "payment";
     public static final String COLLECT_USER_DATA_SHIPPING_ADDRESS_SECTION_TAG = "shipping";
-    public static final String COLLECT_USER_DATA_DATE_RANGE_START_TAG = "date_start";
-    public static final String COLLECT_USER_DATA_DATE_RANGE_END_TAG = "date_end";
     public static final String COLLECT_USER_DATA_RADIO_TERMS_SECTION_TAG = "radio_terms";
     public static final String COLLECT_USER_DATA_CHECKBOX_TERMS_SECTION_TAG = "checkbox_terms";
     public static final String COLLECT_USER_DATA_INFO_SECTION_TAG = "info_section";
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryImpl.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryImpl.java
index b61fc680a..bebfb95 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryImpl.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantModuleEntryImpl.java
@@ -42,7 +42,7 @@
         return new AutofillAssistantActionHandlerImpl(
                 new OnboardingCoordinatorFactory(context, bottomSheetController, browserControls,
                         rootView, staticDependencies.getAccessibilityUtil(),
-                        staticDependencies.getInfoPageUtil()),
+                        staticDependencies.createInfoPageUtil()),
                 webContentsSupplier, dependenciesFactory);
     }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index 1896cffd..04a92c4 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -13,13 +13,12 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
+import org.chromium.base.lifetime.Destroyable;
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
 import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.WindowAndroid;
@@ -49,7 +48,7 @@
     private final Activity mActivity;
     private final AssistantCoordinator mCoordinator;
     private final AssistantDependencies mDependencies;
-    private final ActivityTabProvider.ActivityTabTabObserver mActivityTabObserver;
+    private final Destroyable mTabObserverDestroyer;
     private WebContents mWebContents;
 
     private final AssistantSnackbarFactory mSnackbarFactory;
@@ -91,7 +90,7 @@
 
         mNativeUiController = nativeUiController;
         mSnackbarFactory = dependencies.getSnackbarFactory();
-        mFeedbackUtil = dependencies.getFeedbackUtil();
+        mFeedbackUtil = dependencies.createFeedbackUtil();
 
         // NOTE: Only create one instance of this unless you know what you are doing.
         @Nullable
@@ -102,29 +101,29 @@
                 tabObscuringUtil, overlayCoordinator, this::safeNativeOnKeyboardVisibilityChanged,
                 dependencies.getKeyboardVisibilityDelegate(), dependencies.getRootView(),
                 dependencies.getBrowserControls(), dependencies.getBottomInsetProvider(),
-                dependencies.getAccessibilityUtil(), dependencies.getInfoPageUtil(),
-                dependencies.getProfileImageUtilOrNull(mActivity));
+                dependencies.getAccessibilityUtil(), dependencies.createInfoPageUtil(),
+                dependencies.createProfileImageUtilOrNull(mActivity));
 
-        mActivityTabObserver = new ActivityTabProvider.ActivityTabTabObserver(
-                dependencies.getActivityTabProvider(), /* shouldTrigger = */ true) {
+        mTabObserverDestroyer = dependencies.observeTabChanges(new AssistantTabObserver() {
             @Override
-            protected void onObservingDifferentTab(Tab tab, boolean hint) {
+            public void onObservingDifferentTab(
+                    boolean isTabNull, @Nullable WebContents webContents, boolean isHint) {
                 if (mWebContents == null) {
-                    if (!hint) {
+                    if (!isHint) {
                         // This particular scenario would happen only if we're switching
                         // from a tab with no Autofill Assistant running to a tab with AA
                         // running with no tab switching hinting (i.e. a first notification
-                        // with |hint| set to true).
+                        // with |isHint| set to true).
                         // In this case the native side is not yet fully initialized, so we
                         // need to wait for the web contents to be set from native before
                         // notifying native that the tab was selected.
-                        setWebContentObserver(tab);
+                        setWebContentObserver(isTabNull, webContents);
                     }
                     return;
                 }
 
                 if (!allowTabSwitching) {
-                    if (tab == null || tab.getWebContents() != mWebContents) {
+                    if (isTabNull || webContents != mWebContents) {
                         safeNativeOnFatalError(
                                 mActivity.getString(R.string.autofill_assistant_give_up),
                                 DropOutReason.TAB_CHANGED);
@@ -136,19 +135,19 @@
                 // confusion.
                 dismissSnackbar();
 
-                if (tab == null) {
+                if (isTabNull) {
                     safeOnTabSwitched(getModel().getBottomSheetState(),
                             /* activityChanged = */ false);
                     // A null tab indicates that there's no selected tab; Most likely, we're
                     // in the process of selecting a new tab. Hide the UI for possible reuse
                     // later.
                     safeNativeSetVisible(false);
-                } else if (tab.getWebContents() == mWebContents) {
+                } else if (webContents == mWebContents) {
                     // The original tab was re-selected. Show it again and force an
                     // expansion on the bottom sheet.
-                    if (!hint) {
+                    if (!isHint) {
                         // Here and below, we're only interested in restoring the UI for the
-                        // case where hint is false, meaning that the tab is shown. This is
+                        // case where isHint is false, meaning that the tab is shown. This is
                         // the only way to be sure that the bottomsheet is unsuppressed when
                         // we try to restore the status to what it was prior to switching.
                         safeOnTabSelected();
@@ -163,20 +162,21 @@
                     AutofillAssistantClient client =
                             AutofillAssistantClient.fromWebContents(mWebContents);
                     if (client != null) {
-                        client.transferUiTo(tab.getWebContents());
+                        client.transferUiTo(webContents);
                     }
 
-                    if (!hint) {
+                    if (!isHint) {
                         safeOnTabSelected();
                     }
                 }
             }
 
             @Override
-            public void onActivityAttachmentChanged(Tab tab, @Nullable WindowAndroid window) {
+            public void onActivityAttachmentChanged(
+                    @Nullable WebContents webContents, @Nullable WindowAndroid window) {
                 if (mWebContents == null) return;
 
-                if (window == null && tab.getWebContents() == mWebContents) {
+                if (window == null && webContents == mWebContents) {
                     if (!allowTabSwitching) {
                         safeNativeStop(DropOutReason.TAB_DETACHED);
                         return;
@@ -198,19 +198,17 @@
                     }
                 }
             }
-        };
+        });
     }
 
-    private void setWebContentObserver(Tab tab) {
+    private void setWebContentObserver(boolean isTabNull, @Nullable WebContents webContents) {
         getModel().addObserver(new PropertyObserver<PropertyKey>() {
             @Override
             public void onPropertyChanged(
                     PropertyObservable<PropertyKey> source, @Nullable PropertyKey propertyKey) {
                 if (AssistantModel.WEB_CONTENTS == propertyKey) {
                     getModel().removeObserver(this);
-                    if (tab != null
-                            && tab.getWebContents()
-                                    == getModel().get(AssistantModel.WEB_CONTENTS)) {
+                    if (!isTabNull && webContents == getModel().get(AssistantModel.WEB_CONTENTS)) {
                         safeOnTabSelected();
                     }
                 }
@@ -244,7 +242,7 @@
     @CalledByNative
     private void clearNativePtr() {
         mNativeUiController = 0;
-        mActivityTabObserver.destroy();
+        mTabObserverDestroyer.destroy();
         mCoordinator.destroy();
         sActiveActivities.remove(mActivity);
     }
@@ -255,7 +253,7 @@
      */
     @CalledByNative
     private void scheduleCloseCustomTab() {
-        mDependencies.getTabUtil().scheduleCloseCustomTab(mActivity);
+        mDependencies.createTabUtil().scheduleCloseCustomTab(mActivity);
     }
 
     @CalledByNative
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsDecoration.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsDecoration.java
index a9d943e8..2177ee38 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsDecoration.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantActionsDecoration.java
@@ -20,6 +20,7 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.autofill_assistant.R;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 
 /**
  * Decoration added to the actions carousel that add offsets to each action to have the right inner
@@ -88,8 +89,7 @@
         mShadowPaint.setStyle(Paint.Style.STROKE);
         mShadowPaint.setStrokeWidth(mShadowLayerWidth);
 
-        mOverlayPaint.setColor(
-                ApiCompatibilityUtils.getColor(context.getResources(), R.color.sheet_bg_color));
+        mOverlayPaint.setColor(SemanticColorUtils.getSheetBgColor(context));
     }
 
     @Override
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScriptBridge.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScriptBridge.java
index 9138d7a8..09b12fd 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScriptBridge.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScriptBridge.java
@@ -59,15 +59,15 @@
 
                     @Override
                     public void onFeedbackButtonClicked() {
-                        dependencies.getFeedbackUtil().showFeedback(dependencies.getActivity(),
-                                webContents, /* screenshotMode */ 0, /* debugContext */ null);
+                        dependencies.createFeedbackUtil().showFeedback(dependencies.getActivity(),
+                                webContents, /* screenshotMode= */ 0, /* debugContext= */ null);
                     }
                 };
 
         mTriggerScript = new AssistantTriggerScript(dependencies.getActivity(), delegate,
                 webContents, dependencies.getBottomSheetController(),
                 dependencies.getBottomInsetProvider(), dependencies.getAccessibilityUtil(),
-                dependencies.getProfileImageUtilOrNull(dependencies.getActivity()));
+                dependencies.createProfileImageUtilOrNull(dependencies.getActivity()));
 
         mKeyboardVisibilityListener = this::safeNativeOnKeyboardVisibilityChanged;
     }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataBinder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataBinder.java
index eaab934..a4c11c8 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataBinder.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataBinder.java
@@ -8,8 +8,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import androidx.annotation.Nullable;
-
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.browser.autofill.prefeditor.EditorDialog;
 import org.chromium.chrome.browser.autofill.settings.AddressEditor;
@@ -55,8 +53,6 @@
         private final int mSectionToSectionPadding;
         private final AssistantLoginSection mLoginSection;
         private final AssistantContactDetailsSection mContactDetailsSection;
-        private final AssistantDateSection mDateRangeStartSection;
-        private final AssistantDateSection mDateRangeEndSection;
         private final AssistantPaymentMethodSection mPaymentMethodSection;
         private final AssistantShippingAddressSection mShippingAddressSection;
         private final AssistantTermsSection mTermsSection;
@@ -72,8 +68,6 @@
         public ViewHolder(View rootView, AssistantVerticalExpanderAccordion accordion,
                 int sectionPadding, AssistantLoginSection loginSection,
                 AssistantContactDetailsSection contactDetailsSection,
-                AssistantDateSection dateRangeStartSection,
-                AssistantDateSection dateRangeEndSection,
                 AssistantPaymentMethodSection paymentMethodSection,
                 AssistantShippingAddressSection shippingAddressSection,
                 AssistantTermsSection termsSection, AssistantTermsSection termsAsCheckboxSection,
@@ -88,8 +82,6 @@
             mSectionToSectionPadding = sectionPadding;
             mLoginSection = loginSection;
             mContactDetailsSection = contactDetailsSection;
-            mDateRangeStartSection = dateRangeStartSection;
-            mDateRangeEndSection = dateRangeEndSection;
             mPaymentMethodSection = paymentMethodSection;
             mShippingAddressSection = shippingAddressSection;
             mTermsSection = termsSection;
@@ -139,34 +131,6 @@
                             collectUserDataDelegate.onTextLinkClicked(link);
                         }
                     };
-            AssistantDateSection.Delegate dateStartDelegate =
-                    collectUserDataDelegate == null ? null : new AssistantDateSection.Delegate() {
-                        @Override
-                        public void onDateChanged(@Nullable AssistantDateTime date) {
-                            collectUserDataDelegate.onDateTimeRangeStartDateChanged(date);
-                            // Set new start date as calendar fallback for the end date.
-                            view.mDateRangeEndSection.setCalendarFallbackDate(date);
-                        }
-
-                        @Override
-                        public void onTimeSlotChanged(@Nullable Integer index) {
-                            collectUserDataDelegate.onDateTimeRangeStartTimeSlotChanged(index);
-                        }
-                    };
-            AssistantDateSection.Delegate dateEndDelegate =
-                    collectUserDataDelegate == null ? null : new AssistantDateSection.Delegate() {
-                        @Override
-                        public void onDateChanged(@Nullable AssistantDateTime date) {
-                            collectUserDataDelegate.onDateTimeRangeEndDateChanged(date);
-                            // Set new end date as calendar fallback for the start date.
-                            view.mDateRangeStartSection.setCalendarFallbackDate(date);
-                        }
-
-                        @Override
-                        public void onTimeSlotChanged(@Nullable Integer index) {
-                            collectUserDataDelegate.onDateTimeRangeEndTimeSlotChanged(index);
-                        }
-                    };
             view.mTermsSection.setDelegate(termsDelegate);
             view.mTermsAsCheckboxSection.setDelegate(termsDelegate);
             view.mInfoSection.setListener(collectUserDataDelegate != null
@@ -184,8 +148,6 @@
             view.mLoginSection.setDelegate(collectUserDataDelegate == null
                             ? null
                             : collectUserDataDelegate::onLoginChoiceChanged);
-            view.mDateRangeStartSection.setDelegate(dateStartDelegate);
-            view.mDateRangeEndSection.setDelegate(dateEndDelegate);
             view.mPrependedSections.setDelegate(collectUserDataDelegate != null
                             ? getAdditionalSectionsDelegate(collectUserDataDelegate)
                             : null);
@@ -232,22 +194,6 @@
             view.mShippingAddressSection.setTitle(
                     model.get(AssistantCollectUserDataModel.SHIPPING_SECTION_TITLE));
             return true;
-        } else if (propertyKey == AssistantCollectUserDataModel.DATE_RANGE_START_DATE_LABEL) {
-            view.mDateRangeStartSection.setDateTitle(
-                    model.get(AssistantCollectUserDataModel.DATE_RANGE_START_DATE_LABEL));
-            return true;
-        } else if (propertyKey == AssistantCollectUserDataModel.DATE_RANGE_START_TIME_LABEL) {
-            view.mDateRangeStartSection.setTimeTitle(
-                    model.get(AssistantCollectUserDataModel.DATE_RANGE_START_TIME_LABEL));
-            return true;
-        } else if (propertyKey == AssistantCollectUserDataModel.DATE_RANGE_END_DATE_LABEL) {
-            view.mDateRangeEndSection.setDateTitle(
-                    model.get(AssistantCollectUserDataModel.DATE_RANGE_END_DATE_LABEL));
-            return true;
-        } else if (propertyKey == AssistantCollectUserDataModel.DATE_RANGE_END_TIME_LABEL) {
-            view.mDateRangeEndSection.setTimeTitle(
-                    model.get(AssistantCollectUserDataModel.DATE_RANGE_END_TIME_LABEL));
-            return true;
         }
         return false;
     }
@@ -302,44 +248,6 @@
                 }
             }
             return true;
-        } else if (propertyKey == AssistantCollectUserDataModel.DATE_RANGE_START_OPTIONS) {
-            view.mDateRangeStartSection.setDateChoiceOptions(
-                    model.get(AssistantCollectUserDataModel.DATE_RANGE_START_OPTIONS));
-            return true;
-        } else if (propertyKey == AssistantCollectUserDataModel.DATE_RANGE_START_DATE) {
-            view.mDateRangeStartSection.setCurrentDate(
-                    model.get(AssistantCollectUserDataModel.DATE_RANGE_START_DATE));
-            return true;
-        } else if (propertyKey == AssistantCollectUserDataModel.DATE_RANGE_START_TIMESLOT) {
-            view.mDateRangeStartSection.setCurrentTimeSlot(
-                    model.get(AssistantCollectUserDataModel.DATE_RANGE_START_TIMESLOT));
-            return true;
-        } else if (propertyKey == AssistantCollectUserDataModel.DATE_RANGE_END_OPTIONS) {
-            view.mDateRangeEndSection.setDateChoiceOptions(
-                    model.get(AssistantCollectUserDataModel.DATE_RANGE_END_OPTIONS));
-            return true;
-        } else if (propertyKey == AssistantCollectUserDataModel.DATE_RANGE_END_DATE) {
-            view.mDateRangeEndSection.setCurrentDate(
-                    model.get(AssistantCollectUserDataModel.DATE_RANGE_END_DATE));
-            return true;
-        } else if (propertyKey == AssistantCollectUserDataModel.DATE_RANGE_END_TIMESLOT) {
-            view.mDateRangeEndSection.setCurrentTimeSlot(
-                    model.get(AssistantCollectUserDataModel.DATE_RANGE_END_TIMESLOT));
-            return true;
-        } else if (propertyKey
-                == AssistantCollectUserDataModel.DATE_RANGE_DATE_NOT_SET_ERROR_MESSAGE) {
-            view.mDateRangeStartSection.setDateNotSetErrorMessage(
-                    model.get(AssistantCollectUserDataModel.DATE_RANGE_DATE_NOT_SET_ERROR_MESSAGE));
-            view.mDateRangeEndSection.setDateNotSetErrorMessage(
-                    model.get(AssistantCollectUserDataModel.DATE_RANGE_DATE_NOT_SET_ERROR_MESSAGE));
-            return true;
-        } else if (propertyKey
-                == AssistantCollectUserDataModel.DATE_RANGE_TIME_NOT_SET_ERROR_MESSAGE) {
-            view.mDateRangeStartSection.setTimeNotSetErrorMessage(
-                    model.get(AssistantCollectUserDataModel.DATE_RANGE_TIME_NOT_SET_ERROR_MESSAGE));
-            view.mDateRangeEndSection.setTimeNotSetErrorMessage(
-                    model.get(AssistantCollectUserDataModel.DATE_RANGE_TIME_NOT_SET_ERROR_MESSAGE));
-            return true;
         } else if (propertyKey == AssistantCollectUserDataModel.PREPENDED_SECTIONS) {
             view.mPrependedSections.setSections(
                     model.get(AssistantCollectUserDataModel.PREPENDED_SECTIONS));
@@ -433,11 +341,6 @@
             view.mLoginSection.setVisible(
                     model.get(AssistantCollectUserDataModel.REQUEST_LOGIN_CHOICE));
             return true;
-        } else if (propertyKey == AssistantCollectUserDataModel.REQUEST_DATE_RANGE) {
-            boolean requestDateRange = model.get(AssistantCollectUserDataModel.REQUEST_DATE_RANGE);
-            view.mDateRangeStartSection.setVisible(requestDateRange);
-            view.mDateRangeEndSection.setVisible(requestDateRange);
-            return true;
         }
         return false;
     }
@@ -536,7 +439,6 @@
                 && (propertyKey != AssistantCollectUserDataModel.REQUEST_PHONE)
                 && (propertyKey != AssistantCollectUserDataModel.REQUEST_PAYMENT)
                 && (propertyKey != AssistantCollectUserDataModel.REQUEST_LOGIN_CHOICE)
-                && (propertyKey != AssistantCollectUserDataModel.REQUEST_DATE_RANGE)
                 && (propertyKey != AssistantCollectUserDataModel.EXPANDED_SECTION)
                 && (propertyKey != AssistantCollectUserDataModel.PREPENDED_SECTIONS)
                 && (propertyKey != AssistantCollectUserDataModel.APPENDED_SECTIONS)) {
@@ -553,10 +455,6 @@
                     view.mSectionToSectionPadding, view.mSectionToSectionPadding);
             view.mContactDetailsSection.setPaddings(
                     view.mSectionToSectionPadding, view.mSectionToSectionPadding);
-            view.mDateRangeStartSection.setPaddings(
-                    view.mSectionToSectionPadding, view.mSectionToSectionPadding);
-            view.mDateRangeEndSection.setPaddings(
-                    view.mSectionToSectionPadding, view.mSectionToSectionPadding);
             view.mPaymentMethodSection.setPaddings(
                     view.mSectionToSectionPadding, view.mSectionToSectionPadding);
             view.mShippingAddressSection.setPaddings(
@@ -567,10 +465,6 @@
             view.mLoginSection.setPaddings(0, view.mSectionToSectionPadding);
             view.mContactDetailsSection.setPaddings(
                     view.mSectionToSectionPadding, view.mSectionToSectionPadding);
-            view.mDateRangeStartSection.setPaddings(
-                    view.mSectionToSectionPadding, view.mSectionToSectionPadding);
-            view.mDateRangeEndSection.setPaddings(
-                    view.mSectionToSectionPadding, view.mSectionToSectionPadding);
             view.mPaymentMethodSection.setPaddings(
                     view.mSectionToSectionPadding, view.mSectionToSectionPadding);
             view.mShippingAddressSection.setPaddings(
@@ -579,20 +473,6 @@
                     view.mSectionToSectionPadding, view.mSectionToSectionPadding);
         } else if (shouldShowContactDetails(model)) {
             view.mContactDetailsSection.setPaddings(0, view.mSectionToSectionPadding);
-            view.mDateRangeStartSection.setPaddings(
-                    view.mSectionToSectionPadding, view.mSectionToSectionPadding);
-            view.mDateRangeEndSection.setPaddings(
-                    view.mSectionToSectionPadding, view.mSectionToSectionPadding);
-            view.mPaymentMethodSection.setPaddings(
-                    view.mSectionToSectionPadding, view.mSectionToSectionPadding);
-            view.mShippingAddressSection.setPaddings(
-                    view.mSectionToSectionPadding, view.mSectionToSectionPadding);
-            view.mAppendedSections.setPaddings(view.mSectionToSectionPadding,
-                    view.mSectionToSectionPadding, view.mSectionToSectionPadding);
-        } else if (model.get(AssistantCollectUserDataModel.REQUEST_DATE_RANGE)) {
-            view.mDateRangeStartSection.setPaddings(0, view.mSectionToSectionPadding);
-            view.mDateRangeEndSection.setPaddings(
-                    view.mSectionToSectionPadding, view.mSectionToSectionPadding);
             view.mPaymentMethodSection.setPaddings(
                     view.mSectionToSectionPadding, view.mSectionToSectionPadding);
             view.mShippingAddressSection.setPaddings(
@@ -622,9 +502,7 @@
         boolean prevSectionIsExpandedOrInvisible = false;
         for (int i = 0; i < view.mPaymentRequestExpanderAccordion.getChildCount(); i++) {
             View child = view.mPaymentRequestExpanderAccordion.getChildAt(i);
-            if (child instanceof AssistantVerticalExpander
-                    || child == view.mDateRangeStartSection.getView()
-                    || child == view.mDateRangeEndSection.getView()) {
+            if (child instanceof AssistantVerticalExpander) {
                 prevSectionIsExpandedOrInvisible = child.getVisibility() != View.VISIBLE
                         || (child instanceof AssistantVerticalExpander
                                 && ((AssistantVerticalExpander) child).isExpanded());
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataCoordinator.java
index 49b1e14d..78a52de 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataCoordinator.java
@@ -89,13 +89,6 @@
                 new AssistantContactDetailsSection(mActivity, paymentRequestExpanderAccordion);
         createSeparator(paymentRequestExpanderAccordion);
 
-        AssistantDateSection dateRangeStartSection = new AssistantDateSection(
-                mActivity, paymentRequestExpanderAccordion, locale, dateFormat);
-        createSeparator(paymentRequestExpanderAccordion);
-        AssistantDateSection dateRangeEndSection = new AssistantDateSection(
-                mActivity, paymentRequestExpanderAccordion, locale, dateFormat);
-        createSeparator(paymentRequestExpanderAccordion);
-
         AssistantPaymentMethodSection paymentMethodSection =
                 new AssistantPaymentMethodSection(mActivity, paymentRequestExpanderAccordion);
         createSeparator(paymentRequestExpanderAccordion);
@@ -124,10 +117,6 @@
         paymentRequestExpanderAccordion.setTag(
                 AssistantTagsForTesting.COLLECT_USER_DATA_ACCORDION_TAG);
         loginSection.getView().setTag(AssistantTagsForTesting.COLLECT_USER_DATA_LOGIN_SECTION_TAG);
-        dateRangeStartSection.getView().setTag(
-                AssistantTagsForTesting.COLLECT_USER_DATA_DATE_RANGE_START_TAG);
-        dateRangeEndSection.getView().setTag(
-                AssistantTagsForTesting.COLLECT_USER_DATA_DATE_RANGE_END_TAG);
         contactDetailsSection.getView().setTag(
                 AssistantTagsForTesting.COLLECT_USER_DATA_CONTACT_DETAILS_SECTION_TAG);
         paymentMethodSection.getView().setTag(
@@ -143,9 +132,8 @@
         // Bind view and mediator through the model.
         mViewHolder = new AssistantCollectUserDataBinder.ViewHolder(mPaymentRequestUI,
                 paymentRequestExpanderAccordion, sectionToSectionPadding, loginSection,
-                contactDetailsSection, dateRangeStartSection, dateRangeEndSection,
-                paymentMethodSection, shippingAddressSection, termsSection, termsAsCheckboxSection,
-                infoSection, prependedSections, appendedSections,
+                contactDetailsSection, paymentMethodSection, shippingAddressSection, termsSection,
+                termsAsCheckboxSection, infoSection, prependedSections, appendedSections,
                 genericUserInterfaceContainerPrepended, genericUserInterfaceContainerAppended,
                 DIVIDER_TAG, mActivity);
         AssistantCollectUserDataBinder binder = new AssistantCollectUserDataBinder();
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataDelegate.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataDelegate.java
index 11e6f83d..4a8c1a0 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataDelegate.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataDelegate.java
@@ -39,18 +39,6 @@
             @Nullable AssistantCollectUserDataModel.LoginChoiceModel loginChoiceModel,
             @AssistantUserDataEventType int eventType);
 
-    /** The start date of the date/time range has changed. */
-    void onDateTimeRangeStartDateChanged(@Nullable AssistantDateTime date);
-
-    /** The start time of the date/time range has changed. */
-    void onDateTimeRangeStartTimeSlotChanged(@Nullable Integer index);
-
-    /** The start date of the date/time range has changed. */
-    void onDateTimeRangeEndDateChanged(@Nullable AssistantDateTime date);
-
-    /** The end time of the date/time range has changed. */
-    void onDateTimeRangeEndTimeSlotChanged(@Nullable Integer index);
-
     /** The value of a key/value pair has changed. */
     void onKeyValueChanged(String key, AssistantValue value);
 
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataModel.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataModel.java
index d5d9059..eb4b0fe 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataModel.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataModel.java
@@ -194,45 +194,6 @@
     public static final WritableObjectPropertyKey<AssistantVerticalExpander> EXPANDED_SECTION =
             new WritableObjectPropertyKey<>();
 
-    public static final WritableBooleanPropertyKey REQUEST_DATE_RANGE =
-            new WritableBooleanPropertyKey();
-
-    public static final WritableObjectPropertyKey<AssistantDateChoiceOptions>
-            DATE_RANGE_START_OPTIONS = new WritableObjectPropertyKey<>();
-
-    public static final WritableObjectPropertyKey<AssistantDateTime> DATE_RANGE_START_DATE =
-            new WritableObjectPropertyKey<>();
-
-    public static final WritableObjectPropertyKey<Integer> DATE_RANGE_START_TIMESLOT =
-            new WritableObjectPropertyKey<>();
-
-    public static final WritableObjectPropertyKey<String> DATE_RANGE_START_DATE_LABEL =
-            new WritableObjectPropertyKey<>();
-
-    public static final WritableObjectPropertyKey<String> DATE_RANGE_START_TIME_LABEL =
-            new WritableObjectPropertyKey<>();
-
-    public static final WritableObjectPropertyKey<AssistantDateChoiceOptions>
-            DATE_RANGE_END_OPTIONS = new WritableObjectPropertyKey<>();
-
-    public static final WritableObjectPropertyKey<AssistantDateTime> DATE_RANGE_END_DATE =
-            new WritableObjectPropertyKey<>();
-
-    public static final WritableObjectPropertyKey<Integer> DATE_RANGE_END_TIMESLOT =
-            new WritableObjectPropertyKey<>();
-
-    public static final WritableObjectPropertyKey<String> DATE_RANGE_END_DATE_LABEL =
-            new WritableObjectPropertyKey<>();
-
-    public static final WritableObjectPropertyKey<String> DATE_RANGE_END_TIME_LABEL =
-            new WritableObjectPropertyKey<>();
-
-    public static final WritableObjectPropertyKey<String> DATE_RANGE_DATE_NOT_SET_ERROR_MESSAGE =
-            new WritableObjectPropertyKey<>();
-
-    public static final WritableObjectPropertyKey<String> DATE_RANGE_TIME_NOT_SET_ERROR_MESSAGE =
-            new WritableObjectPropertyKey<>();
-
     public static final WritableObjectPropertyKey<List<AssistantAdditionalSectionFactory>>
             PREPENDED_SECTIONS = new WritableObjectPropertyKey<>();
 
@@ -274,11 +235,6 @@
                 REQUEST_LOGIN_CHOICE, AVAILABLE_BILLING_ADDRESSES, AVAILABLE_CONTACTS,
                 AVAILABLE_SHIPPING_ADDRESSES, AVAILABLE_PAYMENT_INSTRUMENTS,
                 SUPPORTED_BASIC_CARD_NETWORKS, AVAILABLE_LOGINS, EXPANDED_SECTION,
-                REQUEST_DATE_RANGE, DATE_RANGE_START_OPTIONS, DATE_RANGE_START_DATE,
-                DATE_RANGE_START_TIMESLOT, DATE_RANGE_START_DATE_LABEL, DATE_RANGE_START_TIME_LABEL,
-                DATE_RANGE_END_OPTIONS, DATE_RANGE_END_DATE, DATE_RANGE_END_TIMESLOT,
-                DATE_RANGE_END_DATE_LABEL, DATE_RANGE_END_TIME_LABEL,
-                DATE_RANGE_DATE_NOT_SET_ERROR_MESSAGE, DATE_RANGE_TIME_NOT_SET_ERROR_MESSAGE,
                 PREPENDED_SECTIONS, APPENDED_SECTIONS, TERMS_REQUIRE_REVIEW_TEXT,
                 PRIVACY_NOTICE_TEXT, INFO_SECTION_TEXT, INFO_SECTION_TEXT_CENTER,
                 GENERIC_USER_INTERFACE_PREPENDED, GENERIC_USER_INTERFACE_APPENDED,
@@ -455,106 +411,6 @@
     }
 
     @CalledByNative
-    private void setRequestDateRange(boolean requestDateRange) {
-        set(REQUEST_DATE_RANGE, requestDateRange);
-    }
-
-    /** Create an instance of {@code AssistantDateTime}. */
-    @CalledByNative
-    private static AssistantDateTime createAssistantDateTime(
-            int year, int month, int day, int hour, int minute, int second) {
-        return new AssistantDateTime(year, month, day, hour, minute, second);
-    }
-
-    /** Configures the start of the date/time range. */
-    @CalledByNative
-    private void setDateTimeRangeStartOptions(
-            AssistantDateTime minDate, AssistantDateTime maxDate, String[] timeSlots) {
-        AssistantDateChoiceOptions options =
-                new AssistantDateChoiceOptions(minDate, maxDate, Arrays.asList(timeSlots));
-        set(DATE_RANGE_START_OPTIONS, options);
-    }
-
-    /** Configures the end of the date/time range. */
-    @CalledByNative
-    private void setDateTimeRangeEndOptions(
-            AssistantDateTime minDate, AssistantDateTime maxDate, String[] timeSlots) {
-        AssistantDateChoiceOptions options =
-                new AssistantDateChoiceOptions(minDate, maxDate, Arrays.asList(timeSlots));
-        set(DATE_RANGE_END_OPTIONS, options);
-    }
-
-    @CalledByNative
-    private void setDateTimeRangeStartDate(AssistantDateTime date) {
-        set(DATE_RANGE_START_DATE, date);
-    }
-
-    @CalledByNative
-    private void setDateTimeRangeStartTimeSlot(int timeSlot) {
-        set(DATE_RANGE_START_TIMESLOT, timeSlot);
-    }
-
-    @CalledByNative
-    private void setDateTimeRangeEndDate(AssistantDateTime date) {
-        set(DATE_RANGE_END_DATE, date);
-    }
-
-    @CalledByNative
-    private void setDateTimeRangeEndTimeSlot(int timeSlot) {
-        set(DATE_RANGE_END_TIMESLOT, timeSlot);
-    }
-
-    @CalledByNative
-    private void clearDateTimeRangeStartDate() {
-        set(DATE_RANGE_START_DATE, null);
-    }
-
-    @CalledByNative
-    private void clearDateTimeRangeStartTimeSlot() {
-        set(DATE_RANGE_START_TIMESLOT, null);
-    }
-
-    @CalledByNative
-    private void clearDateTimeRangeEndDate() {
-        set(DATE_RANGE_END_DATE, null);
-    }
-
-    @CalledByNative
-    private void clearDateTimeRangeEndTimeSlot() {
-        set(DATE_RANGE_END_TIMESLOT, null);
-    }
-
-    @CalledByNative
-    private void setDateTimeRangeStartDateLabel(String label) {
-        set(DATE_RANGE_START_DATE_LABEL, label);
-    }
-
-    @CalledByNative
-    private void setDateTimeRangeStartTimeLabel(String label) {
-        set(DATE_RANGE_START_TIME_LABEL, label);
-    }
-
-    @CalledByNative
-    private void setDateTimeRangeEndDateLabel(String label) {
-        set(DATE_RANGE_END_DATE_LABEL, label);
-    }
-
-    @CalledByNative
-    private void setDateTimeRangeEndTimeLabel(String label) {
-        set(DATE_RANGE_END_TIME_LABEL, label);
-    }
-
-    @CalledByNative
-    private void setDateTimeRangeDateNotSetErrorMessage(String message) {
-        set(DATE_RANGE_DATE_NOT_SET_ERROR_MESSAGE, message);
-    }
-
-    @CalledByNative
-    private void setDateTimeRangeTimeNotSetErrorMessage(String message) {
-        set(DATE_RANGE_TIME_NOT_SET_ERROR_MESSAGE, message);
-    }
-
-    @CalledByNative
     private static List<AssistantAdditionalSectionFactory> createAdditionalSectionsList() {
         return new ArrayList<>();
     }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataNativeDelegate.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataNativeDelegate.java
index c45ee14..fcba42a 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataNativeDelegate.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataNativeDelegate.java
@@ -100,68 +100,6 @@
     }
 
     @Override
-    public void onDateTimeRangeStartDateChanged(@Nullable AssistantDateTime date) {
-        if (mNativeAssistantCollectUserDataDelegate != 0) {
-            if (date != null) {
-                AssistantCollectUserDataNativeDelegateJni.get().onDateTimeRangeStartDateChanged(
-                        mNativeAssistantCollectUserDataDelegate,
-                        AssistantCollectUserDataNativeDelegate.this, date.getYear(),
-                        date.getMonth(), date.getDay());
-            } else {
-                AssistantCollectUserDataNativeDelegateJni.get().onDateTimeRangeStartDateCleared(
-                        mNativeAssistantCollectUserDataDelegate,
-                        AssistantCollectUserDataNativeDelegate.this);
-            }
-        }
-    }
-
-    @Override
-    public void onDateTimeRangeStartTimeSlotChanged(@Nullable Integer index) {
-        if (mNativeAssistantCollectUserDataDelegate != 0) {
-            if (index != null) {
-                AssistantCollectUserDataNativeDelegateJni.get().onDateTimeRangeStartTimeSlotChanged(
-                        mNativeAssistantCollectUserDataDelegate,
-                        AssistantCollectUserDataNativeDelegate.this, (int) index);
-            } else {
-                AssistantCollectUserDataNativeDelegateJni.get().onDateTimeRangeStartTimeSlotCleared(
-                        mNativeAssistantCollectUserDataDelegate,
-                        AssistantCollectUserDataNativeDelegate.this);
-            }
-        }
-    }
-
-    @Override
-    public void onDateTimeRangeEndDateChanged(@Nullable AssistantDateTime date) {
-        if (mNativeAssistantCollectUserDataDelegate != 0) {
-            if (date != null) {
-                AssistantCollectUserDataNativeDelegateJni.get().onDateTimeRangeEndDateChanged(
-                        mNativeAssistantCollectUserDataDelegate,
-                        AssistantCollectUserDataNativeDelegate.this, date.getYear(),
-                        date.getMonth(), date.getDay());
-            } else {
-                AssistantCollectUserDataNativeDelegateJni.get().onDateTimeRangeEndDateCleared(
-                        mNativeAssistantCollectUserDataDelegate,
-                        AssistantCollectUserDataNativeDelegate.this);
-            }
-        }
-    }
-
-    @Override
-    public void onDateTimeRangeEndTimeSlotChanged(@Nullable Integer index) {
-        if (mNativeAssistantCollectUserDataDelegate != 0) {
-            if (index != null) {
-                AssistantCollectUserDataNativeDelegateJni.get().onDateTimeRangeEndTimeSlotChanged(
-                        mNativeAssistantCollectUserDataDelegate,
-                        AssistantCollectUserDataNativeDelegate.this, (int) index);
-            } else {
-                AssistantCollectUserDataNativeDelegateJni.get().onDateTimeRangeEndTimeSlotCleared(
-                        mNativeAssistantCollectUserDataDelegate,
-                        AssistantCollectUserDataNativeDelegate.this);
-            }
-        }
-    }
-
-    @Override
     public void onKeyValueChanged(String key, AssistantValue value) {
         if (mNativeAssistantCollectUserDataDelegate != 0) {
             AssistantCollectUserDataNativeDelegateJni.get().onKeyValueChanged(
@@ -202,22 +140,6 @@
                 AssistantCollectUserDataNativeDelegate caller, int link);
         void onLoginChoiceChanged(long nativeAssistantCollectUserDataDelegate,
                 AssistantCollectUserDataNativeDelegate caller, String choice, int eventType);
-        void onDateTimeRangeStartDateChanged(long nativeAssistantCollectUserDataDelegate,
-                AssistantCollectUserDataNativeDelegate caller, int year, int month, int day);
-        void onDateTimeRangeStartTimeSlotChanged(long nativeAssistantCollectUserDataDelegate,
-                AssistantCollectUserDataNativeDelegate caller, int index);
-        void onDateTimeRangeEndDateChanged(long nativeAssistantCollectUserDataDelegate,
-                AssistantCollectUserDataNativeDelegate caller, int year, int month, int day);
-        void onDateTimeRangeEndTimeSlotChanged(long nativeAssistantCollectUserDataDelegate,
-                AssistantCollectUserDataNativeDelegate caller, int index);
-        void onDateTimeRangeStartDateCleared(long nativeAssistantCollectUserDataDelegate,
-                AssistantCollectUserDataNativeDelegate caller);
-        void onDateTimeRangeStartTimeSlotCleared(long nativeAssistantCollectUserDataDelegate,
-                AssistantCollectUserDataNativeDelegate caller);
-        void onDateTimeRangeEndDateCleared(long nativeAssistantCollectUserDataDelegate,
-                AssistantCollectUserDataNativeDelegate caller);
-        void onDateTimeRangeEndTimeSlotCleared(long nativeAssistantCollectUserDataDelegate,
-                AssistantCollectUserDataNativeDelegate caller);
         void onKeyValueChanged(long nativeAssistantCollectUserDataDelegate,
                 AssistantCollectUserDataNativeDelegate caller, String key, AssistantValue value);
         void onInputTextFocusChanged(long nativeAssistantCollectUserDataDelegate,
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantDateChoiceOptions.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantDateChoiceOptions.java
deleted file mode 100644
index 0350280..0000000
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantDateChoiceOptions.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// 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.
-
-package org.chromium.chrome.browser.autofill_assistant.user_data;
-
-import java.util.List;
-
-/**
- * Represents a request to let the user choose a single date/time value.
- */
-public class AssistantDateChoiceOptions {
-    private final AssistantDateTime mMinDate;
-    private final AssistantDateTime mMaxDate;
-    private final List<String> mTimeSlots;
-
-    /**
-     * @param minDate The minimum allowed value for the date/time value.
-     * @param maxDate The maximum allowed value for the date/time value.
-     * @param timeSlots The list of allowed time slots to pick from.
-     */
-    public AssistantDateChoiceOptions(
-            AssistantDateTime minDate, AssistantDateTime maxDate, List<String> timeSlots) {
-        mMinDate = minDate;
-        mMaxDate = maxDate;
-        mTimeSlots = timeSlots;
-    }
-
-    AssistantDateTime getMinDate() {
-        return mMinDate;
-    }
-
-    AssistantDateTime getMaxDate() {
-        return mMaxDate;
-    }
-
-    List<String> getTimeSlots() {
-        return mTimeSlots;
-    }
-}
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantDateSection.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantDateSection.java
deleted file mode 100644
index be3911f3..0000000
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantDateSection.java
+++ /dev/null
@@ -1,275 +0,0 @@
-// 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.
-
-package org.chromium.chrome.browser.autofill_assistant.user_data;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.autofill_assistant.AssistantChevronStyle;
-import org.chromium.chrome.browser.autofill_assistant.LayoutUtils;
-import org.chromium.content.browser.input.PopupItemType;
-import org.chromium.content.browser.input.SelectPopupDialog;
-import org.chromium.content.browser.input.SelectPopupItem;
-import org.chromium.content.browser.picker.InputDialogContainer;
-import org.chromium.content.browser.picker.InputDialogContainer.InputActionDelegate;
-import org.chromium.ui.base.ime.TextInputType;
-
-import java.text.DateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * A section which allows the user to specify a single date/time value.
- */
-public class AssistantDateSection {
-    interface Delegate {
-        void onDateChanged(@Nullable AssistantDateTime date);
-        void onTimeSlotChanged(@Nullable Integer index);
-    }
-
-    private final Context mContext;
-    private final LinearLayout mRootLayout;
-    private final TextView mDateSummaryView;
-    private final TextView mTimeSummaryView;
-    private final int mTitleToContentPadding;
-    private final Locale mLocale;
-    private final DateFormat mDateTimeFormat;
-    @Nullable
-    private Delegate mDelegate;
-    @Nullable
-    private AssistantDateChoiceOptions mDateChoiceOptions;
-    private @Nullable AssistantDateTime mCurrentValue;
-    private @Nullable AssistantDateTime mCalendarFallbackValue;
-    private @Nullable Integer mCurrentTimeSlot;
-    private String mDateNotSetErrorMessage;
-    private String mTimeNotSetErrorMessage;
-
-    AssistantDateSection(Context context, ViewGroup parent, Locale locale, DateFormat dateFormat) {
-        mContext = context;
-        mLocale = locale;
-        mDateTimeFormat = dateFormat;
-        mTitleToContentPadding = context.getResources().getDimensionPixelSize(
-                R.dimen.autofill_assistant_payment_request_title_padding);
-
-        LayoutInflater inflater = LayoutUtils.createInflater(context);
-        mRootLayout = (LinearLayout) inflater.inflate(R.layout.autofill_assistant_datetime, null);
-        mRootLayout.setVisibility(View.GONE);
-
-        AssistantVerticalExpander dateExpander = mRootLayout.findViewById(R.id.date_expander);
-        AssistantVerticalExpander timeExpander = mRootLayout.findViewById(R.id.time_expander);
-
-        setupExpander(context, dateExpander);
-        setupExpander(context, timeExpander);
-        mDateSummaryView = (TextView) dateExpander.getCollapsedView();
-        mTimeSummaryView = (TextView) timeExpander.getCollapsedView();
-
-        dateExpander.getTitleAndChevronContainer().setOnClickListener(unusedView -> {
-            if (mDateChoiceOptions == null) {
-                return;
-            }
-            InputDialogContainer inputDialogContainer =
-                    new InputDialogContainer(mContext, new InputActionDelegate() {
-                        @Override
-                        public void cancelDateTimeDialog() {
-                            // Do nothing.
-                        }
-
-                        @Override
-                        public void replaceDateTime(double value) {
-                            // User tapped the 'clear' button.
-                            if (Double.isNaN(value)) {
-                                setCurrentDate(null);
-                            } else {
-                                setCurrentDate(new AssistantDateTime((long) value));
-                            }
-                        }
-                    });
-
-            double selectedValue = Double.NaN;
-            if (mCurrentValue != null) {
-                selectedValue = mCurrentValue.getTimeInUtcMillis();
-            } else if (mCalendarFallbackValue != null) {
-                selectedValue = mCalendarFallbackValue.getTimeInUtcMillis();
-            }
-            inputDialogContainer.showDialog(TextInputType.DATE, selectedValue,
-                    mDateChoiceOptions.getMinDate().getTimeInUtcMillis(),
-                    mDateChoiceOptions.getMaxDate().getTimeInUtcMillis(), -1, null);
-        });
-
-        timeExpander.getTitleAndChevronContainer().setOnClickListener(unusedView -> {
-            if (mDateChoiceOptions == null) {
-                return;
-            }
-            List<SelectPopupItem> items = new ArrayList<>();
-            for (int i = 0; i < mDateChoiceOptions.getTimeSlots().size(); i++) {
-                items.add(new SelectPopupItem(
-                        mDateChoiceOptions.getTimeSlots().get(i), PopupItemType.ENABLED));
-            }
-
-            SelectPopupDialog dialog = new SelectPopupDialog(context,
-                    (indices)
-                            -> {
-                        if (indices == null) {
-                            return;
-                        }
-                        setCurrentTimeSlot(indices[0]);
-                    },
-                    items, false,
-                    mCurrentTimeSlot != null ? new int[] {mCurrentTimeSlot} : new int[] {});
-            dialog.show();
-        });
-
-        updateDateSummaryView();
-        updateTimeSummaryView();
-        parent.addView(mRootLayout,
-                new ViewGroup.LayoutParams(
-                        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
-    }
-
-    /** Common view configuration for the vertical expanders. */
-    private void setupExpander(Context context, AssistantVerticalExpander expander) {
-        View titleView = LayoutUtils.createInflater(context).inflate(
-                R.layout.autofill_assistant_payment_request_section_title, null);
-        TextView summaryView = new TextView(context);
-        summaryView.setSingleLine();
-
-        expander.setTitleView(titleView,
-                new LinearLayout.LayoutParams(
-                        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
-        expander.setCollapsedView(summaryView,
-                new LinearLayout.LayoutParams(
-                        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
-
-        expander.setFixed(true);
-        expander.setChevronStyle(AssistantChevronStyle.ALWAYS);
-        expander.findViewById(R.id.section_title_add_button).setVisibility(View.GONE);
-    }
-
-    void setDateChoiceOptions(AssistantDateChoiceOptions dateChoiceOptions) {
-        mDateChoiceOptions = dateChoiceOptions;
-    }
-
-    void setDelegate(Delegate delegate) {
-        mDelegate = delegate;
-    }
-
-    View getView() {
-        return mRootLayout;
-    }
-
-    void setVisible(boolean visible) {
-        mRootLayout.setVisibility(visible ? View.VISIBLE : View.GONE);
-    }
-
-    void setPaddings(int topPadding, int bottomPadding) {
-        AssistantVerticalExpander dateExpander = mRootLayout.findViewById(R.id.date_expander);
-        AssistantVerticalExpander timeExpander = mRootLayout.findViewById(R.id.time_expander);
-
-        View dateTitleView = dateExpander.getTitleView();
-        View timeTitleView = timeExpander.getTitleView();
-        dateTitleView.setPadding(dateTitleView.getPaddingLeft(), topPadding,
-                dateTitleView.getPaddingRight(), mTitleToContentPadding);
-        timeTitleView.setPadding(timeTitleView.getPaddingLeft(), topPadding,
-                timeTitleView.getPaddingRight(), mTitleToContentPadding);
-
-        View dateChevronView = dateExpander.getChevronButton();
-        View timeChevronView = timeExpander.getChevronButton();
-        dateChevronView.setPadding(dateChevronView.getPaddingLeft(), topPadding,
-                dateChevronView.getPaddingRight(), dateChevronView.getPaddingBottom());
-        timeChevronView.setPadding(timeChevronView.getPaddingLeft(), topPadding,
-                timeChevronView.getPaddingRight(), timeChevronView.getPaddingBottom());
-
-        mRootLayout.setPadding(mRootLayout.getPaddingLeft(), mRootLayout.getPaddingTop(),
-                mRootLayout.getPaddingRight(), bottomPadding);
-    }
-
-    void setDateTitle(String title) {
-        AssistantVerticalExpander dateExpander = mRootLayout.findViewById(R.id.date_expander);
-        TextView titleView = dateExpander.getTitleView().findViewById(R.id.section_title);
-        titleView.setText(title);
-    }
-
-    void setTimeTitle(String title) {
-        AssistantVerticalExpander dateExpander = mRootLayout.findViewById(R.id.time_expander);
-        TextView titleView = dateExpander.getTitleView().findViewById(R.id.section_title);
-        titleView.setText(title);
-    }
-
-    void setCurrentDate(@Nullable AssistantDateTime value) {
-        mCurrentValue = value;
-        updateDateSummaryView();
-
-        if (mDelegate != null) {
-            mDelegate.onDateChanged(mCurrentValue);
-        }
-    }
-
-    /**
-     * Sets the date that the calendar should fall back to if {@code mCurrentValue} is not set. The
-     * default is the current date.
-     */
-    void setCalendarFallbackDate(@Nullable AssistantDateTime value) {
-        mCalendarFallbackValue = value;
-    }
-
-    void setDateNotSetErrorMessage(String errorMessage) {
-        mDateNotSetErrorMessage = errorMessage;
-        updateDateSummaryView();
-    }
-
-    void setTimeNotSetErrorMessage(String errorMessage) {
-        mTimeNotSetErrorMessage = errorMessage;
-        updateTimeSummaryView();
-    }
-
-    /**
-     * @param index the current timeslot. Can be null to indicate that no timeslot is selected.
-     */
-    void setCurrentTimeSlot(@Nullable Integer index) {
-        mCurrentTimeSlot = index;
-        updateTimeSummaryView();
-
-        if (mDelegate != null) {
-            mDelegate.onTimeSlotChanged(mCurrentTimeSlot);
-        }
-    }
-
-    private void updateDateSummaryView() {
-        if (mCurrentValue == null) {
-            mDateSummaryView.setText(mDateNotSetErrorMessage);
-            ApiCompatibilityUtils.setTextAppearance(
-                    mDateSummaryView, R.style.TextAppearance_ErrorCaption);
-            return;
-        }
-
-        Calendar calendar = Calendar.getInstance(mLocale);
-        calendar.setTimeInMillis(mCurrentValue.getTimeInMillis(mLocale));
-        mDateSummaryView.setText(mDateTimeFormat.format(calendar.getTime()));
-        ApiCompatibilityUtils.setTextAppearance(
-                mDateSummaryView, R.style.TextAppearance_TextMedium_Secondary);
-    }
-
-    private void updateTimeSummaryView() {
-        if (mDateChoiceOptions == null || mCurrentTimeSlot == null) {
-            mTimeSummaryView.setText(mTimeNotSetErrorMessage);
-            ApiCompatibilityUtils.setTextAppearance(
-                    mTimeSummaryView, R.style.TextAppearance_ErrorCaption);
-            return;
-        }
-        mTimeSummaryView.setText(mDateChoiceOptions.getTimeSlots().get(mCurrentTimeSlot));
-        ApiCompatibilityUtils.setTextAppearance(
-                mTimeSummaryView, R.style.TextAppearance_TextMedium_Secondary);
-    }
-}
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionsCarouselUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionsCarouselUiTest.java
index e58e4da..e653758 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionsCarouselUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionsCarouselUiTest.java
@@ -17,7 +17,9 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.allOf;
 
+import android.content.Context;
 import android.support.test.InstrumentationRegistry;
+import android.view.ContextThemeWrapper;
 import android.widget.LinearLayout;
 
 import androidx.test.filters.MediumTest;
@@ -28,6 +30,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantActionsCarouselCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantCarouselModel;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
@@ -50,19 +53,20 @@
     @Rule
     public CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
 
+    Context mTargetContext = new ContextThemeWrapper(
+            InstrumentationRegistry.getTargetContext(), R.style.Theme_Chromium_Activity);
+
     /** Creates a coordinator for use in UI tests, and adds it to the global view hierarchy. */
     private AssistantActionsCarouselCoordinator createCoordinator(AssistantCarouselModel model)
             throws Exception {
         AssistantActionsCarouselCoordinator coordinator = TestThreadUtils.runOnUiThreadBlocking(
-                ()
-                        -> new AssistantActionsCarouselCoordinator(
-                                InstrumentationRegistry.getTargetContext(), model));
+                () -> new AssistantActionsCarouselCoordinator(mTargetContext, model));
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             // Note: apparently, we need an intermediate container for this coordinator's view,
             // otherwise the view will be invisible.
             // @TODO(crbug.com/806868) figure out why this is the case.
-            LinearLayout container = new LinearLayout(InstrumentationRegistry.getTargetContext());
+            LinearLayout container = new LinearLayout(mTargetContext);
             container.addView(coordinator.getView());
             AutofillAssistantUiTestUtil.attachToCoordinator(mTestRule.getActivity(), container);
         });
@@ -72,8 +76,8 @@
 
     @Before
     public void setUp() {
-        mTestRule.startCustomTabActivityWithIntent(CustomTabsTestUtils.createMinimalCustomTabIntent(
-                InstrumentationRegistry.getTargetContext(), "about:blank"));
+        mTestRule.startCustomTabActivityWithIntent(
+                CustomTabsTestUtils.createMinimalCustomTabIntent(mTargetContext, "about:blank"));
     }
 
     /** Tests assumptions about the initial state of the carousel. */
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java
index cc593c9e..13f34d7 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataIntegrationTest.java
@@ -12,15 +12,12 @@
 import static androidx.test.espresso.action.ViewActions.typeText;
 import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.contrib.PickerActions.setDate;
-import static androidx.test.espresso.matcher.RootMatchers.isDialog;
 import static androidx.test.espresso.matcher.ViewMatchers.hasSibling;
 import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
 import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
-import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
 import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.matcher.ViewMatchers.withParent;
@@ -48,7 +45,6 @@
 import static org.chromium.chrome.browser.autofill_assistant.ProtoTestUtil.toVisibleCssSelector;
 
 import android.os.Build;
-import android.widget.DatePicker;
 import android.widget.RadioButton;
 
 import androidx.test.espresso.matcher.ViewMatchers;
@@ -62,13 +58,11 @@
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.LocaleUtils;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
-import org.chromium.chrome.browser.autofill_assistant.carousel.ButtonView;
 import org.chromium.chrome.browser.autofill_assistant.proto.ActionProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.AutofillEntryProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.ChipIcon;
@@ -80,8 +74,6 @@
 import org.chromium.chrome.browser.autofill_assistant.proto.CollectUserDataProto.UserDataProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.CollectUserDataResultProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.ContactDetailsProto;
-import org.chromium.chrome.browser.autofill_assistant.proto.DateProto;
-import org.chromium.chrome.browser.autofill_assistant.proto.DateTimeRangeProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.DropdownSelectStrategy;
 import org.chromium.chrome.browser.autofill_assistant.proto.ElementAreaProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.ElementAreaProto.Rectangle;
@@ -113,14 +105,10 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.content_public.browser.WebContents;
 
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Calendar;
 import java.util.Collections;
 import java.util.List;
-import java.util.Locale;
 
 /**
  * Integration tests for the collect user data action.
@@ -590,137 +578,6 @@
                 .check(matches(withText(containsString("Enter a valid address"))));
     }
 
-    @Test
-    @MediumTest
-    public void testDateRange() {
-        // Create timeslots from 08:00 AM to 04:00 PM in 30 minute steps.
-        List<DateTimeRangeProto.TimeSlot> timeSlots = new ArrayList<>();
-        Calendar calendar = Calendar.getInstance();
-        calendar.set(2020, 1, 1, 8, 0, 0);
-        Locale locale = LocaleUtils.forLanguageTag("en-US");
-        DateFormat dateFormat = new SimpleDateFormat("hh:mm a", locale);
-        for (int i = 0; i <= 16; i++) {
-            timeSlots.add((DateTimeRangeProto.TimeSlot) DateTimeRangeProto.TimeSlot.newBuilder()
-                                  .setLabel(dateFormat.format(calendar.getTime()))
-                                  .setComparisonValue(i)
-                                  .build());
-            calendar.add(Calendar.MINUTE, 30);
-        }
-
-        ArrayList<ActionProto> list = new ArrayList<>();
-        list.add(ActionProto.newBuilder()
-                         .setCollectUserData(
-                                 CollectUserDataProto.newBuilder()
-                                         .setDateTimeRange(
-                                                 DateTimeRangeProto.newBuilder()
-                                                         .setStartDate(DateProto.newBuilder()
-                                                                               .setYear(2020)
-                                                                               .setMonth(1)
-                                                                               .setDay(1))
-                                                         .setEndDate(DateProto.newBuilder()
-                                                                             .setYear(2020)
-                                                                             .setMonth(1)
-                                                                             .setDay(13))
-                                                         .setMinDate(DateProto.newBuilder()
-                                                                             .setYear(2020)
-                                                                             .setMonth(1)
-                                                                             .setDay(1))
-                                                         .setMaxDate(DateProto.newBuilder()
-                                                                             .setYear(2020)
-                                                                             .setMonth(12)
-                                                                             .setDay(31))
-                                                         .addAllTimeSlots(timeSlots)
-                                                         .setStartTimeSlot(4)
-                                                         .setEndTimeSlot(4)
-                                                         .setStartDateLabel("Start date")
-                                                         .setStartTimeLabel("Start time")
-                                                         .setEndDateLabel("End date")
-                                                         .setEndTimeLabel("End time")
-                                                         .setDateNotSetError("Date not set")
-                                                         .setTimeNotSetError("Time not set"))
-                                         .setRequestTermsAndConditions(false))
-                         .build());
-        AutofillAssistantTestScript script = new AutofillAssistantTestScript(
-                SupportedScriptProto.newBuilder()
-                        .setPath("form_target_website.html")
-                        .setPresentation(PresentationProto.newBuilder().setAutostart(true))
-                        .build(),
-                list);
-
-        AutofillAssistantTestService testService =
-                new AutofillAssistantTestService(Collections.singletonList(script));
-        startAutofillAssistant(mTestRule.getActivity(), testService);
-
-        waitUntilViewMatchesCondition(withText("Continue"), isCompletelyDisplayed());
-
-        // Set end date to same as start date. Because time slots are both the same, start time will
-        // be unset.
-        onView(withText("End date")).perform(click());
-        onView(withClassName(equalTo(DatePicker.class.getName())))
-                .inRoot(isDialog())
-                .perform(setDate(2020, 1, 1));
-        onView(withText(R.string.date_picker_dialog_set)).inRoot(isDialog()).perform(click());
-
-        // Continue should be disabled, because start time is not set.
-        onView(allOf(withText("Continue"),
-                isDescendantOfA(allOf(instanceOf(ButtonView.class), not(isEnabled())))));
-
-        // Set valid start time.
-        onView(withText("Start time")).perform(click());
-        onView(withText("09:00 AM")).inRoot(isDialog()).perform(click());
-
-        onView(allOf(withText("Continue"),
-                isDescendantOfA(allOf(instanceOf(ButtonView.class), isEnabled()))));
-
-        // Change end date and time.
-        onView(withText("End date")).perform(click());
-        onView(withClassName(equalTo(DatePicker.class.getName())))
-                .inRoot(isDialog())
-                .perform(setDate(2020, 2, 16));
-        onView(withText(R.string.date_picker_dialog_set)).inRoot(isDialog()).perform(click());
-        onView(withText("End time")).perform(click());
-        onView(withText("10:30 AM")).inRoot(isDialog()).perform(click());
-
-        // Change start date and time.
-        onView(withText("Start date")).perform(click());
-        onView(withClassName(equalTo(DatePicker.class.getName())))
-                .inRoot(isDialog())
-                .perform(setDate(2020, 2, 7));
-        onView(withText(R.string.date_picker_dialog_set)).inRoot(isDialog()).perform(click());
-        onView(withText("Start time")).perform(click());
-        onView(withText("09:30 AM")).inRoot(isDialog()).perform(scrollTo(), click());
-
-        // Finish action, wait for response and prepare next set of actions.
-        List<ActionProto> nextActions = new ArrayList<>();
-        nextActions.add(
-                ActionProto.newBuilder()
-                        .setPrompt(PromptProto.newBuilder()
-                                           .setMessage("Finished")
-                                           .addChoices(PromptProto.Choice.newBuilder().setChip(
-                                                   ChipProto.newBuilder()
-                                                           .setType(ChipType.DONE_ACTION)
-                                                           .setText("End"))))
-                        .build());
-        testService.setNextActions(nextActions);
-        int numNextActionsCalled = testService.getNextActionsCounter();
-        onView(withText("Continue")).perform(click());
-        testService.waitUntilGetNextActions(numNextActionsCalled + 1);
-
-        List<ProcessedActionProto> processedActions = testService.getProcessedActions();
-        ViewMatchers.assertThat(processedActions, iterableWithSize(1));
-        ViewMatchers.assertThat(processedActions.get(0).getStatus(),
-                CoreMatchers.is(ProcessedActionStatusProto.ACTION_APPLIED));
-        CollectUserDataResultProto result = processedActions.get(0).getCollectUserDataResult();
-        assertThat(result.getDateRangeStartDate(),
-                equalTo(DateProto.newBuilder().setYear(2020).setMonth(2).setDay(7).build()));
-        // Index 3 == 09:30 PM.
-        assertThat(result.getDateRangeStartTimeslot(), is(3));
-        assertThat(result.getDateRangeEndDate(),
-                equalTo(DateProto.newBuilder().setYear(2020).setMonth(2).setDay(16).build()));
-        // Index 5 == 10:30 PM.
-        assertThat(result.getDateRangeEndTimeslot(), is(5));
-    }
-
     /**
      * Select an item in the popup list section
      */
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataTestHelper.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataTestHelper.java
index 4b107c0..72452d7 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataTestHelper.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataTestHelper.java
@@ -23,7 +23,6 @@
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataDelegate;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataModel;
-import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantDateTime;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantLoginChoice;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantTermsAndConditionsState;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantUserDataEventType;
@@ -53,8 +52,6 @@
         final AssistantVerticalExpander mPaymentSection;
         final AssistantVerticalExpander mShippingSection;
         final AssistantVerticalExpander mLoginsSection;
-        final LinearLayout mDateRangeStartSection;
-        final LinearLayout mDateRangeEndSection;
         final LinearLayout mTermsSection;
         final TextView mInfoSection;
         final AssistantChoiceList mContactList;
@@ -74,10 +71,6 @@
                     AssistantTagsForTesting.COLLECT_USER_DATA_SHIPPING_ADDRESS_SECTION_TAG);
             mLoginsSection = coordinator.getView().findViewWithTag(
                     AssistantTagsForTesting.COLLECT_USER_DATA_LOGIN_SECTION_TAG);
-            mDateRangeStartSection = coordinator.getView().findViewWithTag(
-                    AssistantTagsForTesting.COLLECT_USER_DATA_DATE_RANGE_START_TAG);
-            mDateRangeEndSection = coordinator.getView().findViewWithTag(
-                    AssistantTagsForTesting.COLLECT_USER_DATA_DATE_RANGE_END_TAG);
             mTermsSection = coordinator.getView().findViewWithTag(
                     AssistantTagsForTesting.COLLECT_USER_DATA_RADIO_TERMS_SECTION_TAG);
             mInfoSection = coordinator.getView().findViewWithTag(
@@ -108,14 +101,6 @@
         AutofillAddress mAddress;
         AutofillPaymentInstrument mPaymentMethod;
         AssistantLoginChoice mLoginChoice;
-        @Nullable
-        AssistantDateTime mDateRangeStartDate;
-        @Nullable
-        AssistantDateTime mDateRangeEndDate;
-        @Nullable
-        Integer mDateRangeStartTimeSlot;
-        @Nullable
-        Integer mDateRangeEndTimeSlot;
 
         @AssistantTermsAndConditionsState
         int mTermsStatus;
@@ -162,26 +147,6 @@
         }
 
         @Override
-        public void onDateTimeRangeStartDateChanged(@Nullable AssistantDateTime date) {
-            mDateRangeStartDate = date;
-        }
-
-        @Override
-        public void onDateTimeRangeStartTimeSlotChanged(@Nullable Integer index) {
-            mDateRangeStartTimeSlot = index;
-        }
-
-        @Override
-        public void onDateTimeRangeEndDateChanged(@Nullable AssistantDateTime date) {
-            mDateRangeEndDate = date;
-        }
-
-        @Override
-        public void onDateTimeRangeEndTimeSlotChanged(@Nullable Integer index) {
-            mDateRangeEndTimeSlot = index;
-        }
-
-        @Override
         public void onKeyValueChanged(String key, AssistantValue value) {
             mAdditionalValues.put(key, value);
         }
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataUiTest.java
index 531d935..750ea55a 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantCollectUserDataUiTest.java
@@ -11,11 +11,8 @@
 import static androidx.test.espresso.assertion.PositionAssertions.isBelow;
 import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.contrib.PickerActions.setDate;
-import static androidx.test.espresso.matcher.RootMatchers.isDialog;
 import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
 import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
 import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
@@ -24,7 +21,6 @@
 
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.empty;
-import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.lessThan;
 import static org.hamcrest.Matchers.not;
@@ -39,7 +35,6 @@
 
 import android.support.test.InstrumentationRegistry;
 import android.view.View;
-import android.widget.DatePicker;
 import android.widget.TextView;
 
 import androidx.test.espresso.matcher.ViewMatchers.Visibility;
@@ -51,7 +46,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.LocaleUtils;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
@@ -63,8 +57,6 @@
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataModel.ContactModel;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataModel.PaymentInstrumentModel;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantContactField;
-import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantDateChoiceOptions;
-import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantDateTime;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantLoginChoice;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantTermsAndConditionsState;
 import org.chromium.chrome.browser.autofill_assistant.user_data.additional_sections.AssistantAdditionalSectionFactory;
@@ -82,7 +74,6 @@
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 import java.text.DateFormat;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -228,8 +219,6 @@
         onView(is(viewHolder.mContactSection)).check(matches(not(isDisplayed())));
         onView(is(viewHolder.mPaymentSection)).check(matches(not(isDisplayed())));
         onView(is(viewHolder.mShippingSection)).check(matches(not(isDisplayed())));
-        onView(is(viewHolder.mDateRangeStartSection)).check(matches(not(isDisplayed())));
-        onView(is(viewHolder.mDateRangeEndSection)).check(matches(not(isDisplayed())));
 
         /* Contact details should be visible if either name, phone, or email is requested. */
         TestThreadUtils.runOnUiThreadBlocking(
@@ -323,11 +312,10 @@
             model.set(AssistantCollectUserDataModel.REQUEST_PAYMENT, true);
             model.set(AssistantCollectUserDataModel.REQUEST_SHIPPING_ADDRESS, true);
             model.set(AssistantCollectUserDataModel.REQUEST_LOGIN_CHOICE, true);
-            model.set(AssistantCollectUserDataModel.REQUEST_DATE_RANGE, true);
             model.set(AssistantCollectUserDataModel.VISIBLE, true);
         });
 
-        /* Empty sections should display the 'add' button in their title. */
+        // Empty sections should display the 'add' button in their title.
         onView(allOf(withId(R.id.section_title_add_button),
                        isDescendantOfA(is(viewHolder.mContactSection))))
                 .check(matches(isDisplayed()));
@@ -337,27 +325,11 @@
         onView(allOf(withId(R.id.section_title_add_button),
                        isDescendantOfA(is(viewHolder.mShippingSection))))
                 .check(matches(isDisplayed()));
-        /* ... Except for logins, date/time and additional sections, which currently do not support
-         * adding items.*/
+        // ... Except for logins and additional sections, which currently do not support
+        // adding items.
         onView(allOf(withId(R.id.section_title_add_button),
                        isDescendantOfA(is(viewHolder.mLoginsSection))))
                 .check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.section_title_add_button),
-                       isDescendantOfA(withId(R.id.date_expander)),
-                       isDescendantOfA(is(viewHolder.mDateRangeStartSection))))
-                .check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.section_title_add_button),
-                       isDescendantOfA(withId(R.id.time_expander)),
-                       isDescendantOfA(is(viewHolder.mDateRangeStartSection))))
-                .check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.section_title_add_button),
-                       isDescendantOfA(withId(R.id.date_expander)),
-                       isDescendantOfA(is(viewHolder.mDateRangeEndSection))))
-                .check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.section_title_add_button),
-                       isDescendantOfA(withId(R.id.time_expander)),
-                       isDescendantOfA(is(viewHolder.mDateRangeEndSection))))
-                .check(matches(not(isDisplayed())));
 
         /* Empty sections should be 'fixed', i.e., they can not be expanded. */
         onView(allOf(withTagValue(is(VERTICAL_EXPANDER_CHEVRON)),
@@ -370,24 +342,6 @@
                        isDescendantOfA(is(viewHolder.mShippingSection))))
                 .check(matches(not(isDisplayed())));
 
-        /* Date/time range sections should always display the chevron. */
-        onView(allOf(withTagValue(is(VERTICAL_EXPANDER_CHEVRON)),
-                       isDescendantOfA(is(viewHolder.mDateRangeStartSection)),
-                       isDescendantOfA(withId(R.id.date_expander))))
-                .check(matches(isDisplayed()));
-        onView(allOf(withTagValue(is(VERTICAL_EXPANDER_CHEVRON)),
-                       isDescendantOfA(is(viewHolder.mDateRangeStartSection)),
-                       isDescendantOfA(withId(R.id.time_expander))))
-                .check(matches(isDisplayed()));
-        onView(allOf(withTagValue(is(VERTICAL_EXPANDER_CHEVRON)),
-                       isDescendantOfA(is(viewHolder.mDateRangeEndSection)),
-                       isDescendantOfA(withId(R.id.date_expander))))
-                .check(matches(isDisplayed()));
-        onView(allOf(withTagValue(is(VERTICAL_EXPANDER_CHEVRON)),
-                       isDescendantOfA(is(viewHolder.mDateRangeEndSection)),
-                       isDescendantOfA(withId(R.id.time_expander))))
-                .check(matches(isDisplayed()));
-
         /* Empty sections are collapsed. */
         onView(allOf(withTagValue(is(COLLECT_USER_DATA_CHOICE_LIST)),
                        isDescendantOfA(is(viewHolder.mContactSection))))
@@ -658,7 +612,6 @@
                     mDefaultContactFullOptions);
             model.set(AssistantCollectUserDataModel.REQUEST_PAYMENT, true);
             model.set(AssistantCollectUserDataModel.REQUEST_SHIPPING_ADDRESS, true);
-            model.set(AssistantCollectUserDataModel.REQUEST_DATE_RANGE, true);
             AutofillContact contact = AssistantCollectUserDataModel.createAutofillContact(
                     mTestRule.getActivity(), profile, /* requestName= */ true,
                     /* requestPhone= */ true, /* requestEmail= */ true);
@@ -981,305 +934,6 @@
 
     @Test
     @MediumTest
-    public void testDateRangeLocaleUS() throws Exception {
-        AssistantCollectUserDataModel model = createCollectUserDataModel();
-        Locale locale = LocaleUtils.forLanguageTag("en-US");
-        AssistantCollectUserDataCoordinator coordinator = createCollectUserDataCoordinator(
-                model, locale, new SimpleDateFormat("MMM d, yyyy", locale));
-        AutofillAssistantCollectUserDataTestHelper.MockDelegate delegate =
-                new AutofillAssistantCollectUserDataTestHelper.MockDelegate();
-        AutofillAssistantCollectUserDataTestHelper
-                .ViewHolder viewHolder = TestThreadUtils.runOnUiThreadBlocking(
-                () -> new AutofillAssistantCollectUserDataTestHelper.ViewHolder(coordinator));
-
-        List<String> timeSlots = new ArrayList<>();
-        timeSlots.add("08:00 AM");
-        timeSlots.add("09:00 AM");
-
-        AssistantDateTime startDate = new AssistantDateTime(2019, 10, 21, 0, 0, 0);
-        AssistantDateTime endDate = new AssistantDateTime(2019, 11, 7, 0, 0, 0);
-
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            model.set(AssistantCollectUserDataModel.DELEGATE, delegate);
-            model.set(AssistantCollectUserDataModel.REQUEST_DATE_RANGE, true);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_OPTIONS,
-                    new AssistantDateChoiceOptions(new AssistantDateTime(2019, 10, 21, 0, 0, 0),
-                            new AssistantDateTime(2020, 10, 21, 0, 0, 0), timeSlots));
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_OPTIONS,
-                    new AssistantDateChoiceOptions(new AssistantDateTime(2019, 10, 21, 0, 0, 0),
-                            new AssistantDateTime(2020, 10, 21, 0, 0, 0), timeSlots));
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_DATE, startDate);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_TIMESLOT, 0);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_DATE, endDate);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_TIMESLOT, 1);
-
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_DATE_LABEL, "Start date");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_TIME_LABEL, "Start time");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_DATE_LABEL, "End date");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_TIME_LABEL, "End time");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_DATE_NOT_SET_ERROR_MESSAGE,
-                    "Date not set");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_TIME_NOT_SET_ERROR_MESSAGE,
-                    "Time not set");
-            model.set(AssistantCollectUserDataModel.VISIBLE, true);
-        });
-
-        onView(withText("Date not set")).check(doesNotExist());
-        onView(withText("Time not set")).check(doesNotExist());
-
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeStartSection)),
-                       withText("Oct 21, 2019")))
-                .check(matches(isDisplayed()));
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeStartSection)), withText("08:00 AM")))
-                .check(matches(isDisplayed()));
-
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeEndSection)), withText("Nov 7, 2019")))
-                .check(matches(isDisplayed()));
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeEndSection)), withText("09:00 AM")))
-                .check(matches(isDisplayed()));
-
-        assertThat(delegate.mDateRangeStartDate.getTimeInUtcMillis(),
-                is(startDate.getTimeInUtcMillis()));
-        assertThat(delegate.mDateRangeStartTimeSlot, is(0));
-        assertThat(
-                delegate.mDateRangeEndDate.getTimeInUtcMillis(), is(endDate.getTimeInUtcMillis()));
-        assertThat(delegate.mDateRangeEndTimeSlot, is(1));
-    }
-
-    @Test
-    @MediumTest
-    public void testDateRangeLocaleDE() throws Exception {
-        AssistantCollectUserDataModel model = createCollectUserDataModel();
-        Locale locale = LocaleUtils.forLanguageTag("de-DE");
-        AssistantCollectUserDataCoordinator coordinator = createCollectUserDataCoordinator(
-                model, locale, new SimpleDateFormat("dd.MM.yyyy", locale));
-        AutofillAssistantCollectUserDataTestHelper.MockDelegate delegate =
-                new AutofillAssistantCollectUserDataTestHelper.MockDelegate();
-        AutofillAssistantCollectUserDataTestHelper
-                .ViewHolder viewHolder = TestThreadUtils.runOnUiThreadBlocking(
-                () -> new AutofillAssistantCollectUserDataTestHelper.ViewHolder(coordinator));
-
-        List<String> timeSlots = new ArrayList<>();
-        timeSlots.add("08:00");
-        timeSlots.add("09:00");
-
-        AssistantDateTime startDate = new AssistantDateTime(2019, 10, 21, 0, 0, 0);
-        AssistantDateTime endDate = new AssistantDateTime(2019, 11, 7, 0, 0, 0);
-
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            model.set(AssistantCollectUserDataModel.DELEGATE, delegate);
-            model.set(AssistantCollectUserDataModel.REQUEST_DATE_RANGE, true);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_OPTIONS,
-                    new AssistantDateChoiceOptions(new AssistantDateTime(2019, 10, 21, 0, 0, 0),
-                            new AssistantDateTime(2020, 10, 21, 0, 0, 0), timeSlots));
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_OPTIONS,
-                    new AssistantDateChoiceOptions(new AssistantDateTime(2019, 10, 21, 0, 0, 0),
-                            new AssistantDateTime(2020, 10, 21, 0, 0, 0), timeSlots));
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_DATE, startDate);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_TIMESLOT, 0);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_DATE, endDate);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_TIMESLOT, 1);
-
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_DATE_LABEL, "Start date");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_TIME_LABEL, "Start time");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_DATE_LABEL, "End date");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_TIME_LABEL, "End time");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_DATE_NOT_SET_ERROR_MESSAGE,
-                    "Date not set");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_TIME_NOT_SET_ERROR_MESSAGE,
-                    "Time not set");
-            model.set(AssistantCollectUserDataModel.VISIBLE, true);
-        });
-
-        onView(withText("Date not set")).check(doesNotExist());
-        onView(withText("Time not set")).check(doesNotExist());
-
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeStartSection)),
-                       withText("21.10.2019")))
-                .check(matches(isDisplayed()));
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeStartSection)), withText("08:00")))
-                .check(matches(isDisplayed()));
-
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeEndSection)), withText("07.11.2019")))
-                .check(matches(isDisplayed()));
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeEndSection)), withText("09:00")))
-                .check(matches(isDisplayed()));
-
-        assertThat(delegate.mDateRangeStartDate.getTimeInUtcMillis(),
-                is(startDate.getTimeInUtcMillis()));
-        assertThat(delegate.mDateRangeStartTimeSlot, is(0));
-        assertThat(
-                delegate.mDateRangeEndDate.getTimeInUtcMillis(), is(endDate.getTimeInUtcMillis()));
-        assertThat(delegate.mDateRangeEndTimeSlot, is(1));
-    }
-
-    @Test
-    @MediumTest
-    public void testDateOrTimeNotSet() throws Exception {
-        AssistantCollectUserDataModel model = createCollectUserDataModel();
-        Locale locale = LocaleUtils.forLanguageTag("en-US");
-        AssistantCollectUserDataCoordinator coordinator = createCollectUserDataCoordinator(
-                model, locale, new SimpleDateFormat("MMM d, yyyy", locale));
-        AutofillAssistantCollectUserDataTestHelper.MockDelegate delegate =
-                new AutofillAssistantCollectUserDataTestHelper.MockDelegate();
-        AutofillAssistantCollectUserDataTestHelper
-                .ViewHolder viewHolder = TestThreadUtils.runOnUiThreadBlocking(
-                () -> new AutofillAssistantCollectUserDataTestHelper.ViewHolder(coordinator));
-
-        List<String> timeSlots = new ArrayList<>();
-        timeSlots.add("08:00 AM");
-        timeSlots.add("09:00 AM");
-
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            model.set(AssistantCollectUserDataModel.DELEGATE, delegate);
-            model.set(AssistantCollectUserDataModel.REQUEST_DATE_RANGE, true);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_OPTIONS,
-                    new AssistantDateChoiceOptions(new AssistantDateTime(2019, 10, 21, 0, 0, 0),
-                            new AssistantDateTime(2020, 10, 21, 0, 0, 0), timeSlots));
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_OPTIONS,
-                    new AssistantDateChoiceOptions(new AssistantDateTime(2019, 10, 21, 0, 0, 0),
-                            new AssistantDateTime(2020, 10, 21, 0, 0, 0), timeSlots));
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_DATE, null);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_TIMESLOT, null);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_DATE, null);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_TIMESLOT, null);
-
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_DATE_LABEL, "Start date");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_TIME_LABEL, "Start time");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_DATE_LABEL, "End date");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_TIME_LABEL, "End time");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_DATE_NOT_SET_ERROR_MESSAGE,
-                    "Date not set");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_TIME_NOT_SET_ERROR_MESSAGE,
-                    "Time not set");
-            model.set(AssistantCollectUserDataModel.VISIBLE, true);
-        });
-
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeStartSection)),
-                       withText("Date not set")))
-                .check(matches(isDisplayed()));
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeStartSection)),
-                       withText("Time not set")))
-                .check(matches(isDisplayed()));
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeEndSection)),
-                       withText("Date not set")))
-                .check(matches(isDisplayed()));
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeEndSection)),
-                       withText("Time not set")))
-                .check(matches(isDisplayed()));
-
-        TestThreadUtils.runOnUiThreadBlocking(
-                ()
-                        -> model.set(AssistantCollectUserDataModel.DATE_RANGE_START_DATE,
-                                new AssistantDateTime(2019, 10, 21, 0, 0, 0)));
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> model.set(AssistantCollectUserDataModel.DATE_RANGE_START_TIMESLOT, 0));
-        TestThreadUtils.runOnUiThreadBlocking(
-                ()
-                        -> model.set(AssistantCollectUserDataModel.DATE_RANGE_END_DATE,
-                                new AssistantDateTime(2019, 11, 7, 0, 0, 0)));
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> model.set(AssistantCollectUserDataModel.DATE_RANGE_END_TIMESLOT, 0));
-
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeStartSection)),
-                       withText("Date not set")))
-                .check(doesNotExist());
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeStartSection)),
-                       withText("Time not set")))
-                .check(doesNotExist());
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeEndSection)),
-                       withText("Date not set")))
-                .check(doesNotExist());
-        onView(allOf(isDescendantOfA(is(viewHolder.mDateRangeEndSection)),
-                       withText("Time not set")))
-                .check(doesNotExist());
-    }
-
-    @Test
-    @MediumTest
-    public void testDateRangePopups() throws Exception {
-        AssistantCollectUserDataModel model = createCollectUserDataModel();
-        Locale locale = LocaleUtils.forLanguageTag("en-US");
-        AssistantCollectUserDataCoordinator coordinator = createCollectUserDataCoordinator(
-                model, locale, new SimpleDateFormat("MMM d, yyyy", locale));
-        AutofillAssistantCollectUserDataTestHelper.MockDelegate delegate =
-                new AutofillAssistantCollectUserDataTestHelper.MockDelegate();
-        AutofillAssistantCollectUserDataTestHelper
-                .ViewHolder viewHolder = TestThreadUtils.runOnUiThreadBlocking(
-                () -> new AutofillAssistantCollectUserDataTestHelper.ViewHolder(coordinator));
-
-        List<String> timeSlots = new ArrayList<>();
-        timeSlots.add("08:00 AM");
-        timeSlots.add("09:00 AM");
-
-        AssistantDateTime startDate = new AssistantDateTime(2019, 10, 21, 0, 0, 0);
-        AssistantDateTime endDate = new AssistantDateTime(2019, 11, 7, 0, 0, 0);
-
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            model.set(AssistantCollectUserDataModel.DELEGATE, delegate);
-            model.set(AssistantCollectUserDataModel.REQUEST_DATE_RANGE, true);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_OPTIONS,
-                    new AssistantDateChoiceOptions(new AssistantDateTime(2019, 10, 21, 0, 0, 0),
-                            new AssistantDateTime(2020, 10, 21, 0, 0, 0), timeSlots));
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_OPTIONS,
-                    new AssistantDateChoiceOptions(new AssistantDateTime(2019, 10, 21, 0, 0, 0),
-                            new AssistantDateTime(2020, 10, 21, 0, 0, 0), timeSlots));
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_DATE, startDate);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_TIMESLOT, 0);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_DATE, endDate);
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_TIMESLOT, 1);
-
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_DATE_LABEL, "Start date");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_START_TIME_LABEL, "Start time");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_DATE_LABEL, "End date");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_END_TIME_LABEL, "End time");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_DATE_NOT_SET_ERROR_MESSAGE,
-                    "Date not set");
-            model.set(AssistantCollectUserDataModel.DATE_RANGE_TIME_NOT_SET_ERROR_MESSAGE,
-                    "Time not set");
-            model.set(AssistantCollectUserDataModel.VISIBLE, true);
-        });
-
-        AssistantDateTime newStartDate = new AssistantDateTime(2019, 11, 3, 0, 0, 0);
-        AssistantDateTime newEndDate = new AssistantDateTime(2019, 11, 12, 0, 0, 0);
-
-        onView(allOf(withId(R.id.date_expander),
-                       isDescendantOfA(is(viewHolder.mDateRangeStartSection))))
-                .perform(click());
-        onView(withClassName(equalTo(DatePicker.class.getName())))
-                .inRoot(isDialog())
-                .perform(setDate(
-                        newStartDate.getYear(), newStartDate.getMonth(), newStartDate.getDay()));
-        onView(withText(R.string.date_picker_dialog_set)).inRoot(isDialog()).perform(click());
-
-        onView(allOf(withId(R.id.time_expander),
-                       isDescendantOfA(is(viewHolder.mDateRangeStartSection))))
-                .perform(click());
-        onView(withText("09:00 AM")).inRoot(isDialog()).perform(click());
-
-        onView(allOf(withId(R.id.date_expander),
-                       isDescendantOfA(is(viewHolder.mDateRangeEndSection))))
-                .perform(click());
-        onView(withClassName(equalTo(DatePicker.class.getName())))
-                .inRoot(isDialog())
-                .perform(setDate(newEndDate.getYear(), newEndDate.getMonth(), newEndDate.getDay()));
-        onView(withText(R.string.date_picker_dialog_set)).inRoot(isDialog()).perform(click());
-
-        onView(allOf(withId(R.id.time_expander),
-                       isDescendantOfA(is(viewHolder.mDateRangeEndSection))))
-                .perform(click());
-        onView(withText("08:00 AM")).inRoot(isDialog()).perform(click());
-
-        assertThat(delegate.mDateRangeStartDate.getTimeInUtcMillis(),
-                is(newStartDate.getTimeInUtcMillis()));
-        assertThat(delegate.mDateRangeStartTimeSlot, is(1));
-        assertThat(delegate.mDateRangeEndDate.getTimeInUtcMillis(),
-                is(newEndDate.getTimeInUtcMillis()));
-        assertThat(delegate.mDateRangeEndTimeSlot, is(0));
-    }
-
-    @Test
-    @MediumTest
     public void testAdditionalStaticSections() throws Exception {
         AssistantCollectUserDataModel model = createCollectUserDataModel();
         AssistantCollectUserDataCoordinator coordinator = createCollectUserDataCoordinator(model);
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDetailsUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDetailsUiTest.java
index 3da6ae03..e6348a6 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDetailsUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDetailsUiTest.java
@@ -120,7 +120,7 @@
                     new AssistantDependenciesFactoryChrome().createStaticDependencies();
 
             return new AssistantDetailsCoordinator(InstrumentationRegistry.getTargetContext(),
-                    staticDependencies.getInfoPageUtil(), model,
+                    staticDependencies.createInfoPageUtil(), model,
                     new AutofillAssistantUiTestUtil.MockImageFetcher(testImage, null));
         });
 
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java
index f8e36a4..2816571a 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java
@@ -107,7 +107,7 @@
             AssistantDependencies dependencies = new AssistantDependenciesChrome(getActivity());
             AssistantHeaderCoordinator coordinator = new AssistantHeaderCoordinator(getActivity(),
                     model, dependencies.getAccessibilityUtil(),
-                    dependencies.getProfileImageUtilOrNull(getActivity()));
+                    dependencies.createProfileImageUtilOrNull(getActivity()));
 
             CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams(
                     ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
index 370324c..db896ed 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
@@ -116,8 +116,9 @@
                     getActivity().getCompositorViewHolderForTesting(),
                     getActivity().getBrowserControlsManager(),
                     getActivity().getWindowAndroid().getApplicationBottomInsetProvider(),
-                    staticDependencies.getAccessibilityUtil(), staticDependencies.getInfoPageUtil(),
-                    staticDependencies.getProfileImageUtilOrNull(getActivity()));
+                    staticDependencies.getAccessibilityUtil(),
+                    staticDependencies.createInfoPageUtil(),
+                    staticDependencies.createProfileImageUtilOrNull(getActivity()));
             coordinator.show();
             return coordinator;
         });
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinatorTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinatorTest.java
index cb74346..4db538f 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinatorTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinatorTest.java
@@ -110,7 +110,7 @@
         mOnboardingCoordinatorFactory = new OnboardingCoordinatorFactory(mActivity,
                 mBottomSheetController, mActivity.getBrowserControlsManager(),
                 mActivity.getCompositorViewHolderForTesting(),
-                staticDependencies.getAccessibilityUtil(), staticDependencies.getInfoPageUtil());
+                staticDependencies.getAccessibilityUtil(), staticDependencies.createInfoPageUtil());
     }
 
     private BaseOnboardingCoordinator createCoordinator() {
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/DirectActionsIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/DirectActionsIntegrationTest.java
index cf6ae57d..02bc77f3 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/DirectActionsIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/DirectActionsIntegrationTest.java
@@ -103,7 +103,7 @@
             mDirectActionHandler = AutofillAssistantFacade.createDirectActionHandler(
                     mTestRule.getActivity(), dependencies.getBottomSheetController(),
                     dependencies.getBrowserControls(), dependencies.getRootView(),
-                    dependencies.getActivityTabProvider());
+                    mTestRule.getActivity().getActivityTabProvider());
         });
     }
 
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/TestingAutofillAssistantModuleEntryProvider.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/TestingAutofillAssistantModuleEntryProvider.java
index fed6e3bb..a74eef0 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/TestingAutofillAssistantModuleEntryProvider.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/TestingAutofillAssistantModuleEntryProvider.java
@@ -41,7 +41,7 @@
             super(new OnboardingCoordinatorFactory(context, bottomSheetController, browserControls,
                           rootView,
                           dependenciesFactory.createStaticDependencies().getAccessibilityUtil(),
-                          dependenciesFactory.createStaticDependencies().getInfoPageUtil()),
+                          dependenciesFactory.createStaticDependencies().createInfoPageUtil()),
                     webContentsSupplier, dependenciesFactory);
         }
 
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependencies.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependencies.java
index 4622757..f27bf22b 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependencies.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDependencies.java
@@ -9,9 +9,8 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
-import org.chromium.chrome.browser.ActivityTabProvider;
+import org.chromium.base.lifetime.Destroyable;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
-import org.chromium.chrome.browser.ui.TabObscuringHandler;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.KeyboardVisibilityDelegate;
@@ -47,14 +46,16 @@
 
     ApplicationViewportInsetSupplier getBottomInsetProvider();
 
-    ActivityTabProvider getActivityTabProvider();
-
-    TabObscuringHandler getTabObscuringHandler();
-
     View getRootView();
 
     AssistantSnackbarFactory getSnackbarFactory();
 
+    /**
+     * Observes tab changes.
+     * @return The destroyer that must be called to unregister the internal observer.
+     */
+    Destroyable observeTabChanges(AssistantTabObserver tabObserver);
+
     // Only called by native to guarantee future type safety.
     @CalledByNative
     default AssistantStaticDependencies getStaticDependencies() {
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependencies.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependencies.java
index 87702320..7e4430d1 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependencies.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependencies.java
@@ -32,18 +32,18 @@
     AssistantTabObscuringUtil getTabObscuringUtilOrNull(WindowAndroid windowAndroid);
 
     @CalledByNative
-    AssistantInfoPageUtil getInfoPageUtil();
+    AssistantInfoPageUtil createInfoPageUtil();
 
-    AssistantFeedbackUtil getFeedbackUtil();
+    AssistantFeedbackUtil createFeedbackUtil();
 
-    AssistantTabUtil getTabUtil();
+    AssistantTabUtil createTabUtil();
 
     @CalledByNative
-    AssistantAccessTokenUtil getAccessTokenUtil();
+    AssistantAccessTokenUtil createAccessTokenUtil();
 
     @Nullable
     String getSignedInAccountEmailOrNull();
 
     @Nullable
-    AssistantProfileImageUtil getProfileImageUtilOrNull(Context context);
+    AssistantProfileImageUtil createProfileImageUtilOrNull(Context context);
 }
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabObserver.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabObserver.java
new file mode 100644
index 0000000..be7d687
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabObserver.java
@@ -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.
+
+package org.chromium.chrome.browser.autofill_assistant;
+
+import androidx.annotation.Nullable;
+
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * Observer for different tab events.
+ */
+public interface AssistantTabObserver {
+    void onObservingDifferentTab(
+            boolean isTabNull, @Nullable WebContents webContents, boolean isHint);
+
+    void onActivityAttachmentChanged(
+            @Nullable WebContents webContents, @Nullable WindowAndroid windowAndroid);
+}
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/Starter.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/Starter.java
index 374c24e..b692dc4c 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/Starter.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/Starter.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.autofill_assistant;
 
+import android.app.Activity;
+
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
@@ -11,6 +13,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
+import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.autofill_assistant.metrics.FeatureModuleInstallation;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
@@ -27,15 +30,15 @@
  */
 @JNINamespace("autofill_assistant")
 public class Starter extends EmptyTabObserver implements UserData {
-    /** The tab that this starter tracks. */
-    private final Tab mTab;
+    /** A supplier for the activity of the tab that this starter tracks. */
+    private final Supplier<Activity> mActivitySupplier;
 
     private final AssistantIsGsaFunction mIsGsaFunction;
     private final AssistantIsMsbbEnabledFunction mIsMsbbEnabledFunction;
     private final AssistantModuleInstallUi.Provider mModuleInstallUiProvider;
 
     /**
-     * The WebContents associated with the Tab which this starter is monitoring, unless detached.
+     * The WebContents associated with the tab which this starter is monitoring, unless detached.
      */
     private @Nullable WebContents mWebContents;
 
@@ -68,7 +71,7 @@
     public Starter(Tab tab, AssistantIsGsaFunction isGsaFunction,
             AssistantIsMsbbEnabledFunction isMsbbEnabledFunction,
             AssistantModuleInstallUi.Provider moduleInstallUiProvider) {
-        mTab = tab;
+        mActivitySupplier = () -> TabUtils.getActivity(tab);
         mIsGsaFunction = isGsaFunction;
         mIsMsbbEnabledFunction = isMsbbEnabledFunction;
         mModuleInstallUiProvider = moduleInstallUiProvider;
@@ -299,8 +302,8 @@
     private Object[] getOrCreateDependenciesAndOnboardingHelper() {
         if (mDependencies == null) {
             AutofillAssistantModuleEntry module = getModuleOrThrow();
-            mDependencies = module.createDependenciesFactory().createDependencies(
-                    TabUtils.getActivity(mTab));
+            mDependencies =
+                    module.createDependenciesFactory().createDependencies(mActivitySupplier.get());
             mOnboardingHelper = module.createOnboardingHelper(mWebContents, mDependencies);
         }
 
@@ -309,7 +312,7 @@
 
     @CalledByNative
     private boolean getIsTabCreatedByGSA() {
-        return mIsGsaFunction.apply(TabUtils.getActivity(mTab));
+        return mIsGsaFunction.apply(mActivitySupplier.get());
     }
 
     @NativeMethods
diff --git a/chrome/android/features/autofill_assistant/public/java_sources.gni b/chrome/android/features/autofill_assistant/public/java_sources.gni
index 5c409dc..e645c1a 100644
--- a/chrome/android/features/autofill_assistant/public/java_sources.gni
+++ b/chrome/android/features/autofill_assistant/public/java_sources.gni
@@ -20,6 +20,7 @@
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantSnackbarFactory.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantStaticDependencies.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabObscuringUtil.java",
+  "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabObserver.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantTabUtil.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionHandler.java",
   "//chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectAction.java",
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
index fb8430e..1b77772c 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
@@ -33,6 +33,7 @@
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.compositor.CompositorViewHolder;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager;
+import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagerSupplier;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.fullscreen.FullscreenOptions;
@@ -485,7 +486,8 @@
         if (VrModuleProvider.getDelegate().isInVr()) return false;
 
         // Don't open the accessory inside the contextual search panel.
-        ContextualSearchManager contextualSearch = mActivity.getContextualSearchManager();
+        ContextualSearchManager contextualSearch =
+                ContextualSearchManagerSupplier.getValueOrNullFrom(mWindowAndroid);
         if (contextualSearch != null && contextualSearch.isSearchPanelOpened()) return false;
 
         // If an accessory sheet was opened, the accessory bar must be visible.
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTabSwitcherTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTabSwitcherTest.java
index 04b3601..12d8676 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTabSwitcherTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTabSwitcherTest.java
@@ -478,10 +478,10 @@
 
     @Test
     @MediumTest
+    @UseMethodParameter(LVTIsSRPTestParams.class)
     // clang-format off
     @CommandLineFlags.Add({ChromeSwitches.DISABLE_NATIVE_INITIALIZATION,
-        INSTANT_START_TEST_BASE_PARAMS  + "/show_last_active_tab_only/true"})
-    @UseMethodParameter(LVTIsSRPTestParams.class)
+        INSTANT_START_TEST_BASE_PARAMS})
     public void testRecordLastVisitedTabIsSRPHistogram(boolean isSingleTabSwitcher, boolean isSRP)
             throws IOException {
         // clang-format on
diff --git a/chrome/android/features/tab_ui/java/res/drawable/ic_check_googblue_20dp_animated.xml b/chrome/android/features/tab_ui/java/res/drawable/ic_check_googblue_20dp_animated.xml
index d41f72e5..abda2141 100644
--- a/chrome/android/features/tab_ui/java/res/drawable/ic_check_googblue_20dp_animated.xml
+++ b/chrome/android/features/tab_ui/java/res/drawable/ic_check_googblue_20dp_animated.xml
@@ -6,8 +6,7 @@
 <animated-vector
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:aapt="http://schemas.android.com/aapt"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21">
+    xmlns:tools="http://schemas.android.com/tools">
 
     <aapt:attr name="android:drawable">
         <vector
@@ -35,4 +34,4 @@
                 tools:valueFrom="1"/>
         </aapt:attr>
     </target>
-</animated-vector>
\ No newline at end of file
+</animated-vector>
diff --git a/chrome/android/features/tab_ui/java/res/layout/new_tab_tile_card_item.xml b/chrome/android/features/tab_ui/java/res/layout/new_tab_tile_card_item.xml
index b7556a0..f07b91d 100644
--- a/chrome/android/features/tab_ui/java/res/layout/new_tab_tile_card_item.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/new_tab_tile_card_item.xml
@@ -16,7 +16,7 @@
         android:layout_width="48dp"
         android:layout_height="48dp"
         app:srcCompat="@drawable/new_tab_icon"
-        android:tint="?attr/default_icon_color_secondary"
+        android:tint="@macro/default_icon_color_secondary"
         android:contentDescription="@string/accessibility_toolbar_btn_new_tab"/>
 
 </org.chromium.chrome.browser.tasks.tab_management.NewTabTileView>
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 b9e70f0..1a1adb41 100644
--- a/chrome/android/features/tab_ui/java/res/values/colors.xml
+++ b/chrome/android/features/tab_ui/java/res/values/colors.xml
@@ -6,7 +6,7 @@
 <resources xmlns:tools="http://schemas.android.com/tools">
     <!-- Tab Switcher colors. -->
     <color name="tab_grid_card_view_tint_color">@color/legacy_bg_color_elev_4</color>
-    <color name="tab_grid_card_view_tint_color_incognito">@color/default_bg_color_dark_elev_4</color>
+    <color name="tab_grid_card_view_tint_color_incognito">@color/default_bg_color_dark_elev_4_baseline</color>
 
     <color name="tab_grid_card_title_text_color">@color/default_text_color_list</color>
     <color name="tab_grid_card_title_text_color_incognito">@color/default_text_color_light_list</color>
@@ -20,7 +20,7 @@
     <color name="tab_grid_card_divider_tint_color_incognito">@color/divider_line_bg_color_light</color>
 
     <color name="tab_grid_card_thumbnail_placeholder_color">@color/default_bg_color_secondary</color>
-    <color name="tab_grid_card_thumbnail_placeholder_color_incognito">@color/default_bg_color_dark_elev_3</color>
+    <color name="tab_grid_card_thumbnail_placeholder_color_incognito">@color/default_bg_color_dark_elev_3_baseline</color>
 
     <color name="tab_grid_card_selected_color">@color/default_control_color_active_baseline</color>
     <color name="tab_grid_card_selected_color_incognito">@color/default_control_color_active_dark</color>
@@ -30,7 +30,7 @@
     <color name="tab_list_mini_card_default_background_color_incognito">@color/default_bg_color_secondary_dark</color>
 
     <color name="tab_grid_dialog_background_color">@color/legacy_bg_color_elev_1</color>
-    <color name="tab_grid_dialog_background_color_incognito">@color/default_bg_color_dark_elev_1</color>
+    <color name="tab_grid_dialog_background_color_incognito">@color/default_bg_color_dark_elev_1_baseline</color>
 
     <color name="tab_grid_dialog_ungroup_button_text_color">@color/default_icon_color_accent1_baseline</color>
     <color name="tab_grid_dialog_ungroup_button_text_color_incognito">@color/default_icon_color_blue_light</color>
@@ -44,7 +44,7 @@
     <color name="hovered_tab_grid_card_background_color">@color/default_bg_color_secondary</color>
     <color name="hovered_tab_grid_card_background_color_incognito">@color/default_bg_color_secondary_dark</color>
 
-    <color name="new_tab_tile_plus_color">@color/default_icon_color_secondary</color>
+    <color name="new_tab_tile_plus_color">@color/default_icon_color_secondary_tint_list</color>
     <color name="new_tab_tile_plus_color_incognito">@color/default_icon_color_secondary_light</color>
 
     <!-- Incognito colors for theme refactor 2021. -->
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
index 2e96544..864956f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
@@ -25,6 +25,7 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.tab_ui.R;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.widget.ChromeImageView;
 
@@ -150,10 +151,10 @@
     }
 
     void setIsIncognito(boolean isIncognito) {
-        @ColorRes
-        int primaryColorRes = isIncognito ? R.color.dialog_bg_color_dark : R.color.dialog_bg_color;
         @ColorInt
-        int primaryColor = getResources().getColor(primaryColorRes);
+        int primaryColor =
+                isIncognito ? getResources().getColor(R.color.dialog_bg_color_dark_baseline)
+                            : SemanticColorUtils.getDialogBgColor(getContext());
         setPrimaryColor(primaryColor);
 
         @ColorRes
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewBinderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewBinderTest.java
index 89fadd5..de3252c 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewBinderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewBinderTest.java
@@ -182,9 +182,8 @@
                                 .getDefaultColor()));
         assertThat(actionButton.getCurrentTextColor(),
                 equalTo(SemanticColorUtils.getDefaultTextColorLink(mItemView.getContext())));
-        assertThat(closeButton.getImageTintList(),
-                equalTo(AppCompatResources.getColorStateList(
-                        getActivity(), R.color.default_icon_color_tint_list)));
+        assertThat(closeButton.getImageTintList().getDefaultColor(),
+                equalTo(getActivity().getColor(R.color.default_icon_color_tint_list)));
 
         mItemViewModel.set(MessageCardViewProperties.IS_INCOGNITO, true);
         assertThat(description.getCurrentTextColor(),
@@ -193,9 +192,8 @@
         assertThat(actionButton.getCurrentTextColor(),
                 equalTo(ApiCompatibilityUtils.getColor(
                         mItemView.getResources(), R.color.default_text_color_link_light)));
-        assertThat(closeButton.getImageTintList(),
-                equalTo(AppCompatResources.getColorStateList(
-                        getActivity(), R.color.default_icon_color_light)));
+        assertThat(closeButton.getImageTintList().getDefaultColor(),
+                equalTo(getActivity().getColor(R.color.default_icon_color_light)));
     }
 
     @Override
diff --git a/chrome/android/java/res/drawable-ldrtl/google_pay_with_divider.xml b/chrome/android/java/res/drawable-ldrtl/google_pay_with_divider.xml
index d8d4d77f..ba9f0f5b 100644
--- a/chrome/android/java/res/drawable-ldrtl/google_pay_with_divider.xml
+++ b/chrome/android/java/res/drawable-ldrtl/google_pay_with_divider.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:viewportWidth="51"
     android:viewportHeight="18"
     android:width="51dp"
diff --git a/chrome/android/java/res/drawable/data_reduction_big.xml b/chrome/android/java/res/drawable/data_reduction_big.xml
index d0ab0cd..f319630 100644
--- a/chrome/android/java/res/drawable/data_reduction_big.xml
+++ b/chrome/android/java/res/drawable/data_reduction_big.xml
@@ -4,22 +4,21 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="40dp"
     android:height="40dp"
     android:viewportWidth="40"
-    android:viewportHeight="40">
+    android:viewportHeight="40"
+    android:tint="@macro/default_icon_color_secondary">
   <path
       android:pathData="M31.7987,13.7962C32.597,15.3095 33.1187,17.0012 33.2803,18.8095C33.9387,26.1295 28.5137,32.6245 21.192,33.2795C13.872,33.9378 7.3787,28.5128 6.722,21.1912C6.0637,13.8712 11.4887,7.3762 18.8103,6.7212C21.547,6.4745 24.167,7.0812 26.412,8.3162L29.1687,6.0978C26.1303,4.0895 22.4203,3.0495 18.5103,3.4012C9.3487,4.2228 2.5787,12.3262 3.4003,21.4895C4.2237,30.6528 12.327,37.4228 21.4903,36.6012C30.6537,35.7778 37.4237,27.6745 36.6003,18.5112C36.3303,15.4945 35.2587,12.7445 33.6303,10.4312L31.7987,13.7962Z"
       android:strokeWidth="1"
-      android:fillColor="?attr/default_icon_color_secondary"
+      android:fillColor="@android:color/white"
       android:fillType="evenOdd"
       android:strokeColor="#00000000"/>
   <path
       android:pathData="M17.6515,22.3508C18.2765,22.9775 19.1248,23.3292 20.0098,23.3275C20.8948,23.3275 21.7432,22.9758 22.3682,22.3492L31.7982,8.1975L17.6515,17.6342C17.0248,18.2608 16.6732,19.1092 16.6732,19.9925C16.6732,20.8775 17.0265,21.7258 17.6515,22.3508"
       android:strokeWidth="1"
-      android:fillColor="?attr/default_icon_color_secondary"
+      android:fillColor="@android:color/white"
       android:fillType="evenOdd"
       android:strokeColor="#00000000"/>
 </vector>
diff --git a/chrome/android/java/res/drawable/data_reduction_illustration.xml b/chrome/android/java/res/drawable/data_reduction_illustration.xml
index f5a18fc..18b4542 100644
--- a/chrome/android/java/res/drawable/data_reduction_illustration.xml
+++ b/chrome/android/java/res/drawable/data_reduction_illustration.xml
@@ -7,7 +7,6 @@
      be greater than 200. -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     tools:ignore="VectorRaster"
     android:width="224dp"
     android:height="107dp"
diff --git a/chrome/android/java/res/drawable/discover_card.xml b/chrome/android/java/res/drawable/discover_card.xml
index ce0fff6..be64396 100644
--- a/chrome/android/java/res/drawable/discover_card.xml
+++ b/chrome/android/java/res/drawable/discover_card.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--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.-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:targetApi="21" android:width="32dp" android:height="20dp" android:viewportWidth="32" android:viewportHeight="20">
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="32dp" android:height="20dp" android:viewportWidth="32" android:viewportHeight="20">
     <path android:pathData="M3 1h26a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2z" android:strokeWidth="1" android:fillColor="#FFFFFF" android:fillType="evenOdd"/>
     <path android:pathData="M9 19h20.997C30.551 19 31 18.553 31 18.004V11.5S22.722 16.774 9 19z" android:strokeWidth="1" android:fillColor="#F68221" android:fillType="evenOdd"/>
     <path android:pathData="M4.201 7H3.014v4h1.18c0.629 0 1.083-0.144 1.48-0.463 0.473-0.377 0.753-0.947 0.753-1.535C6.427 7.822 5.513 7 4.2 7zm0.945 3.004c-0.255 0.222-0.585 0.318-1.107 0.318H3.822V7.677H4.04c0.522 0 0.84 0.09 1.107 0.324 0.28 0.24 0.447 0.612 0.447 0.995 0.001 0.384-0.167 0.768-0.447 1.008zM6.799 7h1v4h-1zm2.787 1.54C9.1 8.366 8.958 8.252 8.958 8.035c0-0.253 0.253-0.445 0.602-0.445 0.242 0 0.442 0.096 0.653 0.325l0.423-0.536c-0.348-0.295-0.764-0.446-1.218-0.446-0.734 0-1.294 0.494-1.294 1.15 0 0.554 0.26 0.838 1.02 1.102 0.316 0.108 0.477 0.18 0.56 0.228 0.16 0.102 0.24 0.247 0.24 0.417 0 0.325-0.267 0.567-0.627 0.567-0.386 0-0.697-0.187-0.883-0.536l-0.522 0.488c0.373 0.53 0.82 0.764 1.435 0.764 0.84 0 1.43-0.54 1.43-1.319 0-0.638-0.273-0.926-1.192-1.253zm1.447 0.47c0 1.18 0.957 2.095 2.189 2.095 0.348 0 0.646-0.066 1.013-0.234V9.95c-0.323 0.313-0.61 0.44-0.976 0.44-0.814 0-1.393-0.573-1.393-1.386 0-0.77 0.597-1.379 1.356-1.379 0.385 0 0.677 0.133 1.013 0.452V7.155C13.88 6.981 13.588 6.91 13.241 6.91c-1.226 0-2.208 0.934-2.208 2.101zm9.615 0.688l-1.106-2.699h-0.883l1.76 4.12h0.435L22.644 7h-0.876z" android:strokeWidth="1" android:fillColor="#231F20" android:fillType="evenOdd"/>
diff --git a/chrome/android/java/res/drawable/elo_card.xml b/chrome/android/java/res/drawable/elo_card.xml
index 3d7862cd..e50e4ca2 100644
--- a/chrome/android/java/res/drawable/elo_card.xml
+++ b/chrome/android/java/res/drawable/elo_card.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--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.-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:targetApi="21" android:width="32dp" android:height="20dp" android:viewportWidth="32" android:viewportHeight="20">
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="32dp" android:height="20dp" android:viewportWidth="32" android:viewportHeight="20">
     <path android:pathData="M3 1h26a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2z" android:strokeWidth="1" android:fillColor="#FFFFFF" android:fillType="evenOdd"/>
     <path android:pathData="M9 10c0-3.867 3.134-7 7-7s7 3.133 7 7-3.134 7-7 7-7-3.133-7-7" android:strokeWidth="1" android:fillColor="#000000" android:fillType="evenOdd"/>
     <path android:pathData="M13.87 10.85c-0.216 0.213-0.511 0.341-0.836 0.337-0.224-0.004-0.43-0.07-0.606-0.182l-0.436 0.694c0.298 0.189 0.65 0.3 1.03 0.307 0.552 0.008 1.055-0.21 1.421-0.568l-0.572-0.587zm-1.984-0.713c-0.005-0.046-0.008-0.094-0.007-0.142 0.01-0.648 0.544-1.165 1.191-1.154 0.353 0.005 0.666 0.166 0.877 0.416l-2.061 0.88zm1.196-2.116c-1.1-0.017-2.005 0.862-2.021 1.962-0.007 0.412 0.112 0.797 0.322 1.118l3.608-1.543c-0.203-0.869-0.976-1.522-1.91-1.537zm3.04-0.898v3.847l0.668 0.277-0.316 0.758-0.66-0.274c-0.148-0.064-0.249-0.162-0.325-0.273-0.073-0.113-0.128-0.268-0.128-0.477V7.123h0.761z" android:strokeWidth="1" android:fillColor="#FFFFFF" android:fillType="evenOdd"/>
diff --git a/chrome/android/java/res/drawable/google_pay_with_divider.xml b/chrome/android/java/res/drawable/google_pay_with_divider.xml
index 8e5994b..861e8807 100644
--- a/chrome/android/java/res/drawable/google_pay_with_divider.xml
+++ b/chrome/android/java/res/drawable/google_pay_with_divider.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:viewportWidth="51"
     android:viewportHeight="18"
     android:width="51dp"
diff --git a/chrome/android/java/res/drawable/ic_block_red.xml b/chrome/android/java/res/drawable/ic_block_red.xml
index 301d7406..cde5b53 100644
--- a/chrome/android/java/res/drawable/ic_block_red.xml
+++ b/chrome/android/java/res/drawable/ic_block_red.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/chrome/android/java/res/drawable/ic_cloud_offline_24dp.xml b/chrome/android/java/res/drawable/ic_cloud_offline_24dp.xml
index 1c0e8b3..d86a09d 100644
--- a/chrome/android/java/res/drawable/ic_cloud_offline_24dp.xml
+++ b/chrome/android/java/res/drawable/ic_cloud_offline_24dp.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/chrome/android/java/res/drawable/ic_credit_card_black.xml b/chrome/android/java/res/drawable/ic_credit_card_black.xml
index 5bf41f10..a0d1442 100644
--- a/chrome/android/java/res/drawable/ic_credit_card_black.xml
+++ b/chrome/android/java/res/drawable/ic_credit_card_black.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="32dp"
     android:height="20dp"
     android:viewportWidth="128"
diff --git a/chrome/android/java/res/drawable/ic_error.xml b/chrome/android/java/res/drawable/ic_error.xml
index 6666ba83..48d626c 100644
--- a/chrome/android/java/res/drawable/ic_error.xml
+++ b/chrome/android/java/res/drawable/ic_error.xml
@@ -3,8 +3,6 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
diff --git a/chrome/android/java/res/drawable/ic_offer_tag.xml b/chrome/android/java/res/drawable/ic_offer_tag.xml
index 0081c4b..afb12e7 100644
--- a/chrome/android/java/res/drawable/ic_offer_tag.xml
+++ b/chrome/android/java/res/drawable/ic_offer_tag.xml
@@ -6,9 +6,10 @@
     android:width="32dp"
     android:height="20dp"
     android:viewportWidth="32"
-    android:viewportHeight="20">
+    android:viewportHeight="20"
+    android:tint="@macro/default_icon_color_secondary">
   <path
       android:pathData="M25.528,9.664L18.328,2.464C18.04,2.176 17.64,2 17.2,2H11.6C10.72,2 10,2.72 10,3.6V9.2C10,9.64 10.176,10.04 10.472,10.336L17.672,17.536C17.96,17.824 18.36,18 18.8,18C19.24,18 19.64,17.824 19.928,17.528L25.528,11.928C25.824,11.64 26,11.24 26,10.8C26,10.36 25.816,9.952 25.528,9.664ZM14.8,5.6C14.8,6.2627 14.2627,6.8 13.6,6.8C12.9373,6.8 12.4,6.2627 12.4,5.6C12.4,4.9373 12.9373,4.4 13.6,4.4C14.2627,4.4 14.8,4.9373 14.8,5.6Z"
-      android:fillColor="?attr/default_icon_color_secondary"
+      android:fillColor="@android:color/white"
       android:fillType="evenOdd"/>
 </vector>
diff --git a/chrome/android/java/res/drawable/ic_toolbar_share_offset_24dp.xml b/chrome/android/java/res/drawable/ic_toolbar_share_offset_24dp.xml
index d2e23b3..df7a511 100644
--- a/chrome/android/java/res/drawable/ic_toolbar_share_offset_24dp.xml
+++ b/chrome/android/java/res/drawable/ic_toolbar_share_offset_24dp.xml
@@ -5,7 +5,6 @@
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     tools:ignore="VectorRaster"
     android:width="24dp"
     android:height="24dp"
@@ -16,4 +15,3 @@
       android:fillColor="@color/default_primary_color"
       android:fillType="evenOdd"/>
 </vector>
-
diff --git a/chrome/android/java/res/drawable/ic_vpn_key_blue.xml b/chrome/android/java/res/drawable/ic_vpn_key_blue.xml
index cf596ff..016012e 100644
--- a/chrome/android/java/res/drawable/ic_vpn_key_blue.xml
+++ b/chrome/android/java/res/drawable/ic_vpn_key_blue.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/chrome/android/java/res/drawable/infobar_autofill_cc.xml b/chrome/android/java/res/drawable/infobar_autofill_cc.xml
index a03550a..effa3a2 100644
--- a/chrome/android/java/res/drawable/infobar_autofill_cc.xml
+++ b/chrome/android/java/res/drawable/infobar_autofill_cc.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--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.-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:targetApi="21" android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0">
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0">
     <path android:pathData="M20 4H4C2.89 4 2.01 4.89 2.01 6L2 18c0 1.11 0.89 2 2 2h16c1.11 0 2-0.89 2-2V6c0-1.11-0.89-2-2-2zm0 14H4v-7h16v7zm0-10H4V6h16v2z" android:strokeWidth="1" android:fillColor="#000000"/>
 </vector>
diff --git a/chrome/android/java/res/drawable/infobar_download_complete.xml b/chrome/android/java/res/drawable/infobar_download_complete.xml
index 704a0806..3e83e4c 100644
--- a/chrome/android/java/res/drawable/infobar_download_complete.xml
+++ b/chrome/android/java/res/drawable/infobar_download_complete.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/chrome/android/java/res/drawable/mir_card.xml b/chrome/android/java/res/drawable/mir_card.xml
index 7737cbe..76c2da5 100644
--- a/chrome/android/java/res/drawable/mir_card.xml
+++ b/chrome/android/java/res/drawable/mir_card.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--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.-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:targetApi="21" android:width="32dp" android:height="20dp" android:viewportWidth="32" android:viewportHeight="20">
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="32dp" android:height="20dp" android:viewportWidth="32" android:viewportHeight="20">
     <path android:pathData="M5.328 6.5l0.138 0.007c0.502 0.049 0.935 0.362 1.122 0.81l0.045 0.126 0.952 3.187c0.021 0.071 0.112 0.083 0.157 0.035l0.02-0.035 0.952-3.187C8.868 6.928 9.334 6.56 9.88 6.507L10.019 6.5h2.328v7H10.02V9.405c0-0.086-0.109-0.115-0.16-0.06L9.84 9.379 8.48 13.5H6.868l-1.36-4.121C5.48 9.296 5.368 9.3 5.336 9.367L5.328 9.405V13.5H3v-7h2.328zm10.357 0v4.025c0 0.079 0.094 0.112 0.149 0.07l0.027-0.033 1.6-3.315c0.199-0.414 0.61-0.693 1.076-0.74L18.678 6.5h1.97v7H18.32V9.254c0-0.082-0.1-0.114-0.154-0.067l-0.022 0.031-1.622 3.535c-0.19 0.412-0.594 0.692-1.055 0.74l-0.14 0.007h-1.97v-7h2.328zm13.103 3.226c-0.355 0.899-1.262 1.538-2.325 1.538h-2.477V13.5h-2.328V9.726z" android:strokeWidth="1" android:fillColor="#3BA536" android:fillType="evenOdd"/>
     <path android:pathData="M26.463 6.5h-5.108c0.18 1.62 1.61 2.882 3.347 2.882h4.192c0.036-0.161 0.055-0.328 0.055-0.5 0-1.316-1.113-2.382-2.486-2.382" android:strokeWidth="1" android:fillColor="#0093D3" android:fillType="evenOdd"/>
     <path android:pathData="M3 1h26a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2z" android:strokeWidth="1" android:fillColor="#FFFFFF" android:fillType="evenOdd"/>
diff --git a/chrome/android/java/res/drawable/shared_clipboard_zero_state_dark.xml b/chrome/android/java/res/drawable/shared_clipboard_zero_state_dark.xml
index dc110e2..433d28a4 100644
--- a/chrome/android/java/res/drawable/shared_clipboard_zero_state_dark.xml
+++ b/chrome/android/java/res/drawable/shared_clipboard_zero_state_dark.xml
@@ -5,25 +5,12 @@
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     tools:ignore="VectorRaster"
     android:width="412dp"
     android:height="129dp"
     android:viewportWidth="412"
     android:viewportHeight="129">
   <path
-      android:pathData="M358.064,-6.508L45.311,-6.508C40.761,-6.508 37.071,-2.818 37.071,1.732L37.071,86.055L366.304,86.055L366.304,1.732C366.304,-2.818 362.614,-6.508 358.064,-6.508Z"
-      android:strokeWidth="1"
-      android:fillColor="#F8F9FA"
-      android:fillType="evenOdd"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M427,-6.508l-460.925,0l-0,121.025l460.925,0z"
-      android:strokeWidth="1"
-      android:fillColor="#202124"
-      android:fillType="evenOdd"
-      android:strokeColor="#00000000"/>
-  <path
       android:pathData="M148.9,95.729L148.9,104.445C153.828,106.017 159.328,107.063 165.265,107.088C180.692,107.155 195.369,100.337 208.885,86.826L203.081,81.024C191.195,92.907 178.51,98.917 165.38,98.886C159.338,98.871 153.765,97.568 148.9,95.729Z"
       android:strokeWidth="1"
       android:fillColor="#3C4043"
diff --git a/chrome/android/java/res/drawable/shared_clipboard_zero_state_light.xml b/chrome/android/java/res/drawable/shared_clipboard_zero_state_light.xml
index b767fdf3..294432c 100644
--- a/chrome/android/java/res/drawable/shared_clipboard_zero_state_light.xml
+++ b/chrome/android/java/res/drawable/shared_clipboard_zero_state_light.xml
@@ -5,25 +5,12 @@
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     tools:ignore="VectorRaster"
     android:width="412dp"
     android:height="129dp"
     android:viewportWidth="412"
     android:viewportHeight="129">
   <path
-      android:pathData="M0,-6.508h460.925v121.025h-460.925z"
-      android:strokeWidth="1"
-      android:fillColor="#F8F9FA"
-      android:fillType="evenOdd"
-      android:strokeColor="#00000000"/>
-  <path
-      android:pathData="M149.35,95.785L149.35,104.501C154.279,106.074 159.781,107.121 165.72,107.146C181.147,107.213 195.823,100.395 209.339,86.884L203.536,81.082C191.649,92.965 178.965,98.974 165.835,98.944C159.791,98.929 154.216,97.625 149.35,95.785Z"
-      android:strokeWidth="1"
-      android:fillColor="#E8E9EB"
-      android:fillType="evenOdd"
-      android:strokeColor="#00000000"/>
-  <path
       android:pathData="M102.339,18.49L98.518,18.49C92.836,18.49 88.229,23.094 88.229,28.774C88.229,66.31 88.229,91.058 88.229,103.017"
       android:strokeLineJoin="round"
       android:strokeWidth="1.03"
diff --git a/chrome/android/java/res/drawable/visa_card.xml b/chrome/android/java/res/drawable/visa_card.xml
index 8f1f7922..8fc27a87 100644
--- a/chrome/android/java/res/drawable/visa_card.xml
+++ b/chrome/android/java/res/drawable/visa_card.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--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.-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:targetApi="21" android:width="32dp" android:height="20dp" android:viewportWidth="32" android:viewportHeight="20">
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="32dp" android:height="20dp" android:viewportWidth="32" android:viewportHeight="20">
     <path android:pathData="M3 1h26a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2z" android:strokeWidth="1" android:fillColor="#FFFFFF" android:fillType="evenOdd"/>
     <path android:pathData="M15.873 5.996l-1.737 7.999h-2.1l1.736-7.999h2.101zm8.84 5.166l1.106-3.005 0.636 3.005h-1.742zm2.344 2.833H29l-1.695-7.999h-1.794c-0.403 0-0.745 0.23-0.894 0.587l-3.152 7.412h2.205l0.44-1.193h2.694l0.253 1.193zm-5.482-2.611c0.008-2.113-2.965-2.228-2.945-3.17 0.006-0.29 0.285-0.593 0.891-0.671 0.3-0.038 1.13-0.07 2.072 0.357l0.368-1.695c-0.505-0.182-1.156-0.355-1.964-0.355-2.076 0-3.539 1.087-3.551 2.646-0.014 1.149 1.042 1.791 1.84 2.174 0.818 0.393 1.092 0.645 1.09 0.995-0.006 0.537-0.654 0.772-1.256 0.781-1.057 0.018-1.67-0.28-2.16-0.503l-0.38 1.75c0.49 0.224 1.396 0.417 2.337 0.427 2.206 0 3.649-1.073 3.657-2.736zM9.47 13.993H7.248L5.573 7.612C5.47 7.218 5.38 7.074 5.073 6.909 4.567 6.639 3.733 6.385 3 6.227l0.05-0.23h3.575c0.456 0 0.865 0.297 0.97 0.815l0.883 4.628 2.187-5.444h2.207L9.47 13.995z" android:strokeWidth="1" android:fillColor="#10347E" android:fillType="evenOdd"/>
     <path android:pathData="M15.873 5.996l-1.737 7.999h-2.1l1.736-7.999h2.101zm8.84 5.166l1.106-3.005 0.636 3.005h-1.742zm2.344 2.833H29l-1.695-7.999h-1.794c-0.403 0-0.745 0.23-0.894 0.587l-3.152 7.412h2.205l0.44-1.193h2.694l0.253 1.193zm-5.482-2.611c0.008-2.113-2.965-2.228-2.945-3.17 0.006-0.29 0.285-0.593 0.891-0.671 0.3-0.038 1.13-0.07 2.072 0.357l0.368-1.695c-0.505-0.182-1.156-0.355-1.964-0.355-2.076 0-3.539 1.087-3.551 2.646-0.014 1.149 1.042 1.791 1.84 2.174 0.818 0.393 1.092 0.645 1.09 0.995-0.006 0.537-0.654 0.772-1.256 0.781-1.057 0.018-1.67-0.28-2.16-0.503l-0.38 1.75c0.49 0.224 1.396 0.417 2.337 0.427 2.206 0 3.649-1.073 3.657-2.736zM9.47 13.993H7.248L5.573 7.612C5.47 7.218 5.38 7.074 5.073 6.909 4.567 6.639 3.733 6.385 3 6.227l0.05-0.23h3.575c0.456 0 0.865 0.297 0.97 0.815l0.883 4.628 2.187-5.444h2.207L9.47 13.995z" android:strokeWidth="1" android:fillColor="#10347E" android:fillType="evenOdd"/>
diff --git a/chrome/android/java/res/layout/custom_view_menu_item.xml b/chrome/android/java/res/layout/custom_view_menu_item.xml
index 3c4e2da..c6f7dae 100644
--- a/chrome/android/java/res/layout/custom_view_menu_item.xml
+++ b/chrome/android/java/res/layout/custom_view_menu_item.xml
@@ -26,7 +26,7 @@
         android:layout_height="match_parent"
         android:gravity="center_vertical"
         android:importantForAccessibility="no"
-        android:tint="?attr/default_icon_color_secondary"
+        android:tint="@macro/default_icon_color_secondary"
         android:visibility="gone" />
 
 
diff --git a/chrome/android/java/res/layout/data_reduction_main_menu_item.xml b/chrome/android/java/res/layout/data_reduction_main_menu_item.xml
deleted file mode 100644
index 136dd8a..0000000
--- a/chrome/android/java/res/layout/data_reduction_main_menu_item.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-
-<org.chromium.chrome.browser.datareduction.DataReductionMainMenuItem
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/data_reduction_menu_item"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical">
-
-    <View
-        android:id="@+id/data_reduction_menu_divider"
-        style="@style/HorizontalDivider" />
-
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="start"
-        android:layout_marginTop="@dimen/divider_height"
-        android:paddingTop="12dp"
-        android:paddingBottom="12dp"
-        style="@style/AppMenuItem" >
-
-        <!-- ContentDescription is set in Java code. -->
-        <org.chromium.ui.widget.ChromeImageView
-            tools:ignore="ContentDescription"
-            android:id="@+id/icon"
-            android:src="@drawable/preview_pin_round"
-            android:layout_width="@dimen/data_reduction_main_menu_icon_width"
-            android:layout_height="match_parent"
-            android:layout_gravity="start|center_vertical"
-            app:tint="@macro/default_icon_color_accent1" />
-
-        <LinearLayout
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_marginStart="@dimen/data_reduction_main_menu_icon_width"
-            android:orientation="vertical"
-            android:paddingStart="16dp" >
-
-            <TextView
-                android:id="@+id/menu_item_text"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:textAppearance="?android:attr/textAppearanceLargePopupMenu" />
-
-            <TextView
-                android:id="@+id/menu_item_summary"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:textAppearance="@style/TextAppearance.TextMedium.Secondary" />
-        </LinearLayout>
-
-    </FrameLayout>
-
-</org.chromium.chrome.browser.datareduction.DataReductionMainMenuItem>
diff --git a/chrome/android/java/res/layout/managed_by_menu_item.xml b/chrome/android/java/res/layout/managed_by_menu_item.xml
index b363c47..34c1b4d 100644
--- a/chrome/android/java/res/layout/managed_by_menu_item.xml
+++ b/chrome/android/java/res/layout/managed_by_menu_item.xml
@@ -26,7 +26,7 @@
         android:singleLine="true"
         android:drawableStart="@drawable/ic_business"
         android:drawablePadding="12dp"
-        app:chromeDrawableTint="?attr/default_icon_color_secondary"
+        app:chromeDrawableTint="@macro/default_icon_color_secondary"
         android:tintMode="src_in"
         app:drawableWidth="20dp"
         app:drawableHeight="20dp"/>
diff --git a/chrome/android/java/res/layout/sharing_device_picker.xml b/chrome/android/java/res/layout/sharing_device_picker.xml
index b228435..56b3aa35 100644
--- a/chrome/android/java/res/layout/sharing_device_picker.xml
+++ b/chrome/android/java/res/layout/sharing_device_picker.xml
@@ -21,7 +21,7 @@
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="vertical"
-      android:background="@color/sheet_bg_color">
+      android:background="@drawable/sheet_background">
 
     <TextView
         android:id="@+id/device_picker_toolbar"
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index b2547931..c0b729bb 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -105,8 +105,6 @@
 
     <!-- Data Saver -->
     <dimen name="data_usage_chart_height">252dp</dimen>
-    <dimen name="data_reduction_main_menu_icon_width">24dp</dimen>
-    <dimen name="data_saver_menu_footer_min_show_height">240dp</dimen>
 
     <!-- The size of the text for tab titles. -->
     <dimen name="compositor_tab_title_text_size">13sp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 99a4267..eb62eaa8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1608,7 +1608,7 @@
     protected RootUiCoordinator createRootUiCoordinator() {
         return new TabbedRootUiCoordinator(this, this::onOmniboxFocusChanged,
                 getShareDelegateSupplier(), getActivityTabProvider(), mTabModelProfileSupplier,
-                mBookmarkBridgeSupplier, this::getContextualSearchManager,
+                mBookmarkBridgeSupplier, mContextualSearchManagerSupplier,
                 getTabModelSelectorSupplier(), mStartSurfaceSupplier,
                 mIntentMetadataOneshotSupplier, mLayoutStateProviderOneshotSupplier,
                 mStartSurfaceParentTabSupplier, getBrowserControlsManager(), getWindowAndroid(),
@@ -2620,7 +2620,7 @@
         // TODO(crbug.com/1157310): Transition this::method refs to dedicated suppliers.
         mTabModalHandler = new TabModalLifetimeHandler(this, getLifecycleDispatcher(), manager,
                 this::getAppBrowserControlsVisibilityDelegate, this::getTabObscuringHandler,
-                this::getToolbarManager, this::getContextualSearchManager,
+                this::getToolbarManager, mContextualSearchManagerSupplier,
                 getTabModelSelectorSupplier(), this::getBrowserControlsManager,
                 this::getFullscreenManager);
         return manager;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
index c65c5ff7..a288fdd6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/DEPS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
@@ -24,9 +24,6 @@
   "LaunchIntentDispatcher\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
-  "ContextualSearchTabHelper\.java": [
-    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
-  ],
   "BaseCustomTabActivity\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/accessibility_tab_switcher/AccessibilityTabModelListItem.java b/chrome/android/java/src/org/chromium/chrome/browser/accessibility_tab_switcher/AccessibilityTabModelListItem.java
index acf4b51..e574bf48 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/accessibility_tab_switcher/AccessibilityTabModelListItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/accessibility_tab_switcher/AccessibilityTabModelListItem.java
@@ -222,8 +222,8 @@
         mDefaultIconColor = ChromeColors.getPrimaryIconTint(context, false);
         mIncognitoIconColor =
                 AppCompatResources.getColorStateList(context, R.color.default_icon_color_dark);
-        mDefaultCloseIconColor =
-                AppCompatResources.getColorStateList(context, R.color.default_icon_color_secondary);
+        mDefaultCloseIconColor = AppCompatResources.getColorStateList(
+                context, R.color.default_icon_color_secondary_tint_list);
         mIncognitoCloseIconColor =
                 AppCompatResources.getColorStateList(context, R.color.white_alpha_70);
         mDefaultLevel = getResources().getInteger(R.integer.list_item_level_default);
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 ad9c155..c3aad0f 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
@@ -99,6 +99,7 @@
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager.ContextualSearchTabPromotionDelegate;
+import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagerSupplier;
 import org.chromium.chrome.browser.dependency_injection.ChromeActivityCommonsModule;
 import org.chromium.chrome.browser.dependency_injection.ChromeActivityComponent;
 import org.chromium.chrome.browser.dependency_injection.ModuleFactoryOverrides;
@@ -281,6 +282,9 @@
     // TODO(crbug.com/1209864): Move ownership to RootUiCoordinator.
     private final UnownedUserDataSupplier<BrowserControlsManager> mBrowserControlsManagerSupplier =
             new BrowserControlsManagerSupplier();
+    // Provided as a supplier by other clasess in ChromeActivity hierarchy.
+    protected final UnownedUserDataSupplier<ContextualSearchManager>
+            mContextualSearchManagerSupplier = new ContextualSearchManagerSupplier();
 
     protected TabModelSelectorProfileSupplier mTabModelProfileSupplier =
             new TabModelSelectorProfileSupplier(mTabModelSelectorSupplier);
@@ -321,7 +325,6 @@
     private ObservableSupplierImpl<LayoutManagerImpl> mLayoutManagerSupplier =
             new ObservableSupplierImpl<>();
     private InsetObserverView mInsetObserverView;
-    private ContextualSearchManager mContextualSearchManager;
     private SnackbarManager mSnackbarManager;
 
     // Timestamp in ms when initial layout inflation begins
@@ -490,6 +493,7 @@
         mTabModelSelectorSupplier.attach(getWindowAndroid().getUnownedUserDataHost());
         mTabCreatorManagerSupplier.attach(getWindowAndroid().getUnownedUserDataHost());
         mManualFillingComponentSupplier.attach(getWindowAndroid().getUnownedUserDataHost());
+        mContextualSearchManagerSupplier.attach(getWindowAndroid().getUnownedUserDataHost());
         mBrowserControlsManagerSupplier.attach(getWindowAndroid().getUnownedUserDataHost());
         // BrowserControlsManager is ready immediately.
         mBrowserControlsManagerSupplier.set(
@@ -505,7 +509,7 @@
         // clang-format off
         return new RootUiCoordinator(this, null, getShareDelegateSupplier(),
                 getActivityTabProvider(), mTabModelProfileSupplier, mBookmarkBridgeSupplier,
-                this::getContextualSearchManager, getTabModelSelectorSupplier(),
+                mContextualSearchManagerSupplier, getTabModelSelectorSupplier(),
                 new OneshotSupplierImpl<>(), new OneshotSupplierImpl<>(),
                 new OneshotSupplierImpl<>(),
                 () -> null, mBrowserControlsManagerSupplier.get(), getWindowAndroid(),
@@ -976,10 +980,10 @@
 
         // TODO(1107916): Move contextual search initialization to the RootUiCoordinator.
         if (ContextualSearchFieldTrial.isEnabled()) {
-            mContextualSearchManager = new ContextualSearchManager(this, this,
+            mContextualSearchManagerSupplier.set(new ContextualSearchManager(this, this,
                     mRootUiCoordinator.getScrimCoordinator(), getActivityTabProvider(),
                     getFullscreenManager(), getBrowserControlsManager(), getWindowAndroid(),
-                    getTabModelSelectorSupplier().get(), () -> getLastUserInteractionTime());
+                    getTabModelSelectorSupplier().get(), () -> getLastUserInteractionTime()));
         }
 
         TraceEvent.end("ChromeActivity:CompositorInitialization");
@@ -1085,18 +1089,20 @@
         final SyncService syncService = SyncService.get();
 
         if (syncService != null && syncService.isSyncingUrlsWithKeystorePassphrase()) {
-            ContextReporter.SelectionReporter controller =
-                    getContextualSearchManager() != null ? new ContextReporter.SelectionReporter() {
-                        @Override
-                        public void enable(Callback<GSAContextDisplaySelection> callback) {
-                            getContextualSearchManager().enableContextReporting(callback);
-                        }
+            ContextReporter.SelectionReporter controller = null;
+            if (mContextualSearchManagerSupplier.hasValue()) {
+                controller = new ContextReporter.SelectionReporter() {
+                    @Override
+                    public void enable(Callback<GSAContextDisplaySelection> callback) {
+                        mContextualSearchManagerSupplier.get().enableContextReporting(callback);
+                    }
 
-                        @Override
-                        public void disable() {
-                            getContextualSearchManager().disableContextReporting();
-                        }
-                    } : null;
+                    @Override
+                    public void disable() {
+                        mContextualSearchManagerSupplier.get().disableContextReporting();
+                    }
+                };
+            }
             mContextReporter = AppHooks.get().createGsaHelper().getContextReporter(
                     getActivityTabProvider(), mTabModelSelectorSupplier, controller);
 
@@ -1526,10 +1532,10 @@
     @SuppressLint("NewApi")
     @Override
     protected final void onDestroy() {
-        if (mContextualSearchManager != null) {
-            mContextualSearchManager.destroy();
-            mContextualSearchManager = null;
+        if (mContextualSearchManagerSupplier.hasValue()) {
+            mContextualSearchManagerSupplier.get().destroy();
         }
+        mContextualSearchManagerSupplier.destroy();
 
         if (mSnackbarManager != null) {
             SnackbarManagerProvider.detach(mSnackbarManager);
@@ -2150,13 +2156,6 @@
     }
 
     /**
-     * @return The {@code ContextualSearchManager} or {@code null} if none;
-     */
-    public ContextualSearchManager getContextualSearchManager() {
-        return mContextualSearchManager;
-    }
-
-    /**
      * Exits the fullscreen mode, if any. Does nothing if no fullscreen is present.
      * @return Whether the fullscreen mode is currently showing.
      */
@@ -2199,8 +2198,8 @@
 
         mActivityTabProvider.setLayoutStateProvider(layoutManager);
 
-        if (mContextualSearchManager != null) {
-            mContextualSearchManager.initialize(contentContainer, layoutManager,
+        if (mContextualSearchManagerSupplier.hasValue()) {
+            mContextualSearchManagerSupplier.get().initialize(contentContainer, layoutManager,
                     mRootUiCoordinator.getBottomSheetController(), compositorViewHolder,
                     getControlContainerHeightResource() == ActivityUtils.NO_RESOURCE_ID
                             ? 0f
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/tab_activity_glue/ReparentingTask.java b/chrome/android/java/src/org/chromium/chrome/browser/app/tab_activity_glue/ReparentingTask.java
index b6a061ca..78324b0e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/tab_activity_glue/ReparentingTask.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/tab_activity_glue/ReparentingTask.java
@@ -26,7 +26,7 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabDelegateFactory;
-import org.chromium.chrome.browser.tab.TabImpl;
+import org.chromium.chrome.browser.tab.TabStateAttributes;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabReparentingParams;
 import org.chromium.content_public.browser.WebContents;
@@ -168,7 +168,7 @@
     public void finish(@NonNull Delegate delegate, @Nullable Runnable finalizeCallback) {
         delegate.getCompositorViewHolder().prepareForTabReparenting();
         attach(delegate.getWindowAndroid(), delegate.getTabDelegateFactory());
-        ((TabImpl) mTab).setIsTabStateDirty(true);
+        if (!mTab.isDestroyed()) TabStateAttributes.from(mTab).setIsTabStateDirty(true);
         if (finalizeCallback != null) finalizeCallback.run();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediator.java
index 7d0ce4d..4eb609f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediator.java
@@ -16,6 +16,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
+import org.chromium.chrome.browser.bookmarks.PowerBookmarkMetrics.PriceTrackingState;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.power_bookmarks.PowerBookmarkMeta;
 import org.chromium.chrome.browser.power_bookmarks.PowerBookmarkType;
@@ -135,6 +136,8 @@
             if (fromExplicitTrackUi) {
                 mPropertyModel.set(BookmarkSaveFlowProperties.NOTIFICATION_SWITCH_TOGGLED, true);
             }
+            PowerBookmarkMetrics.reportBookmarkSaveFlowPriceTrackingState(
+                    PriceTrackingState.PRICE_TRACKING_SHOWN);
         }
     }
 
@@ -159,6 +162,9 @@
         setPriceTrackingIconForEnabledState(toggled);
         PowerBookmarkUtils.setPriceTrackingEnabled(mSubscriptionsManager, mBookmarkModel,
                 mBookmarkId, toggled, mSubscriptionsManagerCallback);
+        PowerBookmarkMetrics.reportBookmarkSaveFlowPriceTrackingState(toggled
+                        ? PriceTrackingState.PRICE_TRACKING_ENABLED
+                        : PriceTrackingState.PRICE_TRACKING_DISABLED);
     }
 
     void setPriceTrackingNotificationUiEnabled(boolean enabled) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkMetrics.java
new file mode 100644
index 0000000..ffa36ee
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkMetrics.java
@@ -0,0 +1,39 @@
+// Copyright 2022 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.bookmarks;
+
+import androidx.annotation.IntDef;
+
+import org.chromium.base.metrics.RecordHistogram;
+
+/** Metrics utils for use in power bookmarks. */
+public class PowerBookmarkMetrics {
+    // These values are persisted to logs. Entries should not be renumbered and
+    // numeric values should never be reused. Keep up-to-date with the PriceTrackingState enum in
+    // tools/metrics/histograms/enums.xml.
+    @IntDef({PriceTrackingState.PRICE_TRACKING_SHOWN, PriceTrackingState.PRICE_TRACKING_ENABLED,
+            PriceTrackingState.PRICE_TRACKING_DISABLED})
+    public @interface PriceTrackingState {
+        int PRICE_TRACKING_SHOWN = 0;
+        int PRICE_TRACKING_ENABLED = 1;
+        int PRICE_TRACKING_DISABLED = 2;
+        int COUNT = 3;
+    }
+
+    /** Report the price tracking state for the bookmark save flow surface. */
+    public static void reportBookmarkSaveFlowPriceTrackingState(@PriceTrackingState int state) {
+        RecordHistogram.recordEnumeratedHistogram(
+                "PowerBookmarks.BookmarkSaveFlow.PriceTrackingEnabled", state,
+                PriceTrackingState.COUNT);
+    }
+
+    /** Report the price tracking state for the bookmark shopping item row. */
+    public static void reportBookmarkShoppingItemRowPriceTrackingState(
+            @PriceTrackingState int state) {
+        RecordHistogram.recordEnumeratedHistogram(
+                "PowerBookmarks.BookmarkManager.PriceTrackingEnabled", state,
+                PriceTrackingState.COUNT);
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRow.java
index 64cb048..56f86e12 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkShoppingItemRow.java
@@ -18,6 +18,7 @@
 import org.chromium.base.Callback;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
+import org.chromium.chrome.browser.bookmarks.PowerBookmarkMetrics.PriceTrackingState;
 import org.chromium.chrome.browser.power_bookmarks.PowerBookmarkMeta;
 import org.chromium.chrome.browser.power_bookmarks.ProductPrice;
 import org.chromium.chrome.browser.subscriptions.CommerceSubscription;
@@ -135,6 +136,8 @@
 
         setPriceInfoChip(originalPrice, currentPrice);
         setPriceTrackingButton(priceTrackingEnabled);
+        PowerBookmarkMetrics.reportBookmarkShoppingItemRowPriceTrackingState(
+                PriceTrackingState.PRICE_TRACKING_SHOWN);
     }
 
     /** Sets up the chip that displays product price information. */
@@ -188,6 +191,9 @@
             if (mSubscriptionChangeInProgress) return;
             mSubscriptionChangeInProgress = true;
 
+            PowerBookmarkMetrics.reportBookmarkShoppingItemRowPriceTrackingState(
+                    !mIsPriceTrackingEnabled ? PriceTrackingState.PRICE_TRACKING_ENABLED
+                                             : PriceTrackingState.PRICE_TRACKING_DISABLED);
             PowerBookmarkUtils.setPriceTrackingEnabledWithSnackbars(mSubscriptionsManager,
                     mBookmarkModel, mBookmarkId, !mIsPriceTrackingEnabled, mSnackbarManager,
                     getContext().getResources(), subscriptionCallback);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetServiceImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetServiceImpl.java
index 673c8b4b..d66e823 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetServiceImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetServiceImpl.java
@@ -349,9 +349,9 @@
         public void onDestroy() {
             PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> {
                 if (mBookmarkModel != null) mBookmarkModel.destroy();
+                SystemNightModeMonitor.getInstance().removeObserver(this);
             });
             deleteWidgetState(mWidgetId);
-            SystemNightModeMonitor.getInstance().removeObserver(this);
         }
 
         @BinderThread
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
index 601f7f12..55f8adb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
@@ -114,7 +114,7 @@
 
             int closeButtonColor = useIncognitoColors
                     ? Color.WHITE
-                    : ApiCompatibilityUtils.getColor(res, R.color.default_icon_color_secondary);
+                    : SemanticColorUtils.getDefaultIconColorSecondary(context);
             float closeButtonSizePx =
                     res.getDimensionPixelSize(R.dimen.tab_switcher_close_button_size);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
index b94137a..6befa8f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
@@ -146,9 +146,13 @@
         Integer desiredPopupContentWidth = params.getOpenedFromHighlight()
                 ? activity.getResources().getDimensionPixelSize(R.dimen.context_menu_small_width)
                 : null;
+        View webContentView = webContents.getViewAndroidDelegate() != null
+                        && ChromeFeatureList.isEnabled(ChromeFeatureList.DRAG_AND_DROP_ANDROID)
+                ? webContents.getViewAndroidDelegate().getContainerView()
+                : null;
         mDialog = createContextMenuDialog(activity, layout, menu, isPopup, touchPointXPx,
                 touchPointYPx, mTopContentOffsetPx, dialogTopMarginPx, dialogBottomMarginPx,
-                popupMargin, desiredPopupContentWidth);
+                popupMargin, desiredPopupContentWidth, webContentView);
         mDialog.setOnShowListener(dialogInterface -> onMenuShown.run());
         mDialog.setOnDismissListener(dialogInterface -> mOnMenuClosed.run());
 
@@ -243,7 +247,8 @@
     }
 
     /**
-     * Returns the fully complete dialog based off the params and the itemGroups.
+     * Returns the fully complete dialog based off the params, the itemGroups, and related Chrome
+     * feature flags.
      *
      * @param activity Used to inflate the dialog.
      * @param layout The inflated context menu layout that will house the context menu.
@@ -258,6 +263,7 @@
      *                       defined in XML.
      * @param popupMargin The margin for the popup window.
      * @param desiredPopupContentWidth The desired width for the content of the context menu.
+     * @param webContentView The web content view presented behind the context menu.
      * @return Returns a final dialog that does not have a background can be displayed using
      *         {@link AlertDialog#show()}.
      */
@@ -265,14 +271,14 @@
     static ContextMenuDialog createContextMenuDialog(Activity activity, View layout, View view,
             boolean isPopup, float touchPointXPx, float touchPointYPx, float topContentOffsetPx,
             int topMarginPx, int bottomMarginPx, @Nullable Integer popupMargin,
-            @Nullable Integer desiredPopupContentWidth) {
+            @Nullable Integer desiredPopupContentWidth, @Nullable View webContentView) {
         // TODO(sinansahin): Refactor ContextMenuDialog as well.
         boolean shouldRemoveScrim =
                 isPopup && ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE);
-        final ContextMenuDialog dialog =
-                new ContextMenuDialog(activity, R.style.Theme_Chromium_AlertDialog, touchPointXPx,
-                        touchPointYPx, topContentOffsetPx, topMarginPx, bottomMarginPx, layout,
-                        view, isPopup, shouldRemoveScrim, popupMargin, desiredPopupContentWidth);
+        final ContextMenuDialog dialog = new ContextMenuDialog(activity,
+                R.style.Theme_Chromium_AlertDialog, touchPointXPx, touchPointYPx,
+                topContentOffsetPx, topMarginPx, bottomMarginPx, layout, view, isPopup,
+                shouldRemoveScrim, popupMargin, desiredPopupContentWidth, webContentView);
         dialog.setContentView(layout);
 
         return dialog;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerSupplier.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerSupplier.java
new file mode 100644
index 0000000..6dddecd
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerSupplier.java
@@ -0,0 +1,44 @@
+// Copyright 2022 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.contextualsearch;
+
+import androidx.annotation.Nullable;
+
+import org.chromium.base.UnownedUserDataKey;
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.supplier.UnownedUserDataSupplier;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * A {@link UnownedUserDataSupplier} which manages the supplier and UnownedUserData for a
+ * {@link ContextualSearchManager}.
+ */
+public class ContextualSearchManagerSupplier
+        extends UnownedUserDataSupplier<ContextualSearchManager> {
+    private static final UnownedUserDataKey<ContextualSearchManagerSupplier> KEY =
+            new UnownedUserDataKey<ContextualSearchManagerSupplier>(
+                    ContextualSearchManagerSupplier.class);
+
+    /**
+     * Returns {@link ContextualSearchManager} supplier associated with the given {@link
+     * WindowAndroid} or {@code null}.
+     */
+    public static @Nullable ObservableSupplier<ContextualSearchManager> from(
+            @Nullable WindowAndroid windowAndroid) {
+        if (windowAndroid == null) return null;
+        return KEY.retrieveDataFromHost(windowAndroid.getUnownedUserDataHost());
+    }
+
+    /** Retrieves a {@link ContextualSearchManager} from {@link WindowAndroid}.  */
+    public static @Nullable ContextualSearchManager getValueOrNullFrom(
+            @Nullable WindowAndroid windowAndroid) {
+        ObservableSupplier<ContextualSearchManager> supplier = from(windowAndroid);
+        return supplier == null ? null : supplier.get();
+    }
+
+    public ContextualSearchManagerSupplier() {
+        super(KEY);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
index eb2e1e2..6f85415 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTabHelper.java
@@ -4,15 +4,14 @@
 
 package org.chromium.chrome.browser.contextualsearch;
 
-import android.app.Activity;
 import android.content.Context;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import org.chromium.base.Log;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial.ContextualSearchSwitch;
 import org.chromium.chrome.browser.firstrun.FirstRunStatus;
@@ -49,12 +48,6 @@
      */
     private WebContents mWebContents;
 
-    /**
-     * The {@link ContextualSearchManager} that's managing this tab. This may point to
-     * the manager from another activity during reparenting, or be {@code null} during startup.
-     */
-    private ContextualSearchManager mContextualSearchManager;
-
     /** The GestureListener used for handling events from the current WebContents. */
     private GestureStateListener mGestureStateListener;
 
@@ -102,7 +95,9 @@
     public void onPageLoadStarted(Tab tab, GURL url) {
         updateHooksForTab(tab);
         ContextualSearchManager manager = getContextualSearchManager(tab);
-        if (manager != null) manager.onBasePageLoadStarted();
+        if (manager != null) {
+            manager.onBasePageLoadStarted();
+        }
     }
 
     @Override
@@ -151,7 +146,6 @@
         }
         removeContextualSearchHooks(mWebContents);
         mWebContents = null;
-        mContextualSearchManager = null;
         mSelectionClientManager = null;
         mGestureStateListener = null;
     }
@@ -162,7 +156,6 @@
             updateHooksForTab(tab);
         } else {
             removeContextualSearchHooks(mWebContents);
-            mContextualSearchManager = null;
         }
     }
 
@@ -195,8 +188,7 @@
     private void updateHooksForTab(Tab tab) {
         WebContents currentWebContents = tab.getWebContents();
         boolean webContentsChanged = currentWebContents != mWebContents;
-        if (webContentsChanged || mContextualSearchManager != getContextualSearchManager(tab)) {
-            mContextualSearchManager = getContextualSearchManager(tab);
+        if (webContentsChanged) {
             if (webContentsChanged && currentWebContents != null) {
                 // Ensure the hooks are cleared on the old web contents before proceeding. All of
                 // the objects associated with the web content need to be recreated in order for
@@ -265,9 +257,9 @@
                         mSelectionClientManager.removeContextualSearchSelectionClient());
             }
             // Also make sure the UI is hidden if the device is offline.
-            ContextualSearchManager contextualSearchManager = getContextualSearchManager(mTab);
-            if (contextualSearchManager != null && !isDeviceOnline(contextualSearchManager)) {
-                contextualSearchManager.hideContextualSearch(StateChangeReason.UNKNOWN);
+            ContextualSearchManager manager = getContextualSearchManager(mTab);
+            if (manager != null && !isDeviceOnline(manager)) {
+                manager.hideContextualSearch(StateChangeReason.UNKNOWN);
             }
         }
     }
@@ -320,16 +312,12 @@
     }
 
     /**
-     * Gets the {@link ContextualSearchManager} associated with the given tab's activity.
+     * Gets the {@link ContextualSearchManager} associated with the given tab.
      * @param tab The {@link Tab} that we're getting the manager for.
      * @return The Contextual Search manager controlling that Tab.
      */
-    private ContextualSearchManager getContextualSearchManager(Tab tab) {
-        Activity activity = tab.getWindowAndroid().getActivity().get();
-        if (activity instanceof ChromeActivity) {
-            return ((ChromeActivity) activity).getContextualSearchManager();
-        }
-        return null;
+    private ContextualSearchManager getContextualSearchManager(@NonNull Tab tab) {
+        return ContextualSearchManagerSupplier.getValueOrNullFrom(tab.getWindowAndroid());
     }
 
     // ============================================================================================
@@ -353,9 +341,9 @@
     @CalledByNative
     void onShowUnhandledTapUIIfNeeded(int x, int y, int fontSizeDips, int textRunLength) {
         // Only notify the manager if we currently have a valid listener.
-        if (mGestureStateListener != null && getContextualSearchManager(mTab) != null) {
-            getContextualSearchManager(mTab).onShowUnhandledTapUIIfNeeded(
-                    x, y, fontSizeDips, textRunLength);
+        ContextualSearchManager manager = getContextualSearchManager(mTab);
+        if (mGestureStateListener != null && manager != null) {
+            manager.onShowUnhandledTapUIIfNeeded(x, y, fontSizeDips, textRunLength);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
index 807987c..8b6e698 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
@@ -154,7 +154,7 @@
         mBaseCustomTabRootUiCoordinator = new BaseCustomTabRootUiCoordinator(this,
                 getShareDelegateSupplier(),
                 getActivityTabProvider(), mTabModelProfileSupplier, mBookmarkBridgeSupplier,
-                this::getContextualSearchManager, getTabModelSelectorSupplier(),
+                mContextualSearchManagerSupplier, getTabModelSelectorSupplier(),
                 getBrowserControlsManager(), getWindowAndroid(), getLifecycleDispatcher(),
                 getLayoutManagerSupplier(),
                 /* menuOrKeyboardActionController= */ this, this::getActivityThemeColor,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/datareduction/DataReductionMainMenuItem.java b/chrome/android/java/src/org/chromium/chrome/browser/datareduction/DataReductionMainMenuItem.java
deleted file mode 100644
index 8882da19..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/datareduction/DataReductionMainMenuItem.java
+++ /dev/null
@@ -1,111 +0,0 @@
-// 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.
-
-package org.chromium.chrome.browser.datareduction;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.text.format.DateUtils;
-import android.text.format.Formatter;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import org.chromium.base.metrics.RecordUserAction;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.datareduction.settings.DataReductionPreferenceFragment;
-import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
-import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.profiles.ProfileManager;
-import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
-import org.chromium.components.browser_ui.settings.SettingsLauncher;
-import org.chromium.components.feature_engagement.EventConstants;
-import org.chromium.components.feature_engagement.Tracker;
-import org.chromium.third_party.android.datausagechart.ChartDataUsageView;
-
-/**
- * Specific {@link FrameLayout} that displays the data savings of Data Saver in the main menu.
- */
-public class DataReductionMainMenuItem extends FrameLayout implements View.OnClickListener {
-    /**
-     * Constructs a new {@link DataReductionMainMenuItem} with the appropriate context.
-     */
-    public DataReductionMainMenuItem(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        TextView itemText = (TextView) findViewById(R.id.menu_item_text);
-        TextView itemSummary = (TextView) findViewById(R.id.menu_item_summary);
-        ImageView icon = (ImageView) findViewById(R.id.icon);
-        icon.setContentDescription(getContext().getString(R.string.data_reduction_title_lite_mode));
-
-        if (DataReductionProxySettings.getInstance().isDataReductionProxyEnabled()) {
-            DataReductionProxyUma.dataReductionProxyUIAction(
-                    DataReductionProxyUma.ACTION_MAIN_MENU_DISPLAYED_ON);
-
-            String dataSaved = Formatter.formatShortFileSize(getContext(),
-                    DataReductionProxySettings.getInstance()
-                            .getContentLengthSavedInHistorySummary());
-
-            long chartStartDateInMillisSinceEpoch =
-                    DataReductionProxySettings.getInstance().getDataReductionLastUpdateTime()
-                    - DateUtils.DAY_IN_MILLIS * ChartDataUsageView.MAXIMUM_DAYS_IN_CHART;
-            long firstEnabledInMillisSinceEpoch = DataReductionProxySettings.getInstance()
-                                                          .getDataReductionProxyFirstEnabledTime();
-            long mostRecentTime = chartStartDateInMillisSinceEpoch > firstEnabledInMillisSinceEpoch
-                    ? chartStartDateInMillisSinceEpoch
-                    : firstEnabledInMillisSinceEpoch;
-
-            final int flags = DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_NO_YEAR;
-            String date = DateUtils.formatDateTime(getContext(), mostRecentTime, flags).toString();
-
-            itemText.setText(
-                    getContext().getString(R.string.data_reduction_saved_label, dataSaved));
-            itemSummary.setText(getContext().getString(R.string.data_reduction_date_label, date));
-            // TODO (https://crbug.com/1048632): Use the TabModel-specific profile instead of
-            // calling Profile.getLastUsedRegularProfile().
-            Profile profile =
-                    ProfileManager.isInitialized() ? Profile.getLastUsedRegularProfile() : null;
-            if (profile != null) {
-                Tracker tracker = TrackerFactory.getTrackerForProfile(profile);
-                tracker.addOnInitializedCallback((success) -> {
-                    if (success) {
-                        tracker.notifyEvent(EventConstants.OVERFLOW_OPENED_WITH_DATA_SAVER_SHOWN);
-                    }
-                });
-            }
-        } else {
-            DataReductionProxyUma.dataReductionProxyUIAction(
-                    DataReductionProxyUma.ACTION_MAIN_MENU_DISPLAYED_OFF);
-
-            itemText.setText(R.string.data_reduction_title_lite_mode);
-            itemSummary.setText(R.string.text_off);
-        }
-
-        setOnClickListener(this);
-    }
-
-    @Override
-    public void onClick(View v) {
-        RecordUserAction.record("MobileMenuDataSaverOpened");
-        Bundle fragmentArgs = new Bundle();
-        fragmentArgs.putBoolean(DataReductionPreferenceFragment.FROM_MAIN_MENU, true);
-        SettingsLauncher settingsLauncher = new SettingsLauncherImpl();
-        settingsLauncher.launchSettingsActivity(
-                getContext(), DataReductionPreferenceFragment.class, fragmentArgs);
-
-        // TODO (https://crbug.com/1048632): Use the current profile (i.e., regular profile or
-        // incognito profile) instead of always using regular profile. It works correctly now, but
-        // it is not safe.
-        Tracker tracker = TrackerFactory.getTrackerForProfile(Profile.getLastUsedRegularProfile());
-        tracker.notifyEvent(EventConstants.DATA_SAVER_DETAIL_OPENED);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/datareduction/settings/DataReductionPreferenceFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/datareduction/settings/DataReductionPreferenceFragment.java
index b706722..b9f91be 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/datareduction/settings/DataReductionPreferenceFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/datareduction/settings/DataReductionPreferenceFragment.java
@@ -21,10 +21,8 @@
 import org.chromium.chrome.browser.datareduction.DataReductionPromoUtils;
 import org.chromium.chrome.browser.datareduction.DataReductionProxyUma;
 import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherImpl;
-import org.chromium.chrome.browser.infobar.PreviewsLitePageInfoBar;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings.ContentLengths;
-import org.chromium.chrome.browser.previews.HttpsImageCompressionUtils;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
 import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
@@ -49,8 +47,6 @@
     private boolean mIsEnabled;
     private boolean mWasEnabledAtCreation;
     private boolean mFromMainMenu;
-    private boolean mFromInfobar;
-    private boolean mFromLiteModeHttpsImageCompressionInfoBar;
 
     @Override
     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@@ -64,10 +60,6 @@
         setHasOptionsMenu(true);
 
         mFromMainMenu = IntentUtils.safeGetBoolean(getArguments(), FROM_MAIN_MENU, false);
-        mFromInfobar = IntentUtils.safeGetBoolean(
-                getArguments(), PreviewsLitePageInfoBar.FROM_INFOBAR, false);
-        mFromLiteModeHttpsImageCompressionInfoBar = IntentUtils.safeGetBoolean(getArguments(),
-                HttpsImageCompressionUtils.FROM_LITE_MODE_HTTPS_IMAGE_COMPRESSION_INFOBAR, false);
     }
 
     @Override
@@ -88,24 +80,6 @@
                 statusChange = mIsEnabled ? DataReductionProxyUma.ACTION_MAIN_MENU_OFF_TO_ON
                                           : DataReductionProxyUma.ACTION_MAIN_MENU_OFF_TO_OFF;
             }
-        } else if (mFromInfobar) {
-            if (mWasEnabledAtCreation) {
-                statusChange = mIsEnabled ? DataReductionProxyUma.ACTION_INFOBAR_ON_TO_ON
-                                          : DataReductionProxyUma.ACTION_INFOBAR_ON_TO_OFF;
-            } else {
-                statusChange = mIsEnabled ? DataReductionProxyUma.ACTION_INFOBAR_OFF_TO_ON
-                                          : DataReductionProxyUma.ACTION_INFOBAR_OFF_TO_OFF;
-            }
-        } else if (mFromLiteModeHttpsImageCompressionInfoBar) {
-            if (mWasEnabledAtCreation) {
-                statusChange = mIsEnabled
-                        ? DataReductionProxyUma.ACTION_HTTPS_IMAGE_COMPRESSION_INFOBAR_ON_TO_ON
-                        : DataReductionProxyUma.ACTION_HTTPS_IMAGE_COMPRESSION_INFOBAR_ON_TO_OFF;
-            } else {
-                statusChange = mIsEnabled
-                        ? DataReductionProxyUma.ACTION_HTTPS_IMAGE_COMPRESSION_INFOBAR_OFF_TO_ON
-                        : DataReductionProxyUma.ACTION_HTTPS_IMAGE_COMPRESSION_INFOBAR_OFF_TO_OFF;
-            }
         } else if (mWasEnabledAtCreation) {
             statusChange = mIsEnabled ? DataReductionProxyUma.ACTION_ON_TO_ON
                                       : DataReductionProxyUma.ACTION_ON_TO_OFF;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
index e2a9bbda..f5084ba6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
@@ -70,7 +70,7 @@
         mRemoveButton.setContentDescription(getContext().getString((R.string.remove)));
         ApiCompatibilityUtils.setImageTintList(mRemoveButton,
                 AppCompatResources.getColorStateList(
-                        getContext(), R.color.default_icon_color_secondary));
+                        getContext(), R.color.default_icon_color_secondary_tint_list));
         mRemoveButton.setOnClickListener(v -> remove());
         mRemoveButton.setScaleType(ScaleType.CENTER_INSIDE);
         mRemoveButton.setPaddingRelative(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/PreviewsLitePageInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/PreviewsLitePageInfoBar.java
deleted file mode 100644
index cf48e0f..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/PreviewsLitePageInfoBar.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2018 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.infobar;
-
-import android.os.Bundle;
-
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.datareduction.settings.DataReductionPreferenceFragment;
-import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
-import org.chromium.components.browser_ui.settings.SettingsLauncher;
-import org.chromium.components.infobars.ConfirmInfoBar;
-import org.chromium.components.infobars.InfoBar;
-
-/**
- * An InfoBar that lets the user know that Data Saver Lite Mode now also applies to HTTPS pages.
- */
-public class PreviewsLitePageInfoBar extends ConfirmInfoBar {
-    public static final String FROM_INFOBAR = "FromInfoBar";
-
-    @CalledByNative
-    private static InfoBar show(int iconId, String message, String linkText) {
-        return new PreviewsLitePageInfoBar(iconId, message, linkText);
-    }
-
-    private PreviewsLitePageInfoBar(int iconDrawbleId, String message, String linkText) {
-        super(iconDrawbleId, R.color.infobar_icon_drawable_color, null, message, linkText, null,
-                null);
-    }
-
-    @Override
-    public void onLinkClicked() {
-        super.onLinkClicked();
-
-        Bundle fragmentArgs = new Bundle();
-        fragmentArgs.putBoolean(FROM_INFOBAR, true);
-        SettingsLauncher settingsLauncher = new SettingsLauncherImpl();
-        settingsLauncher.launchSettingsActivity(
-                getContext(), DataReductionPreferenceFragment.class, fragmentArgs);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
index e21f6e1e..830b806 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
@@ -11,10 +11,12 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.text.TextUtils;
+import android.text.format.DateUtils;
 import android.util.SparseBooleanArray;
 
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.ActivityState;
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ApplicationStatus.ActivityStateListener;
@@ -264,11 +266,13 @@
                 (ActivityManager) mActivity.getSystemService(Context.ACTIVITY_SERVICE);
         String launchActivityName = ChromeTabbedActivity.MAIN_LAUNCHER_ACTIVITY_NAME;
         if (activityManager != null) {
-            MultiInstanceState.maybeCreate(activityManager::getAppTasks,
+            MultiInstanceState state = MultiInstanceState.maybeCreate(activityManager::getAppTasks,
                     (activityName)
                             -> TextUtils.equals(activityName, ChromeTabbedActivity.class.getName())
                             || TextUtils.equals(activityName, launchActivityName));
+            state.addObserver(this::onMultiInstanceStateChanged);
         }
+        ApplicationStatus.registerStateListenerForActivity(this, mActivity);
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
@@ -566,6 +570,9 @@
         // This handles a case where an instance is deleted within Chrome but not through
         // Window manager UI, and the task is removed by system. See https://crbug.com/1241719.
         removeInvalidInstanceData();
+        if (mInstanceId != INVALID_INSTANCE_ID) {
+            ApplicationStatus.unregisterActivityStateListener(this);
+        }
         super.onDestroy();
     }
 
@@ -588,9 +595,50 @@
     @Override
     public void onActivityStateChange(Activity activity, int newState) {
         if (!MultiWindowUtils.isMultiInstanceApi31Enabled()) return;
-        // TODO: Update UMA metrics:
-        //       - instance/task count
-        //       - multi-instance session (enter/exit/duration)
+
+        if (newState != ActivityState.RESUMED && newState != ActivityState.STOPPED) return;
+
+        SharedPreferencesManager prefs = SharedPreferencesManager.getInstance();
+        // Check the max instance count in a day for every state update if needed.
+        long timestamp = prefs.readLong(ChromePreferenceKeys.MULTI_INSTANCE_MAX_COUNT_TIME, 0);
+        int maxCount = prefs.readInt(ChromePreferenceKeys.MULTI_INSTANCE_MAX_INSTANCE_COUNT, 0);
+        long current = System.currentTimeMillis();
+
+        if (current - timestamp > DateUtils.DAY_IN_MILLIS) {
+            if (timestamp != 0) {
+                RecordHistogram.recordExactLinearHistogram(
+                        "Android.MultiInstance.MaxInstanceCount", maxCount, mMaxInstances + 1);
+            }
+            prefs.writeLong(ChromePreferenceKeys.MULTI_INSTANCE_MAX_COUNT_TIME, current);
+            // Reset the count to 0 to be ready to obtain the max count for the next 24-hour period.
+            maxCount = 0;
+        }
+        int instanceCount = MultiWindowUtils.getInstanceCount();
+        if (instanceCount > maxCount) {
+            prefs.writeInt(ChromePreferenceKeys.MULTI_INSTANCE_MAX_INSTANCE_COUNT, instanceCount);
+        }
+    }
+
+    private void onMultiInstanceStateChanged(boolean inMultiInstanceMode) {
+        if (!MultiWindowUtils.isMultiInstanceApi31Enabled()) return;
+
+        SharedPreferencesManager prefs = SharedPreferencesManager.getInstance();
+        long startTime = prefs.readLong(ChromePreferenceKeys.MULTI_INSTANCE_START_TIME);
+        long current = System.currentTimeMillis();
+
+        // This method in invoked for every ChromeActivity instance. Logging metrics for the first
+        // ChromeActivity is enough. The pref |MULTI_INSTANCE_START_TIME| is set to non-zero once
+        // Android.MultiInstance.Enter is logged, and reset to zero after
+        // Android.MultiInstance.Exit to avoid duplicated logging.
+        if (startTime == 0 && inMultiInstanceMode) {
+            RecordUserAction.record("Android.MultiInstance.Enter");
+            prefs.writeLong(ChromePreferenceKeys.MULTI_INSTANCE_START_TIME, current);
+        } else if (startTime != 0 && !inMultiInstanceMode) {
+            RecordUserAction.record("Android.MultiInstance.Exit");
+            RecordHistogram.recordLongTimesHistogram(
+                    "Android.MultiInstance.TotalDuration", current - startTime);
+            prefs.writeLong(ChromePreferenceKeys.MULTI_INSTANCE_START_TIME, 0);
+        }
     }
 
     @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
index abc32380..2783a601 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
@@ -30,6 +30,7 @@
 import org.chromium.base.ApplicationStatus.ActivityStateListener;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.IntentUtils;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.ChromeTabbedActivity2;
@@ -696,10 +697,11 @@
                     SharedPreferencesManager prefs = SharedPreferencesManager.getInstance();
                     long startTime = prefs.readLong(ChromePreferenceKeys.MULTI_WINDOW_START_TIME);
                     if (startTime > 0) {
+                        long current = System.currentTimeMillis();
                         RecordUserAction.record("Android.MultiWindowMode.Exit");
+                        RecordHistogram.recordLongTimesHistogram(
+                                "Android.MultiWindowMode.TotalDuration", current - startTime);
                         prefs.writeLong(ChromePreferenceKeys.MULTI_WINDOW_START_TIME, 0);
-                        // TODO: Record histogram for time spent in multi-window mode by
-                        //       at least one Chrome instance.
                     }
                 } else {
                     RecordUserAction.record("Android.MultiWindowMode.Exit");
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java
index a905bf24..742ebaf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java
@@ -18,7 +18,6 @@
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.settings.datareduction.DataReductionProxySavingsClearedReason;
-import org.chromium.components.browser_ui.util.ConversionUtils;
 import org.chromium.components.embedder_support.util.UrlConstants;
 
 import java.util.ArrayList;
@@ -63,9 +62,6 @@
 
     private static DataReductionProxySettings sSettings;
 
-    // The saved data threshold for showing the Lite mode menu footer.
-    private static final long DATA_REDUCTION_MAIN_MENU_ITEM_SAVED_KB_THRESHOLD = 100;
-
     // The received data threshold for showing the data reduction chart in settings.
     public static final long DATA_REDUCTION_SHOW_CHART_KB_THRESHOLD = 100;
 
@@ -170,16 +166,6 @@
                 mNativeDataReductionProxySettings, DataReductionProxySettings.this);
     }
 
-    /**
-     * Returns true if the Data Reduction Proxy menu item should be shown in the main menu.
-     */
-    public boolean shouldUseDataReductionMainMenuItem() {
-        if (!isDataReductionProxyEnabled()) return false;
-
-        return ConversionUtils.bytesToKilobytes(getContentLengthSavedInHistorySummary())
-                >= DATA_REDUCTION_MAIN_MENU_ITEM_SAVED_KB_THRESHOLD;
-    }
-
     /** Returns true if the SPDY proxy is managed by an administrator's policy. */
     public boolean isDataReductionProxyManaged() {
         return DataReductionProxySettingsJni.get().isDataReductionProxyManaged(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
index 80b9fcf..211c0d67 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
@@ -445,11 +445,31 @@
      */
     @VisibleForTesting
     protected HttpURLConnection createConnection() throws RequestFailureException {
+        // TODO(crbug.com/1139505): Remove the note about UID when UID fallback is removed.
+        NetworkTrafficAnnotationTag annotation = NetworkTrafficAnnotationTag.createComplete(
+                "omaha_client_android_uc",
+                "semantics {"
+                        + "  sender: 'Updates'"
+                        + "  description: "
+                        + "    'This traffic checks whether the browser is up-to-date and '"
+                        + "    'provides basic browser telemetry using the Omaha protocol.'"
+                        + "  trigger: 'Manual or automatic checks for updates.'"
+                        + "  data:"
+                        + "    'Various OS and browser parameters such as version, '"
+                        + "    'architecture, channel, and the calendar date of the previous '"
+                        + "    'communication. '"
+                        + "    'A unique identifier for the device may be transmitted.'"
+                        + "  destination: GOOGLE_OWNED_SERVICE"
+                        + "}"
+                        + "policy {"
+                        + "  cookies_allowed: NO"
+                        + "  policy_exception_justification: 'Not implemented.'"
+                        + "  setting: 'This feature cannot be disabled.'"
+                        + "}");
         try {
             URL url = new URL(getRequestGenerator().getServerUrl());
             HttpURLConnection connection =
-                    (HttpURLConnection) ChromiumNetworkAdapter.openConnection(
-                            url, NetworkTrafficAnnotationTag.MISSING_TRAFFIC_ANNOTATION);
+                    (HttpURLConnection) ChromiumNetworkAdapter.openConnection(url, annotation);
             connection.setConnectTimeout(MS_CONNECTION_TIMEOUT);
             connection.setReadTimeout(MS_CONNECTION_TIMEOUT);
             return connection;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfoControllerDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfoControllerDelegate.java
index 86fb4ff4..2d0ab65 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfoControllerDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfoControllerDelegate.java
@@ -32,8 +32,6 @@
 import org.chromium.chrome.browser.offlinepages.OfflinePageUtils.OfflinePageLoadUrlDelegate;
 import org.chromium.chrome.browser.omnibox.ChromeAutocompleteSchemeClassifier;
 import org.chromium.chrome.browser.paint_preview.TabbedPaintPreview;
-import org.chromium.chrome.browser.previews.PreviewsAndroidBridge;
-import org.chromium.chrome.browser.previews.PreviewsUma;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.site_settings.ChromeSiteSettingsDelegate;
 import org.chromium.chrome.browser.tab.Tab;
@@ -99,7 +97,6 @@
 
         initOfflinePageParams();
         mOfflinePageLoadUrlDelegate = offlinePageLoadUrlDelegate;
-        initHttpsImageCompressionStateAndRecordUMA();
 
         TrackerFactory.getTrackerForProfile(Profile.getLastUsedRegularProfile())
                 .notifyEvent(EventConstants.PAGE_INFO_OPENED);
@@ -127,14 +124,6 @@
         }
     }
 
-    private void initHttpsImageCompressionStateAndRecordUMA() {
-        mIsHttpsImageCompressionApplied =
-                PreviewsAndroidBridge.getInstance().isHttpsImageCompressionApplied(mWebContents);
-        if (mIsHttpsImageCompressionApplied) {
-            PreviewsUma.recordHttpsImageCompressionPageInfoOpened();
-        }
-    }
-
     /**
      * {@inheritDoc}
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/previews/DIR_METADATA b/chrome/android/java/src/org/chromium/chrome/browser/previews/DIR_METADATA
deleted file mode 100644
index 5ce3ad1e..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/previews/DIR_METADATA
+++ /dev/null
@@ -1,3 +0,0 @@
-monorail {
-  component: "Blink>Previews"
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/previews/HttpsImageCompressionUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/previews/HttpsImageCompressionUtils.java
deleted file mode 100644
index d2d98d9..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/previews/HttpsImageCompressionUtils.java
+++ /dev/null
@@ -1,86 +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.
-
-package org.chromium.chrome.browser.previews;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.datareduction.settings.DataReductionPreferenceFragment;
-import org.chromium.chrome.browser.infobar.InfoBarIdentifier;
-import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
-import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabUtils;
-import org.chromium.chrome.browser.ui.messages.infobar.SimpleConfirmInfoBarBuilder;
-import org.chromium.components.browser_ui.settings.SettingsLauncher;
-
-/**
- * Common utility API used in java land.
- */
-public final class HttpsImageCompressionUtils {
-    public static final String FROM_LITE_MODE_HTTPS_IMAGE_COMPRESSION_INFOBAR =
-            "FromLiteModeHttpsImageCompressionInfoBar";
-
-    /**
-     * Actions taken on the infobar. This enum must remain synchronized with the
-     * HttpsImageCompressionInfoBarAction enum name in metrics/histograms/enums.xml.
-     */
-    private static final int HTTPS_IMAGE_COMPRESSION_INFO_BAR_SHOWN = 0;
-    private static final int HTTPS_IMAGE_COMPRESSION_INFO_BAR_DISMISSED = 1;
-    private static final int HTTPS_IMAGE_COMPRESSION_INFO_BAR_LINK_CLICKED = 2;
-    private static final int HTTPS_IMAGE_COMPRESSION_INFO_BAR_MAX_VALUE = 3;
-
-    /**
-     * Creates InfoBar that shows https images are optimized in the tab.
-     */
-    public static boolean createInfoBar(final Tab tab) {
-        final Activity activity = TabUtils.getActivity(tab);
-        if (activity == null) {
-            return false;
-        }
-        SimpleConfirmInfoBarBuilder.create(tab.getWebContents(),
-                new SimpleConfirmInfoBarBuilder.Listener() {
-                    @Override
-                    public void onInfoBarDismissed() {
-                        recordInfoBarAction(HTTPS_IMAGE_COMPRESSION_INFO_BAR_DISMISSED);
-                    }
-
-                    @Override
-                    public boolean onInfoBarButtonClicked(boolean isPrimary) {
-                        return false;
-                    }
-
-                    @Override
-                    public boolean onInfoBarLinkClicked() {
-                        Bundle fragmentArgs = new Bundle();
-                        fragmentArgs.putBoolean(
-                                FROM_LITE_MODE_HTTPS_IMAGE_COMPRESSION_INFOBAR, true);
-                        SettingsLauncher settingsLauncher = new SettingsLauncherImpl();
-                        settingsLauncher.launchSettingsActivity(tab.getContext(),
-                                DataReductionPreferenceFragment.class, fragmentArgs);
-                        recordInfoBarAction(HTTPS_IMAGE_COMPRESSION_INFO_BAR_LINK_CLICKED);
-                        return true;
-                    }
-                },
-                InfoBarIdentifier.LITE_MODE_HTTPS_IMAGE_COMPRESSION_INFOBAR_ANDROID,
-                tab.getContext(), R.drawable.preview_pin_round /* drawableId */,
-                activity.getString(
-                        R.string.lite_mode_https_image_compression_message) /* message */,
-                null /* primaryText */, null /* secondaryText */,
-                activity.getString(
-                        R.string.lite_mode_https_image_compression_settings_link) /* linkText */,
-                false /*autoExpire */);
-        recordInfoBarAction(HTTPS_IMAGE_COMPRESSION_INFO_BAR_SHOWN);
-        return true;
-    }
-
-    private static void recordInfoBarAction(int action) {
-        assert action >= 0 && action < HTTPS_IMAGE_COMPRESSION_INFO_BAR_MAX_VALUE;
-        RecordHistogram.recordEnumeratedHistogram(
-                "SubresourceRedirect.ImageCompressionNotificationInfoBar", action,
-                HTTPS_IMAGE_COMPRESSION_INFO_BAR_MAX_VALUE);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/previews/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/previews/OWNERS
deleted file mode 100644
index 2783dea..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/previews/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://components/data_reduction_proxy/OWNERS
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java
deleted file mode 100644
index 7f8480b..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsAndroidBridge.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2018 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.previews;
-
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.base.annotations.NativeMethods;
-import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.content_public.browser.WebContents;
-
-/**
- * Java bridge class to C++ Previews code.
- */
-public final class PreviewsAndroidBridge {
-    private static PreviewsAndroidBridge sBridge;
-
-    public static PreviewsAndroidBridge getInstance() {
-        if (sBridge == null) {
-            sBridge = new PreviewsAndroidBridge();
-        }
-        return sBridge;
-    }
-
-    private final long mNativePreviewsAndroidBridge;
-
-    private PreviewsAndroidBridge() {
-        mNativePreviewsAndroidBridge =
-                PreviewsAndroidBridgeJni.get().init(PreviewsAndroidBridge.this);
-    }
-
-    /**
-     * Returns whether LiteMode https image compression is applied.
-     */
-    public boolean isHttpsImageCompressionApplied(WebContents webContents) {
-        return PreviewsAndroidBridgeJni.get().isHttpsImageCompressionApplied(
-                mNativePreviewsAndroidBridge, PreviewsAndroidBridge.this, webContents);
-    }
-
-    @CalledByNative
-    private static boolean createHttpsImageCompressionInfoBar(final Tab tab) {
-        return HttpsImageCompressionUtils.createInfoBar(tab);
-    }
-
-    @NativeMethods
-    interface Natives {
-        long init(PreviewsAndroidBridge caller);
-        boolean isHttpsImageCompressionApplied(long nativePreviewsAndroidBridge,
-                PreviewsAndroidBridge caller, WebContents webContents);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsUma.java b/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsUma.java
deleted file mode 100644
index edfcf27e..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/previews/PreviewsUma.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2018 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.previews;
-
-import org.chromium.base.metrics.RecordHistogram;
-
-/**
- * Central place to record UMA for Previews in Android.
- */
-public final class PreviewsUma {
-    // Don't allow this class to be instantiated.
-    private PreviewsUma() {}
-
-    // The base histogram name. The current previews type will be added as a suffix.
-    private static final String BASE_HISTOGRAM_NAME = "Previews.OmniboxAction.%s";
-
-    // This must remain in sync with PreviewsUserOmniboxAction in
-    // //tools/metrics/histograms/enums.xml.
-
-    // User opted out of the preview.
-    // OBSOLETE: private static final int ACTION_OPT_OUT = 0;
-    // User opened the page info dialog.
-    private static final int ACTION_PAGE_INFO_OPENED = 1;
-    // The Lite Page badge was displayed at commit.
-    // OBSOLETE: private static final int ACTION_LITE_PAGE_AT_COMMIT = 2;
-    // OBSOLETE:  private static final int ACTION_LITE_PAGE_AT_FINISH = 3;
-    private static final int ACTION_INDEX_BOUNDARY = 4;
-
-    /**
-     * Records the given action on the BASE_HISTOGRAM with the committed previews type suffix.
-     * @param webContents the active WebContents
-     * @param action the action to record
-     */
-    private static final void recordHistogram(final String previewType, final int action) {
-        assert action >= 0 && action < ACTION_INDEX_BOUNDARY;
-        if (previewType == null || previewType.length() == 0) return;
-
-        final String histogram = String.format(BASE_HISTOGRAM_NAME, previewType);
-        RecordHistogram.recordEnumeratedHistogram(histogram, action, ACTION_INDEX_BOUNDARY);
-    }
-
-    /**
-     * Records that the user opened the page info dialog, and https image compression message was
-     * shown in the page info.
-     */
-    public static void recordHttpsImageCompressionPageInfoOpened() {
-        recordHistogram("HttpsImageCompression", ACTION_PAGE_INFO_OPENED);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java
index 9fb9dc5..fa76f17 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java
@@ -18,6 +18,7 @@
 import org.chromium.chrome.browser.browserservices.permissiondelegation.TrustedWebActivityPermissionManager;
 import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherImpl;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.incognito.IncognitoUtils;
 import org.chromium.chrome.browser.notifications.channels.SiteChannelsManager;
 import org.chromium.chrome.browser.privacy_sandbox.PrivacySandboxBridge;
 import org.chromium.chrome.browser.privacy_sandbox.PrivacySandboxSnackbarController;
@@ -169,6 +170,11 @@
     }
 
     @Override
+    public boolean isIncognitoModeEnabled() {
+        return IncognitoUtils.isIncognitoModeEnabled();
+    }
+
+    @Override
     public boolean isQuietNotificationPromptsFeatureEnabled() {
         return ChromeFeatureList.isEnabled(ChromeFeatureList.QUIET_NOTIFICATION_PROMPTS);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
index 074a257..e6ef840 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
@@ -135,9 +135,6 @@
     private boolean mIsClosing;
     private boolean mIsShowingErrorPage;
 
-    /** Whether or not the TabState has changed. */
-    private boolean mIsTabStateDirty = true;
-
     /**
      * Saves how this tab was launched (from a link, external app, etc) so that
      * we can determine the different circumstances in which it should be
@@ -785,18 +782,6 @@
         return !(activity instanceof ChromeActivity);
     }
 
-    /**
-     * @return Whether the TabState representing this Tab has been updated.
-     */
-    public boolean isTabStateDirty() {
-        return mIsTabStateDirty;
-    }
-
-    @Override
-    public void setIsTabStateDirty(boolean isDirty) {
-        mIsTabStateDirty = isDirty;
-    }
-
     @Override
     public void setIsTabSaveEnabled(boolean isTabSaveEnabled) {
         mIsTabSaveEnabledSupplier.set(isTabSaveEnabled);
@@ -1058,7 +1043,6 @@
      * @param url URL that was loaded.
      */
     void didFinishPageLoad(GURL url) {
-        mIsTabStateDirty = true;
         updateTitle();
 
         for (TabObserver observer : mObservers) observer.onPageLoadFinished(this, url);
@@ -1119,7 +1103,6 @@
      * Called when navigation entries were removed.
      */
     void notifyNavigationEntriesDeleted() {
-        mIsTabStateDirty = true;
         for (TabObserver observer : mObservers) observer.onNavigationEntriesDeleted(this);
     }
 
@@ -1215,7 +1198,6 @@
     void updateTitle(String title) {
         if (TextUtils.equals(CriticalPersistedTabData.from(this).getTitle(), title)) return;
 
-        mIsTabStateDirty = true;
         CriticalPersistedTabData.from(this).setTitle(title);
         notifyPageTitleChanged();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
index acbc4424..562656b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
@@ -334,7 +334,9 @@
             if (!navigation.hasCommitted()) return;
 
             if (navigation.isInPrimaryMainFrame()) {
-                mTab.setIsTabStateDirty(true);
+                if (!mTab.isDestroyed()) {
+                    TabStateAttributes.from(mTab).setIsTabStateDirty(true);
+                }
                 mTab.updateTitle();
                 mTab.handleDidFinishNavigation(navigation.getUrl(), navigation.pageTransition());
                 mTab.setIsShowingErrorPage(navigation.isErrorPage());
@@ -378,7 +380,9 @@
 
         @Override
         public void navigationEntriesChanged() {
-            mTab.setIsTabStateDirty(true);
+            if (!mTab.isDestroyed()) {
+                TabStateAttributes.from(mTab).setIsTabStateDirty(true);
+            }
         }
 
         @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java
index 2e00a7b..bca68fd1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java
@@ -14,14 +14,12 @@
 import org.chromium.chrome.browser.app.appmenu.AppMenuPropertiesDelegateImpl;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge;
 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
-import org.chromium.chrome.browser.datareduction.DataReductionMainMenuItem;
 import org.chromium.chrome.browser.enterprise.util.ManagedBrowserUtils;
 import org.chromium.chrome.browser.feed.FeedFeatures;
 import org.chromium.chrome.browser.feed.webfeed.WebFeedFaviconFetcher;
 import org.chromium.chrome.browser.feed.webfeed.WebFeedMainMenuItem;
 import org.chromium.chrome.browser.feed.webfeed.WebFeedSnackbarController;
 import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher;
-import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
@@ -61,11 +59,6 @@
         mSnackbarManager = snackbarManager;
     }
 
-    private boolean shouldShowDataSaverMenuItem() {
-        return (mOverviewModeBehavior == null || !mOverviewModeBehavior.overviewVisible())
-                && DataReductionProxySettings.getInstance().shouldUseDataReductionMainMenuItem();
-    }
-
     private boolean shouldShowWebFeedMenuItem() {
         if (!FeedFeatures.isWebFeedUIEnabled()) {
             return false;
@@ -83,8 +76,6 @@
     public int getFooterResourceId() {
         if (shouldShowWebFeedMenuItem()) {
             return R.layout.web_feed_main_menu_item;
-        } else if (shouldShowDataSaverMenuItem()) {
-            return R.layout.data_reduction_main_menu_item;
         }
         return 0;
     }
@@ -105,20 +96,13 @@
     }
 
     @Override
-    public void onHeaderViewInflated(AppMenuHandler appMenuHandler, View view) {
-        if (view instanceof DataReductionMainMenuItem) {
-            view.findViewById(R.id.data_reduction_menu_divider).setVisibility(View.GONE);
-        }
-    }
+    public void onHeaderViewInflated(AppMenuHandler appMenuHandler, View view) {}
 
     @Override
     public boolean shouldShowFooter(int maxMenuHeight) {
         if (shouldShowWebFeedMenuItem()) {
             return true;
         }
-        if (shouldShowDataSaverMenuItem()) {
-            return canShowDataReductionItem(maxMenuHeight);
-        }
         return super.shouldShowFooter(maxMenuHeight);
     }
 
@@ -132,11 +116,4 @@
     public boolean shouldShowIconBeforeItem() {
         return true;
     }
-
-    private boolean canShowDataReductionItem(int maxMenuHeight) {
-        // TODO(twellington): Account for whether a different footer or header is
-        // showing.
-        return maxMenuHeight >= mContext.getResources().getDimension(
-                       R.dimen.data_saver_menu_footer_min_show_height);
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
index 7b2ff52..8b17613 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
@@ -41,9 +41,9 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabCreationState;
 import org.chromium.chrome.browser.tab.TabIdManager;
-import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tab.TabState;
+import org.chromium.chrome.browser.tab.TabStateAttributes;
 import org.chromium.chrome.browser.tab.TabStateExtractor;
 import org.chromium.chrome.browser.tab.state.CriticalPersistedTabData;
 import org.chromium.chrome.browser.tab.state.FilePersistedTabDataStorage;
@@ -131,6 +131,7 @@
         new TabModelSelectorTabObserver(mTabModelSelector) {
             @Override
             public void onNavigationEntriesDeleted(Tab tab) {
+                if (!tab.isDestroyed()) TabStateAttributes.from(tab).setIsTabStateDirty(true);
                 addTabToSaveQueue(tab);
             }
 
@@ -143,6 +144,16 @@
             public void onRootIdChanged(Tab tab, int newRootId) {
                 addTabToSaveQueue(tab);
             }
+
+            @Override
+            public void onPageLoadFinished(Tab tab, GURL url) {
+                if (!tab.isDestroyed()) TabStateAttributes.from(tab).setIsTabStateDirty(true);
+            }
+
+            @Override
+            public void onTitleUpdated(Tab tab) {
+                if (!tab.isDestroyed()) TabStateAttributes.from(tab).setIsTabStateDirty(true);
+            }
         };
 
         mTabModelObserver = new TabModelObserver() {
@@ -810,8 +821,8 @@
     }
 
     private void addTabToSaveQueueIfApplicable(Tab tab) {
-        if (tab == null) return;
-        if (mTabsToSave.contains(tab) || !((TabImpl) tab).isTabStateDirty()
+        if (tab == null || tab.isDestroyed()) return;
+        if (mTabsToSave.contains(tab) || !TabStateAttributes.from(tab).isTabStateDirty()
                 || isTabUrlContentScheme(tab)) {
             return;
         }
@@ -1252,7 +1263,7 @@
         protected void onPostExecute(Void v) {
             if (mDestroyed || isCancelled()) return;
             if (mStateSaved) {
-                ((TabImpl) mTab).setIsTabStateDirty(false);
+                if (!mTab.isDestroyed()) TabStateAttributes.from(mTab).setIsTabStateDirty(false);
                 mTab.setIsTabSaveEnabled(isCriticalPersistedTabDataEnabled());
             }
             mSaveTabTask = null;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/DataSaverAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/DataSaverAppMenuTest.java
deleted file mode 100644
index a9c29a2..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/DataSaverAppMenuTest.java
+++ /dev/null
@@ -1,101 +0,0 @@
-// 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.
-
-package org.chromium.chrome.browser.app.appmenu;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.Feature;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
-import org.chromium.chrome.browser.ui.appmenu.AppMenuTestSupport;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
-
-/**
- * Tests the Data Saver AppMenu footer
- */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-@EnableFeatures("DataReductionProxyEnabledWithNetworkService")
-public class DataSaverAppMenuTest {
-    @Rule
-    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
-
-    private TestDataReductionProxySettings mSettings;
-
-    private static class TestDataReductionProxySettings extends DataReductionProxySettings {
-        private long mContentLengthSavedInHistorySummary;
-
-        @Override
-        public long getContentLengthSavedInHistorySummary() {
-            return mContentLengthSavedInHistorySummary;
-        }
-
-        /**
-         * Sets the content length saved for the number of days shown in the history summary. This
-         * is only used for testing.
-         */
-        public void setContentLengthSavedInHistorySummary(long contentLengthSavedInHistorySummary) {
-            mContentLengthSavedInHistorySummary = contentLengthSavedInHistorySummary;
-        }
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        mActivityTestRule.startMainActivityOnBlankPage();
-
-        mSettings = new TestDataReductionProxySettings();
-        DataReductionProxySettings.setInstanceForTesting(mSettings);
-    }
-
-    /**
-     * Verify the Data Saver footer shows with the flag when the proxy is on and the user has saved
-     * at least 100KB of data.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Browser", "Main"})
-    public void testMenuDataSaver() throws Throwable {
-        mActivityTestRule.runOnUiThread((Runnable) () -> {
-            // Data Saver hasn't been turned on, the footer shouldn't show.
-            Assert.assertEquals(0, getFooterResourceId());
-
-            // Turn Data Saver on, the footer should not show since the user hasn't saved any bytes
-            // yet.
-            DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
-                    mActivityTestRule.getActivity().getApplicationContext(), true);
-            Assert.assertEquals(0, getFooterResourceId());
-
-            // The user has only saved 50KB so far. Ensure footer is not shown since it is not above
-            // the threshold yet.
-            mSettings.setContentLengthSavedInHistorySummary(50 * 1024);
-            Assert.assertEquals(0, getFooterResourceId());
-
-            // The user has now saved 100KB. Ensure the footer is shown.
-            mSettings.setContentLengthSavedInHistorySummary(100 * 1024);
-            Assert.assertEquals(R.layout.data_reduction_main_menu_item, getFooterResourceId());
-
-            // Ensure the footer is removed if the proxy is turned off.
-            DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
-                    mActivityTestRule.getActivity().getApplicationContext(), false);
-            Assert.assertEquals(0, getFooterResourceId());
-        });
-    }
-
-    private int getFooterResourceId() {
-        return AppMenuTestSupport
-                .getAppMenuPropertiesDelegate(mActivityTestRule.getAppMenuCoordinator())
-                .getFooterResourceId();
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
index 25caa16..a1670959 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
@@ -273,6 +273,7 @@
     @MediumTest
     @Feature({"Browser"})
     @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_TRANSLATE_WITH_GOOGLE_LENS})
+    @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE})
     public void testLensTranslateChipNotShowingIfNotEnabled() throws Throwable {
         // Required to avoid runtime error.
         Looper.prepare();
@@ -291,6 +292,7 @@
     @Test
     @MediumTest
     @Feature({"Browser"})
+    @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE})
     public void testSelectLensTranslateChip() throws Throwable {
         // Required to avoid runtime error.
         Looper.prepare();
@@ -319,6 +321,7 @@
     @MediumTest
     @Feature({"Browser"})
     @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_TRANSLATE_WITH_GOOGLE_LENS})
+    @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE})
     public void testLensChipNotShowingAfterMenuDismissed() throws Throwable {
         // Required to avoid runtime error.
         Looper.prepare();
@@ -348,6 +351,7 @@
     @Test
     @MediumTest
     @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_TRANSLATE_WITH_GOOGLE_LENS})
+    @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE})
     public void testDismissContextMenuOnClickLensTranslateChipEnabled() throws TimeoutException {
         // Required to avoid runtime error.
         Looper.prepare();
@@ -374,6 +378,7 @@
     @MediumTest
     @Feature({"Browser"})
     @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_GOOGLE_LENS_CHIP})
+    @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE})
     public void testLensShoppingChipNotShowingIfNotEnabled() throws Throwable {
         // Required to avoid runtime error.
         Looper.prepare();
@@ -392,6 +397,7 @@
     @Test
     @MediumTest
     @Feature({"Browser"})
+    @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE})
     public void testSelectLensShoppingChip() throws Throwable {
         // Required to avoid runtime error.
         Looper.prepare();
@@ -420,6 +426,7 @@
     // context menu.
     @Test
     @MediumTest
+    @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE})
     public void testDismissContextMenuOnClickShoppingLensChipEnabled() throws TimeoutException {
         // Required to avoid runtime error.
         Looper.prepare();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
index 7ccbde21..b235abb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInstrumentationBase.java
@@ -13,7 +13,6 @@
 import android.app.Instrumentation.ActivityMonitor;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.SharedPreferences;
 import android.graphics.Point;
 import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
@@ -31,7 +30,6 @@
 import org.junit.ClassRule;
 import org.junit.Rule;
 
-import org.chromium.base.ContextUtils;
 import org.chromium.base.FeatureList;
 import org.chromium.base.Log;
 import org.chromium.base.ThreadUtils;
@@ -51,6 +49,7 @@
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.locale.LocaleManagerDelegate;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
@@ -221,7 +220,10 @@
 
         sActivityTestRule.loadUrl(mTestServer.getURL(mTestPage));
 
-        mManager = sActivityTestRule.getActivity().getContextualSearchManager();
+        mManager = TestThreadUtils.runOnUiThreadBlocking(() -> {
+            return ContextualSearchManagerSupplier.getValueOrNullFrom(
+                    sActivityTestRule.getActivity().getWindowAndroid());
+        });
         mTestHost = new ContextualSearchInstrumentationTestHost();
 
         Assert.assertNotNull(mManager);
@@ -1028,14 +1030,10 @@
      */
     private void resetCounters() {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            // TODO(donnd): Use SharedPreferencesManager instead to access SharedPreferences.
-            SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
+            SharedPreferencesManager prefs = SharedPreferencesManager.getInstance();
             boolean freStatus =
-                    prefs.getBoolean(ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE, false);
-            prefs.edit()
-                    .clear()
-                    .putBoolean(ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE, freStatus)
-                    .apply();
+                    prefs.readBoolean(ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE, false);
+            prefs.writeBoolean(ChromePreferenceKeys.FIRST_RUN_FLOW_COMPLETE, freStatus);
         });
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index 45ab0b33..b2696fd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -283,7 +283,10 @@
 
         sActivityTestRule.loadUrl(mTestServer.getURL(TEST_PAGE));
 
-        mManager = sActivityTestRule.getActivity().getContextualSearchManager();
+        mManager = TestThreadUtils.runOnUiThreadBlocking(() -> {
+            return ContextualSearchManagerSupplier.getValueOrNullFrom(
+                    sActivityTestRule.getActivity().getWindowAndroid());
+        });
         mTestHost = new ContextualSearchManagerTestHost();
 
         Assert.assertNotNull(mManager);
@@ -3740,9 +3743,10 @@
         // Trigger on a word and wait for the selection to be established.
         triggerNode(activity2.getActivityTab(), "search");
         CriteriaHelper.pollUiThread(() -> {
-            String selection = activity2.getContextualSearchManager()
-                                       .getSelectionController()
-                                       .getSelectedText();
+            String selection =
+                    ContextualSearchManagerSupplier.getValueOrNullFrom(activity2.getWindowAndroid())
+                            .getSelectionController()
+                            .getSelectedText();
             Criteria.checkThat(selection, Matchers.is("Search"));
         });
         TestThreadUtils.runOnUiThreadBlocking(() -> activity2.getCurrentTabModel().closeAllTabs());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/previewtab/PreviewTabTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/previewtab/PreviewTabTest.java
index 751bb96e..09947c48 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/previewtab/PreviewTabTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/previewtab/PreviewTabTest.java
@@ -20,6 +20,7 @@
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.compositor.bottombar.ephemeraltab.EphemeralTabCoordinator;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager;
+import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagerSupplier;
 import org.chromium.chrome.browser.firstrun.DisableFirstRun;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabbed_mode.TabbedRootUiCoordinator;
@@ -158,14 +159,15 @@
     @Feature({"PreviewTab"})
     public void testSuppressContextualSearch() throws Throwable {
         ChromeActivity activity = mActivityTestRule.getActivity();
-        ContextualSearchManager csManager = activity.getContextualSearchManager();
+        ContextualSearchManager csManager = TestThreadUtils.runOnUiThreadBlocking(() -> {
+            return ContextualSearchManagerSupplier.getValueOrNullFrom(activity.getWindowAndroid());
+        });
         Assert.assertFalse("Contextual Search should be active", csManager.isSuppressed());
 
-        TestThreadUtils.runOnUiThreadBlocking(
-                ()
-                        -> mEphemeralTabCoordinator.requestOpenSheet(
-                                new GURL(mTestServer.getServer().getURL(PREVIEW_TAB)), "PreviewTab",
-                                false));
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mEphemeralTabCoordinator.requestOpenSheet(
+                    new GURL(mTestServer.getServer().getURL(PREVIEW_TAB)), "PreviewTab", false);
+        });
         endAnimations();
         Assert.assertTrue("The Preview Tab did not open", mEphemeralTabCoordinator.isOpened());
         Assert.assertTrue("Contextual Search should be suppressed", csManager.isSuppressed());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
index 385a277..ebebb92b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
@@ -39,6 +39,7 @@
 import org.chromium.chrome.browser.browsing_data.BrowsingDataType;
 import org.chromium.chrome.browser.browsing_data.TimePeriod;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.incognito.IncognitoUtils;
 import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions;
 import org.chromium.chrome.browser.notifications.channels.SiteChannelsManager;
 import org.chromium.chrome.browser.permissions.PermissionTestRule;
@@ -268,19 +269,25 @@
         });
     }
 
+    private enum ToggleButtonState { EnabledUnchecked, EnabledChecked, Disabled }
+
     /**
      * Checks if the button representing the given state matches the managed expectation.
      */
-    private void checkFourStateCookieToggleButtonEnabled(final SettingsActivity settingsActivity,
-            final CookieSettingsState state, final boolean expected) {
+    private void checkFourStateCookieToggleButtonState(final SettingsActivity settingsActivity,
+            final CookieSettingsState state, final ToggleButtonState toggleState) {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             SingleCategorySettings preferences =
                     (SingleCategorySettings) settingsActivity.getMainFragment();
             FourStateCookieSettingsPreference fourStateCookieToggle =
                     (FourStateCookieSettingsPreference) preferences.findPreference(
                             SingleCategorySettings.FOUR_STATE_COOKIE_TOGGLE_KEY);
-            Assert.assertEquals(state + " button should be " + (expected ? "enabled" : "disabled"),
-                    expected, fourStateCookieToggle.isButtonEnabledForTesting(state));
+            boolean enabled = toggleState != ToggleButtonState.Disabled;
+            boolean checked = toggleState == ToggleButtonState.EnabledChecked;
+            Assert.assertEquals(state + " button should be " + (enabled ? "enabled" : "disabled"),
+                    enabled, fourStateCookieToggle.isButtonEnabledForTesting(state));
+            Assert.assertEquals(state + " button should be " + (checked ? "checked" : "unchecked"),
+                    checked, fourStateCookieToggle.isButtonCheckedForTesting(state));
         });
     }
 
@@ -538,12 +545,44 @@
         // managed. This means that every button other than BLOCK is enabled.
         SettingsActivity settingsActivity =
                 SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.COOKIES);
-        checkFourStateCookieToggleButtonEnabled(settingsActivity, CookieSettingsState.ALLOW, true);
-        checkFourStateCookieToggleButtonEnabled(
-                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, true);
-        checkFourStateCookieToggleButtonEnabled(
-                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY, true);
-        checkFourStateCookieToggleButtonEnabled(settingsActivity, CookieSettingsState.BLOCK, false);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.ALLOW, ToggleButtonState.EnabledUnchecked);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, ToggleButtonState.EnabledChecked);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY, ToggleButtonState.EnabledUnchecked);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.BLOCK, ToggleButtonState.Disabled);
+        settingsActivity.finish();
+    }
+
+    /**
+     * Set the cookie content setting to allow through policy, disable incognito
+     * mode and ensure the correct radio buttons are enabled.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    @Policies.Add({ @Policies.Item(key = "DefaultCookiesSetting", string = "1") })
+    public void testDefaultCookiesSettingManagedAllowWithIncognitoDisabled() throws Exception {
+        IncognitoUtils.setEnabledForTesting(false);
+        setFourStateCookieToggle(CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO);
+        checkDefaultCookiesSettingManaged(true);
+        checkThirdPartyCookieBlockingManaged(false);
+        // The ContentSetting is managed (and set to ALLOW) while ThirdPartyCookieBlocking managed.
+        // Cookie toggle is set to block third party incognito but since
+        // incognito is disabled the button should be disabled and the allow
+        // toggle should be checked.
+        SettingsActivity settingsActivity =
+                SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.COOKIES);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.ALLOW, ToggleButtonState.EnabledChecked);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, ToggleButtonState.Disabled);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY, ToggleButtonState.EnabledUnchecked);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.BLOCK, ToggleButtonState.Disabled);
         settingsActivity.finish();
     }
 
@@ -563,12 +602,14 @@
         // options and all buttons except the active one should be disabled.
         SettingsActivity settingsActivity =
                 SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.COOKIES);
-        checkFourStateCookieToggleButtonEnabled(settingsActivity, CookieSettingsState.ALLOW, false);
-        checkFourStateCookieToggleButtonEnabled(
-                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, false);
-        checkFourStateCookieToggleButtonEnabled(
-                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY, false);
-        checkFourStateCookieToggleButtonEnabled(settingsActivity, CookieSettingsState.BLOCK, true);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.ALLOW, ToggleButtonState.Disabled);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, ToggleButtonState.Disabled);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY, ToggleButtonState.Disabled);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.BLOCK, ToggleButtonState.EnabledChecked);
         settingsActivity.finish();
     }
 
@@ -589,12 +630,14 @@
         // these should be enabled.
         SettingsActivity settingsActivity =
                 SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.COOKIES);
-        checkFourStateCookieToggleButtonEnabled(settingsActivity, CookieSettingsState.ALLOW, false);
-        checkFourStateCookieToggleButtonEnabled(
-                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, false);
-        checkFourStateCookieToggleButtonEnabled(
-                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY, true);
-        checkFourStateCookieToggleButtonEnabled(settingsActivity, CookieSettingsState.BLOCK, true);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.ALLOW, ToggleButtonState.Disabled);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, ToggleButtonState.Disabled);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY, ToggleButtonState.EnabledChecked);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.BLOCK, ToggleButtonState.EnabledUnchecked);
         settingsActivity.finish();
     }
 
@@ -615,12 +658,14 @@
         // only these should be enabled.
         SettingsActivity settingsActivity =
                 SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.COOKIES);
-        checkFourStateCookieToggleButtonEnabled(settingsActivity, CookieSettingsState.ALLOW, true);
-        checkFourStateCookieToggleButtonEnabled(
-                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, false);
-        checkFourStateCookieToggleButtonEnabled(
-                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY, false);
-        checkFourStateCookieToggleButtonEnabled(settingsActivity, CookieSettingsState.BLOCK, true);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.ALLOW, ToggleButtonState.EnabledChecked);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, ToggleButtonState.Disabled);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY, ToggleButtonState.Disabled);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.BLOCK, ToggleButtonState.EnabledUnchecked);
         settingsActivity.finish();
     }
 
@@ -644,12 +689,14 @@
         // selected one should be disabled.
         SettingsActivity settingsActivity =
                 SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.COOKIES);
-        checkFourStateCookieToggleButtonEnabled(settingsActivity, CookieSettingsState.ALLOW, true);
-        checkFourStateCookieToggleButtonEnabled(
-                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, false);
-        checkFourStateCookieToggleButtonEnabled(
-                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY, false);
-        checkFourStateCookieToggleButtonEnabled(settingsActivity, CookieSettingsState.BLOCK, false);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.ALLOW, ToggleButtonState.EnabledChecked);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, ToggleButtonState.Disabled);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY, ToggleButtonState.Disabled);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.BLOCK, ToggleButtonState.Disabled);
         settingsActivity.finish();
     }
 
@@ -666,12 +713,39 @@
         // should be enabled.
         SettingsActivity settingsActivity =
                 SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.COOKIES);
-        checkFourStateCookieToggleButtonEnabled(settingsActivity, CookieSettingsState.ALLOW, true);
-        checkFourStateCookieToggleButtonEnabled(
-                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, true);
-        checkFourStateCookieToggleButtonEnabled(
-                settingsActivity, CookieSettingsState.BLOCK_THIRD_PARTY, true);
-        checkFourStateCookieToggleButtonEnabled(settingsActivity, CookieSettingsState.BLOCK, true);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.ALLOW, ToggleButtonState.EnabledUnchecked);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, ToggleButtonState.EnabledChecked);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY, ToggleButtonState.EnabledUnchecked);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.BLOCK, ToggleButtonState.EnabledUnchecked);
+        settingsActivity.finish();
+    }
+
+    /**
+     * Ensure no radio buttons are enforced when cookie settings are unmanaged.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Preferences"})
+    public void testNoCookieSettingsManagedWithIncognitoDisabled() throws Exception {
+        IncognitoUtils.setEnabledForTesting(false);
+        checkDefaultCookiesSettingManaged(false);
+        checkThirdPartyCookieBlockingManaged(false);
+        // The ContentSetting and ThirdPartyCookieBlocking are unmanaged. This means all buttons
+        // should be enabled.
+        SettingsActivity settingsActivity =
+                SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.COOKIES);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.ALLOW, ToggleButtonState.EnabledChecked);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO, ToggleButtonState.Disabled);
+        checkFourStateCookieToggleButtonState(settingsActivity,
+                CookieSettingsState.BLOCK_THIRD_PARTY, ToggleButtonState.EnabledUnchecked);
+        checkFourStateCookieToggleButtonState(
+                settingsActivity, CookieSettingsState.BLOCK, ToggleButtonState.EnabledUnchecked);
         settingsActivity.finish();
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java
index 343faedc..f66b43ac 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabDataTest.java
@@ -312,14 +312,14 @@
     @SmallTest
     @Test
     public void testWebContentsStateBug_crbug_1220839() throws InterruptedException {
-        try (StrictModeContext ignored = StrictModeContext.allowAllThreadPolicies()) {
-            PersistedTabDataConfiguration.setUseTestConfig(false);
-            String url = mTestServer.getURL("/chrome/test/data/browsing_data/e.html");
-            Tab tab = sActivityTestRule.loadUrlInNewTab(url);
-            final Semaphore semaphore = new Semaphore(0);
-            // Saving serialized CriticalPersistedTabData ensures we get a direct ByteBuffer
-            // which is assumed in the rest of Clank. See crbug.com/1220839 for more details.
-            ThreadUtils.runOnUiThreadBlocking(() -> {
+        PersistedTabDataConfiguration.setUseTestConfig(false);
+        String url = mTestServer.getURL("/chrome/test/data/browsing_data/e.html");
+        Tab tab = sActivityTestRule.loadUrlInNewTab(url);
+        final Semaphore semaphore = new Semaphore(0);
+        // Saving serialized CriticalPersistedTabData ensures we get a direct ByteBuffer
+        // which is assumed in the rest of Clank. See crbug.com/1220839 for more details.
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            try (StrictModeContext ignored = StrictModeContext.allowAllThreadPolicies()) {
                 CriticalPersistedTabData criticalPersistedTabData =
                         new CriticalPersistedTabData(tab, "", "", PARENT_ID, ROOT_ID, TIMESTAMP,
                                 TabStateExtractor.getWebContentsState(tab), CONTENT_STATE_VERSION,
@@ -331,9 +331,11 @@
                 persistedTabDataStorage.save(tab.getId(), config.getId(), () -> {
                     return criticalPersistedTabData.getSerializeSupplier().get();
                 }, semaphore::release);
-            });
-            semaphore.acquire();
-            ThreadUtils.runOnUiThreadBlocking(() -> {
+            }
+        });
+        semaphore.acquire();
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            try (StrictModeContext ignored = StrictModeContext.allowAllThreadPolicies()) {
                 PersistedTabDataConfiguration config = PersistedTabDataConfiguration.get(
                         CriticalPersistedTabData.class, tab.isIncognito());
 
@@ -345,8 +347,8 @@
                         deserialized.getWebContentsState().getDisplayTitleFromState());
                 Assert.assertEquals(
                         url, deserialized.getWebContentsState().getVirtualUrlFromState());
-            });
-        }
+            }
+        });
     }
 
     @UiThreadTest
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java
index 565cac067..93bc2aa 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java
@@ -29,6 +29,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import org.chromium.base.UserDataHost;
 import org.chromium.base.task.TaskRunner;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.UiThreadTest;
@@ -41,6 +42,7 @@
 import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tab.TabState;
+import org.chromium.chrome.browser.tab.TabStateAttributes;
 import org.chromium.chrome.browser.tabmodel.TabPersistentStore.TabModelSelectorMetadata;
 import org.chromium.chrome.browser.tabmodel.TabPersistentStore.TabRestoreDetails;
 import org.chromium.components.embedder_support.util.UrlConstants;
@@ -135,8 +137,10 @@
         };
 
         TabImpl emptyNtpTab = mock(TabImpl.class);
+        UserDataHost emptyNtpTabUserDataHost = new UserDataHost();
+        when(emptyNtpTab.getUserDataHost()).thenReturn(emptyNtpTabUserDataHost);
         when(emptyNtpTab.getUrl()).thenReturn(new GURL(UrlConstants.NTP_URL));
-        when(emptyNtpTab.isTabStateDirty()).thenReturn(true);
+        TabStateAttributes.from(emptyNtpTab).setIsTabStateDirty(true);
         when(emptyNtpTab.canGoBack()).thenReturn(false);
         when(emptyNtpTab.canGoForward()).thenReturn(false);
 
@@ -144,8 +148,10 @@
         assertFalse(mPersistentStore.isTabPendingSave(emptyNtpTab));
 
         TabImpl ntpWithBackNavTab = mock(TabImpl.class);
+        UserDataHost ntpWithBackNavTabUserDataHost = new UserDataHost();
+        when(ntpWithBackNavTab.getUserDataHost()).thenReturn(ntpWithBackNavTabUserDataHost);
         when(ntpWithBackNavTab.getUrl()).thenReturn(new GURL(UrlConstants.NTP_URL));
-        when(ntpWithBackNavTab.isTabStateDirty()).thenReturn(true);
+        TabStateAttributes.from(ntpWithBackNavTab).setIsTabStateDirty(true);
         when(ntpWithBackNavTab.canGoBack()).thenReturn(true);
         when(ntpWithBackNavTab.canGoForward()).thenReturn(false);
 
@@ -153,8 +159,10 @@
         assertTrue(mPersistentStore.isTabPendingSave(ntpWithBackNavTab));
 
         TabImpl ntpWithForwardNavTab = mock(TabImpl.class);
+        UserDataHost ntpWithForwardNavTabUserDataHost = new UserDataHost();
+        when(ntpWithForwardNavTab.getUserDataHost()).thenReturn(ntpWithForwardNavTabUserDataHost);
         when(ntpWithForwardNavTab.getUrl()).thenReturn(new GURL(UrlConstants.NTP_URL));
-        when(ntpWithForwardNavTab.isTabStateDirty()).thenReturn(true);
+        TabStateAttributes.from(ntpWithForwardNavTab).setIsTabStateDirty(true);
         when(ntpWithForwardNavTab.canGoBack()).thenReturn(false);
         when(ntpWithForwardNavTab.canGoForward()).thenReturn(true);
 
@@ -162,8 +170,10 @@
         assertTrue(mPersistentStore.isTabPendingSave(ntpWithForwardNavTab));
 
         TabImpl ntpWithAllTheNavsTab = mock(TabImpl.class);
+        UserDataHost ntpWithAllTheNavsTabUserDataHost = new UserDataHost();
+        when(ntpWithAllTheNavsTab.getUserDataHost()).thenReturn(ntpWithAllTheNavsTabUserDataHost);
         when(ntpWithAllTheNavsTab.getUrl()).thenReturn(new GURL(UrlConstants.NTP_URL));
-        when(ntpWithAllTheNavsTab.isTabStateDirty()).thenReturn(true);
+        TabStateAttributes.from(ntpWithAllTheNavsTab).setIsTabStateDirty(true);
         when(ntpWithAllTheNavsTab.canGoBack()).thenReturn(true);
         when(ntpWithAllTheNavsTab.canGoForward()).thenReturn(true);
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ChromeActivityUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ChromeActivityUnitTest.java
index 47da3430..e80635e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ChromeActivityUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ChromeActivityUnitTest.java
@@ -16,6 +16,7 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.ui.BottomContainer;
 import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
@@ -31,6 +32,7 @@
     @Before
     public void setup() {
         mActivity = Robolectric.buildActivity(Activity.class).setup().get();
+        mActivity.setTheme(R.style.Theme_BrowserUI_DayNight);
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinatorTest.java
index 1d83086..0774d22 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinatorTest.java
@@ -69,6 +69,8 @@
     @Implements(ContextMenuDialog.class)
     public static class ShadowContextMenuDialog {
         boolean mShouldRemoveScrim;
+        @Nullable
+        View mTouchEventDelegateView;
 
         public ShadowContextMenuDialog() {}
 
@@ -76,8 +78,10 @@
         public void __constructor__(Activity ownerActivity, int theme, float touchPointXPx,
                 float touchPointYPx, float topContentOffsetPx, int topMarginPx, int bottomMarginPx,
                 View layout, View contentView, boolean isPopup, boolean shouldRemoveScrim,
-                @Nullable Integer popupMargin, @Nullable Integer desiredPopupContentWidth) {
+                @Nullable Integer popupMargin, @Nullable Integer desiredPopupContentWidth,
+                @Nullable View touchEventDelegateView) {
             mShouldRemoveScrim = shouldRemoveScrim;
+            mTouchEventDelegateView = touchEventDelegateView;
         }
     }
 
@@ -193,11 +197,7 @@
 
     @Test
     public void testCreateContextMenuDialog() {
-        View contentView = Mockito.mock(View.class);
-        View rootView = Mockito.mock(View.class);
-
-        ContextMenuDialog dialog = ContextMenuCoordinator.createContextMenuDialog(
-                mActivity, rootView, contentView, /*isPopup=*/false, 0, 0, 0, 0, 0, 0, 0);
+        ContextMenuDialog dialog = createContextMenuDialogForTest(/*isPopup=*/false);
         ShadowContextMenuDialog shadowDialog = (ShadowContextMenuDialog) Shadow.extract(dialog);
 
         Assert.assertFalse("Dialog should have scrim behind.", shadowDialog.mShouldRemoveScrim);
@@ -206,14 +206,12 @@
     @Test
     @Features.EnableFeatures(ChromeFeatureList.CONTEXT_MENU_POPUP_STYLE)
     public void testCreateContextMenuDialog_PopupStyle() {
-        View contentView = Mockito.mock(View.class);
-        View rootView = Mockito.mock(View.class);
-
-        ContextMenuDialog dialog = ContextMenuCoordinator.createContextMenuDialog(
-                mActivity, rootView, contentView, /*isPopup=*/true, 0, 0, 0, 0, 0, 0, 0);
+        ContextMenuDialog dialog = createContextMenuDialogForTest(/*isPopup=*/true);
         ShadowContextMenuDialog shadowDialog = (ShadowContextMenuDialog) Shadow.extract(dialog);
 
         Assert.assertTrue("Dialog should remove scrim behind.", shadowDialog.mShouldRemoveScrim);
+        Assert.assertNotNull("TouchEventDelegateView should not be null when drag drop is enabled.",
+                shadowDialog.mTouchEventDelegateView);
     }
 
     private ListItem createListItem(@Item int item) {
@@ -233,4 +231,13 @@
                         .build();
         return new ListItem(ListItemType.CONTEXT_MENU_ITEM_WITH_ICON_BUTTON, model);
     }
+
+    private ContextMenuDialog createContextMenuDialogForTest(boolean isPopup) {
+        View contentView = Mockito.mock(View.class);
+        View rootView = Mockito.mock(View.class);
+        View webContentView = Mockito.mock(View.class);
+
+        return ContextMenuCoordinator.createContextMenuDialog(
+                mActivity, rootView, contentView, isPopup, 0, 0, 0, 0, 0, 0, 0, webContentView);
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java
index 299d3da..4dc1c7c 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31UnitTest.java
@@ -27,6 +27,7 @@
 import org.mockito.stubbing.Answer;
 
 import org.chromium.base.ActivityState;
+import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ApplicationStatus.ActivityStateListener;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
@@ -238,6 +239,7 @@
                 new TestMultiInstanceManagerApi31(mCurrentActivity, mTabModelOrchestratorSupplier,
                         mMultiWindowModeStateDispatcher, mActivityLifecycleDispatcher,
                         mModalDialogManagerSupplier, mMenuOrKeyboardActionController);
+        ApplicationStatus.onStateChangeForTesting(mCurrentActivity, ActivityState.CREATED);
         SharedPreferencesManager.getInstance().removeKeysWithPrefix(
                 ChromePreferenceKeys.MULTI_INSTANCE_TASK_MAP);
     }
@@ -247,6 +249,7 @@
         SharedPreferencesManager.getInstance().removeKeysWithPrefix(
                 ChromePreferenceKeys.MULTI_INSTANCE_TASK_MAP);
         TabWindowManagerSingleton.resetTabModelSelectorFactoryForTesting();
+        ApplicationStatus.destroyForJUnitTests();
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java
index 7e70563..ccf2a54 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabUnitTest.java
@@ -119,21 +119,21 @@
         verify(mCriticalPersistedTabDataObserver).onRootIdChanged(mTab, TAB2_ID);
 
         assertThat(CriticalPersistedTabData.from(mTab).getRootId(), equalTo(TAB2_ID));
-        assertThat(mTab.isTabStateDirty(), equalTo(true));
+        assertThat(TabStateAttributes.from(mTab).isTabStateDirty(), equalTo(true));
     }
 
     @Test
     @SmallTest
     public void testSetRootIdWithoutChange() {
         assertThat(CriticalPersistedTabData.from(mTab).getRootId(), equalTo(TAB1_ID));
-        mTab.setIsTabStateDirty(false);
+        TabStateAttributes.from(mTab).setIsTabStateDirty(false);
 
         CriticalPersistedTabData.from(mTab).setRootId(TAB1_ID);
 
         verify(mCriticalPersistedTabDataObserver, never())
                 .onRootIdChanged(any(Tab.class), anyInt());
         assertThat(CriticalPersistedTabData.from(mTab).getRootId(), equalTo(TAB1_ID));
-        assertThat(mTab.isTabStateDirty(), equalTo(false));
+        assertThat(TabStateAttributes.from(mTab).isTabStateDirty(), equalTo(false));
     }
 
     @Test
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index fd1477e..26bdd51 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -558,15 +558,7 @@
   {
     const crosapi::mojom::BrowserInitParams* init_params =
         lacros_service_->init_params();
-    // default_paths may null on browser_tests.
-    if (init_params->default_paths) {
-      base::FilePath drivefs;
-      if (init_params->default_paths->drivefs.has_value())
-        drivefs = init_params->default_paths->drivefs.value();
-      chrome::SetLacrosDefaultPaths(init_params->default_paths->documents,
-                                    init_params->default_paths->downloads,
-                                    drivefs);
-    }
+    chrome::SetLacrosDefaultPathsFromInitParams(init_params);
     // This lives here rather than in ChromeBrowserMainExtraPartsLacros due to
     // timing constraints. If we relocate it, then the flags aren't propagated
     // to the GPU process.
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index c1a0c200..3e6a5bc 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -1476,12 +1476,21 @@
   <message name="IDS_LOGIN_SAML_NOTICE" desc="Text message displayed above SAML portal to early indicate that the user is being redirected to another sign-in provider. This is the version of the string used in the GAIA flow.">
     This sign-in service is hosted by <ph name="SAML_DOMAIN">$1<ex>saml.com</ex></ph>
   </message>
+  <message name="IDS_LOGIN_SAML_NOTICE_SHORT" desc="Text message displayed above SAML portal to early indicate that the user is being redirected to another sign-in provider. This is the version of the string used in the GAIA flow.">
+    Sign-in hosted by <ph name="SAML_DOMAIN">$1<ex>saml.com</ex></ph>
+  </message>
   <message name="IDS_LOGIN_SAML_PASSWORD_CHANGE_NOTICE" desc="Text message displayed in the system dialog title. The dialog shows a web-page where the user can change their SAML password while staying in the session">
     This authentication service is hosted by <ph name="SAML_DOMAIN">$1<ex>saml.com</ex></ph>
   </message>
   <message name="IDS_LOGIN_SAML_NOTICE_WITH_VIDEO" desc="Text message displayed above SAML portal to early indicate that the user is being redirected to another sign-in provider which has requested video media access.">
     This sign-in service, hosted by <ph name="SAML_DOMAIN">$1<ex>saml.com</ex></ph>, is accessing your camera.
   </message>
+  <message name="IDS_LOGIN_SAML_CHANGE_PROVIDER_MESSAGE" desc="Text message displayed below SAML portal to let user change sign-in provider.">
+    Wrong sign-in option?
+  </message>
+  <message name="IDS_LOGIN_SAML_CHANGE_PROVIDER_BUTTON" desc="Button to change sign-in provider.">
+    Enter Google Account info
+  </message>
   <message name="IDS_LOGIN_CONFIRM_PASSWORD_TITLE" desc="Title for the confirm password dialog.">
     Please re-enter your password to update your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> profile.
   </message>
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_SAML_CHANGE_PROVIDER_BUTTON.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_SAML_CHANGE_PROVIDER_BUTTON.png.sha1
new file mode 100644
index 0000000..0627045
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_SAML_CHANGE_PROVIDER_BUTTON.png.sha1
@@ -0,0 +1 @@
+2b17fda176eca45bb2012a4bcca9f17dfaee1252
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_SAML_CHANGE_PROVIDER_MESSAGE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_SAML_CHANGE_PROVIDER_MESSAGE.png.sha1
new file mode 100644
index 0000000..0627045
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_SAML_CHANGE_PROVIDER_MESSAGE.png.sha1
@@ -0,0 +1 @@
+2b17fda176eca45bb2012a4bcca9f17dfaee1252
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_LOGIN_SAML_NOTICE_SHORT.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_SAML_NOTICE_SHORT.png.sha1
new file mode 100644
index 0000000..0627045
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_LOGIN_SAML_NOTICE_SHORT.png.sha1
@@ -0,0 +1 @@
+2b17fda176eca45bb2012a4bcca9f17dfaee1252
\ No newline at end of file
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 16341121..7a0eb60e 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -39,7 +39,9 @@
       <output filename="chromium_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="chromium_strings_af.pak" type="data_package" lang="af" />
       <output filename="chromium_strings_is.pak" type="data_package" lang="is" />
+      <output filename="chromium_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="chromium_strings_am.pak" type="data_package" lang="am" />
     <output filename="chromium_strings_ar.pak" type="data_package" lang="ar" />
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 8313891..e21d63b 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -44,7 +44,9 @@
       <output filename="generated_resources_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="generated_resources_af.pak" type="data_package" lang="af" />
       <output filename="generated_resources_is.pak" type="data_package" lang="is" />
+      <output filename="generated_resources_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="generated_resources_am.pak" type="data_package" lang="am" />
     <output filename="generated_resources_ar.pak" type="data_package" lang="ar" />
@@ -11888,6 +11890,9 @@
     <message name="IDS_DEEP_SCANNING_DIALOG_DOWNLOADS_DISCARD_FILE_BUTTON" desc="Text of the button used for discarding a dangerous file from the Content Analysis Dialog.">
       Discard file
     </message>
+    <message name="IDS_DEEP_SCANNING_DIALOG_UPLOAD_BYPASS_JUSTIFICATION_LABEL" desc="Text displayed in the label above the bypass justification text area, explaining to the user that they have to enter a justification for uploading the file/pasted data even though there was a data loss prevention warning.">
+      Please enter a reason for uploading this data:
+    </message>
 
 
     <!-- Deep scanning open now dialog strings -->
diff --git a/chrome/app/generated_resources_grd/IDS_DEEP_SCANNING_DIALOG_UPLOAD_BYPASS_JUSTIFICATION_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_DEEP_SCANNING_DIALOG_UPLOAD_BYPASS_JUSTIFICATION_LABEL.png.sha1
new file mode 100644
index 0000000..1bd8a92d
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_DEEP_SCANNING_DIALOG_UPLOAD_BYPASS_JUSTIFICATION_LABEL.png.sha1
@@ -0,0 +1 @@
+3f9664d3e54b5f8e05674ba86b23b40e977898f9
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index d869918..b0dd14b 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -41,7 +41,9 @@
       <output filename="google_chrome_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="google_chrome_strings_af.pak" type="data_package" lang="af" />
       <output filename="google_chrome_strings_is.pak" type="data_package" lang="is" />
+      <output filename="google_chrome_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="google_chrome_strings_am.pak" type="data_package" lang="am" />
     <output filename="google_chrome_strings_ar.pak" type="data_package" lang="ar" />
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index b07dd1c..cd89bea 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -2229,6 +2229,12 @@
   <message name="IDS_BLUETOOTH_ENABLE_FAST_PAIR_SUBTITLE" desc="Subtitle that describes the toggle that enables Fast Pair on the Bluetooth settings page.">
     Easily connect and set up devices close by
   </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_ENABLED_A11Y_LABEL" desc="Bluetooth device details page: Accessibility announcement when Bluetooth state is enabled.">
+    Bluetooth enabled
+  </message>
+  <message name="IDS_SETTINGS_BLUETOOTH_DISABLED_A11Y_LABEL" desc="Bluetooth device details page: Accessibility announcement when Bluetooth state is disabled.">
+    Bluetooth disabled
+  </message>
 
 
   <!-- Parental Controls -->
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_DISABLED_A11Y_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_DISABLED_A11Y_LABEL.png.sha1
new file mode 100644
index 0000000..52315684
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_DISABLED_A11Y_LABEL.png.sha1
@@ -0,0 +1 @@
+fa7e315d9afa1742a2d469223c24a9651dcc8edd
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_ENABLED_A11Y_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_ENABLED_A11Y_LABEL.png.sha1
new file mode 100644
index 0000000..09c25cf
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_BLUETOOTH_ENABLED_A11Y_LABEL.png.sha1
@@ -0,0 +1 @@
+8c8701c9b1b57d3209693813935bdfc40360b7f2
\ No newline at end of file
diff --git a/chrome/app/resources/locale_settings.grd b/chrome/app/resources/locale_settings.grd
index 0f44e076..5c9fce9 100644
--- a/chrome/app/resources/locale_settings.grd
+++ b/chrome/app/resources/locale_settings.grd
@@ -35,7 +35,9 @@
       <output filename="locale_settings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="locale_settings_af.pak" type="data_package" lang="af" />
       <output filename="locale_settings_is.pak" type="data_package" lang="is" />
+      <output filename="locale_settings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="locale_settings_am.pak" type="data_package" lang="am" />
     <output filename="locale_settings_ar.pak" type="data_package" lang="ar" />
diff --git a/chrome/app/resources/locale_settings_chromiumos.grd b/chrome/app/resources/locale_settings_chromiumos.grd
index e1b3cca..23588fa 100644
--- a/chrome/app/resources/locale_settings_chromiumos.grd
+++ b/chrome/app/resources/locale_settings_chromiumos.grd
@@ -4,6 +4,7 @@
     <output filename="grit/platform_locale_settings.h" type="rc_header">
       <emit emit_type='prepend'></emit>
     </output>
+    <output filename="platform_locale_settings_af.pak" type="data_package" lang="af" />
     <output filename="platform_locale_settings_am.pak" type="data_package" lang="am" />
     <output filename="platform_locale_settings_ar.pak" type="data_package" lang="ar" />
     <output filename="platform_locale_settings_bg.pak" type="data_package" lang="bg" />
@@ -60,6 +61,7 @@
     <output filename="platform_locale_settings_vi.pak" type="data_package" lang="vi" />
     <output filename="platform_locale_settings_zh-CN.pak" type="data_package" lang="zh-CN" />
     <output filename="platform_locale_settings_zh-TW.pak" type="data_package" lang="zh-TW" />
+    <output filename="platform_locale_settings_zu.pak" type="data_package" lang="zu" />
 
     <!-- Pseudolocales -->
     <output filename="platform_locale_settings_ar-XB.pak" type="data_package" lang="ar-XB" />
diff --git a/chrome/app/resources/locale_settings_google_chromeos.grd b/chrome/app/resources/locale_settings_google_chromeos.grd
index b16cac46..573c2aa 100644
--- a/chrome/app/resources/locale_settings_google_chromeos.grd
+++ b/chrome/app/resources/locale_settings_google_chromeos.grd
@@ -4,6 +4,7 @@
     <output filename="grit/platform_locale_settings.h" type="rc_header">
       <emit emit_type='prepend'></emit>
     </output>
+    <output filename="platform_locale_settings_af.pak" type="data_package" lang="af" />
     <output filename="platform_locale_settings_am.pak" type="data_package" lang="am" />
     <output filename="platform_locale_settings_ar.pak" type="data_package" lang="ar" />
     <output filename="platform_locale_settings_bg.pak" type="data_package" lang="bg" />
@@ -60,6 +61,7 @@
     <output filename="platform_locale_settings_vi.pak" type="data_package" lang="vi" />
     <output filename="platform_locale_settings_zh-CN.pak" type="data_package" lang="zh-CN" />
     <output filename="platform_locale_settings_zh-TW.pak" type="data_package" lang="zh-TW" />
+    <output filename="platform_locale_settings_zu.pak" type="data_package" lang="zu" />
 
     <!-- Pseudolocales -->
     <output filename="platform_locale_settings_ar-XB.pak" type="data_package" lang="ar-XB" />
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index cefe418..90cb261 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -730,10 +730,10 @@
     On-device encryption
   </message>
   <message name="IDS_SETTINGS_TRUSTED_VAULT_BANNER_SUB_LABEL_OFFER_OPT_IN" desc="Sub-label for the on-device encryption banner in settings, when the user is being offered to opt in.">
-    Encrypt passwords on your device before they‘re saved to Google Password Manager
+    For added safety, encrypt passwords on your device before they‘re saved to Google Password Manager
   </message>
   <message name="IDS_SETTINGS_TRUSTED_VAULT_BANNER_SUB_LABEL_OPTED_IN" desc="Sub-label for the on-device encryption banner in settings, when the user is already opted in.">
-    Your password are encrypted on your device before they‘re saved to Google Password Manager
+    Your passwords are encrypted on your device before they‘re saved to Google Password Manager
   </message>
   <message name="IDS_SETTINGS_PASSWORD_SHOW_PASSWORD_A11Y" desc="The ARIA (accessibility) message for the 'Show Password' button, which sits in every row of the password list. For this row does not include a password, only information of the account. Reveals password when clicked.">
     Show password for <ph name="USERNAME">$1<ex>example@gmail.com</ex></ph> on <ph name="DOMAIN">$2<ex>www.google.com</ex></ph>
@@ -2816,14 +2816,20 @@
   <message name="IDS_SETTINGS_SITE_SETTINGS_AUTOMATIC_DOWNLOAD_BLOCK" desc="The block label for automatic download in site settings.">
     Do not allow any site to download multiple files automatically
   </message>
-  <message name="IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK" desc="The ask label for the window placement site settings.">
-    Ask when a site wants to open and place windows on your screens
+  <message name="IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_DESCRIPTION" desc="Description of the window placement site setting.">
+    Sites usually open and place windows to show additional documents or fullscreen content on your screens
   </message>
-  <message name="IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK_RECOMMENDED" desc="The ask label for the window placement site settings (with the 'recommended' suffix).">
-    Ask when a site wants to open and place windows on your screens (recommended)
+  <message name="IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK" desc="Label for the enabled option of the window placement site setting.">
+    Sites can ask to use info about your screens to open and place windows
   </message>
-  <message name="IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCK" desc="The block label for the window placement site settings.">
-    Block sites from opening and placing windows on your screens
+  <message name="IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCKED" desc="Label for the disabled option of the window placement site setting.">
+    Don't allow sites use info about your screens to open and place windows
+  </message>
+  <message name="IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK_EXCEPTIONS" desc="Label for the enabled exceptions site list of the window placement site setting.">
+    Allowed to use info about your screens to open and place windows
+  </message>
+  <message name="IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCKED_EXCEPTIONS" desc="Label for the blocked exceptions site list of the window placement site setting.">
+    Not allowed to use info about your screens to open and place windows
   </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_FONT_ACCESS_ASK" desc="The ask label for the local font access site settings.">
     Ask when a site wants to use fonts installed on your device
@@ -3841,4 +3847,25 @@
   <message name="IDS_SETTINGS_SECURITY_KEYS_FORCE_PIN_CHANGE" desc="An error message shown when a user attempts to use a security key (an authentication hardware device) that has a default PIN (short, often numeric codes that are often used with, for example, ATM cards) set. Before using the key, the user needs to change the PIN to a new code.">
     To use your new security key, set a new PIN
   </message>
+  <message name="IDS_SETTINGS_SECURITY_KEYS_PHONE_EDIT_DIALOG_TITLE" desc="The title of a dialog that is shown when a user chooses to edit details of a phone that can be used to sign-in with.">
+    Edit phone
+  </message>
+  <message name="IDS_SETTINGS_SECURITY_KEYS_PHONES_YOUR_DEVICES" desc="The title of a section on a settings page that lists phoens that are signed into (and syncing with) the user's Google account.">
+    Your devices
+  </message>
+  <message name="IDS_SETTINGS_SECURITY_KEYS_PHONES_SYNCED_DESC" desc="The description of a section on a settings page that lists phoens that are signed into (and syncing with) the user's Google account.">
+    These devices can be used as security keys because you're signed into Chrome on them.
+  </message>
+  <message name="IDS_SETTINGS_SECURITY_KEYS_PHONES_LINKED_DEVICES" desc="The title of a section on a settings page that lists phoens that can be used for signing into websites. The word 'linked' was picked to be distinct from 'paired', as in 'Bluetooth pairing'. The technology does use Bluetooth, but is distinct from Bluetooth pairing.">
+    Linked devices
+  </message>
+  <message name="IDS_SETTINGS_SECURITY_KEYS_PHONES_LINKED_DESC" desc="The description of a section on a settings page that lists phoens that can be used for signing into websites. The word 'linked' was picked to be distinct from 'paired', as in 'Bluetooth pairing'. The technology does use Bluetooth, but is distinct from Bluetooth pairing.">
+    These devices were linked by scanning a QR code.
+  </message>
+  <message name="IDS_SETTINGS_SECURITY_KEYS_PHONES_MANAGE" desc="The title of an option in Chrome's privacy settings for managing phones that can be used to sign into websites.">
+    Manage phones
+  </message>
+  <message name="IDS_SETTINGS_SECURITY_KEYS_PHONES_MANAGE_DESC" desc="The description of an option in Chrome's privacy settings for managing phones that can be used for signing into websites. A 'security key' is typically a USB peripherial used to secure sign-ins. In this case a phone can be used as a security key.">
+    Control which phones you use as security keys
+  </message>
 </grit-part>
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_LINKED_DESC.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_LINKED_DESC.png.sha1
new file mode 100644
index 0000000..9a290bf
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_LINKED_DESC.png.sha1
@@ -0,0 +1 @@
+4ba7d88d7a28d6b4b80c324ac91db3b8c0b50359
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_LINKED_DEVICES.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_LINKED_DEVICES.png.sha1
new file mode 100644
index 0000000..9a290bf
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_LINKED_DEVICES.png.sha1
@@ -0,0 +1 @@
+4ba7d88d7a28d6b4b80c324ac91db3b8c0b50359
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_MANAGE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_MANAGE.png.sha1
new file mode 100644
index 0000000..5be65d00
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_MANAGE.png.sha1
@@ -0,0 +1 @@
+0528714568ba075ed0ac24586a17944c1558343c
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_MANAGE_DESC.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_MANAGE_DESC.png.sha1
new file mode 100644
index 0000000..5be65d00
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_MANAGE_DESC.png.sha1
@@ -0,0 +1 @@
+0528714568ba075ed0ac24586a17944c1558343c
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_SYNCED_DESC.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_SYNCED_DESC.png.sha1
new file mode 100644
index 0000000..9a290bf
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_SYNCED_DESC.png.sha1
@@ -0,0 +1 @@
+4ba7d88d7a28d6b4b80c324ac91db3b8c0b50359
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_YOUR_DEVICES.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_YOUR_DEVICES.png.sha1
new file mode 100644
index 0000000..9a290bf
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONES_YOUR_DEVICES.png.sha1
@@ -0,0 +1 @@
+4ba7d88d7a28d6b4b80c324ac91db3b8c0b50359
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONE_EDIT_DIALOG_TITLE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONE_EDIT_DIALOG_TITLE.png.sha1
new file mode 100644
index 0000000..9ab2c7f7
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SECURITY_KEYS_PHONE_EDIT_DIALOG_TITLE.png.sha1
@@ -0,0 +1 @@
+c9c685033d4de64016f512ece926d5a14cae0a7d
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK.png.sha1
index 3fd065b..d9d5d91 100644
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK.png.sha1
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK.png.sha1
@@ -1 +1 @@
-3dfdc25dd5e7b2f57838a804cb4b17e8b5cbf7c4
\ No newline at end of file
+2be4d57cfe52ef9d9efb00bd246a11e153893915
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK_EXCEPTIONS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK_EXCEPTIONS.png.sha1
new file mode 100644
index 0000000..d9d5d91
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK_EXCEPTIONS.png.sha1
@@ -0,0 +1 @@
+2be4d57cfe52ef9d9efb00bd246a11e153893915
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK_RECOMMENDED.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK_RECOMMENDED.png.sha1
deleted file mode 100644
index e3b9401..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK_RECOMMENDED.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-dd5742e9af3b515c10d3653295a7320b7d0ae1c4
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCK.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCK.png.sha1
deleted file mode 100644
index eb712b6..0000000
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCK.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-3ba50d9ee7e1f1347fd0dad8f4aa18d276d1197c
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCKED.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCKED.png.sha1
new file mode 100644
index 0000000..d9d5d91
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCKED.png.sha1
@@ -0,0 +1 @@
+2be4d57cfe52ef9d9efb00bd246a11e153893915
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCKED_EXCEPTIONS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCKED_EXCEPTIONS.png.sha1
new file mode 100644
index 0000000..d9d5d91
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCKED_EXCEPTIONS.png.sha1
@@ -0,0 +1 @@
+2be4d57cfe52ef9d9efb00bd246a11e153893915
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_DESCRIPTION.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..d9d5d91
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+2be4d57cfe52ef9d9efb00bd246a11e153893915
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_TRUSTED_VAULT_BANNER_LABEL.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_TRUSTED_VAULT_BANNER_LABEL.png.sha1
index fdf2ccd..956be6c 100644
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_TRUSTED_VAULT_BANNER_LABEL.png.sha1
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_TRUSTED_VAULT_BANNER_LABEL.png.sha1
@@ -1 +1 @@
-d5480fb7ac6f87fea116024a821ad4a49c1f8fa0
\ No newline at end of file
+61634d207caa8482e163c40e29f9af85e565501f
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_TRUSTED_VAULT_BANNER_SUB_LABEL_OFFER_OPT_IN.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_TRUSTED_VAULT_BANNER_SUB_LABEL_OFFER_OPT_IN.png.sha1
index fdf2ccd..956be6c 100644
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_TRUSTED_VAULT_BANNER_SUB_LABEL_OFFER_OPT_IN.png.sha1
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_TRUSTED_VAULT_BANNER_SUB_LABEL_OFFER_OPT_IN.png.sha1
@@ -1 +1 @@
-d5480fb7ac6f87fea116024a821ad4a49c1f8fa0
\ No newline at end of file
+61634d207caa8482e163c40e29f9af85e565501f
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_TRUSTED_VAULT_BANNER_SUB_LABEL_OPTED_IN.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_TRUSTED_VAULT_BANNER_SUB_LABEL_OPTED_IN.png.sha1
index 2a11878..c83aa9b 100644
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_TRUSTED_VAULT_BANNER_SUB_LABEL_OPTED_IN.png.sha1
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_TRUSTED_VAULT_BANNER_SUB_LABEL_OPTED_IN.png.sha1
@@ -1 +1 @@
-27104462c1dfd56d0bff3576689e2ed601514e8e
\ No newline at end of file
+44d9df3cb403594474605f7c27794f1924f2e2ae
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index fa185249..599783f 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1733,18 +1733,6 @@
     "subresource_filter/subresource_filter_history_observer.h",
     "subresource_filter/subresource_filter_profile_context_factory.cc",
     "subresource_filter/subresource_filter_profile_context_factory.h",
-    "subresource_redirect/https_image_compression_infobar_decider.cc",
-    "subresource_redirect/https_image_compression_infobar_decider.h",
-    "subresource_redirect/litepages_service_bypass_decider.cc",
-    "subresource_redirect/litepages_service_bypass_decider.h",
-    "subresource_redirect/origin_robots_rules.cc",
-    "subresource_redirect/origin_robots_rules.h",
-    "subresource_redirect/origin_robots_rules_cache.cc",
-    "subresource_redirect/origin_robots_rules_cache.h",
-    "subresource_redirect/subresource_redirect_observer.cc",
-    "subresource_redirect/subresource_redirect_observer.h",
-    "subresource_redirect/subresource_redirect_util.cc",
-    "subresource_redirect/subresource_redirect_util.h",
     "sync/bookmark_sync_service_factory.cc",
     "sync/bookmark_sync_service_factory.h",
     "sync/chrome_sync_client.cc",
@@ -2269,7 +2257,6 @@
     "//components/subresource_filter/content/browser",
     "//components/subresource_filter/core/browser",
     "//components/subresource_filter/core/common",
-    "//components/subresource_redirect/common",
     "//components/sync",
     "//components/sync_bookmarks",
     "//components/sync_preferences",
@@ -3338,8 +3325,6 @@
       "ssl/chrome_security_state_model_delegate.h",
       "ssl/known_interception_disclosure_infobar.cc",
       "ssl/known_interception_disclosure_infobar.h",
-      "subresource_redirect/android/previews_android_bridge.cc",
-      "subresource_redirect/android/previews_android_bridge.h",
       "sync/android/sync_service_android_bridge.cc",
       "sync/android/sync_service_android_bridge.h",
       "sync/glue/synced_tab_delegate_android.cc",
@@ -4805,6 +4790,8 @@
       "nearby_sharing/sharing_mojo_service.h",
       "nearby_sharing/tachyon_ice_config_fetcher.cc",
       "nearby_sharing/tachyon_ice_config_fetcher.h",
+      "nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory.cc",
+      "nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory.h",
       "nearby_sharing/transfer_metadata.cc",
       "nearby_sharing/transfer_metadata.h",
       "nearby_sharing/transfer_metadata_builder.cc",
@@ -4914,6 +4901,7 @@
       "//ash/public/cpp",
       "//ash/public/cpp/external_arc",
       "//ash/services/nearby/public/cpp",
+      "//ash/services/nearby/public/cpp:tcp_server_socket_port",
       "//ash/services/nearby/public/mojom",
       "//ash/webui/projector_app",
       "//build:chromeos_buildflags",
@@ -7903,7 +7891,11 @@
       "//content/test:test_support",
     ]
 
-    public_deps = [ "//net:test_support" ]
+    public_deps = [
+      "//chrome/browser/web_applications/app_service:app_service",
+      "//net:test_support",
+      "//ui/webui/resources/cr_components/app_management:mojo_bindings",
+    ]
   }
 }
 
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 0d8812d..f7cbd57 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2365,20 +2365,6 @@
      nullptr}};
 #endif  // defined(OS_ANDROID)
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
-const FeatureEntry::FeatureParam
-    kSendWebUIJavaScriptErrorReportsVariationSendToStaging[] = {
-        {features::kSendWebUIJavaScriptErrorReportsSendToProductionVariation,
-         "false"}};
-
-const FeatureEntry::FeatureVariation
-    kSendWebUIJavaScriptErrorReportsVariations[] = {
-        {"Send reports to staging server.",
-         kSendWebUIJavaScriptErrorReportsVariationSendToStaging,
-         base::size(kSendWebUIJavaScriptErrorReportsVariationSendToStaging),
-         nullptr}};
-#endif
-
 #if defined(OS_ANDROID)
 // The variations of --metrics-settings-android.
 const FeatureEntry::FeatureParam kMetricsSettingsAndroidAlternativeOne[] = {
@@ -2513,6 +2499,7 @@
          base::size(kPlatformProvidedTrustTokenIssuance), nullptr}};
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+constexpr char kAmbientModeAnimationInternalName[] = "ambient-mode-animation";
 constexpr char kPersonalizationHubInternalName[] = "personalization-hub";
 constexpr char kWallpaperFullScreenPreviewInternalName[] =
     "wallpaper-fullscreen-preview";
@@ -2779,16 +2766,6 @@
      flag_descriptions::kWebKioskEnableLacrosDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(features::kWebKioskEnableLacros)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
-    {"send-webui-javascript-error-reports",
-     flag_descriptions::kSendWebUIJavaScriptErrorReportsName,
-     flag_descriptions::kSendWebUIJavaScriptErrorReportsDescription,
-     kOsLinux | kOsCrOS,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(
-         features::kSendWebUIJavaScriptErrorReports,
-         kSendWebUIJavaScriptErrorReportsVariations,
-         "SendWebUIJavaScriptErrorReportsVariations")},
-#endif
 #if !defined(OS_ANDROID)
     {"enable-webrtc-remote-event-log",
      flag_descriptions::kWebRtcRemoteEventLogName,
@@ -3177,6 +3154,10 @@
      flag_descriptions::kLacrosProfileMigrationForAnyUserName,
      flag_descriptions::kLacrosProfileMigrationForAnyUserDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kLacrosProfileMigrationForAnyUser)},
+    {"lacros-profile-migration-force-off",
+     flag_descriptions::kLacrosProfileMigrationForceOffName,
+     flag_descriptions::kLacrosProfileMigrationForceOffDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kLacrosProfileMigrationForceOff)},
     {kLacrosSelectionInternalName, flag_descriptions::kLacrosSelectionName,
      flag_descriptions::kLacrosSelectionDescription, kOsCrOS,
      MULTI_VALUE_TYPE(kLacrosSelectionChoices)},
@@ -6499,6 +6480,10 @@
      flag_descriptions::kNearbySharingBackgroundScanningName,
      flag_descriptions::kNearbySharingBackgroundScanningDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(features::kNearbySharingBackgroundScanning)},
+    {"nearby-sharing-one-page-onboarding",
+     flag_descriptions::kNearbySharingOnePageOnboardingName,
+     flag_descriptions::kNearbySharingOnePageOnboardingDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kNearbySharingOnePageOnboarding)},
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -6944,7 +6929,7 @@
 
     {"enable-first-party-sets", flag_descriptions::kEnableFirstPartySetsName,
      flag_descriptions::kEnableFirstPartySetsDescription, kOsAll,
-     FEATURE_VALUE_TYPE(net::features::kFirstPartySets)},
+     FEATURE_VALUE_TYPE(features::kFirstPartySets)},
 
 #if defined(OS_ANDROID)
     {"autofill-enable-offers-in-clank-keyboard-accessory",
@@ -7026,6 +7011,11 @@
      flag_descriptions::kVaapiAV1DecoderName,
      flag_descriptions::kVaapiAV1DecoderDescription, kOsCrOS | kOsLinux,
      FEATURE_VALUE_TYPE(media::kVaapiAV1Decoder)},
+
+    {"default-chrome-apps-migration",
+     flag_descriptions::kDefaultChromeAppsMigrationName,
+     flag_descriptions::kDefaultChromeAppsMigrationDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(policy::features::kDefaultChromeAppsMigration)},
 #endif  // defined(OS_CHROMEOS)
 
 #if defined(ARCH_CPU_X86_FAMILY) && BUILDFLAG(IS_CHROMEOS_ASH)
@@ -7794,6 +7784,13 @@
      FEATURE_VALUE_TYPE(
          autofill::features::kAutofillEnableUpdateVirtualCardEnrollment)},
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    {kAmbientModeAnimationInternalName,
+     flag_descriptions::kAmbientModeAnimationName,
+     flag_descriptions::kAmbientModeAnimationDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ash::features::kAmbientModeAnimationFeature)},
+#endif
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
@@ -7893,8 +7890,9 @@
   if (!strcmp(kWallpaperFullScreenPreviewInternalName, entry.internal_name))
     return !ash::features::IsWallpaperWebUIEnabled();
 
-  // personalization-hub is only available for Unknown/Canary/Dev channels.
-  if (!strcmp(kPersonalizationHubInternalName, entry.internal_name) &&
+  // Features that are only available for Unknown/Canary/Dev channels.
+  if ((!strcmp(kPersonalizationHubInternalName, entry.internal_name) ||
+       !strcmp(kAmbientModeAnimationInternalName, entry.internal_name)) &&
       channel != version_info::Channel::DEV &&
       channel != version_info::Channel::CANARY &&
       channel != version_info::Channel::UNKNOWN) {
diff --git a/chrome/browser/accessibility/live_caption_speech_recognition_host.cc b/chrome/browser/accessibility/live_caption_speech_recognition_host.cc
index 1efa9878..3f6925bf5 100644
--- a/chrome/browser/accessibility/live_caption_speech_recognition_host.cc
+++ b/chrome/browser/accessibility/live_caption_speech_recognition_host.cc
@@ -74,6 +74,12 @@
     live_caption_controller->OnError(context_.get());
 }
 
+void LiveCaptionSpeechRecognitionHost::OnSpeechRecognitionStopped() {
+  LiveCaptionController* live_caption_controller = GetLiveCaptionController();
+  if (live_caption_controller)
+    live_caption_controller->OnAudioStreamEnd(context_.get());
+}
+
 #if defined(OS_MAC) || defined(OS_CHROMEOS)
 void LiveCaptionSpeechRecognitionHost::MediaEffectivelyFullscreenChanged(
     bool is_fullscreen) {
diff --git a/chrome/browser/accessibility/live_caption_speech_recognition_host.h b/chrome/browser/accessibility/live_caption_speech_recognition_host.h
index 85d95806..8b12349 100644
--- a/chrome/browser/accessibility/live_caption_speech_recognition_host.h
+++ b/chrome/browser/accessibility/live_caption_speech_recognition_host.h
@@ -57,6 +57,7 @@
   void OnLanguageIdentificationEvent(
       media::mojom::LanguageIdentificationEventPtr event) override;
   void OnSpeechRecognitionError() override;
+  void OnSpeechRecognitionStopped() override;
 
  protected:
   // Mac and ChromeOS move the fullscreened window into a new workspace. When
diff --git a/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.cc b/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.cc
index 8b1ea1faf..0eb484b 100644
--- a/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.cc
+++ b/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.cc
@@ -121,62 +121,6 @@
   ui_controller_->OnLoginChoiceChanged(identifier);
 }
 
-void AssistantCollectUserDataDelegate::OnDateTimeRangeStartDateChanged(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller,
-    jint year,
-    jint month,
-    jint day) {
-  ui_controller_->OnDateTimeRangeStartDateChanged(year, month, day);
-}
-
-void AssistantCollectUserDataDelegate::OnDateTimeRangeStartDateCleared(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller) {
-  ui_controller_->OnDateTimeRangeStartDateCleared();
-}
-
-void AssistantCollectUserDataDelegate::OnDateTimeRangeStartTimeSlotChanged(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller,
-    jint index) {
-  ui_controller_->OnDateTimeRangeStartTimeSlotChanged(index);
-}
-
-void AssistantCollectUserDataDelegate::OnDateTimeRangeStartTimeSlotCleared(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller) {
-  ui_controller_->OnDateTimeRangeStartTimeSlotCleared();
-}
-
-void AssistantCollectUserDataDelegate::OnDateTimeRangeEndDateChanged(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller,
-    jint year,
-    jint month,
-    jint day) {
-  ui_controller_->OnDateTimeRangeEndDateChanged(year, month, day);
-}
-
-void AssistantCollectUserDataDelegate::OnDateTimeRangeEndDateCleared(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller) {
-  ui_controller_->OnDateTimeRangeEndDateCleared();
-}
-
-void AssistantCollectUserDataDelegate::OnDateTimeRangeEndTimeSlotChanged(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller,
-    jint index) {
-  ui_controller_->OnDateTimeRangeEndTimeSlotChanged(index);
-}
-
-void AssistantCollectUserDataDelegate::OnDateTimeRangeEndTimeSlotCleared(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller) {
-  ui_controller_->OnDateTimeRangeEndTimeSlotCleared();
-}
-
 void AssistantCollectUserDataDelegate::OnKeyValueChanged(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller,
diff --git a/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.h b/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.h
index 4f1e25f..c9bba0d 100644
--- a/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.h
+++ b/chrome/browser/android/autofill_assistant/assistant_collect_user_data_delegate.h
@@ -50,46 +50,6 @@
       const base::android::JavaParamRef<jstring>& jidentifier,
       jint event_type);
 
-  void OnDateTimeRangeStartDateChanged(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& jcaller,
-      jint year,
-      jint month,
-      jint day);
-
-  void OnDateTimeRangeStartDateCleared(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& jcaller);
-
-  void OnDateTimeRangeStartTimeSlotChanged(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& jcaller,
-      jint index);
-
-  void OnDateTimeRangeStartTimeSlotCleared(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& jcaller);
-
-  void OnDateTimeRangeEndDateChanged(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& jcaller,
-      jint year,
-      jint month,
-      jint day);
-
-  void OnDateTimeRangeEndDateCleared(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& jcaller);
-
-  void OnDateTimeRangeEndTimeSlotChanged(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& jcaller,
-      jint index);
-
-  void OnDateTimeRangeEndTimeSlotCleared(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& jcaller);
-
   void OnKeyValueChanged(JNIEnv* env,
                          const base::android::JavaParamRef<jobject>& jcaller,
                          const base::android::JavaParamRef<jstring>& jkey,
diff --git a/chrome/browser/android/autofill_assistant/client_android.cc b/chrome/browser/android/autofill_assistant/client_android.cc
index f89a8abc..64f660a 100644
--- a/chrome/browser/android/autofill_assistant/client_android.cc
+++ b/chrome/browser/android/autofill_assistant/client_android.cc
@@ -102,7 +102,7 @@
       java_object_(Java_AutofillAssistantClient_Constructor(
           AttachCurrentThread(),
           reinterpret_cast<intptr_t>(this),
-          dependencies->GetAccessTokenUtil())),
+          dependencies->CreateAccessTokenUtil())),
       dependencies_(std::move(dependencies)) {}
 
 ClientAndroid::~ClientAndroid() {
diff --git a/chrome/browser/android/autofill_assistant/dependencies.cc b/chrome/browser/android/autofill_assistant/dependencies.cc
index dfff396..7f10ab9b 100644
--- a/chrome/browser/android/autofill_assistant/dependencies.cc
+++ b/chrome/browser/android/autofill_assistant/dependencies.cc
@@ -31,17 +31,17 @@
   return java_object_;
 }
 
-ScopedJavaGlobalRef<jobject> Dependencies::GetInfoPageUtil(
+ScopedJavaGlobalRef<jobject> Dependencies::CreateInfoPageUtil(
     const ScopedJavaGlobalRef<jobject>& java_object) {
   return ScopedJavaGlobalRef<jobject>(
-      Java_AssistantStaticDependencies_getInfoPageUtil(AttachCurrentThread(),
-                                                       java_object));
+      Java_AssistantStaticDependencies_createInfoPageUtil(AttachCurrentThread(),
+                                                          java_object));
 }
 
-ScopedJavaGlobalRef<jobject> Dependencies::GetAccessTokenUtil() const {
+ScopedJavaGlobalRef<jobject> Dependencies::CreateAccessTokenUtil() const {
   return ScopedJavaGlobalRef<jobject>(
-      Java_AssistantStaticDependencies_getAccessTokenUtil(AttachCurrentThread(),
-                                                          java_object_));
+      Java_AssistantStaticDependencies_createAccessTokenUtil(
+          AttachCurrentThread(), java_object_));
 }
 
 Dependencies::~Dependencies() = default;
diff --git a/chrome/browser/android/autofill_assistant/dependencies.h b/chrome/browser/android/autofill_assistant/dependencies.h
index f8c7de2..e48a316 100644
--- a/chrome/browser/android/autofill_assistant/dependencies.h
+++ b/chrome/browser/android/autofill_assistant/dependencies.h
@@ -23,10 +23,10 @@
 
   base::android::ScopedJavaGlobalRef<jobject> GetJavaObject() const;
 
-  static base::android::ScopedJavaGlobalRef<jobject> GetInfoPageUtil(
+  static base::android::ScopedJavaGlobalRef<jobject> CreateInfoPageUtil(
       const base::android::ScopedJavaGlobalRef<jobject>& java_object);
 
-  base::android::ScopedJavaGlobalRef<jobject> GetAccessTokenUtil() const;
+  base::android::ScopedJavaGlobalRef<jobject> CreateAccessTokenUtil() const;
 
   virtual ~Dependencies();
 
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index fbaf3452..97ee196d 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -81,22 +81,6 @@
   return flattened;
 }
 
-base::android::ScopedJavaLocalRef<jobject> CreateJavaDateTime(
-    JNIEnv* env,
-    const DateTimeProto& proto) {
-  return Java_AssistantCollectUserDataModel_createAssistantDateTime(
-      env, (int)proto.date().year(), proto.date().month(), proto.date().day(),
-      proto.time().hour(), proto.time().minute(), proto.time().second());
-}
-
-base::android::ScopedJavaLocalRef<jobject> CreateJavaDate(
-    JNIEnv* env,
-    const DateProto& proto) {
-  DateTimeProto date_time;
-  *date_time.mutable_date() = proto;
-  return CreateJavaDateTime(env, date_time);
-}
-
 ScopedJavaLocalRef<jobject> CreateOptionalJavaInfoPopup(
     JNIEnv* env,
     const LoginChoice& login_choice,
@@ -1145,50 +1129,6 @@
   ui_delegate_->OnFormActionLinkClicked(link);
 }
 
-void UiControllerAndroid::OnDateTimeRangeStartDateChanged(int year,
-                                                          int month,
-                                                          int day) {
-  auto date = absl::make_optional<DateProto>();
-  date->set_year(year);
-  date->set_month(month);
-  date->set_day(day);
-  ui_delegate_->SetDateTimeRangeStartDate(date);
-}
-
-void UiControllerAndroid::OnDateTimeRangeStartDateCleared() {
-  ui_delegate_->SetDateTimeRangeStartDate(absl::nullopt);
-}
-
-void UiControllerAndroid::OnDateTimeRangeStartTimeSlotChanged(int index) {
-  ui_delegate_->SetDateTimeRangeStartTimeSlot(absl::make_optional<int>(index));
-}
-
-void UiControllerAndroid::OnDateTimeRangeStartTimeSlotCleared() {
-  ui_delegate_->SetDateTimeRangeStartTimeSlot(absl::nullopt);
-}
-
-void UiControllerAndroid::OnDateTimeRangeEndDateChanged(int year,
-                                                        int month,
-                                                        int day) {
-  auto date = absl::make_optional<DateProto>();
-  date->set_year(year);
-  date->set_month(month);
-  date->set_day(day);
-  ui_delegate_->SetDateTimeRangeEndDate(date);
-}
-
-void UiControllerAndroid::OnDateTimeRangeEndDateCleared() {
-  ui_delegate_->SetDateTimeRangeEndDate(absl::nullopt);
-}
-
-void UiControllerAndroid::OnDateTimeRangeEndTimeSlotChanged(int index) {
-  ui_delegate_->SetDateTimeRangeEndTimeSlot(absl::make_optional<int>(index));
-}
-
-void UiControllerAndroid::OnDateTimeRangeEndTimeSlotCleared() {
-  ui_delegate_->SetDateTimeRangeEndTimeSlot(absl::nullopt);
-}
-
 void UiControllerAndroid::OnKeyValueChanged(const std::string& key,
                                             const ValueProto& value) {
   ui_delegate_->SetAdditionalValue(key, value);
@@ -1215,7 +1155,7 @@
 }
 
 ScopedJavaGlobalRef<jobject> UiControllerAndroid::GetInfoPageUtil() const {
-  return Dependencies::GetInfoPageUtil(jstatic_dependencies_);
+  return Dependencies::CreateInfoPageUtil(jstatic_dependencies_);
 }
 
 void UiControllerAndroid::OnCollectUserDataOptionsChanged(
@@ -1288,52 +1228,6 @@
         ui_delegate_->GetClientSettings(), GetInfoPageUtil());
     Java_AssistantCollectUserDataModel_setLoginChoices(env, jmodel, jlist);
   }
-  Java_AssistantCollectUserDataModel_setRequestDateRange(
-      env, jmodel, collect_user_data_options->request_date_time_range);
-  if (collect_user_data_options->request_date_time_range) {
-    auto jmin_date = CreateJavaDate(
-        env, collect_user_data_options->date_time_range.min_date());
-    auto jmax_date = CreateJavaDate(
-        env, collect_user_data_options->date_time_range.max_date());
-    std::vector<std::string> time_slots;
-    for (const auto& slot :
-         collect_user_data_options->date_time_range.time_slots()) {
-      time_slots.emplace_back(slot.label());
-    }
-    auto jtime_slots = base::android::ToJavaArrayOfStrings(env, time_slots);
-    Java_AssistantCollectUserDataModel_setDateTimeRangeStartOptions(
-        env, jmodel, jmin_date, jmax_date, jtime_slots);
-    Java_AssistantCollectUserDataModel_setDateTimeRangeEndOptions(
-        env, jmodel, jmin_date, jmax_date, jtime_slots);
-    Java_AssistantCollectUserDataModel_setDateTimeRangeStartDateLabel(
-        env, jmodel,
-        ConvertUTF8ToJavaString(
-            env,
-            collect_user_data_options->date_time_range.start_date_label()));
-    Java_AssistantCollectUserDataModel_setDateTimeRangeStartTimeLabel(
-        env, jmodel,
-        ConvertUTF8ToJavaString(
-            env,
-            collect_user_data_options->date_time_range.start_time_label()));
-    Java_AssistantCollectUserDataModel_setDateTimeRangeEndDateLabel(
-        env, jmodel,
-        ConvertUTF8ToJavaString(
-            env, collect_user_data_options->date_time_range.end_date_label()));
-    Java_AssistantCollectUserDataModel_setDateTimeRangeEndTimeLabel(
-        env, jmodel,
-        ConvertUTF8ToJavaString(
-            env, collect_user_data_options->date_time_range.end_time_label()));
-    Java_AssistantCollectUserDataModel_setDateTimeRangeDateNotSetErrorMessage(
-        env, jmodel,
-        ConvertUTF8ToJavaString(
-            env,
-            collect_user_data_options->date_time_range.date_not_set_error()));
-    Java_AssistantCollectUserDataModel_setDateTimeRangeTimeNotSetErrorMessage(
-        env, jmodel,
-        ConvertUTF8ToJavaString(
-            env,
-            collect_user_data_options->date_time_range.time_not_set_error()));
-  }
   Java_AssistantCollectUserDataModel_setTermsRequireReviewText(
       env, jmodel,
       ConvertUTF8ToJavaString(
@@ -1593,45 +1487,6 @@
   }
 
   if (field_change == UserData::FieldChange::ALL ||
-      field_change == UserData::FieldChange::DATE_TIME_RANGE_START) {
-    if (user_data.date_time_range_start_date_.has_value()) {
-      Java_AssistantCollectUserDataModel_setDateTimeRangeStartDate(
-          env, jmodel,
-          CreateJavaDate(env, *user_data.date_time_range_start_date_));
-    } else {
-      Java_AssistantCollectUserDataModel_clearDateTimeRangeStartDate(env,
-                                                                     jmodel);
-    }
-
-    if (user_data.date_time_range_start_timeslot_.has_value()) {
-      Java_AssistantCollectUserDataModel_setDateTimeRangeStartTimeSlot(
-          env, jmodel, *user_data.date_time_range_start_timeslot_);
-    } else {
-      Java_AssistantCollectUserDataModel_clearDateTimeRangeStartTimeSlot(
-          env, jmodel);
-    }
-  }
-
-  if (field_change == UserData::FieldChange::ALL ||
-      field_change == UserData::FieldChange::DATE_TIME_RANGE_END) {
-    if (user_data.date_time_range_end_date_.has_value()) {
-      Java_AssistantCollectUserDataModel_setDateTimeRangeEndDate(
-          env, jmodel,
-          CreateJavaDate(env, *user_data.date_time_range_end_date_));
-    } else {
-      Java_AssistantCollectUserDataModel_clearDateTimeRangeEndDate(env, jmodel);
-    }
-
-    if (user_data.date_time_range_end_timeslot_.has_value()) {
-      Java_AssistantCollectUserDataModel_setDateTimeRangeEndTimeSlot(
-          env, jmodel, *user_data.date_time_range_end_timeslot_);
-    } else {
-      Java_AssistantCollectUserDataModel_clearDateTimeRangeEndTimeSlot(env,
-                                                                       jmodel);
-    }
-  }
-
-  if (field_change == UserData::FieldChange::ALL ||
       field_change == UserData::FieldChange::LOGIN_CHOICE) {
     ScopedJavaLocalRef<jobject> jselected_login_choice =
         user_data.selected_login_choice() == nullptr
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index 562fadc0..4b4eeb0 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -162,14 +162,6 @@
   void OnLoginChoiceChanged(const std::string& identifier);
   void OnTextLinkClicked(int link);
   void OnFormActionLinkClicked(int link);
-  void OnDateTimeRangeStartDateChanged(int year, int month, int day);
-  void OnDateTimeRangeStartDateCleared();
-  void OnDateTimeRangeStartTimeSlotChanged(int index);
-  void OnDateTimeRangeStartTimeSlotCleared();
-  void OnDateTimeRangeEndDateChanged(int year, int month, int day);
-  void OnDateTimeRangeEndDateCleared();
-  void OnDateTimeRangeEndTimeSlotChanged(int index);
-  void OnDateTimeRangeEndTimeSlotCleared();
   void OnKeyValueChanged(const std::string& key, const ValueProto& value);
   void OnInputTextFocusChanged(bool is_text_focused);
 
diff --git a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
index b901a5d..f946336 100644
--- a/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
@@ -62,8 +62,7 @@
   layer()->AddChild(contextual_search_layer_->layer());
 }
 
-ContextualSearchSceneLayer::~ContextualSearchSceneLayer() {
-}
+ContextualSearchSceneLayer::~ContextualSearchSceneLayer() {}
 
 void ContextualSearchSceneLayer::UpdateContextualSearchLayer(
     JNIEnv* env,
@@ -219,16 +218,46 @@
 
   GURL gurl(thumbnail_url_);
   Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile);
+  // Semantic details for this "Thumbnail" request.
+  // The URLs processed access gstatic.com, which is considered a Google-owned
+  // service.
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("contextual_search_thumbnail",
+                                          R"(
+            semantics {
+              sender: "Contextual Search"
+              description:
+                "This request is for a thumbnail image to show in the "
+                "Contextual Search bottom sheet for an entity or similar "
+                "object identified by the selected text."
+              trigger:
+                "Triggered by a server response to the "
+                "contextual_search_resolve request which contains a thumbnail "
+                "URL."
+              data:
+                "The URL of the thumbnail."
+              destination: GOOGLE_OWNED_SERVICE
+            }
+            policy {
+              cookies_allowed: NO
+              setting:
+                "This feature can be disabled by turning off 'Touch to Search' "
+                "in Chrome for Android settings."
+              chrome_policy {
+                ContextualSearchEnabled {
+                    policy_options {mode: MANDATORY}
+                    ContextualSearchEnabled: false
+                }
+              }
+            })");
   network::mojom::URLLoaderFactory* loader_factory =
       profile->GetDefaultStoragePartition()
           ->GetURLLoaderFactoryForBrowserProcess()
           .get();
-  fetcher_ =
-      std::make_unique<BitmapFetcher>(gurl, this, MISSING_TRAFFIC_ANNOTATION);
+  fetcher_ = std::make_unique<BitmapFetcher>(gurl, this, traffic_annotation);
   fetcher_->Init(
-      std::string(),
       net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
-      network::mojom::CredentialsMode::kInclude);
+      network::mojom::CredentialsMode::kOmit);
   fetcher_->Start(loader_factory);
 }
 
@@ -247,16 +276,17 @@
     const JavaParamRef<jobject>& jobj,
     const JavaParamRef<jobject>& jcontent_tree) {
   SceneLayer* content_tree = FromJavaObject(env, jcontent_tree);
-  if (!content_tree || !content_tree->layer()) return;
+  if (!content_tree || !content_tree->layer())
+    return;
 
-  if (!content_tree->layer()->parent()
-      || (content_tree->layer()->parent()->id() != content_container_->id())) {
+  if (!content_tree->layer()->parent() ||
+      (content_tree->layer()->parent()->id() != content_container_->id())) {
     content_container_->AddChild(content_tree->layer());
   }
 }
 
 void ContextualSearchSceneLayer::HideTree(JNIEnv* env,
-    const JavaParamRef<jobject>& jobj) {
+                                          const JavaParamRef<jobject>& jobj) {
   // TODO(mdjones): Create super class for this logic.
   if (contextual_search_layer_) {
     contextual_search_layer_->layer()->SetHideLayerAndSubtree(true);
diff --git a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
index 8523c8a..0dd8bd39 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
+++ b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
@@ -177,7 +177,8 @@
                "data to Google and the response identifies what to search for "
                "plus additional actionable information."
             trigger:
-              "Triggered by an unhandled tap on plain text on most pages."
+              "Triggered by an unhandled tap or touch and hold gesture on "
+              "plain text on most pages."
             data:
               "The URL and some page content from the current tab."
             destination: GOOGLE_OWNED_SERVICE
diff --git a/chrome/browser/android/foreign_session_helper.cc b/chrome/browser/android/foreign_session_helper.cc
index 1333e57..c1512fd 100644
--- a/chrome/browser/android/foreign_session_helper.cc
+++ b/chrome/browser/android/foreign_session_helper.cc
@@ -212,8 +212,8 @@
   // Use a pref to keep track of sessions that were collapsed by the user.
   // To prevent the pref from accumulating stale sessions, clear it each time
   // and only add back sessions that are still current.
-  DictionaryPrefUpdate pref_update(profile_->GetPrefs(),
-                                   prefs::kNtpCollapsedForeignSessions);
+  DictionaryPrefUpdateDeprecated pref_update(
+      profile_->GetPrefs(), prefs::kNtpCollapsedForeignSessions);
   base::DictionaryValue* pref_collapsed_sessions = pref_update.Get();
   std::unique_ptr<base::DictionaryValue> collapsed_sessions(
       pref_collapsed_sessions->DeepCopy());
diff --git a/chrome/browser/android/ntp/recent_tabs_page_prefs.cc b/chrome/browser/android/ntp/recent_tabs_page_prefs.cc
index ed385425..35e8eac 100644
--- a/chrome/browser/android/ntp/recent_tabs_page_prefs.cc
+++ b/chrome/browser/android/ntp/recent_tabs_page_prefs.cc
@@ -83,7 +83,8 @@
   // Store session tags for collapsed sessions in a preference so that the
   // collapsed state persists.
   PrefService* prefs = profile_->GetPrefs();
-  DictionaryPrefUpdate update(prefs, prefs::kNtpCollapsedForeignSessions);
+  DictionaryPrefUpdateDeprecated update(prefs,
+                                        prefs::kNtpCollapsedForeignSessions);
   if (is_collapsed)
     update.Get()->SetBoolean(ConvertJavaStringToUTF8(env, session_tag), true);
   else
diff --git a/chrome/browser/android/oom_intervention/oom_intervention_decider.cc b/chrome/browser/android/oom_intervention/oom_intervention_decider.cc
index e84479d5..b8263e6 100644
--- a/chrome/browser/android/oom_intervention/oom_intervention_decider.cc
+++ b/chrome/browser/android/oom_intervention/oom_intervention_decider.cc
@@ -140,7 +140,7 @@
   auto* old_pref_value = prefs_->GetList(kBlacklist);
   if (!old_pref_value->GetList().empty()) {
     prefs_->Set(kBlocklist, *old_pref_value);
-    ListPrefUpdate update(prefs_, kBlacklist);
+    ListPrefUpdateDeprecated update(prefs_, kBlacklist);
     update->ClearList();
   }
 
@@ -176,7 +176,7 @@
                                        const std::string& host) {
   if (IsInList(list_name, host))
     return;
-  ListPrefUpdate update(prefs_, list_name);
+  ListPrefUpdateDeprecated update(prefs_, list_name);
   update->Append(host);
   if (update->GetList().size() > kMaxListSize)
     update->EraseListIter(update->GetList().begin());
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index fc8a075b..93c3ee8 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -1296,7 +1296,7 @@
         chrome::ExecuteCommand(browser, IDC_NEW_TAB);
         break;
       }
-      FALLTHROUGH;  // To create new window.
+      [[fallthrough]];  // To create new window.
     case IDC_NEW_WINDOW:
       CreateBrowser(profile);
       break;
@@ -2041,7 +2041,7 @@
       break;
     case Profile::CREATE_STATUS_CREATED:
       NOTREACHED() << "Should only be called when profile loading is complete";
-      FALLTHROUGH;
+      [[fallthrough]];
     case Profile::CREATE_STATUS_LOCAL_FAIL:
       return nullptr;
   }
diff --git a/chrome/browser/apps/app_service/browser_app_instance.cc b/chrome/browser/apps/app_service/browser_app_instance.cc
index c7b08d9..9557c7c 100644
--- a/chrome/browser/apps/app_service/browser_app_instance.cc
+++ b/chrome/browser/apps/app_service/browser_app_instance.cc
@@ -35,14 +35,18 @@
                                        aura::Window* window,
                                        std::string title,
                                        bool is_browser_active,
-                                       bool is_web_contents_active)
+                                       bool is_web_contents_active,
+                                       uint32_t browser_session_id,
+                                       uint32_t restored_browser_session_id)
     : id(id),
       type(type),
       app_id(app_id),
       window(window),
       title(title),
       is_browser_active(is_browser_active),
-      is_web_contents_active(is_web_contents_active) {}
+      is_web_contents_active(is_web_contents_active),
+      browser_session_id(browser_session_id),
+      restored_browser_session_id(restored_browser_session_id) {}
 
 BrowserAppInstance::BrowserAppInstance(BrowserAppInstanceUpdate update,
                                        aura::Window* window)
@@ -52,23 +56,31 @@
       window(window),
       title(update.title),
       is_browser_active(update.is_browser_active),
-      is_web_contents_active(update.is_web_contents_active) {}
+      is_web_contents_active(update.is_web_contents_active),
+      browser_session_id(update.browser_session_id),
+      restored_browser_session_id(update.restored_browser_session_id) {}
 
 BrowserAppInstance::~BrowserAppInstance() = default;
 
 bool BrowserAppInstance::MaybeUpdate(aura::Window* window,
                                      std::string title,
                                      bool is_browser_active,
-                                     bool is_web_contents_active) {
+                                     bool is_web_contents_active,
+                                     uint32_t browser_session_id,
+                                     uint32_t restored_browser_session_id) {
   if (this->window == window && this->title == title &&
       this->is_browser_active == is_browser_active &&
-      this->is_web_contents_active == is_web_contents_active) {
+      this->is_web_contents_active == is_web_contents_active &&
+      this->browser_session_id == browser_session_id &&
+      this->restored_browser_session_id == restored_browser_session_id) {
     return false;
   }
   this->window = window;
   this->title = std::move(title);
   this->is_browser_active = is_browser_active;
   this->is_web_contents_active = is_web_contents_active;
+  this->browser_session_id = browser_session_id;
+  this->restored_browser_session_id = restored_browser_session_id;
   return true;
 }
 
@@ -81,17 +93,30 @@
   update.title = title;
   update.is_browser_active = is_browser_active;
   update.is_web_contents_active = is_web_contents_active;
+  update.browser_session_id = browser_session_id;
+  update.restored_browser_session_id = restored_browser_session_id;
   return update;
 }
 
-BrowserWindowInstance::BrowserWindowInstance(base::UnguessableToken id,
-                                             aura::Window* window,
-                                             bool is_active)
-    : id(id), window(window), is_active(is_active) {}
+BrowserWindowInstance::BrowserWindowInstance(
+    base::UnguessableToken id,
+    aura::Window* window,
+    uint32_t browser_session_id,
+    uint32_t restored_browser_session_id,
+    bool is_active)
+    : id(id),
+      window(window),
+      browser_session_id(browser_session_id),
+      restored_browser_session_id(restored_browser_session_id),
+      is_active(is_active) {}
 
 BrowserWindowInstance::BrowserWindowInstance(BrowserWindowInstanceUpdate update,
                                              aura::Window* window)
-    : id(update.id), window(window), is_active(update.is_active) {}
+    : id(update.id),
+      window(window),
+      browser_session_id(update.browser_session_id),
+      restored_browser_session_id(update.restored_browser_session_id),
+      is_active(update.is_active) {}
 
 BrowserWindowInstance::~BrowserWindowInstance() = default;
 
@@ -104,7 +129,9 @@
 }
 
 BrowserWindowInstanceUpdate BrowserWindowInstance::ToUpdate() const {
-  return BrowserWindowInstanceUpdate{id, GetWindowUniqueId(window), is_active};
+  return BrowserWindowInstanceUpdate{id, GetWindowUniqueId(window), is_active,
+                                     browser_session_id,
+                                     restored_browser_session_id};
 }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/apps/app_service/browser_app_instance.h b/chrome/browser/apps/app_service/browser_app_instance.h
index 0ed70b25..e0c59d56 100644
--- a/chrome/browser/apps/app_service/browser_app_instance.h
+++ b/chrome/browser/apps/app_service/browser_app_instance.h
@@ -35,7 +35,9 @@
                      aura::Window* window,
                      std::string title,
                      bool is_browser_active,
-                     bool is_web_contents_active);
+                     bool is_web_contents_active,
+                     uint32_t browser_session_id,
+                     uint32_t restored_browser_session_id);
   BrowserAppInstance(BrowserAppInstanceUpdate update, aura::Window* window);
   ~BrowserAppInstance();
   BrowserAppInstance(const BrowserAppInstance&) = delete;
@@ -45,7 +47,9 @@
   bool MaybeUpdate(aura::Window* window,
                    std::string title,
                    bool is_browser_active,
-                   bool is_web_contents_active);
+                   bool is_web_contents_active,
+                   uint32_t browser_session_id,
+                   uint32_t restored_browser_session_id);
 
   BrowserAppInstanceUpdate ToUpdate() const;
 
@@ -61,12 +65,16 @@
   std::string title;
   bool is_browser_active;
   bool is_web_contents_active;
+  uint32_t browser_session_id;
+  uint32_t restored_browser_session_id;
 };
 
 // An instance representing a single Chrome browser window.
 struct BrowserWindowInstance {
   BrowserWindowInstance(base::UnguessableToken id,
                         aura::Window* window,
+                        uint32_t browser_session_id,
+                        uint32_t restored_browser_session_id,
                         bool is_active);
   BrowserWindowInstance(BrowserWindowInstanceUpdate update,
                         aura::Window* window);
@@ -85,6 +93,8 @@
   // Immutable attributes.
   const base::UnguessableToken id;
   aura::Window* const window;
+  uint32_t browser_session_id;
+  uint32_t restored_browser_session_id;
 
   // Mutable attributes.
   bool is_active;
diff --git a/chrome/browser/apps/app_service/browser_app_instance_registry.cc b/chrome/browser/apps/app_service/browser_app_instance_registry.cc
index 9c768109..b850825e 100644
--- a/chrome/browser/apps/app_service/browser_app_instance_registry.cc
+++ b/chrome/browser/apps/app_service/browser_app_instance_registry.cc
@@ -385,7 +385,9 @@
   BrowserAppInstance* instance = GetInstance(lacros_app_instances_, update.id);
   DCHECK(instance);
   if (instance->MaybeUpdate(window, update.title, update.is_browser_active,
-                            update.is_web_contents_active)) {
+                            update.is_web_contents_active,
+                            update.browser_session_id,
+                            update.restored_browser_session_id)) {
     for (auto& observer : observers_) {
       observer.OnBrowserAppUpdated(*instance);
     }
diff --git a/chrome/browser/apps/app_service/browser_app_instance_tracker.cc b/chrome/browser/apps/app_service/browser_app_instance_tracker.cc
index 6eb0be42..97428e36 100644
--- a/chrome/browser/apps/app_service/browser_app_instance_tracker.cc
+++ b/chrome/browser/apps/app_service/browser_app_instance_tracker.cc
@@ -464,7 +464,8 @@
               : BrowserAppInstance::Type::kAppTab,
           std::move(app_id), browser->window()->GetNativeWindow(),
           base::UTF16ToUTF8(contents->GetTitle()), IsBrowserActive(browser),
-          IsWebContentsActive(browser, contents)));
+          IsWebContentsActive(browser, contents), browser->session_id().id(),
+          browser->create_params().restore_id));
   for (auto& observer : observers_) {
     observer.OnBrowserAppAdded(instance);
   }
@@ -474,10 +475,11 @@
     BrowserAppInstance& instance,
     Browser* browser,
     content::WebContents* contents) {
-  if (instance.MaybeUpdate(browser->window()->GetNativeWindow(),
-                           base::UTF16ToUTF8(contents->GetTitle()),
-                           IsBrowserActive(browser),
-                           IsWebContentsActive(browser, contents))) {
+  if (instance.MaybeUpdate(
+          browser->window()->GetNativeWindow(),
+          base::UTF16ToUTF8(contents->GetTitle()), IsBrowserActive(browser),
+          IsWebContentsActive(browser, contents), browser->session_id().id(),
+          browser->create_params().restore_id)) {
     for (auto& observer : observers_) {
       observer.OnBrowserAppUpdated(instance);
     }
@@ -495,11 +497,12 @@
 }
 
 void BrowserAppInstanceTracker::CreateWindowInstance(Browser* browser) {
-  auto& instance =
-      AddInstance(window_instances_, browser,
-                  std::make_unique<BrowserWindowInstance>(
-                      GenerateId(), browser->window()->GetNativeWindow(),
-                      IsBrowserActive(browser)));
+  auto& instance = AddInstance(
+      window_instances_, browser,
+      std::make_unique<BrowserWindowInstance>(
+          GenerateId(), browser->window()->GetNativeWindow(),
+          browser->session_id().id(), browser->create_params().restore_id,
+          IsBrowserActive(browser)));
   for (auto& observer : observers_) {
     observer.OnBrowserWindowAdded(instance);
   }
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
index 8d7e7c2..f53f014 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
@@ -567,8 +567,8 @@
 void AppPlatformMetrics::OnTenMinutes() {
   if (should_refresh_activated_count_pref) {
     should_refresh_activated_count_pref = false;
-    DictionaryPrefUpdate activated_count_update(profile_->GetPrefs(),
-                                                kAppActivatedCount);
+    DictionaryPrefUpdateDeprecated activated_count_update(profile_->GetPrefs(),
+                                                          kAppActivatedCount);
     for (auto it : activated_count_) {
       std::string app_type_name = GetAppTypeHistogramName(it.first);
       DCHECK(!app_type_name.empty());
@@ -578,8 +578,8 @@
 
   if (should_refresh_duration_pref) {
     should_refresh_duration_pref = false;
-    DictionaryPrefUpdate running_duration_update(profile_->GetPrefs(),
-                                                 kAppRunningDuration);
+    DictionaryPrefUpdateDeprecated running_duration_update(profile_->GetPrefs(),
+                                                           kAppRunningDuration);
     for (auto it : running_duration_) {
       std::string app_type_name = GetAppTypeHistogramName(it.first);
       DCHECK(!app_type_name.empty());
@@ -864,10 +864,10 @@
 }
 
 void AppPlatformMetrics::InitRunningDuration() {
-  DictionaryPrefUpdate running_duration_update(profile_->GetPrefs(),
-                                               kAppRunningDuration);
-  DictionaryPrefUpdate activated_count_update(profile_->GetPrefs(),
-                                              kAppActivatedCount);
+  DictionaryPrefUpdateDeprecated running_duration_update(profile_->GetPrefs(),
+                                                         kAppRunningDuration);
+  DictionaryPrefUpdateDeprecated activated_count_update(profile_->GetPrefs(),
+                                                        kAppActivatedCount);
 
   for (auto app_type_name : GetAppTypeNameSet()) {
     std::string key = GetAppTypeHistogramName(app_type_name);
@@ -892,11 +892,11 @@
   running_duration_.clear();
   activated_count_.clear();
 
-  DictionaryPrefUpdate running_duration_update(profile_->GetPrefs(),
-                                               kAppRunningDuration);
+  DictionaryPrefUpdateDeprecated running_duration_update(profile_->GetPrefs(),
+                                                         kAppRunningDuration);
   running_duration_update->DictClear();
-  DictionaryPrefUpdate activated_count_update(profile_->GetPrefs(),
-                                              kAppActivatedCount);
+  DictionaryPrefUpdateDeprecated activated_count_update(profile_->GetPrefs(),
+                                                        kAppActivatedCount);
   activated_count_update->DictClear();
 }
 
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc
index b5104d3..ab3b876 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc
@@ -475,7 +475,8 @@
 
   void VerifyAppRunningDuration(const base::TimeDelta time_delta,
                                 AppTypeName app_type_name) {
-    DictionaryPrefUpdate update(GetPrefService(), kAppRunningDuration);
+    DictionaryPrefUpdateDeprecated update(GetPrefService(),
+                                          kAppRunningDuration);
     std::string key = GetAppTypeHistogramName(app_type_name);
 
     absl::optional<base::TimeDelta> unreported_duration =
@@ -526,7 +527,7 @@
   }
 
   void VerifyAppActivatedCount(int count, AppTypeName app_type_name) {
-    DictionaryPrefUpdate update(GetPrefService(), kAppActivatedCount);
+    DictionaryPrefUpdateDeprecated update(GetPrefService(), kAppActivatedCount);
     std::string key = GetAppTypeHistogramName(app_type_name);
 
     absl::optional<int> activated_count = update->FindIntPath(key);
diff --git a/chrome/browser/apps/app_service/publishers/app_publisher.cc b/chrome/browser/apps/app_service/publishers/app_publisher.cc
index 15fc6468..9ae0e2de 100644
--- a/chrome/browser/apps/app_service/publishers/app_publisher.cc
+++ b/chrome/browser/apps/app_service/publishers/app_publisher.cc
@@ -18,11 +18,17 @@
 std::unique_ptr<App> AppPublisher::MakeApp(AppType app_type,
                                            const std::string& app_id,
                                            Readiness readiness,
-                                           const std::string& name) {
+                                           const std::string& name,
+                                           InstallReason install_reason,
+                                           InstallSource install_source) {
   std::unique_ptr<App> app = std::make_unique<App>(app_type, app_id);
   app->readiness = readiness;
   app->name = name;
   app->short_name = name;
+
+  app->install_reason = install_reason;
+  app->install_source = install_source;
+
   return app;
 }
 
diff --git a/chrome/browser/apps/app_service/publishers/app_publisher.h b/chrome/browser/apps/app_service/publishers/app_publisher.h
index b9fdbca..41acf6f1 100644
--- a/chrome/browser/apps/app_service/publishers/app_publisher.h
+++ b/chrome/browser/apps/app_service/publishers/app_publisher.h
@@ -34,7 +34,9 @@
   static std::unique_ptr<App> MakeApp(AppType app_type,
                                       const std::string& app_id,
                                       Readiness readiness,
-                                      const std::string& name);
+                                      const std::string& name,
+                                      InstallReason install_reason,
+                                      InstallSource install_source);
 
   // Registers this AppPublisher to AppServiceProxy, allowing it to receive App
   // Service API calls. This function must be called after the object's
diff --git a/chrome/browser/apps/app_service/publishers/arc_apps.cc b/chrome/browser/apps/app_service/publishers/arc_apps.cc
index 035743e..056ddd6 100644
--- a/chrome/browser/apps/app_service/publishers/arc_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/arc_apps.cc
@@ -1482,10 +1482,14 @@
     const std::string& app_id,
     const ArcAppListPrefs::AppInfo& app_info,
     bool update_icon) {
+  auto install_reason = ConvertMojomInstallReasonToInstallReason(
+      GetInstallReason(prefs, app_id, app_info));
   std::unique_ptr<App> app = AppPublisher::MakeApp(
       AppType::kArc, app_id,
       app_info.suspended ? Readiness::kDisabledByPolicy : Readiness::kReady,
-      app_info.name);
+      app_info.name, install_reason,
+      install_reason == InstallReason::kSystem ? InstallSource::kSystem
+                                               : InstallSource::kPlayStore);
 
   app->publisher_id = app_info.package_name;
 
diff --git a/chrome/browser/apps/app_service/publishers/borealis_apps.cc b/chrome/browser/apps/app_service/publishers/borealis_apps.cc
index 01a9be9..08a3f76 100644
--- a/chrome/browser/apps/app_service/publishers/borealis_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/borealis_apps.cc
@@ -72,7 +72,8 @@
   std::unique_ptr<apps::App> app = apps::AppPublisher::MakeApp(
       apps::AppType::kBorealis, borealis::kInstallerAppId,
       allowed ? apps::Readiness::kReady : apps::Readiness::kDisabledByPolicy,
-      l10n_util::GetStringUTF8(IDS_BOREALIS_APP_NAME));
+      l10n_util::GetStringUTF8(IDS_BOREALIS_APP_NAME),
+      apps::InstallReason::kDefault, apps::InstallSource::kUnknown);
 
   app->icon_key =
       apps::IconKey(apps::IconKey::kDoesNotChangeOverTime,
@@ -166,9 +167,9 @@
   // it can't be converted.
   DCHECK_NE(registration.app_id(), borealis::kInstallerAppId);
 
-  std::unique_ptr<App> app =
-      AppPublisher::MakeApp(AppType::kBorealis, registration.app_id(),
-                            Readiness::kReady, registration.Name());
+  std::unique_ptr<App> app = AppPublisher::MakeApp(
+      AppType::kBorealis, registration.app_id(), Readiness::kReady,
+      registration.Name(), InstallReason::kUser, InstallSource::kUnknown);
 
   if (generate_new_icon_key) {
     app->icon_key = std::move(
@@ -434,7 +435,8 @@
   PublisherBase::Publish(std::move(mojom_app), subscribers_);
 
   std::unique_ptr<App> app = AppPublisher::MakeApp(
-      AppType::kBorealis, shelf_app_id, Readiness::kReady, shelf_app_name);
+      AppType::kBorealis, shelf_app_id, Readiness::kReady, shelf_app_name,
+      InstallReason::kUser, InstallSource::kUnknown);
 
   app->icon_key = IconKey(IconKey::kDoesNotChangeOverTime,
                           IDR_LOGO_BOREALIS_DEFAULT_192, IconEffects::kNone);
diff --git a/chrome/browser/apps/app_service/publishers/built_in_chromeos_apps.cc b/chrome/browser/apps/app_service/publishers/built_in_chromeos_apps.cc
index e02f32da..16dc1a5 100644
--- a/chrome/browser/apps/app_service/publishers/built_in_chromeos_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/built_in_chromeos_apps.cc
@@ -37,7 +37,8 @@
 
   std::unique_ptr<apps::App> app = apps::AppPublisher::MakeApp(
       apps::AppType::kBuiltIn, internal_app.app_id, apps::Readiness::kReady,
-      l10n_util::GetStringUTF8(internal_app.name_string_resource_id));
+      l10n_util::GetStringUTF8(internal_app.name_string_resource_id),
+      apps::InstallReason::kSystem, apps::InstallSource::kSystem);
 
   app->icon_key =
       apps::IconKey(apps::IconKey::kDoesNotChangeOverTime,
diff --git a/chrome/browser/apps/app_service/publishers/crostini_apps.cc b/chrome/browser/apps/app_service/publishers/crostini_apps.cc
index ea5bdd20..dee38f42 100644
--- a/chrome/browser/apps/app_service/publishers/crostini_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/crostini_apps.cc
@@ -345,9 +345,9 @@
       registration.VmType(),
       guest_os::GuestOsRegistryService::VmType::ApplicationList_VmType_TERMINA);
 
-  std::unique_ptr<App> app =
-      AppPublisher::MakeApp(AppType::kCrostini, registration.app_id(),
-                            Readiness::kReady, registration.Name());
+  std::unique_ptr<App> app = AppPublisher::MakeApp(
+      AppType::kCrostini, registration.app_id(), Readiness::kReady,
+      registration.Name(), InstallReason::kUser, InstallSource::kUnknown);
 
   if (generate_new_icon_key) {
     if (registration.app_id() == crostini::kCrostiniTerminalSystemAppId) {
diff --git a/chrome/browser/apps/app_service/publishers/extension_apps_base.cc b/chrome/browser/apps/app_service/publishers/extension_apps_base.cc
index e64e6d8..60ec013 100644
--- a/chrome/browser/apps/app_service/publishers/extension_apps_base.cc
+++ b/chrome/browser/apps/app_service/publishers/extension_apps_base.cc
@@ -209,8 +209,13 @@
 std::unique_ptr<App> ExtensionAppsBase::CreateAppImpl(
     const extensions::Extension* extension,
     Readiness readiness) {
+  auto install_reason = ConvertMojomInstallReasonToInstallReason(
+      GetInstallReason(profile_, extension));
   std::unique_ptr<App> app = AppPublisher::MakeApp(
-      app_type(), extension->id(), readiness, extension->name());
+      app_type(), extension->id(), readiness, extension->name(), install_reason,
+      install_reason == InstallReason::kSystem
+          ? InstallSource::kSystem
+          : InstallSource::kChromeWebStore);
   app->short_name = extension->short_name();
   app->description = extension->description();
   app->version = extension->GetVersionForDisplay();
@@ -703,8 +708,8 @@
   mojom_app->readiness = mojom_readiness;
   PublisherBase::Publish(std::move(mojom_app), subscribers_);
 
-  std::unique_ptr<App> app = AppPublisher::MakeApp(
-      app_type(), extension->id(), readiness, extension->name());
+  std::unique_ptr<App> app = std::make_unique<App>(app_type(), extension->id());
+  app->readiness = readiness;
   AppPublisher::Publish(std::move(app));
 }
 
diff --git a/chrome/browser/apps/app_service/publishers/plugin_vm_apps.cc b/chrome/browser/apps/app_service/publishers/plugin_vm_apps.cc
index 667c1db8..9cbb945 100644
--- a/chrome/browser/apps/app_service/publishers/plugin_vm_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/plugin_vm_apps.cc
@@ -95,7 +95,8 @@
   std::unique_ptr<apps::App> app = apps::AppPublisher::MakeApp(
       apps::AppType::kPluginVm, plugin_vm::kPluginVmShelfAppId,
       allowed ? apps::Readiness::kReady : apps::Readiness::kDisabledByPolicy,
-      l10n_util::GetStringUTF8(IDS_PLUGIN_VM_APP_NAME));
+      l10n_util::GetStringUTF8(IDS_PLUGIN_VM_APP_NAME),
+      apps::InstallReason::kUser, apps::InstallSource::kUnknown);
 
   app->icon_key =
       apps::IconKey(apps::IconKey::kDoesNotChangeOverTime,
@@ -361,9 +362,9 @@
   DCHECK_EQ(registration.VmType(), guest_os::GuestOsRegistryService::VmType::
                                        ApplicationList_VmType_PLUGIN_VM);
 
-  std::unique_ptr<App> app =
-      AppPublisher::MakeApp(AppType::kPluginVm, registration.app_id(),
-                            Readiness::kReady, registration.Name());
+  std::unique_ptr<App> app = AppPublisher::MakeApp(
+      AppType::kPluginVm, registration.app_id(), Readiness::kReady,
+      registration.Name(), InstallReason::kUser, apps::InstallSource::kUnknown);
 
   if (generate_new_icon_key) {
     app->icon_key = std::move(
diff --git a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
index 92ba7b08..f0e3d0e 100644
--- a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
+++ b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
@@ -71,7 +71,7 @@
   apps::mojom::AppPtr app = apps::PublisherBase::MakeApp(
       app_type, app_id, apps::mojom::Readiness::kReady, name,
       apps::mojom::InstallReason::kUser);
-
+  app->install_source = apps::mojom::InstallSource::kSync;
   app->icon_key = apps::mojom::IconKey::New(
       /*timeline=*/1, apps::mojom::IconKey::kInvalidResourceId,
       /*icon_effects=*/0);
@@ -139,7 +139,9 @@
   void VerifyApp(AppType app_type,
                  const std::string& app_id,
                  const std::string& name,
-                 apps::Readiness readiness) {
+                 apps::Readiness readiness,
+                 InstallReason install_reason,
+                 InstallSource install_source) {
     AppRegistryCache& cache =
         AppServiceProxyFactory::GetForProfile(profile())->AppRegistryCache();
 
@@ -149,6 +151,8 @@
     EXPECT_EQ(name, cache.states_[app_id]->name.value());
     EXPECT_EQ(readiness, cache.states_[app_id]->readiness);
     ASSERT_TRUE(cache.states_[app_id]->icon_key.has_value());
+    EXPECT_EQ(install_reason, cache.states_[app_id]->install_reason);
+    EXPECT_EQ(install_source, cache.states_[app_id]->install_source);
   }
 
   void VerifyAppIsRemoved(const std::string& app_id) {
@@ -178,7 +182,11 @@
   for (const auto& app_id : prefs->GetAppIds()) {
     std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id);
     if (app_info) {
-      VerifyApp(AppType::kArc, app_id, app_info->name, Readiness::kReady);
+      VerifyApp(
+          AppType::kArc, app_id, app_info->name, Readiness::kReady,
+          app_info->sticky ? InstallReason::kSystem : InstallReason::kUser,
+          app_info->sticky ? InstallSource::kSystem
+                           : InstallSource::kPlayStore);
 
       // Simulate the app is removed.
       RemoveArcApp(app_id);
@@ -195,7 +203,11 @@
   for (const auto& app_id : prefs->GetAppIds()) {
     std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs->GetApp(app_id);
     if (app_info) {
-      VerifyApp(AppType::kArc, app_id, app_info->name, Readiness::kReady);
+      VerifyApp(
+          AppType::kArc, app_id, app_info->name, Readiness::kReady,
+          app_info->sticky ? InstallReason::kSystem : InstallReason::kUser,
+          app_info->sticky ? InstallSource::kSystem
+                           : InstallSource::kPlayStore);
     }
   }
 
@@ -212,7 +224,8 @@
     }
     VerifyApp(AppType::kBuiltIn, internal_app.app_id,
               l10n_util::GetStringUTF8(internal_app.name_string_resource_id),
-              Readiness::kReady);
+              Readiness::kReady, InstallReason::kSystem,
+              InstallSource::kSystem);
   }
 }
 
@@ -273,18 +286,19 @@
 
 TEST_F(StandaloneBrowserPublisherTest, StandaloneBrowserAppsOnApps) {
   VerifyApp(AppType::kStandaloneBrowser, extension_misc::kLacrosAppId, "Lacros",
-            Readiness::kReady);
+            Readiness::kReady, InstallReason::kSystem, InstallSource::kSystem);
 }
 
 TEST_F(StandaloneBrowserPublisherTest, StandaloneBrowserExtensionAppsOnApps) {
   ExtensionAppsOnApps();
   VerifyApp(AppType::kStandaloneBrowserChromeApp, "a", "TestApp",
-            Readiness::kReady);
+            Readiness::kReady, InstallReason::kUser, InstallSource::kSync);
 }
 
 TEST_F(StandaloneBrowserPublisherTest, WebAppsCrosapiOnApps) {
   WebAppsCrosapiOnApps();
-  VerifyApp(AppType::kWeb, "a", "TestApp", Readiness::kReady);
+  VerifyApp(AppType::kWeb, "a", "TestApp", Readiness::kReady,
+            InstallReason::kUser, InstallSource::kSync);
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
@@ -299,17 +313,20 @@
   // Re-init AppService to verify the init process.
   AppServiceTest app_service_test;
   app_service_test.SetUp(profile());
-  VerifyApp(AppType::kChromeApp, store->id(), store->name(), Readiness::kReady);
+  VerifyApp(AppType::kChromeApp, store->id(), store->name(), Readiness::kReady,
+            InstallReason::kDefault, InstallSource::kChromeWebStore);
 
   // Uninstall the Chrome app.
   service_->UninstallExtension(
       store->id(), extensions::UNINSTALL_REASON_FOR_TESTING, nullptr);
   VerifyApp(AppType::kChromeApp, store->id(), store->name(),
-            Readiness::kUninstalledByUser);
+            Readiness::kUninstalledByUser, InstallReason::kDefault,
+            InstallSource::kChromeWebStore);
 
   // Reinstall the Chrome app.
   service_->AddExtension(store.get());
-  VerifyApp(AppType::kChromeApp, store->id(), store->name(), Readiness::kReady);
+  VerifyApp(AppType::kChromeApp, store->id(), store->name(), Readiness::kReady,
+            InstallReason::kDefault, InstallSource::kChromeWebStore);
 }
 
 TEST_F(PublisherTest, WebAppsOnApps) {
@@ -318,7 +335,8 @@
   AppServiceTest app_service_test_;
   app_service_test_.SetUp(profile());
 
-  VerifyApp(AppType::kWeb, app_id, kAppName, Readiness::kReady);
+  VerifyApp(AppType::kWeb, app_id, kAppName, Readiness::kReady,
+            InstallReason::kSync, InstallSource::kBrowser);
 }
 
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/apps/app_service/publishers/remote_apps.cc b/chrome/browser/apps/app_service/publishers/remote_apps.cc
index 263a503..51a9dcbf 100644
--- a/chrome/browser/apps/app_service/publishers/remote_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/remote_apps.cc
@@ -64,7 +64,8 @@
 std::unique_ptr<App> RemoteApps::CreateApp(
     const ash::RemoteAppsModel::AppInfo& info) {
   std::unique_ptr<App> app = AppPublisher::MakeApp(
-      AppType::kRemote, info.id, Readiness::kReady, info.name);
+      AppType::kRemote, info.id, Readiness::kReady, info.name,
+      InstallReason::kUser, apps::InstallSource::kUnknown);
   app->icon_key =
       std::move(*icon_key_factory_.CreateIconKey(IconEffects::kNone));
   return app;
diff --git a/chrome/browser/apps/app_service/publishers/standalone_browser_apps.cc b/chrome/browser/apps/app_service/publishers/standalone_browser_apps.cc
index 887a6e6..6bce7b9 100644
--- a/chrome/browser/apps/app_service/publishers/standalone_browser_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/standalone_browser_apps.cc
@@ -60,8 +60,8 @@
 std::unique_ptr<App> StandaloneBrowserApps::CreateStandaloneBrowserApp() {
   std::unique_ptr<App> app = AppPublisher::MakeApp(
       AppType::kStandaloneBrowser, extension_misc::kLacrosAppId,
-      Readiness::kReady, "Lacros" /* TODO(crbug.com/1267752): Localized name.*/
-  );
+      Readiness::kReady, "Lacros" /* TODO(crbug.com/1267752): Localized name.*/,
+      InstallReason::kSystem, InstallSource::kSystem);
 
   app->icon_key = std::move(*CreateIconKey(/*is_browser_load_success=*/true));
   return app;
@@ -73,6 +73,7 @@
       apps::mojom::Readiness::kReady,
       "Lacros",  // TODO(jamescook): Localized name.
       apps::mojom::InstallReason::kSystem);
+  app->install_source = apps::mojom::InstallSource::kSystem;
   // Make Lacros searchable with the term "chrome", too.
   app->additional_search_terms.push_back("chrome");
   app->icon_key = NewIconKey();
diff --git a/chrome/browser/apps/app_service/webapk/webapk_prefs.cc b/chrome/browser/apps/app_service/webapk/webapk_prefs.cc
index 2737e9a..4d46f57 100644
--- a/chrome/browser/apps/app_service/webapk/webapk_prefs.cc
+++ b/chrome/browser/apps/app_service/webapk/webapk_prefs.cc
@@ -46,8 +46,8 @@
 void AddWebApk(Profile* profile,
                const std::string& app_id,
                const std::string& package_name) {
-  DictionaryPrefUpdate generated_webapks(profile->GetPrefs(),
-                                         kGeneratedWebApksPref);
+  DictionaryPrefUpdateDeprecated generated_webapks(profile->GetPrefs(),
+                                                   kGeneratedWebApksPref);
 
   generated_webapks->SetPath({app_id, kPackageNameKey},
                              base::Value(package_name));
@@ -100,8 +100,8 @@
 absl::optional<std::string> RemoveWebApkByPackageName(
     Profile* profile,
     const std::string& package_name) {
-  DictionaryPrefUpdate generated_webapks(profile->GetPrefs(),
-                                         kGeneratedWebApksPref);
+  DictionaryPrefUpdateDeprecated generated_webapks(profile->GetPrefs(),
+                                                   kGeneratedWebApksPref);
 
   for (auto kv : generated_webapks->DictItems()) {
     const std::string* item_package_name =
@@ -119,8 +119,8 @@
 void SetUpdateNeededForApp(Profile* profile,
                            const std::string& app_id,
                            bool update_needed) {
-  DictionaryPrefUpdate generated_webapks(profile->GetPrefs(),
-                                         kGeneratedWebApksPref);
+  DictionaryPrefUpdateDeprecated generated_webapks(profile->GetPrefs(),
+                                                   kGeneratedWebApksPref);
   if (generated_webapks->HasKey(app_id)) {
     generated_webapks->SetPath({app_id, kUpdateNeededKey},
                                base::Value(update_needed));
diff --git a/chrome/browser/apps/platform_apps/app_browsertest.cc b/chrome/browser/apps/platform_apps/app_browsertest.cc
index 7dcc6b02..7b21950c 100644
--- a/chrome/browser/apps/platform_apps/app_browsertest.cc
+++ b/chrome/browser/apps/platform_apps/app_browsertest.cc
@@ -1133,8 +1133,8 @@
   extensions::EventRouter::Get(browser()->profile())
       ->ClearRegisteredEventsForTest(extension->id());
 
-  DictionaryPrefUpdate update(extension_prefs->pref_service(),
-                              extensions::pref_names::kExtensions);
+  DictionaryPrefUpdateDeprecated update(extension_prefs->pref_service(),
+                                        extensions::pref_names::kExtensions);
   base::DictionaryValue* dict = update.Get();
   std::string key(extension->id());
   key += ".manifest.version";
diff --git a/chrome/browser/ash/accessibility/accessibility_manager.cc b/chrome/browser/ash/accessibility/accessibility_manager.cc
index 1692fcc..b8d83bfe 100644
--- a/chrome/browser/ash/accessibility/accessibility_manager.cc
+++ b/chrome/browser/ash/accessibility/accessibility_manager.cc
@@ -963,7 +963,7 @@
         // If the SODA language isn't installed yet, update the preference to
         // ensure the nudge gets shown for this locale when installation
         // completes.
-        DictionaryPrefUpdate update(
+        DictionaryPrefUpdateDeprecated update(
             pref_service, prefs::kAccessibilityDictationLocaleOfflineNudge);
         update.Get()->SetBoolKey(dictation_locale, false);
       }
@@ -1010,8 +1010,8 @@
   // for this particular locale.
   AccessibilityController::Get()->ShowDictationLanguageUpgradedNudge(
       dictation_locale, g_browser_process->GetApplicationLocale());
-  DictionaryPrefUpdate update(profile_->GetPrefs(),
-                              prefs::kAccessibilityDictationLocaleOfflineNudge);
+  DictionaryPrefUpdateDeprecated update(
+      profile_->GetPrefs(), prefs::kAccessibilityDictationLocaleOfflineNudge);
   update.Get()->SetBoolKey(dictation_locale, true);
 }
 
@@ -1960,7 +1960,7 @@
 void AccessibilityManager::SetSwitchAccessKeysForTest(
     const std::set<int>& action_keys,
     const std::string& pref_name) {
-  DictionaryPrefUpdate pref_update(profile_->GetPrefs(), pref_name);
+  DictionaryPrefUpdateDeprecated pref_update(profile_->GetPrefs(), pref_name);
   base::ListValue devices;
   devices.Append(kSwitchAccessInternalDevice);
   devices.Append(kSwitchAccessUsbDevice);
diff --git a/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc b/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc
index 999f1c4..c61c26d 100644
--- a/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc
+++ b/chrome/browser/ash/accessibility/accessibility_manager_browsertest.cc
@@ -293,8 +293,8 @@
 }
 
 void ClearDictationOfflineNudgePref(const std::string& locale) {
-  DictionaryPrefUpdate update(GetActiveUserPrefs(),
-                              prefs::kAccessibilityDictationLocaleOfflineNudge);
+  DictionaryPrefUpdateDeprecated update(
+      GetActiveUserPrefs(), prefs::kAccessibilityDictationLocaleOfflineNudge);
   update.Get()->RemovePath(locale);
 }
 
diff --git a/chrome/browser/ash/accessibility/dictation.h b/chrome/browser/ash/accessibility/dictation.h
index c1389f5..62beb43 100644
--- a/chrome/browser/ash/accessibility/dictation.h
+++ b/chrome/browser/ash/accessibility/dictation.h
@@ -69,6 +69,7 @@
   void OnSpeechSoundLevelChanged(int16_t level) override;
   void OnSpeechRecognitionStateChanged(
       SpeechRecognizerStatus new_state) override;
+  void OnSpeechRecognitionStopped() override {}
 
   // ui::InputMethodObserver:
   void OnTextInputStateChanged(const ui::TextInputClient* client) override;
diff --git a/chrome/browser/ash/accessibility/dictation_browsertest.cc b/chrome/browser/ash/accessibility/dictation_browsertest.cc
index 5583b3a..0dc57bb 100644
--- a/chrome/browser/ash/accessibility/dictation_browsertest.cc
+++ b/chrome/browser/ash/accessibility/dictation_browsertest.cc
@@ -67,6 +67,8 @@
 const char* kLocaleMetric = "Accessibility.CrosDictation.Language";
 const char* kOnDeviceSpeechMetric =
     "Accessibility.CrosDictation.UsedOnDeviceSpeech";
+const char* kMacroRecognizedMetric =
+    "Accessibility.CrosDictation.MacroRecognized";
 const char* kMacroSucceededMetric =
     "Accessibility.CrosDictation.MacroSucceeded";
 const char* kMacroFailedMetric = "Accessibility.CrosDictation.MacroFailed";
@@ -1155,6 +1157,9 @@
   histogram_tester_.ExpectUniqueSample(/*name=*/kMacroFailedMetric,
                                        /*sample=*/kInputTextViewMetricValue,
                                        /*expected_bucket_count=*/0);
+  histogram_tester_.ExpectUniqueSample(/*name=*/kMacroRecognizedMetric,
+                                       /*sample=*/kInputTextViewMetricValue,
+                                       /*expected_bucket_count=*/1);
 }
 
 // TODO(1266696): DictationCommandsExtensionTest.Help is failing on
diff --git a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
index af175d3..f6add6037 100644
--- a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
@@ -1503,7 +1503,7 @@
   // Move through all tabs; make a few expectations along the way.
   sm_.Call([this]() { SendKeyPressWithSearch(ui::VKEY_RIGHT); });
   sm_.ExpectSpeech("Popular Shortcuts, tab");
-  sm_.ExpectSpeech("1 of 7");
+  sm_.ExpectSpeech("1 of 6");
   sm_.Call([this]() { SendKeyPressWithSearch(ui::VKEY_RIGHT); });
   sm_.Call([this]() { SendKeyPressWithSearch(ui::VKEY_RIGHT); });
   sm_.Call([this]() { SendKeyPressWithSearch(ui::VKEY_RIGHT); });
@@ -1511,8 +1511,8 @@
   sm_.Call([this]() { SendKeyPressWithSearch(ui::VKEY_RIGHT); });
   sm_.ExpectSpeech("Accessibility, tab");
   sm_.Call([this]() { SendKeyPressWithSearch(ui::VKEY_RIGHT); });
-  sm_.ExpectSpeech("Popular Shortcuts, tab");
-  sm_.ExpectSpeech("7 of 7");
+  sm_.ExpectSpeech("Popular Shortcuts");
+  sm_.ExpectSpeech("Tab");
 
   // Moving forward again should dive into the list of shortcuts for the
   // category.
diff --git a/chrome/browser/ash/account_manager/account_apps_availability.cc b/chrome/browser/ash/account_manager/account_apps_availability.cc
index 1248190..3339d1b 100644
--- a/chrome/browser/ash/account_manager/account_apps_availability.cc
+++ b/chrome/browser/ash/account_manager/account_apps_availability.cc
@@ -149,8 +149,8 @@
 void RemoveAccountFromPrefs(PrefService* prefs, const std::string& gaia_id) {
   DCHECK(!IsPrimaryGaiaAccount(gaia_id));
 
-  DictionaryPrefUpdate update(prefs,
-                              account_manager::prefs::kAccountAppsAvailability);
+  DictionaryPrefUpdateDeprecated update(
+      prefs, account_manager::prefs::kAccountAppsAvailability);
   const bool success = update->RemoveKey(gaia_id);
   DCHECK(success);
 }
@@ -165,16 +165,16 @@
   account_entry.SetKey(account_manager::prefs::kIsAvailableInArcKey,
                        base::Value(is_available_in_arc));
 
-  DictionaryPrefUpdate update(prefs,
-                              account_manager::prefs::kAccountAppsAvailability);
+  DictionaryPrefUpdateDeprecated update(
+      prefs, account_manager::prefs::kAccountAppsAvailability);
   update->SetKey(gaia_id, std::move(account_entry));
 }
 
 void UpdateAccountInPrefs(PrefService* prefs,
                           const std::string& gaia_id,
                           bool is_available_in_arc) {
-  DictionaryPrefUpdate update(prefs,
-                              account_manager::prefs::kAccountAppsAvailability);
+  DictionaryPrefUpdateDeprecated update(
+      prefs, account_manager::prefs::kAccountAppsAvailability);
   base::Value* account_entry = update->FindDictKey(gaia_id);
   DCHECK(account_entry);
 
@@ -376,8 +376,8 @@
   prefs_->Set(account_manager::prefs::kAccountAppsAvailability,
               base::Value(base::Value::Type::DICTIONARY));
 
-  DictionaryPrefUpdate update(prefs_,
-                              account_manager::prefs::kAccountAppsAvailability);
+  DictionaryPrefUpdateDeprecated update(
+      prefs_, account_manager::prefs::kAccountAppsAvailability);
   DCHECK(update->DictEmpty());
 
   // See structure of `update` dictionary at the top of the file.
diff --git a/chrome/browser/ash/app_mode/OWNERS b/chrome/browser/ash/app_mode/OWNERS
index 9743b3f9..732620f 100644
--- a/chrome/browser/ash/app_mode/OWNERS
+++ b/chrome/browser/ash/app_mode/OWNERS
@@ -1,3 +1,7 @@
+# Primary owners
+bfranz@chromium.org
+
+# Backup owners
 anqing@chromium.org
 apotapchuk@chromium.org
 poromov@chromium.org
diff --git a/chrome/browser/ash/app_mode/arc/arc_kiosk_app_data.cc b/chrome/browser/ash/app_mode/arc/arc_kiosk_app_data.cc
index f3489e3..9c313d5f 100644
--- a/chrome/browser/ash/app_mode/arc/arc_kiosk_app_data.cc
+++ b/chrome/browser/ash/app_mode/arc/arc_kiosk_app_data.cc
@@ -59,7 +59,7 @@
   SaveIcon(*icon_.bitmap(), cache_dir);
 
   PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate dict_update(local_state, dictionary_name());
+  DictionaryPrefUpdateDeprecated dict_update(local_state, dictionary_name());
 
   SaveToDictionary(dict_update);
 }
diff --git a/chrome/browser/ash/app_mode/chrome_app_kiosk_app_installer.cc b/chrome/browser/ash/app_mode/chrome_app_kiosk_app_installer.cc
index 48f35fd..3fbd5bec 100644
--- a/chrome/browser/ash/app_mode/chrome_app_kiosk_app_installer.cc
+++ b/chrome/browser/ash/app_mode/chrome_app_kiosk_app_installer.cc
@@ -49,7 +49,6 @@
   extensions::file_util::SetUseSafeInstallation(true);
   KioskAppManager::Get()->UpdatePrimaryAppLoaderPrefs(app_id_);
   if (IsAppInstallPending(app_id_)) {
-    delegate_->OnAppInstalling();
     ObserveActiveInstallations();
     return;
   }
@@ -94,7 +93,6 @@
 
   KioskAppManager::Get()->UpdateSecondaryAppsLoaderPrefs(secondary_app_ids);
   if (IsAnySecondaryAppPending()) {
-    delegate_->OnAppInstalling();
     ObserveActiveInstallations();
     return;
   }
diff --git a/chrome/browser/ash/app_mode/kiosk_app_data.cc b/chrome/browser/ash/app_mode/kiosk_app_data.cc
index 0b80b98..2741d61 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_data.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_data.cc
@@ -407,7 +407,7 @@
   SaveIcon(icon, cache_dir);
 
   PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate dict_update(local_state, dictionary_name());
+  DictionaryPrefUpdateDeprecated dict_update(local_state, dictionary_name());
   SaveToDictionary(dict_update);
 
   const std::string app_key = std::string(kKeyApps) + '.' + app_id();
diff --git a/chrome/browser/ash/app_mode/kiosk_app_data_base.cc b/chrome/browser/ash/app_mode/kiosk_app_data_base.cc
index 2510454..0ad857a3a 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_data_base.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_data_base.cc
@@ -78,7 +78,8 @@
 
 KioskAppDataBase::~KioskAppDataBase() = default;
 
-void KioskAppDataBase::SaveToDictionary(DictionaryPrefUpdate& dict_update) {
+void KioskAppDataBase::SaveToDictionary(
+    DictionaryPrefUpdateDeprecated& dict_update) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   const std::string app_key = std::string(kKeyApps) + '.' + app_id_;
   const std::string name_key = app_key + '.' + kKeyName;
@@ -88,7 +89,8 @@
   dict_update->SetString(icon_path_key, icon_path_.value());
 }
 
-void KioskAppDataBase::SaveIconToDictionary(DictionaryPrefUpdate& dict_update) {
+void KioskAppDataBase::SaveIconToDictionary(
+    DictionaryPrefUpdateDeprecated& dict_update) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   const std::string app_key = std::string(kKeyApps) + '.' + app_id_;
   const std::string icon_path_key = app_key + '.' + kKeyIcon;
@@ -151,7 +153,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   PrefService* local_state = g_browser_process->local_state();
 
-  DictionaryPrefUpdate dict_update(local_state, dictionary_name());
+  DictionaryPrefUpdateDeprecated dict_update(local_state, dictionary_name());
 
   const std::string app_key =
       std::string(KioskAppDataBase::kKeyApps) + '.' + app_id_;
diff --git a/chrome/browser/ash/app_mode/kiosk_app_data_base.h b/chrome/browser/ash/app_mode/kiosk_app_data_base.h
index d211f2f..e54c414 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_data_base.h
+++ b/chrome/browser/ash/app_mode/kiosk_app_data_base.h
@@ -47,10 +47,10 @@
   ~KioskAppDataBase() override;
 
   // Helper to save name and icon to provided dictionary.
-  void SaveToDictionary(DictionaryPrefUpdate& dict_update);
+  void SaveToDictionary(DictionaryPrefUpdateDeprecated& dict_update);
 
   // Helper to save icon to provided dictionary.
-  void SaveIconToDictionary(DictionaryPrefUpdate& dict_update);
+  void SaveIconToDictionary(DictionaryPrefUpdateDeprecated& dict_update);
 
   // Helper to load name and icon from provided dictionary.
   // if |lazy_icon_load| is set to true, the icon will not be updated, only
diff --git a/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc b/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc
index 78dde4b..16f752f 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc
@@ -79,8 +79,8 @@
 // static
 void KioskAppLaunchError::Save(KioskAppLaunchError::Error error) {
   PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate dict_update(local_state,
-                                   KioskAppManager::kKioskDictionaryName);
+  DictionaryPrefUpdateDeprecated dict_update(
+      local_state, KioskAppManager::kKioskDictionaryName);
   dict_update->SetInteger(kKeyLaunchError, static_cast<int>(error));
   s_last_error = error;
 }
@@ -89,8 +89,8 @@
 void KioskAppLaunchError::SaveCryptohomeFailure(
     const AuthFailure& auth_failure) {
   PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate dict_update(local_state,
-                                   KioskAppManager::kKioskDictionaryName);
+  DictionaryPrefUpdateDeprecated dict_update(
+      local_state, KioskAppManager::kKioskDictionaryName);
   dict_update->SetInteger(kKeyCryptohomeFailure, auth_failure.reason());
 }
 
@@ -115,8 +115,8 @@
 // static
 void KioskAppLaunchError::RecordMetricAndClear() {
   PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate dict_update(local_state,
-                                   KioskAppManager::kKioskDictionaryName);
+  DictionaryPrefUpdateDeprecated dict_update(
+      local_state, KioskAppManager::kKioskDictionaryName);
 
   absl::optional<int> error = dict_update->FindIntKey(kKeyLaunchError);
   if (error) {
diff --git a/chrome/browser/ash/app_mode/kiosk_app_manager.cc b/chrome/browser/ash/app_mode/kiosk_app_manager.cc
index f3b2cce..6733805 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_manager.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_manager.cc
@@ -870,8 +870,8 @@
 
 void KioskAppManager::SetAutoLoginState(AutoLoginState state) {
   PrefService* prefs = g_browser_process->local_state();
-  DictionaryPrefUpdate dict_update(prefs,
-                                   KioskAppManager::kKioskDictionaryName);
+  DictionaryPrefUpdateDeprecated dict_update(
+      prefs, KioskAppManager::kKioskDictionaryName);
   dict_update->SetInteger(kKeyAutoLoginState, static_cast<int>(state));
   prefs->CommitPendingWrite();
 }
diff --git a/chrome/browser/ash/app_mode/kiosk_app_manager_browsertest.cc b/chrome/browser/ash/app_mode/kiosk_app_manager_browsertest.cc
index 554ddfe..cf62012e 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_manager_browsertest.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_manager_browsertest.cc
@@ -324,8 +324,8 @@
                         required_platform_version);
 
     PrefService* local_state = g_browser_process->local_state();
-    DictionaryPrefUpdate dict_update(local_state,
-                                     KioskAppManager::kKioskDictionaryName);
+    DictionaryPrefUpdateDeprecated dict_update(
+        local_state, KioskAppManager::kKioskDictionaryName);
     dict_update->SetKey(KioskAppDataBase::kKeyApps, std::move(apps_dict));
 
     // Make the app appear in device settings.
diff --git a/chrome/browser/ash/app_mode/kiosk_cryptohome_remover.cc b/chrome/browser/ash/app_mode/kiosk_cryptohome_remover.cc
index f1d1399f..a4a4ac6 100644
--- a/chrome/browser/ash/app_mode/kiosk_cryptohome_remover.cc
+++ b/chrome/browser/ash/app_mode/kiosk_cryptohome_remover.cc
@@ -32,8 +32,8 @@
 void ScheduleDelayedCryptohomeRemoval(const AccountId& account_id) {
   PrefService* const local_state = g_browser_process->local_state();
   {
-    DictionaryPrefUpdate dict_update(local_state,
-                                     prefs::kAllKioskUsersToRemove);
+    DictionaryPrefUpdateDeprecated dict_update(local_state,
+                                               prefs::kAllKioskUsersToRemove);
     dict_update->SetKey(cryptohome::Identification(account_id).id(),
                         base::Value(account_id.GetUserEmail()));
   }
@@ -43,8 +43,8 @@
 void UnscheduleDelayedCryptohomeRemoval(const cryptohome::Identification& id) {
   PrefService* const local_state = g_browser_process->local_state();
   {
-    DictionaryPrefUpdate dict_update(local_state,
-                                     prefs::kAllKioskUsersToRemove);
+    DictionaryPrefUpdateDeprecated dict_update(local_state,
+                                               prefs::kAllKioskUsersToRemove);
     dict_update->RemoveKey(id.id());
   }
   local_state->CommitPendingWrite();
diff --git a/chrome/browser/ash/app_mode/startup_app_launcher.cc b/chrome/browser/ash/app_mode/startup_app_launcher.cc
index 7145bb3e..a25cd90 100644
--- a/chrome/browser/ash/app_mode/startup_app_launcher.cc
+++ b/chrome/browser/ash/app_mode/startup_app_launcher.cc
@@ -242,6 +242,9 @@
 }
 
 void StartupAppLauncher::BeginInstall(bool finalize_only) {
+  if (!finalize_only) {
+    delegate_->OnAppInstalling();
+  }
   installer_ = std::make_unique<ChromeAppKioskAppInstaller>(
       profile_, app_id_, delegate_, finalize_only);
   installer_->BeginInstall(base::BindOnce(
diff --git a/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc b/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc
index 31e9a734..a80e588 100644
--- a/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc
+++ b/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc
@@ -641,7 +641,8 @@
   // Given that the app is offline enabled and installed, the app should be
   // launched immediately, without waiting for network or checking for updates.
   startup_launch_delegate_.WaitForLaunchStates({LaunchState::kReadyToLaunch});
-  EXPECT_EQ(std::vector<LaunchState>({LaunchState::kReadyToLaunch}),
+  EXPECT_EQ(std::vector<LaunchState>(
+                {LaunchState::kInstallingApp, LaunchState::kReadyToLaunch}),
             startup_launch_delegate_.launch_state_changes());
   startup_launch_delegate_.ClearLaunchStateChanges();
 
@@ -683,7 +684,8 @@
   // Given that the app is offline enabled and installed, the app should be
   // launched immediately, without waiting for network or checking for updates.
   startup_launch_delegate_.WaitForLaunchStates({LaunchState::kReadyToLaunch});
-  EXPECT_EQ(std::vector<LaunchState>({LaunchState::kReadyToLaunch}),
+  EXPECT_EQ(std::vector<LaunchState>(
+                {LaunchState::kInstallingApp, LaunchState::kReadyToLaunch}),
             startup_launch_delegate_.launch_state_changes());
   startup_launch_delegate_.ClearLaunchStateChanges();
 
@@ -813,12 +815,6 @@
 
   ASSERT_TRUE(FinishPrimaryAppInstall(primary_app_builder));
 
-  // Installing primary app with a non-installed secondary app should notify
-  // delegate about pending app installation - in this case for secondary app.
-  EXPECT_EQ(std::vector<LaunchState>({LaunchState::kInstallingApp}),
-            startup_launch_delegate_.launch_state_changes());
-  startup_launch_delegate_.ClearLaunchStateChanges();
-
   TestKioskExtensionBuilder secondary_app_builder(Manifest::TYPE_PLATFORM_APP,
                                                   kSecondaryAppId);
   secondary_app_builder.set_kiosk_enabled(false);
@@ -873,12 +869,6 @@
 
   ASSERT_TRUE(FinishPrimaryAppInstall(primary_app_builder));
 
-  // Installing primary app with a non-installed secondary app should notify
-  // delegate about pending app installation - in this case for secondary app.
-  EXPECT_EQ(std::vector<LaunchState>({LaunchState::kInstallingApp}),
-            startup_launch_delegate_.launch_state_changes());
-  startup_launch_delegate_.ClearLaunchStateChanges();
-
   TestKioskExtensionBuilder secondary_extension_builder(
       Manifest::TYPE_EXTENSION, kSecondaryAppId);
   secondary_extension_builder.set_kiosk_enabled(false);
@@ -923,7 +913,8 @@
   // Given that the app is offline enabled and installed, the app should be
   // launched immediately, without waiting for network or checking for updates.
   startup_launch_delegate_.WaitForLaunchStates({LaunchState::kReadyToLaunch});
-  EXPECT_EQ(std::vector<LaunchState>({LaunchState::kReadyToLaunch}),
+  EXPECT_EQ(std::vector<LaunchState>(
+                {LaunchState::kInstallingApp, LaunchState::kReadyToLaunch}),
             startup_launch_delegate_.launch_state_changes());
   startup_launch_delegate_.ClearLaunchStateChanges();
 
diff --git a/chrome/browser/ash/app_mode/web_app/web_kiosk_app_data.cc b/chrome/browser/ash/app_mode/web_app/web_kiosk_app_data.cc
index 9e4aa50f..71a679a1 100644
--- a/chrome/browser/ash/app_mode/web_app/web_kiosk_app_data.cc
+++ b/chrome/browser/ash/app_mode/web_app/web_kiosk_app_data.cc
@@ -230,7 +230,7 @@
   }
 
   PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate dict_update(local_state, dictionary_name());
+  DictionaryPrefUpdateDeprecated dict_update(local_state, dictionary_name());
   SaveToDictionary(dict_update);
 
   launch_url_ = GURL(app_info->start_url);
@@ -299,7 +299,7 @@
   SaveIcon(icon, cache_dir);
 
   PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate dict_update(local_state, dictionary_name());
+  DictionaryPrefUpdateDeprecated dict_update(local_state, dictionary_name());
   SaveIconToDictionary(dict_update);
 
   dict_update->FindDictKey(KioskAppDataBase::kKeyApps)
diff --git a/chrome/browser/ash/apps/apk_web_app_service.cc b/chrome/browser/ash/apps/apk_web_app_service.cc
index 0e88ef2..c2286da 100644
--- a/chrome/browser/ash/apps/apk_web_app_service.cc
+++ b/chrome/browser/ash/apps/apk_web_app_service.cc
@@ -98,8 +98,8 @@
   if (!IsWebAppInstalledFromArc(app_id))
     return false;
 
-  DictionaryPrefUpdate web_apps_to_apks(profile_->GetPrefs(),
-                                        kWebAppToApkDictPref);
+  DictionaryPrefUpdateDeprecated web_apps_to_apks(profile_->GetPrefs(),
+                                                  kWebAppToApkDictPref);
 
   // Find the entry associated with the provided web app id.
   const base::Value* v = web_apps_to_apks->FindPathOfType(
@@ -116,8 +116,8 @@
 
 absl::optional<std::string> ApkWebAppService::GetPackageNameForWebApp(
     const web_app::AppId& app_id) {
-  DictionaryPrefUpdate web_apps_to_apks(profile_->GetPrefs(),
-                                        kWebAppToApkDictPref);
+  DictionaryPrefUpdateDeprecated web_apps_to_apks(profile_->GetPrefs(),
+                                                  kWebAppToApkDictPref);
 
   // Find the entry associated with the provided web app id.
   const base::Value* v = web_apps_to_apks->FindPathOfType(
@@ -145,8 +145,8 @@
   if (!IsWebAppInstalledFromArc(app_id))
     return absl::nullopt;
 
-  DictionaryPrefUpdate web_apps_to_apks(profile_->GetPrefs(),
-                                        kWebAppToApkDictPref);
+  DictionaryPrefUpdateDeprecated web_apps_to_apks(profile_->GetPrefs(),
+                                                  kWebAppToApkDictPref);
 
   // Find the entry associated with the provided web app id.
   const base::Value* v = web_apps_to_apks->FindPathOfType(
@@ -266,8 +266,8 @@
   // packages are updated. In (b), there are two cases to handle: the package
   // could previously have been an Android app and has now become a web app, and
   // vice-versa.
-  DictionaryPrefUpdate web_apps_to_apks(profile_->GetPrefs(),
-                                        kWebAppToApkDictPref);
+  DictionaryPrefUpdateDeprecated web_apps_to_apks(profile_->GetPrefs(),
+                                                  kWebAppToApkDictPref);
 
   // Search the pref dict for any |web_app_id| that has a value matching the
   // provided package name.
@@ -343,8 +343,8 @@
   if (!base::FeatureList::IsEnabled(features::kApkWebAppInstalls))
     return;
 
-  DictionaryPrefUpdate web_apps_to_apks(profile_->GetPrefs(),
-                                        kWebAppToApkDictPref);
+  DictionaryPrefUpdateDeprecated web_apps_to_apks(profile_->GetPrefs(),
+                                                  kWebAppToApkDictPref);
 
   // Search the pref dict for any |web_app_id| that has a value matching the
   // provided package name. We need to uninstall that |web_app_id|.
@@ -373,8 +373,8 @@
 
   // Scan through the list of apps to see if any were uninstalled while ARC
   // wasn't running.
-  DictionaryPrefUpdate web_apps_to_apks(profile_->GetPrefs(),
-                                        kWebAppToApkDictPref);
+  DictionaryPrefUpdateDeprecated web_apps_to_apks(profile_->GetPrefs(),
+                                                  kWebAppToApkDictPref);
 
   // If ARC isn't unavailable, it's not going to become available since we're
   // occupying the UI thread. We'll try again later.
@@ -423,8 +423,8 @@
   if (!base::FeatureList::IsEnabled(features::kApkWebAppInstalls))
     return;
 
-  DictionaryPrefUpdate web_apps_to_apks(profile_->GetPrefs(),
-                                        kWebAppToApkDictPref);
+  DictionaryPrefUpdateDeprecated web_apps_to_apks(profile_->GetPrefs(),
+                                                  kWebAppToApkDictPref);
 
   // Find the package name associated with the provided web app id.
   const base::Value* package_name_value = web_apps_to_apks->FindPathOfType(
@@ -480,7 +480,8 @@
     return;
 
   // Set a pref to map |web_app_id| to |package_name| for future uninstallation.
-  DictionaryPrefUpdate dict_update(profile_->GetPrefs(), kWebAppToApkDictPref);
+  DictionaryPrefUpdateDeprecated dict_update(profile_->GetPrefs(),
+                                             kWebAppToApkDictPref);
   dict_update->SetPath({web_app_id, kPackageNameKey},
                        base::Value(package_name));
 
@@ -509,7 +510,8 @@
 void ApkWebAppService::UpdatePackageInfo(
     const std::string& app_id,
     const arc::mojom::WebAppInfoPtr& web_app_info) {
-  DictionaryPrefUpdate dict_update(profile_->GetPrefs(), kWebAppToApkDictPref);
+  DictionaryPrefUpdateDeprecated dict_update(profile_->GetPrefs(),
+                                             kWebAppToApkDictPref);
   dict_update->SetPath({app_id, kIsWebOnlyTwaKey},
                        base::Value(web_app_info->is_web_only_twa));
   dict_update->SetPath(
diff --git a/chrome/browser/ash/arc/auth/arc_auth_service.cc b/chrome/browser/ash/arc/auth/arc_auth_service.cc
index ea344c3..a938ae1 100644
--- a/chrome/browser/ash/arc/auth/arc_auth_service.cc
+++ b/chrome/browser/ash/arc/auth/arc_auth_service.cc
@@ -17,10 +17,12 @@
 #include "ash/components/arc/session/arc_service_manager.h"
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/containers/flat_set.h"
 #include "base/memory/singleton.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "chrome/browser/ash/account_manager/account_apps_availability_factory.h"
 #include "chrome/browser/ash/account_manager/account_manager_util.h"
 #include "chrome/browser/ash/arc/arc_optin_uma.h"
 #include "chrome/browser/ash/arc/arc_util.h"
@@ -71,7 +73,10 @@
  private:
   friend struct base::DefaultSingletonTraits<ArcAuthServiceFactory>;
 
-  ArcAuthServiceFactory() { DependsOn(IdentityManagerFactory::GetInstance()); }
+  ArcAuthServiceFactory() {
+    DependsOn(IdentityManagerFactory::GetInstance());
+    DependsOn(ash::AccountAppsAvailabilityFactory::GetInstance());
+  }
   ~ArcAuthServiceFactory() override = default;
 };
 
@@ -227,6 +232,14 @@
 
   ArcSessionManager::Get()->AddObserver(this);
   identity_manager_->AddObserver(this);
+
+  if (ash::IsAccountManagerAvailable(profile_) &&
+      ash::AccountAppsAvailability::IsArcAccountRestrictionsEnabled()) {
+    account_apps_availability_ =
+        ash::AccountAppsAvailabilityFactory::GetForProfile(profile_);
+
+    account_apps_availability_->AddObserver(this);
+  }
 }
 
 ArcAuthService::~ArcAuthService() {
@@ -528,8 +541,58 @@
 
 void ArcAuthService::OnRefreshTokenUpdatedForAccount(
     const CoreAccountInfo& account_info) {
-  // TODO(sinhak): Identity Manager is specific to a Profile. Move this to a
-  // proper Profile independent entity once we have that.
+  // Should be consistent with OnAccountAvailableInArc.
+  // TODO(crbug/1260909): Remove IdentityManager::Observer implementation.
+  if (ash::AccountAppsAvailability::IsArcAccountRestrictionsEnabled())
+    return;
+
+  UpsertAccountToArc(account_info);
+}
+
+void ArcAuthService::OnExtendedAccountInfoRemoved(
+    const AccountInfo& account_info) {
+  // Should be consistent with OnAccountUnavailableInArc.
+  // TODO(crbug/1260909): Remove IdentityManager::Observer implementation.
+  if (ash::AccountAppsAvailability::IsArcAccountRestrictionsEnabled())
+    return;
+
+  DCHECK(!IsPrimaryGaiaAccount(account_info.gaia));
+
+  RemoveAccountFromArc(account_info.email);
+}
+
+void ArcAuthService::OnAccountAvailableInArc(
+    const account_manager::Account& account) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(ash::AccountAppsAvailability::IsArcAccountRestrictionsEnabled());
+  DCHECK(ash::IsAccountManagerAvailable(profile_));
+
+  UpsertAccountToArc(identity_manager_->FindExtendedAccountInfoByEmailAddress(
+      account.raw_email));
+}
+
+void ArcAuthService::OnAccountUnavailableInArc(
+    const account_manager::Account& account) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(ash::AccountAppsAvailability::IsArcAccountRestrictionsEnabled());
+  DCHECK(ash::IsAccountManagerAvailable(profile_));
+
+  DCHECK(!IsPrimaryGaiaAccount(account.key.id()));
+
+  RemoveAccountFromArc(account.raw_email);
+}
+
+void ArcAuthService::OnArcInitialStart() {
+  TriggerAccountsPushToArc(true /* filter_primary_account */);
+}
+
+void ArcAuthService::Shutdown() {
+  identity_manager_->RemoveObserver(this);
+  if (account_apps_availability_)
+    account_apps_availability_->RemoveObserver(this);
+}
+
+void ArcAuthService::UpsertAccountToArc(const CoreAccountInfo& account_info) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   if (!ash::IsAccountManagerAvailable(profile_))
@@ -556,15 +619,12 @@
   instance->OnAccountUpdated(account_name, mojom::AccountUpdateType::UPSERT);
 }
 
-void ArcAuthService::OnExtendedAccountInfoRemoved(
-    const AccountInfo& account_info) {
+void ArcAuthService::RemoveAccountFromArc(const std::string& email) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   if (!ash::IsAccountManagerAvailable(profile_))
     return;
 
-  DCHECK(!IsPrimaryGaiaAccount(account_info.gaia));
-
   // Ignore the update if ARC has not been provisioned yet.
   if (!arc::IsArcProvisioned(profile_))
     return;
@@ -574,17 +634,8 @@
   if (!instance)
     return;
 
-  DCHECK(!account_info.email.empty());
-  instance->OnAccountUpdated(account_info.email,
-                             mojom::AccountUpdateType::REMOVAL);
-}
-
-void ArcAuthService::OnArcInitialStart() {
-  TriggerAccountsPushToArc(true /* filter_primary_account */);
-}
-
-void ArcAuthService::Shutdown() {
-  identity_manager_->RemoveObserver(this);
+  DCHECK(!email.empty());
+  instance->OnAccountUpdated(email, mojom::AccountUpdateType::REMOVAL);
 }
 
 void ArcAuthService::OnActiveDirectoryEnrollmentTokenFetched(
@@ -796,6 +847,17 @@
   if (!ash::IsAccountManagerAvailable(profile_))
     return;
 
+  VLOG(1) << "Pushing accounts to ARC "
+          << (filter_primary_account ? "without primary account"
+                                     : "with primary account");
+  if (ash::AccountAppsAvailability::IsArcAccountRestrictionsEnabled()) {
+    VLOG(1) << "Using AccountAppsAvailability to get available accounts";
+    account_apps_availability_->GetAccountsAvailableInArc(
+        base::BindOnce(&ArcAuthService::CompleteAccountsPushToArc,
+                       weak_ptr_factory_.GetWeakPtr(), filter_primary_account));
+    return;
+  }
+
   const std::vector<CoreAccountInfo> accounts =
       identity_manager_->GetAccountsWithRefreshTokens();
   for (const CoreAccountInfo& account : accounts) {
@@ -806,6 +868,19 @@
   }
 }
 
+void ArcAuthService::CompleteAccountsPushToArc(
+    bool filter_primary_account,
+    const base::flat_set<account_manager::Account>& accounts) {
+  // TODO(crbug/1260909): call `SetAccounts` when the API is implemented in ARC.
+  for (const auto& account : accounts) {
+    DCHECK(account.key.account_type() == account_manager::AccountType::kGaia);
+    if (filter_primary_account && IsPrimaryGaiaAccount(account.key.id()))
+      continue;
+
+    OnAccountAvailableInArc(account);
+  }
+}
+
 void ArcAuthService::DispatchAccountsInArc(
     GetGoogleAccountsInArcCallback callback) {
   auto* instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->auth(),
diff --git a/chrome/browser/ash/arc/auth/arc_auth_service.h b/chrome/browser/ash/arc/auth/arc_auth_service.h
index a6de7f6..56ea223c 100644
--- a/chrome/browser/ash/arc/auth/arc_auth_service.h
+++ b/chrome/browser/ash/arc/auth/arc_auth_service.h
@@ -12,8 +12,10 @@
 #include "ash/components/arc/mojom/auth.mojom.h"
 #include "ash/components/arc/session/connection_observer.h"
 #include "base/callback.h"
+#include "base/containers/flat_set.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/ash/account_manager/account_apps_availability.h"
 #include "chrome/browser/ash/arc/auth/arc_active_directory_enrollment_token_fetcher.h"
 #include "chrome/browser/ash/arc/session/arc_session_manager_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -50,6 +52,7 @@
                        public mojom::AuthHost,
                        public ConnectionObserver<mojom::AuthInstance>,
                        public signin::IdentityManager::Observer,
+                       public ash::AccountAppsAvailability::Observer,
                        public ArcSessionManagerObserver {
  public:
   using GetGoogleAccountsInArcCallback =
@@ -100,6 +103,9 @@
   void HandleRemoveAccountRequest(const std::string& email) override;
   void HandleUpdateCredentialsRequest(const std::string& email) override;
 
+ private:
+  friend class ArcAuthServiceTest;
+
   void SetURLLoaderFactoryForTesting(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
 
@@ -108,13 +114,27 @@
       const CoreAccountInfo& account_info) override;
   void OnExtendedAccountInfoRemoved(const AccountInfo& account_info) override;
 
+  // ash::AccountAppsAvailability::Observer:
+  void OnAccountAvailableInArc(
+      const account_manager::Account& account) override;
+  void OnAccountUnavailableInArc(
+      const account_manager::Account& account) override;
+
   // ArcSessionManagerObserver:
   void OnArcInitialStart() override;
 
   // KeyedService:
   void Shutdown() override;
 
- private:
+  // Calls `mojom::OnAccountUpdated` with update type
+  // `mojom::AccountUpdateType::UPSERT` if the account with provided
+  // `account_info` doesn't have refresh token in persistent error state.
+  void UpsertAccountToArc(const CoreAccountInfo& account_info);
+
+  // Calls `mojom::OnAccountUpdated` with update type
+  // `mojom::AccountUpdateType::REMOVAL` for the provided email.
+  void RemoveAccountFromArc(const std::string& email);
+
   // Callback when Active Directory Enrollment Token is fetched.
   // |callback| is completed with |ArcAuthCodeStatus| and |AccountInfo|
   // depending on the success / failure of the operation.
@@ -186,6 +206,14 @@
   // OS Account Manager will not be pushed to ARC as part of this call.
   void TriggerAccountsPushToArc(bool filter_primary_account);
 
+  // Pushes accounts in the `accounts` set to ARC. `accounts` set must contain
+  // only Gaia accounts. If `filter_primary_account` is set to `true`, the
+  // Primary Account in Chrome OS Account Manager will not be pushed to ARC as
+  // part of this call.
+  void CompleteAccountsPushToArc(
+      bool filter_primary_account,
+      const base::flat_set<account_manager::Account>& accounts);
+
   // Issues a request to ARC, which will complete callback with the list of
   // Google accounts in ARC.
   void DispatchAccountsInArc(GetGoogleAccountsInArcCallback callback);
@@ -197,6 +225,7 @@
   Profile* const profile_;
   signin::IdentityManager* const identity_manager_;
   ArcBridgeService* const arc_bridge_service_;
+  ash::AccountAppsAvailability* account_apps_availability_ = nullptr;
 
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   bool url_loader_factory_for_testing_set_ = false;
diff --git a/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc b/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc
index 7d4e5f70..663576f3 100644
--- a/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc
+++ b/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc
@@ -15,9 +15,11 @@
 #include "ash/components/arc/test/arc_util_test_support.h"
 #include "ash/components/arc/test/connection_holder_util.h"
 #include "ash/components/arc/test/fake_arc_session.h"
+#include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
+#include "base/containers/flat_set.h"
 #include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/run_loop.h"
@@ -25,6 +27,7 @@
 #include "base/task/post_task.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "chrome/browser/ash/account_manager/account_apps_availability_factory.h"
 #include "chrome/browser/ash/arc/arc_util.h"
 #include "chrome/browser/ash/arc/auth/arc_auth_context.h"
 #include "chrome/browser/ash/arc/auth/arc_auth_service.h"
@@ -53,6 +56,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
 #include "components/account_id/account_id.h"
+#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/policy/core/common/cloud/device_management_service.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
 #include "components/policy/core/common/policy_switches.h"
@@ -198,7 +202,53 @@
   base::WeakPtrFactory<FakeAuthInstance> weak_ptr_factory_{this};
 };
 
-class ArcAuthServiceTest : public InProcessBrowserTest {
+// Set account availability in ARC by gaia id.
+class AccountAppsAvailabilitySetter {
+ public:
+  AccountAppsAvailabilitySetter(
+      ash::AccountAppsAvailability* account_apps_availability,
+      account_manager::AccountManagerFacade* account_manager_facade)
+      : account_apps_availability_(account_apps_availability),
+        account_manager_facade_(account_manager_facade) {}
+
+  AccountAppsAvailabilitySetter(const AccountAppsAvailabilitySetter&) = delete;
+  AccountAppsAvailabilitySetter& operator=(
+      const AccountAppsAvailabilitySetter&) = delete;
+
+  ~AccountAppsAvailabilitySetter() = default;
+
+  // Returns `true` if account with `gaia_id` was found in AccountManager and
+  // `SetIsAccountAvailableInArc` for this account was called. Returns `false`
+  // otherwise.
+  bool SetIsAccountAvailableInArc(const std::string& gaia_id,
+                                  bool is_available) {
+    std::vector<account_manager::Account> result;
+    base::RunLoop run_loop;
+    account_manager_facade_->GetAccounts(base::BindLambdaForTesting(
+        [&result,
+         &run_loop](const std::vector<account_manager::Account>& accounts) {
+          result = accounts;
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+
+    for (auto account : result) {
+      if (account.key.id() == gaia_id) {
+        account_apps_availability_->SetIsAccountAvailableInArc(account,
+                                                               is_available);
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  ash::AccountAppsAvailability* const account_apps_availability_;
+  account_manager::AccountManagerFacade* const account_manager_facade_;
+};
+
+class ArcAuthServiceTest : public InProcessBrowserTest,
+                           public ::testing::WithParamInterface<bool> {
  public:
   ArcAuthServiceTest(const ArcAuthServiceTest&) = delete;
   ArcAuthServiceTest& operator=(const ArcAuthServiceTest&) = delete;
@@ -209,6 +259,16 @@
   // InProcessBrowserTest:
   ~ArcAuthServiceTest() override = default;
 
+  void SetUp() override {
+    if (IsArcAccountRestrictionsEnabled()) {
+      feature_list_.InitWithFeatures(
+          /*enabled_features=*/{chromeos::features::kArcAccountRestrictions,
+                                chromeos::features::kLacrosSupport},
+          /*disabled_features=*/{});
+    }
+    InProcessBrowserTest::SetUp();
+  }
+
   void SetUpCommandLine(base::CommandLine* command_line) override {
     arc::SetArcAvailableCommandLineForTesting(command_line);
   }
@@ -238,8 +298,11 @@
     // a dangling pointer to the User.
     // TODO(nya): Consider removing all users from ProfileHelper in the
     // destructor of ash::FakeChromeUserManager.
-    GetFakeUserManager()->RemoveUserFromList(
-        GetFakeUserManager()->GetActiveUser()->GetAccountId());
+    auto* user = GetFakeUserManager()->GetActiveUser();
+    if (user) {
+      GetFakeUserManager()->RemoveUserFromList(
+          GetFakeUserManager()->GetActiveUser()->GetAccountId());
+    }
     // Since ArcServiceLauncher is (re-)set up with profile() in
     // SetUpOnMainThread() it is necessary to Shutdown() before the profile()
     // is destroyed. ArcServiceLauncher::Shutdown() will be called again on
@@ -247,6 +310,9 @@
     // instance in fixture, once), but it should be no op.
     // TODO(hidehiko): Think about a way to test the code cleanly.
     ArcServiceLauncher::Get()->Shutdown();
+    if (IsArcAccountRestrictionsEnabled()) {
+      arc_availability_setter_.reset();
+    }
     identity_test_environment_adaptor_.reset();
     profile_.reset();
     user_manager_enabler_.reset();
@@ -325,6 +391,9 @@
       // browser sync consent.
       identity_test_env->MakePrimaryAccountAvailable(
           kFakeUserName, signin::ConsentLevel::kSignin);
+      // Wait for all callbacks to complete, so that they are not called during
+      // the test execution.
+      base::RunLoop().RunUntilIdle();
     }
 
     profile()->GetPrefs()->SetBoolean(prefs::kArcSignedIn, true);
@@ -347,6 +416,12 @@
         base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
             &test_url_loader_factory_);
     auth_service_->SetURLLoaderFactoryForTesting(test_shared_loader_factory_);
+    if (IsArcAccountRestrictionsEnabled()) {
+      arc_availability_setter_ =
+          std::make_unique<AccountAppsAvailabilitySetter>(
+              ash::AccountAppsAvailabilityFactory::GetForProfile(profile()),
+              ::GetAccountManagerFacade(profile()->GetPath().value()));
+    }
     arc_bridge_service_ = ArcServiceManager::Get()->arc_bridge_service();
     DCHECK(arc_bridge_service_);
     arc_bridge_service_->auth()->SetInstance(&auth_instance_);
@@ -355,19 +430,44 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  AccountInfo SeedAccountInfo(const std::string& email) {
-    return identity_test_environment_adaptor_->identity_test_env()
-        ->MakeAccountAvailable(email);
+  bool SetIsAccountAvailableInArc(std::string gaia, bool is_available) {
+    DCHECK(arc_availability_setter_);
+    return arc_availability_setter_->SetIsAccountAvailableInArc(gaia,
+                                                                is_available);
+  }
+
+  AccountInfo SeedAccountInfo(const std::string& email,
+                              bool make_available_in_arc = true) {
+    auto account_info = identity_test_environment_adaptor_->identity_test_env()
+                            ->MakeAccountAvailable(email);
+    // Wait for async calls to finish.
+    base::RunLoop().RunUntilIdle();
+    if (IsArcAccountRestrictionsEnabled() && make_available_in_arc) {
+      EXPECT_TRUE(
+          SetIsAccountAvailableInArc(account_info.gaia, make_available_in_arc));
+    }
+    return account_info;
   }
 
   void SetInvalidRefreshTokenForAccount(const CoreAccountId& account_id) {
     identity_test_environment_adaptor_->identity_test_env()
         ->SetInvalidRefreshTokenForAccount(account_id);
+    // Wait for async calls to finish.
+    base::RunLoop().RunUntilIdle();
   }
 
   void SetRefreshTokenForAccount(const CoreAccountId& account_id) {
     identity_test_environment_adaptor_->identity_test_env()
         ->SetRefreshTokenForAccount(account_id);
+    // Wait for async calls to finish.
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void RemoveRefreshTokenForAccount(const CoreAccountId& account_id) {
+    identity_test_environment_adaptor_->identity_test_env()
+        ->RemoveRefreshTokenForAccount(account_id);
+    // Wait for async calls to finish.
+    base::RunLoop().RunUntilIdle();
   }
 
   void UpdatePersistentErrorOfRefreshTokenForAccount(
@@ -397,9 +497,10 @@
     auth_service().GetGoogleAccountsInArc(std::move(callback));
   }
 
-  AccountInfo SetupGaiaAccount(const std::string& email) {
+  AccountInfo SetupGaiaAccount(const std::string& email,
+                               bool make_available_in_arc = true) {
     SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
-    return SeedAccountInfo(email);
+    return SeedAccountInfo(email, make_available_in_arc);
   }
 
   void WaitForGoogleAccountsInArcCallback() { run_loop_->RunUntilIdle(); }
@@ -425,6 +526,10 @@
     return std::make_pair(account_name, account_type);
   }
 
+  void OnArcInitialStart() { auth_service().OnArcInitialStart(); }
+
+  bool IsArcAccountRestrictionsEnabled() { return GetParam(); }
+
   Profile* profile() { return profile_.get(); }
 
   void set_profile_name(const std::string& username) {
@@ -457,13 +562,15 @@
   std::vector<mojom::ArcAccountInfoPtr> arc_google_accounts_;
   bool arc_google_accounts_callback_called_ = false;
   std::unique_ptr<base::RunLoop> run_loop_;
+  std::unique_ptr<AccountAppsAvailabilitySetter> arc_availability_setter_;
+  base::test::ScopedFeatureList feature_list_;
 
   // Not owned.
   ArcAuthService* auth_service_ = nullptr;
   ArcBridgeService* arc_bridge_service_ = nullptr;
 };
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest, GetPrimaryAccountForGaiaAccounts) {
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest, GetPrimaryAccountForGaiaAccounts) {
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
   const std::pair<std::string, mojom::ChromeAccountType>
       primary_account = RequestPrimaryAccount();
@@ -471,7 +578,7 @@
   EXPECT_EQ(mojom::ChromeAccountType::USER_ACCOUNT, primary_account.second);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest, GetPrimaryAccountForChildAccounts) {
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest, GetPrimaryAccountForChildAccounts) {
   SetAccountAndProfile(user_manager::USER_TYPE_CHILD);
   const std::pair<std::string, mojom::ChromeAccountType>
       primary_account = RequestPrimaryAccount();
@@ -479,7 +586,7 @@
   EXPECT_EQ(mojom::ChromeAccountType::CHILD_ACCOUNT, primary_account.second);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest,
                        GetPrimaryAccountForActiveDirectoryAccounts) {
   SetAccountAndProfile(user_manager::USER_TYPE_ACTIVE_DIRECTORY);
   const std::pair<std::string, mojom::ChromeAccountType>
@@ -489,7 +596,7 @@
             primary_account.second);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest, GetPrimaryAccountForPublicAccounts) {
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest, GetPrimaryAccountForPublicAccounts) {
   SetAccountAndProfile(user_manager::USER_TYPE_PUBLIC_ACCOUNT);
   const std::pair<std::string, mojom::ChromeAccountType>
       primary_account = RequestPrimaryAccount();
@@ -497,7 +604,7 @@
   EXPECT_EQ(mojom::ChromeAccountType::ROBOT_ACCOUNT, primary_account.second);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest,
                        GetPrimaryAccountForOfflineDemoAccounts) {
   ash::DemoSession::SetDemoConfigForTesting(
       ash::DemoSession::DemoModeConfig::kOffline);
@@ -512,7 +619,7 @@
 
 // Tests that when ARC requests account info for a non-managed account,
 // Chrome supplies the info configured in SetAccountAndProfile() method.
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest, SuccessfulBackgroundFetch) {
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest, SuccessfulBackgroundFetch) {
   base::HistogramTester histogram_tester;
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
   test_url_loader_factory()->AddResponse(arc::kAuthTokenExchangeEndPoint,
@@ -538,7 +645,7 @@
 // Tests that the `ArcBackgroundAuthCodeFetcher` will retry the network request
 // which fetches the auth code to be used for Google Play Store sign-in if the
 // request has failed because of a unreachable mandatory PAC script.
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest, SuccessfulBackgroundProxyBypass) {
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest, SuccessfulBackgroundProxyBypass) {
   base::HistogramTester histogram_tester;
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
   int requests_count = 0;
@@ -585,7 +692,7 @@
   EXPECT_FALSE(auth_instance().account_info()->is_managed);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest,
                        ReAuthenticatePrimaryAccountSucceeds) {
   base::HistogramTester tester;
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
@@ -610,7 +717,7 @@
       mojom::ArcAuthCodeStatus::SUCCESS, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest,
                        RetryAuthTokenExchangeRequestOnUnauthorizedError) {
   base::HistogramTester tester;
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
@@ -636,7 +743,7 @@
       mojom::ArcAuthCodeStatus::SUCCESS, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest,
                        ReAuthenticatePrimaryAccountFailsForInvalidAccount) {
   base::HistogramTester tester;
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
@@ -656,7 +763,7 @@
       mojom::ArcAuthCodeStatus::CHROME_SERVER_COMMUNICATION_ERROR, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest, FetchSecondaryAccountInfoSucceeds) {
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest, FetchSecondaryAccountInfoSucceeds) {
   base::HistogramTester tester;
   // Add a Secondary Account.
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
@@ -683,7 +790,7 @@
       mojom::ArcAuthCodeStatus::SUCCESS, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest,
                        FetchSecondaryAccountInfoFailsForInvalidAccounts) {
   base::HistogramTester tester;
   // Add a Secondary Account.
@@ -706,7 +813,7 @@
       mojom::ArcAuthCodeStatus::CHROME_SERVER_COMMUNICATION_ERROR, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest,
                        FetchSecondaryAccountInfoInvalidRefreshToken) {
   base::HistogramTester tester;
   const AccountInfo account_info = SetupGaiaAccount(kSecondaryAccountEmail);
@@ -729,7 +836,7 @@
       mojom::ArcAuthCodeStatus::CHROME_SERVER_COMMUNICATION_ERROR, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest,
                        FetchSecondaryAccountRefreshTokenHasPersistentError) {
   base::HistogramTester tester;
   const AccountInfo account_info = SetupGaiaAccount(kSecondaryAccountEmail);
@@ -753,7 +860,7 @@
       mojom::ArcAuthCodeStatus::CHROME_SERVER_COMMUNICATION_ERROR, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(
+IN_PROC_BROWSER_TEST_P(
     ArcAuthServiceTest,
     FetchSecondaryAccountInfoReturnsErrorForNotFoundAccounts) {
   base::HistogramTester tester;
@@ -774,7 +881,7 @@
       mojom::ArcAuthCodeStatus::CHROME_ACCOUNT_NOT_FOUND, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest, FetchGoogleAccountsFromArc) {
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest, FetchGoogleAccountsFromArc) {
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
 
   EXPECT_FALSE(arc_google_accounts_callback_called());
@@ -788,7 +895,7 @@
             arc_google_accounts()[0]->gaia_id);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest,
                        FetchGoogleAccountsFromArcWorksAcrossConnectionResets) {
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
 
@@ -811,7 +918,7 @@
             arc_google_accounts()[0]->gaia_id);
 }
 
-IN_PROC_BROWSER_TEST_F(
+IN_PROC_BROWSER_TEST_P(
     ArcAuthServiceTest,
     PrimaryAccountReauthIsNotAttemptedJustAfterProvisioning) {
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
@@ -822,11 +929,11 @@
   EXPECT_EQ(1, initial_num_calls);
 
   // Simulate ARC first time provisioning call.
-  auth_service().OnArcInitialStart();
+  OnArcInitialStart();
   EXPECT_EQ(initial_num_calls, auth_instance().num_account_upserted_calls());
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest,
                        UnAuthenticatedAccountsAreNotPropagated) {
   const AccountInfo account_info = SetupGaiaAccount(kSecondaryAccountEmail);
 
@@ -838,12 +945,13 @@
   EXPECT_EQ(initial_num_calls, auth_instance().num_account_upserted_calls());
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest, AccountUpdatesArePropagated) {
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest, AccountUpdatesArePropagated) {
   AccountInfo account_info = SetupGaiaAccount(kSecondaryAccountEmail);
 
   SetInvalidRefreshTokenForAccount(account_info.account_id);
   const int initial_num_calls = auth_instance().num_account_upserted_calls();
-  // 2 calls: 1 for the Primary Account and 1 for the Secondary Account.
+  // 2 calls: 1 for the Primary account, 1 for the Secondary account on addition
+  // and 0 for Secondary account after SetInvalidRefreshTokenForAccount.
   EXPECT_EQ(2, initial_num_calls);
 
   SetRefreshTokenForAccount(account_info.account_id);
@@ -853,7 +961,52 @@
   EXPECT_EQ(kSecondaryAccountEmail, auth_instance().last_upserted_account());
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest, AccountRemovalsArePropagated) {
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest,
+                       AccountUpdatesArePropagatedForActiveDirectoryUsers) {
+  SetAccountAndProfile(user_manager::USER_TYPE_ACTIVE_DIRECTORY);
+  // Add a Secondary Account.
+  AccountInfo account_info = SeedAccountInfo(kSecondaryAccountEmail);
+
+  SetInvalidRefreshTokenForAccount(account_info.account_id);
+  const int initial_num_calls = auth_instance().num_account_upserted_calls();
+  // 1 call for the Secondary account on addition and 0 for Secondary account
+  // after SetInvalidRefreshTokenForAccount.
+  EXPECT_EQ(1, initial_num_calls);
+
+  SetRefreshTokenForAccount(account_info.account_id);
+  // Expect exactly one call for the account update above.
+  EXPECT_EQ(1,
+            auth_instance().num_account_upserted_calls() - initial_num_calls);
+  EXPECT_EQ(kSecondaryAccountEmail, auth_instance().last_upserted_account());
+}
+
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest,
+                       AccountUpdatesAreNotPropagatedIfAccountIsNotAvailable) {
+  if (!IsArcAccountRestrictionsEnabled())
+    return;
+
+  AccountInfo account_info = SetupGaiaAccount(kSecondaryAccountEmail);
+
+  SetInvalidRefreshTokenForAccount(account_info.account_id);
+  const int initial_num_calls = auth_instance().num_account_upserted_calls();
+  // 2 calls: 1 for the Primary account, 1 for the Secondary account on addition
+  // and 0 for Secondary account after SetInvalidRefreshTokenForAccount.
+  EXPECT_EQ(2, initial_num_calls);
+
+  EXPECT_TRUE(SetIsAccountAvailableInArc(account_info.gaia,
+                                         /*make_available_in_arc=*/false));
+  // Wait for async calls to finish.
+  base::RunLoop().RunUntilIdle();
+  // Expect one call for the account update above.
+  EXPECT_EQ(1, auth_instance().num_account_removed_calls());
+
+  SetRefreshTokenForAccount(account_info.account_id);
+  // Expect zero calls for the account update above.
+  EXPECT_EQ(0,
+            auth_instance().num_account_upserted_calls() - initial_num_calls);
+}
+
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest, AccountRemovalsArePropagated) {
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
   SeedAccountInfo(kSecondaryAccountEmail);
 
@@ -870,14 +1023,48 @@
   // be sent.
   EnableRemovalOfExtendedAccountInfo();
 
-  signin::RemoveRefreshTokenForAccount(identity_manager,
-                                       account_info.account_id);
-  base::RunLoop().RunUntilIdle();
+  RemoveRefreshTokenForAccount(account_info.account_id);
 
   EXPECT_EQ(1, auth_instance().num_account_removed_calls());
   EXPECT_EQ(kSecondaryAccountEmail, auth_instance().last_removed_account());
 }
 
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest,
+                       AccountRemovalsAreNotPropagatedIfAccountIsNotAvailable) {
+  if (!IsArcAccountRestrictionsEnabled())
+    return;
+
+  SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
+  SeedAccountInfo(kSecondaryAccountEmail);
+
+  EXPECT_EQ(0, auth_instance().num_account_removed_calls());
+
+  signin::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForProfile(profile());
+  AccountInfo account_info =
+      identity_manager->FindExtendedAccountInfoByEmailAddress(
+          kSecondaryAccountEmail);
+  ASSERT_TRUE(!account_info.IsEmpty());
+
+  EXPECT_TRUE(SetIsAccountAvailableInArc(account_info.gaia,
+                                         /*make_available_in_arc=*/false));
+
+  // Wait for async calls to finish.
+  base::RunLoop().RunUntilIdle();
+  // Expect one call for the account update above.
+  EXPECT_EQ(1, auth_instance().num_account_removed_calls());
+  const int last_num_calls = auth_instance().num_account_removed_calls();
+
+  // Necessary to ensure that the OnExtendedAccountInfoRemoved() observer will
+  // be sent.
+  EnableRemovalOfExtendedAccountInfo();
+
+  RemoveRefreshTokenForAccount(account_info.account_id);
+
+  // Expect zero calls for the account removal above.
+  EXPECT_EQ(0, auth_instance().num_account_removed_calls() - last_num_calls);
+}
+
 class ArcRobotAccountAuthServiceTest : public ArcAuthServiceTest {
  public:
   ArcRobotAccountAuthServiceTest() = default;
@@ -889,6 +1076,8 @@
 
   ~ArcRobotAccountAuthServiceTest() override = default;
 
+  void SetUp() override { InProcessBrowserTest::SetUp(); }
+
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitchASCII(policy::switches::kDeviceManagementUrl,
                                     "http://localhost");
@@ -938,7 +1127,7 @@
 
 // Tests that when ARC requests account info for a demo session account,
 // Chrome supplies the info configured in SetAccountAndProfile() above.
-IN_PROC_BROWSER_TEST_F(ArcRobotAccountAuthServiceTest, GetDemoAccount) {
+IN_PROC_BROWSER_TEST_P(ArcRobotAccountAuthServiceTest, GetDemoAccount) {
   ash::DemoSession::SetDemoConfigForTesting(
       ash::DemoSession::DemoModeConfig::kOnline);
   ash::DemoSession::StartIfInDemoMode();
@@ -963,7 +1152,7 @@
   EXPECT_FALSE(auth_instance().account_info()->is_managed);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcRobotAccountAuthServiceTest, GetOfflineDemoAccount) {
+IN_PROC_BROWSER_TEST_P(ArcRobotAccountAuthServiceTest, GetOfflineDemoAccount) {
   ash::DemoSession::SetDemoConfigForTesting(
       ash::DemoSession::DemoModeConfig::kOffline);
   ash::DemoSession::StartIfInDemoMode();
@@ -983,7 +1172,7 @@
   EXPECT_TRUE(auth_instance().account_info()->is_managed);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcRobotAccountAuthServiceTest,
+IN_PROC_BROWSER_TEST_P(ArcRobotAccountAuthServiceTest,
                        GetDemoAccountOnAuthTokenFetchFailure) {
   ash::DemoSession::SetDemoConfigForTesting(
       ash::DemoSession::DemoModeConfig::kOnline);
@@ -1010,7 +1199,7 @@
   EXPECT_TRUE(auth_instance().account_info()->is_managed);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcRobotAccountAuthServiceTest,
+IN_PROC_BROWSER_TEST_P(ArcRobotAccountAuthServiceTest,
                        RequestPublicAccountInfo) {
   SetAccountAndProfile(user_manager::USER_TYPE_PUBLIC_ACCOUNT);
   profile()->GetProfilePolicyConnector()->OverrideIsManagedForTesting(true);
@@ -1036,7 +1225,7 @@
 
 // Tests that when ARC requests account info for a child account and
 // Chrome supplies the info configured in SetAccountAndProfile() above.
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest, ChildAccountFetch) {
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest, ChildAccountFetch) {
   SetAccountAndProfile(user_manager::USER_TYPE_CHILD);
   EXPECT_TRUE(profile()->IsChild());
   test_url_loader_factory()->AddResponse(arc::kAuthTokenExchangeEndPoint,
@@ -1056,7 +1245,7 @@
   EXPECT_FALSE(auth_instance().account_info()->is_managed);
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest, ChildTransition) {
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest, ChildTransition) {
   SetAccountAndProfile(user_manager::USER_TYPE_CHILD);
 
   ArcSessionManager* session = ArcSessionManager::Get();
@@ -1163,7 +1352,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest,
                        RegularUserSecondaryAccountsArePropagated) {
   SetAccountAndProfile(user_manager::USER_TYPE_REGULAR);
   SeedAccountInfo(kSecondaryAccountEmail);
@@ -1171,7 +1360,7 @@
 }
 
 // Tests child account propagation for Family Link user.
-IN_PROC_BROWSER_TEST_F(ArcAuthServiceTest,
+IN_PROC_BROWSER_TEST_P(ArcAuthServiceTest,
                        ChildUserSecondaryAccountsPropagation) {
   SetAccountAndProfile(user_manager::USER_TYPE_CHILD);
   SeedAccountInfo(kSecondaryAccountEmail);
@@ -1179,4 +1368,7 @@
   EXPECT_EQ(2, auth_instance().num_account_upserted_calls());
 }
 
+INSTANTIATE_TEST_SUITE_P(All, ArcRobotAccountAuthServiceTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All, ArcAuthServiceTest, testing::Bool());
+
 }  // namespace arc
diff --git a/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc b/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc
index ef54437..53b4a5b9 100644
--- a/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc
+++ b/chrome/browser/ash/arc/bluetooth/arc_bluetooth_bridge.cc
@@ -1830,7 +1830,7 @@
                          weak_factory_.GetWeakPtr(),
                          std::move(split_callback.first), char_id_str),
           base::BindOnce(&OnGattOperationError,
-                         std::move(split_callback.first)));
+                         std::move(split_callback.second)));
       return;
     }
     default:
diff --git a/chrome/browser/ash/cert_provisioning/cert_provisioning_serializer.cc b/chrome/browser/ash/cert_provisioning/cert_provisioning_serializer.cc
index 2710d27b..6eca163e 100644
--- a/chrome/browser/ash/cert_provisioning/cert_provisioning_serializer.cc
+++ b/chrome/browser/ash/cert_provisioning/cert_provisioning_serializer.cc
@@ -154,7 +154,7 @@
 void CertProvisioningSerializer::SerializeWorkerToPrefs(
     PrefService* pref_service,
     const CertProvisioningWorkerImpl& worker) {
-  DictionaryPrefUpdate scoped_dict_updater(
+  DictionaryPrefUpdateDeprecated scoped_dict_updater(
       pref_service, GetPrefNameForSerialization(worker.cert_scope_));
   base::Value* saved_workers = scoped_dict_updater.Get();
   DCHECK(saved_workers);
@@ -165,7 +165,7 @@
 void CertProvisioningSerializer::DeleteWorkerFromPrefs(
     PrefService* pref_service,
     const CertProvisioningWorkerImpl& worker) {
-  DictionaryPrefUpdate scoped_dict_updater(
+  DictionaryPrefUpdateDeprecated scoped_dict_updater(
       pref_service, GetPrefNameForSerialization(worker.cert_scope_));
 
   base::Value* saved_workers = scoped_dict_updater.Get();
diff --git a/chrome/browser/ash/child_accounts/edu_coexistence_tos_store_utils.cc b/chrome/browser/ash/child_accounts/edu_coexistence_tos_store_utils.cc
index 40d12c4..daa496d 100644
--- a/chrome/browser/ash/child_accounts/edu_coexistence_tos_store_utils.cc
+++ b/chrome/browser/ash/child_accounts/edu_coexistence_tos_store_utils.cc
@@ -43,8 +43,8 @@
 
 void UpdateAcceptedToSVersionPref(Profile* profile,
                                   const UserConsentInfo& user_consent_info) {
-  DictionaryPrefUpdate update(profile->GetPrefs(),
-                              prefs::kEduCoexistenceToSAcceptedVersion);
+  DictionaryPrefUpdateDeprecated update(
+      profile->GetPrefs(), prefs::kEduCoexistenceToSAcceptedVersion);
   base::DictionaryValue* dict = update.Get();
 
   dict->SetStringPath(user_consent_info.edu_account_gaia_id,
diff --git a/chrome/browser/ash/child_accounts/family_user_parental_control_metrics_unittest.cc b/chrome/browser/ash/child_accounts/family_user_parental_control_metrics_unittest.cc
index 6726df1..3dc6033 100644
--- a/chrome/browser/ash/child_accounts/family_user_parental_control_metrics_unittest.cc
+++ b/chrome/browser/ash/child_accounts/family_user_parental_control_metrics_unittest.cc
@@ -279,7 +279,8 @@
                                            base::Hours(1), base::Time::Now()));
 
     builder.SetResetTime(6, 0);
-    DictionaryPrefUpdate update(GetPrefs(), prefs::kPerAppTimeLimitsPolicy);
+    DictionaryPrefUpdateDeprecated update(GetPrefs(),
+                                          prefs::kPerAppTimeLimitsPolicy);
     base::Value* value = update.Get();
     *value = builder.value().Clone();
   }
@@ -382,8 +383,8 @@
 
   // Blocks `kExampleHost0`.
   {
-    DictionaryPrefUpdate hosts_update(GetPrefs(),
-                                      prefs::kSupervisedUserManualHosts);
+    DictionaryPrefUpdateDeprecated hosts_update(
+        GetPrefs(), prefs::kSupervisedUserManualHosts);
     base::DictionaryValue* hosts = hosts_update.Get();
     hosts->SetKey(kExampleHost0, base::Value(false));
   }
@@ -402,8 +403,8 @@
 
   // Approves `kExampleHost0`.
   {
-    DictionaryPrefUpdate hosts_update(GetPrefs(),
-                                      prefs::kSupervisedUserManualHosts);
+    DictionaryPrefUpdateDeprecated hosts_update(
+        GetPrefs(), prefs::kSupervisedUserManualHosts);
     base::DictionaryValue* hosts = hosts_update.Get();
     hosts->SetKey(kExampleHost0, base::Value(true));
   }
@@ -422,8 +423,8 @@
 
   // Blocks `kExampleURL1`.
   {
-    DictionaryPrefUpdate urls_update(GetPrefs(),
-                                     prefs::kSupervisedUserManualURLs);
+    DictionaryPrefUpdateDeprecated urls_update(
+        GetPrefs(), prefs::kSupervisedUserManualURLs);
     base::DictionaryValue* urls = urls_update.Get();
     urls->SetKey(kExampleURL1, base::Value(false));
   }
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_activity_registry.cc b/chrome/browser/ash/child_accounts/time_limits/app_activity_registry.cc
index 4011cc7..e7f27d40 100644
--- a/chrome/browser/ash/child_accounts/time_limits/app_activity_registry.cc
+++ b/chrome/browser/ash/child_accounts/time_limits/app_activity_registry.cc
@@ -627,7 +627,8 @@
 
 void AppActivityRegistry::SaveAppActivity() {
   {
-    ListPrefUpdate update(pref_service_, prefs::kPerAppTimeLimitsAppActivities);
+    ListPrefUpdateDeprecated update(pref_service_,
+                                    prefs::kPerAppTimeLimitsAppActivities);
     base::ListValue* list_value = update.Get();
 
     const base::Time now = base::Time::Now();
@@ -698,7 +699,8 @@
 }
 
 void AppActivityRegistry::CleanRegistry(base::Time timestamp) {
-  ListPrefUpdate update(pref_service_, prefs::kPerAppTimeLimitsAppActivities);
+  ListPrefUpdateDeprecated update(pref_service_,
+                                  prefs::kPerAppTimeLimitsAppActivities);
 
   base::ListValue* list_value = update.Get();
 
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_time_controller_unittest.cc b/chrome/browser/ash/child_accounts/time_limits/app_time_controller_unittest.cc
index 2c8adf4..6a68eb4 100644
--- a/chrome/browser/ash/child_accounts/time_limits/app_time_controller_unittest.cc
+++ b/chrome/browser/ash/child_accounts/time_limits/app_time_controller_unittest.cc
@@ -477,8 +477,8 @@
     builder.AddAppLimit(kApp2, AppLimit(AppRestriction::kTimeLimit,
                                         kOneHour / 2, base::Time::Now()));
     builder.SetResetTime(6, 0);
-    DictionaryPrefUpdate update(profile().GetPrefs(),
-                                prefs::kPerAppTimeLimitsPolicy);
+    DictionaryPrefUpdateDeprecated update(profile().GetPrefs(),
+                                          prefs::kPerAppTimeLimitsPolicy);
     base::Value* value = update.Get();
     *value = builder.value().Clone();
   }
@@ -561,8 +561,8 @@
     builder.AddAppLimit(absent_app, app_limit);
     builder.AddAppLimit(kApp2, blocked_app);
     builder.SetResetTime(6, 0);
-    DictionaryPrefUpdate update(profile().GetPrefs(),
-                                prefs::kPerAppTimeLimitsPolicy);
+    DictionaryPrefUpdateDeprecated update(profile().GetPrefs(),
+                                          prefs::kPerAppTimeLimitsPolicy);
     base::Value* value = update.Get();
     *value = builder.value().Clone();
   }
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
index 363f9eb..dcc8cae6 100644
--- a/chrome/browser/ash/chrome_browser_main_parts_ash.cc
+++ b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
@@ -66,7 +66,6 @@
 #include "chrome/browser/ash/crosapi/browser_manager.h"
 #include "chrome/browser/ash/crosapi/crosapi_manager.h"
 #include "chrome/browser/ash/crostini/crostini_unsupported_action_notifier.h"
-#include "chrome/browser/ash/crostini/crosvm_metrics.h"
 #include "chrome/browser/ash/dbus/ash_dbus_helper.h"
 #include "chrome/browser/ash/dbus/chrome_features_service_provider.h"
 #include "chrome/browser/ash/dbus/component_updater_service_provider.h"
@@ -1134,9 +1133,6 @@
   gnubby_notification_ = std::make_unique<GnubbyNotification>();
   demo_mode_resources_remover_ = DemoModeResourcesRemover::CreateIfNeeded(
       g_browser_process->local_state());
-  // Start measuring crosvm processes resource usage.
-  crosvm_metrics_ = std::make_unique<crostini::CrosvmMetrics>();
-  crosvm_metrics_->Start();
 
   login_screen_extensions_lifetime_manager_ =
       std::make_unique<LoginScreenExtensionsLifetimeManager>();
@@ -1516,10 +1512,6 @@
 }
 
 void ChromeBrowserMainPartsAsh::PostDestroyThreads() {
-  // Destroy crosvm_metrics_ after threads are stopped so that no weak_ptr is
-  // held by any task.
-  crosvm_metrics_.reset();
-
   network_change_manager_client_.reset();
   session_termination_manager_.reset();
 
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.h b/chrome/browser/ash/chrome_browser_main_parts_ash.h
index 6fef86d..adc5c51 100644
--- a/chrome/browser/ash/chrome_browser_main_parts_ash.h
+++ b/chrome/browser/ash/chrome_browser_main_parts_ash.h
@@ -48,7 +48,6 @@
 
 namespace crostini {
 class CrostiniUnsupportedActionNotifier;
-class CrosvmMetrics;
 }  // namespace crostini
 
 namespace lock_screen_apps {
@@ -211,7 +210,6 @@
       auto_screen_brightness_controller_;
 
   std::unique_ptr<DemoModeResourcesRemover> demo_mode_resources_remover_;
-  std::unique_ptr<crostini::CrosvmMetrics> crosvm_metrics_;
 
   std::unique_ptr<AshUsbDetector> ash_usb_detector_;
   std::unique_ptr<CrosUsbDetector> cros_usb_detector_;
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn
index 5621768..6b16754 100644
--- a/chrome/browser/ash/crosapi/BUILD.gn
+++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -180,6 +180,7 @@
     "//chromeos/crosapi/mojom",
     "//chromeos/cryptohome",
     "//chromeos/dbus",
+    "//chromeos/dbus/cros_disks",
     "//chromeos/dbus/power",
     "//chromeos/dbus/resourced",
     "//chromeos/dbus/session_manager",
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator.cc b/chrome/browser/ash/crosapi/browser_data_migrator.cc
index a6b0ad5..fe24d69 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator.cc
+++ b/chrome/browser/ash/crosapi/browser_data_migrator.cc
@@ -831,7 +831,8 @@
     const std::string& user_id_hash) {
   int count = GetMigrationAttemptCountForUser(local_state, user_id_hash);
   count += 1;
-  DictionaryPrefUpdate update(local_state, kMigrationAttemptCountPref);
+  DictionaryPrefUpdateDeprecated update(local_state,
+                                        kMigrationAttemptCountPref);
   base::DictionaryValue* dict = update.Get();
   dict->SetKey(user_id_hash, base::Value(count));
 }
@@ -849,7 +850,8 @@
 void BrowserDataMigratorImpl::ClearMigrationAttemptCountForUser(
     PrefService* local_state,
     const std::string& user_id_hash) {
-  DictionaryPrefUpdate update(local_state, kMigrationAttemptCountPref);
+  DictionaryPrefUpdateDeprecated update(local_state,
+                                        kMigrationAttemptCountPref);
   base::DictionaryValue* dict = update.Get();
   dict->RemoveKey(user_id_hash);
 }
diff --git a/chrome/browser/ash/crosapi/browser_util.cc b/chrome/browser/ash/crosapi/browser_util.cc
index 375edb8..06371a347 100644
--- a/chrome/browser/ash/crosapi/browser_util.cc
+++ b/chrome/browser/ash/crosapi/browser_util.cc
@@ -239,10 +239,6 @@
 const base::Feature kLacrosGooglePolicyRollout{
     "LacrosGooglePolicyRollout", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Emergency switch to turn off profile migration via Finch.
-const base::Feature kLacrosProfileMigrationForceOff{
-    "LacrosProfileMigrationForceOff", base::FEATURE_DISABLED_BY_DEFAULT};
-
 const Channel kLacrosDefaultChannel = Channel::DEV;
 
 const char kLacrosStabilitySwitch[] = "lacros-stability";
@@ -355,9 +351,8 @@
 bool IsProfileMigrationEnabled(const AccountId& account_id) {
   // Emergency switch to turn off profile migration. Turn this on via Finch in
   // case profile migration needs to be turned off after launch.
-  if (base::FeatureList::IsEnabled(kLacrosProfileMigrationForceOff)) {
-    LOG(WARNING)
-        << "Profile migration is turned off by kLacrosProfileMigrationForceOff";
+  if (base::FeatureList::IsEnabled(
+          ash::features::kLacrosProfileMigrationForceOff)) {
     return false;
   }
 
@@ -567,7 +562,7 @@
                    const std::string& user_id_hash,
                    const base::Version& version) {
   DCHECK(version.IsValid());
-  DictionaryPrefUpdate update(local_state, kDataVerPref);
+  DictionaryPrefUpdateDeprecated update(local_state, kDataVerPref);
   base::DictionaryValue* dict = update.Get();
   dict->SetString(user_id_hash, version.GetString());
 }
@@ -734,16 +729,16 @@
 
 void SetProfileMigrationCompletedForUser(PrefService* local_state,
                                          const std::string& user_id_hash) {
-  DictionaryPrefUpdate update(local_state,
-                              kProfileMigrationCompletedForUserPref);
+  DictionaryPrefUpdateDeprecated update(local_state,
+                                        kProfileMigrationCompletedForUserPref);
   base::DictionaryValue* dict = update.Get();
   dict->SetBoolKey(user_id_hash, true);
 }
 
 void ClearProfileMigrationCompletedForUser(PrefService* local_state,
                                            const std::string& user_id_hash) {
-  DictionaryPrefUpdate update(local_state,
-                              kProfileMigrationCompletedForUserPref);
+  DictionaryPrefUpdateDeprecated update(local_state,
+                                        kProfileMigrationCompletedForUserPref);
   base::DictionaryValue* dict = update.Get();
   dict->RemoveKey(user_id_hash);
 }
diff --git a/chrome/browser/ash/crosapi/environment_provider.cc b/chrome/browser/ash/crosapi/environment_provider.cc
index 56c606f..ef3c7c6 100644
--- a/chrome/browser/ash/crosapi/environment_provider.cc
+++ b/chrome/browser/ash/crosapi/environment_provider.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profiles_state.h"
 #include "chromeos/crosapi/mojom/crosapi.mojom.h"
+#include "chromeos/dbus/cros_disks/cros_disks_client.h"
 #include "chromeos/tpm/install_attributes.h"
 #include "components/account_id/account_id.h"
 #include "components/account_manager_core/account.h"
@@ -91,6 +92,10 @@
         integration_service->IsMounted()) {
       default_paths->drivefs = integration_service->GetMountPointPath();
     }
+    default_paths->android_files =
+        base::FilePath(file_manager::util::kAndroidFilesPath);
+    default_paths->linux_files =
+        file_manager::util::GetCrostiniMountDirectory(profile);
   } else {
     // On developer linux workstations the above functions do path mangling to
     // support multi-signin which gets undone later in ash-specific code. This
@@ -99,8 +104,15 @@
     default_paths->documents = home.Append("Documents");
     default_paths->downloads = home.Append("Downloads");
     default_paths->drivefs = home.Append("Drive");
+    default_paths->android_files = home.Append("Android");
+    default_paths->linux_files = home.Append("Crostini");
   }
 
+  // CrosDisksClient already has a convention for its removable media directory
+  // when running on Linux workstations.
+  default_paths->removable_media =
+      chromeos::CrosDisksClient::GetRemovableDiskMountPoint();
+
   return default_paths;
 }
 
diff --git a/chrome/browser/ash/crostini/crostini_port_forwarder.cc b/chrome/browser/ash/crostini/crostini_port_forwarder.cc
index 6e2c425..dc277c1 100644
--- a/chrome/browser/ash/crostini/crostini_port_forwarder.cc
+++ b/chrome/browser/ash/crostini/crostini_port_forwarder.cc
@@ -100,7 +100,8 @@
 void CrostiniPortForwarder::AddNewPortPreference(const PortRuleKey& key,
                                                  const std::string& label) {
   PrefService* pref_service = profile_->GetPrefs();
-  ListPrefUpdate update(pref_service, crostini::prefs::kCrostiniPortForwarding);
+  ListPrefUpdateDeprecated update(pref_service,
+                                  crostini::prefs::kCrostiniPortForwarding);
   base::ListValue* all_ports = update.Get();
   base::Value new_port_metadata(base::Value::Type::DICTIONARY);
   new_port_metadata.SetIntKey(kPortNumberKey, key.port_number);
@@ -115,7 +116,8 @@
 
 bool CrostiniPortForwarder::RemovePortPreference(const PortRuleKey& key) {
   PrefService* pref_service = profile_->GetPrefs();
-  ListPrefUpdate update(pref_service, crostini::prefs::kCrostiniPortForwarding);
+  ListPrefUpdateDeprecated update(pref_service,
+                                  crostini::prefs::kCrostiniPortForwarding);
   base::ListValue* all_ports = update.Get();
   base::Value::ListView list_view = all_ports->GetList();
   auto it = std::find_if(
@@ -360,7 +362,8 @@
 
 void CrostiniPortForwarder::RemoveAllPorts(const ContainerId& container_id) {
   PrefService* pref_service = profile_->GetPrefs();
-  ListPrefUpdate update(pref_service, crostini::prefs::kCrostiniPortForwarding);
+  ListPrefUpdateDeprecated update(pref_service,
+                                  crostini::prefs::kCrostiniPortForwarding);
   update->EraseListValueIf([&container_id, this](const auto& dict) {
     return MatchPortRuleContainerId(dict, container_id);
   });
diff --git a/chrome/browser/ash/crostini/crostini_util.cc b/chrome/browser/ash/crostini/crostini_util.cc
index e26f1d8..626989339 100644
--- a/chrome/browser/ash/crostini/crostini_util.cc
+++ b/chrome/browser/ash/crostini/crostini_util.cc
@@ -460,7 +460,8 @@
                           static_cast<int>(ContainerOsVersion::kUnknown));
   new_container.SetStringKey(prefs::kContainerOsPrettyNameKey, "");
 
-  ListPrefUpdate updater(pref_service, crostini::prefs::kCrostiniContainers);
+  ListPrefUpdateDeprecated updater(pref_service,
+                                   crostini::prefs::kCrostiniContainers);
   updater->Append(std::move(new_container));
 }
 
@@ -479,7 +480,8 @@
 void RemoveLxdContainerFromPrefs(Profile* profile,
                                  const ContainerId& container_id) {
   auto* pref_service = profile->GetPrefs();
-  ListPrefUpdate updater(pref_service, crostini::prefs::kCrostiniContainers);
+  ListPrefUpdateDeprecated updater(pref_service,
+                                   crostini::prefs::kCrostiniContainers);
   updater->EraseListIter(
       std::find_if(updater->GetList().begin(), updater->GetList().end(),
                    [&](const auto& dict) {
@@ -513,8 +515,8 @@
                          const ContainerId& container_id,
                          const std::string& key,
                          base::Value value) {
-  ListPrefUpdate updater(profile->GetPrefs(),
-                         crostini::prefs::kCrostiniContainers);
+  ListPrefUpdateDeprecated updater(profile->GetPrefs(),
+                                   crostini::prefs::kCrostiniContainers);
   auto it = std::find_if(
       updater->GetList().begin(), updater->GetList().end(),
       [&](const auto& dict) { return MatchContainerDict(dict, container_id); });
diff --git a/chrome/browser/ash/crostini/crosvm_metrics.cc b/chrome/browser/ash/crostini/crosvm_metrics.cc
deleted file mode 100644
index 184d1ec..0000000
--- a/chrome/browser/ash/crostini/crosvm_metrics.cc
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2018 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/ash/crostini/crosvm_metrics.h"
-
-#include <unistd.h>
-
-#include <cmath>
-
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/task/post_task.h"
-#include "base/task/thread_pool.h"
-#include "chrome/browser/ash/crostini/crosvm_process_list.h"
-
-namespace crostini {
-
-namespace {
-
-constexpr char kCrosvmProcessesHistogram[] = "Crostini.Crosvm.Processes.Count";
-constexpr char kCrosvmCpuPercentageHistogram[] =
-    "Crostini.Crosvm.CpuPercentage";
-constexpr char kCrosvmRssPercentageHistogram[] =
-    "Crostini.Crosvm.RssPercentage";
-
-constexpr base::TimeDelta kCrosvmMetricsInterval = base::Minutes(10);
-
-}  // namespace
-
-CrosvmMetrics::CrosvmMetrics()
-    : task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
-          {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
-      page_size_(sysconf(_SC_PAGESIZE)) {}
-
-CrosvmMetrics::~CrosvmMetrics() = default;
-
-void CrosvmMetrics::Start() {
-  task_runner_->PostNonNestableTask(
-      FROM_HERE, base::BindOnce(&CrosvmMetrics::CollectCycleStartData,
-                                weak_ptr_factory_.GetWeakPtr()));
-  timer_.Start(FROM_HERE, kCrosvmMetricsInterval, this,
-               &CrosvmMetrics::MetricsCycleCallback);
-}
-
-void CrosvmMetrics::CollectCycleStartData() {
-  previous_pid_stat_map_ = GetCrosvmPidStatMap();
-  absl::optional<int64_t> total_cpu_time = ash::system::GetCpuTimeJiffies();
-  if (!total_cpu_time.has_value()) {
-    cycle_start_data_collected_ = false;
-    return;
-  }
-  previous_total_cpu_time_ = total_cpu_time.value();
-  cycle_start_data_collected_ = true;
-}
-
-// static
-int CrosvmMetrics::CalculateCrosvmRssPercentage(const PidStatMap& pid_stat_map,
-                                                int64_t mem_used,
-                                                int64_t page_size) {
-  int64_t total_rss = 0;
-  for (const auto& pair : pid_stat_map) {
-    total_rss += pair.second.rss;
-  }
-  return std::lround(static_cast<double>(total_rss) /
-                     (mem_used * 1024 / page_size) * 100);
-}
-
-// static
-int CrosvmMetrics::CalculateCrosvmCpuPercentage(
-    const PidStatMap& pid_stat_map,
-    const PidStatMap& previous_pid_stat_map,
-    int64_t cycle_cpu_time) {
-  int64_t total_cpu_time = 0;
-  for (const auto& pair : pid_stat_map) {
-    auto it = previous_pid_stat_map.find(pair.first);
-    if (it == previous_pid_stat_map.end()) {
-      total_cpu_time += pair.second.utime + pair.second.stime;
-    } else {
-      total_cpu_time += (pair.second.utime + pair.second.stime) -
-                        (it->second.utime + it->second.stime);
-    }
-  }
-  return std::lround(static_cast<double>(total_cpu_time) / cycle_cpu_time *
-                     100);
-}
-
-void CrosvmMetrics::MetricsCycleCallback() {
-  task_runner_->PostNonNestableTask(
-      FROM_HERE, base::BindOnce(&CrosvmMetrics::MetricsCycle,
-                                weak_ptr_factory_.GetWeakPtr()));
-}
-
-void CrosvmMetrics::MetricsCycle() {
-  if (!cycle_start_data_collected_) {
-    CollectCycleStartData();
-    return;
-  }
-
-  PidStatMap pid_stat_map = GetCrosvmPidStatMap();
-  absl::optional<int64_t> total_cpu_time = ash::system::GetCpuTimeJiffies();
-  if (!total_cpu_time.has_value()) {
-    cycle_start_data_collected_ = false;
-    return;
-  }
-  int64_t cycle_cpu_time = total_cpu_time.value() - previous_total_cpu_time_;
-  absl::optional<int64_t> mem_used = ash::system::GetUsedMemTotalKB();
-  if (!mem_used.has_value()) {
-    cycle_start_data_collected_ = false;
-    return;
-  }
-
-  if (pid_stat_map.empty()) {
-    previous_pid_stat_map_ = pid_stat_map;
-    previous_total_cpu_time_ = total_cpu_time.value();
-    return;
-  }
-
-  int rss_percentage =
-      CalculateCrosvmRssPercentage(pid_stat_map, mem_used.value(), page_size_);
-  int cpu_percentage = CalculateCrosvmCpuPercentage(
-      pid_stat_map, previous_pid_stat_map_, cycle_cpu_time);
-  UMA_HISTOGRAM_COUNTS_100(kCrosvmProcessesHistogram, pid_stat_map.size());
-  UMA_HISTOGRAM_PERCENTAGE(kCrosvmCpuPercentageHistogram, cpu_percentage);
-  UMA_HISTOGRAM_PERCENTAGE(kCrosvmRssPercentageHistogram, rss_percentage);
-  previous_pid_stat_map_ = pid_stat_map;
-  previous_total_cpu_time_ = total_cpu_time.value();
-}
-
-}  // namespace crostini
diff --git a/chrome/browser/ash/crostini/crosvm_metrics.h b/chrome/browser/ash/crostini/crosvm_metrics.h
deleted file mode 100644
index 8f1e8d46..0000000
--- a/chrome/browser/ash/crostini/crosvm_metrics.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2018 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_ASH_CROSTINI_CROSVM_METRICS_H_
-#define CHROME_BROWSER_ASH_CROSTINI_CROSVM_METRICS_H_
-
-#include <set>
-#include <unordered_map>
-
-#include "base/files/file_path.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/timer/timer.h"
-#include "chrome/browser/ash/system/procfs_util.h"
-
-namespace crostini {
-
-// Measure and report crosvm host process CPU and memory usage metrics.
-class CrosvmMetrics {
- public:
-  CrosvmMetrics();
-
-  CrosvmMetrics(const CrosvmMetrics&) = delete;
-  CrosvmMetrics& operator=(const CrosvmMetrics&) = delete;
-
-  ~CrosvmMetrics();
-
-  // Start taking snapshot of crosvm process resource usage.
-  void Start();
-
-  using PidStatMap = std::unordered_map<pid_t, ash::system::SingleProcStat>;
-
-  // Returns Crosvm process resident memory percentage of the total used memory.
-  // This is exposed only for testing.
-  static int CalculateCrosvmRssPercentage(const PidStatMap& pid_stat_map,
-                                          int64_t mem_used,
-                                          int64_t page_size);
-  // Returns Crosvm process CPU usage percentage of the system CPU usage.
-  // This is exposed only for testing.
-  static int CalculateCrosvmCpuPercentage(
-      const PidStatMap& pid_stat_map,
-      const PidStatMap& previous_pid_stat_map,
-      int64_t cycle_cpu_time);
-
- private:
-  // Callback to post MetricsCycle() to a blocking thread.
-  void MetricsCycleCallback();
-
-  // Measures resource metrics and emit UMA histograms.
-  void MetricsCycle();
-
-  // Collect the crosvm process data and system CPU usage data at the start of
-  // the 10-minute cycle.
-  void CollectCycleStartData();
-
-  // The timer is used to call MetricsCycleCallback() to periodically measure
-  // crosvm processes CPU and memory usage and send them as UMA histograms.
-  base::RepeatingTimer timer_;
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-  bool cycle_start_data_collected_ = false;
-  int64_t previous_total_cpu_time_ = 0;
-  PidStatMap previous_pid_stat_map_;
-  int64_t page_size_;  // In bytes.
-
-  base::FilePath slash_proc_ = base::FilePath("/proc");
-
-  base::WeakPtrFactory<CrosvmMetrics> weak_ptr_factory_{this};
-};
-
-}  // namespace crostini
-
-#endif  // CHROME_BROWSER_ASH_CROSTINI_CROSVM_METRICS_H_
diff --git a/chrome/browser/ash/crostini/crosvm_metrics_unittest.cc b/chrome/browser/ash/crostini/crosvm_metrics_unittest.cc
deleted file mode 100644
index ae791f5c..0000000
--- a/chrome/browser/ash/crostini/crosvm_metrics_unittest.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2018 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 "testing/gtest/include/gtest/gtest.h"
-
-#include "chrome/browser/ash/crostini/crosvm_metrics.h"
-
-namespace crostini {
-
-TEST(CrosvmMetricsTest, CalculateCrosvmRssPercentageSuccess) {
-  CrosvmMetrics::PidStatMap pid_proc_map = {
-      {1111, {.pid = 1111, .utime = 1, .stime = 1, .rss = 20}}};
-  int64_t mem_used = 1000;
-  int64_t page_size = 512;
-  int expected = 1;
-  EXPECT_EQ(expected, CrosvmMetrics::CalculateCrosvmRssPercentage(
-                          pid_proc_map, mem_used, page_size));
-}
-
-TEST(CrosvmMetricsTest, CalculateCrosvmCpuPercentageSuccess) {
-  CrosvmMetrics::PidStatMap previous_pid_stat_map = {
-      {1111, {.pid = 1111, .utime = 1, .stime = 0, .rss = 20}}};
-  CrosvmMetrics::PidStatMap pid_stat_map = {
-      {1111, {.pid = 1111, .utime = 6, .stime = 4, .rss = 20}}};
-  int64_t cycle_cpu_time = 1000;
-  int expected = 1;
-  EXPECT_EQ(expected, CrosvmMetrics::CalculateCrosvmCpuPercentage(
-                          pid_stat_map, previous_pid_stat_map, cycle_cpu_time));
-}
-
-}  // namespace crostini
diff --git a/chrome/browser/ash/crostini/crosvm_process_list.cc b/chrome/browser/ash/crostini/crosvm_process_list.cc
deleted file mode 100644
index bea9a6d..0000000
--- a/chrome/browser/ash/crostini/crosvm_process_list.cc
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2018 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/ash/crostini/crosvm_process_list.h"
-
-#include <algorithm>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/files/file_enumerator.h"
-#include "base/files/file_util.h"
-#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-
-namespace crostini {
-
-namespace {
-
-// Reads a proc stat file whose pid is |pid|;
-// inserts into |pid_stat_map| that maps from pid to its proc stat;
-// inserts into |ppid_pids| the maps from its parent pid to its pid;
-// assigns pid to |vm_concierge_pid| if this process is vm_concierge.
-// |slash_proc| is "/proc" for production and is only changed for tests.
-void ProcessProcStatFile(pid_t pid,
-                         PidStatMap* pid_stat_map,
-                         std::unordered_map<pid_t, std::set<pid_t>>* ppid_pids,
-                         pid_t* vm_concierge_pid_out,
-                         const base::FilePath& slash_proc) {
-  base::FilePath file_path =
-      slash_proc.Append(base::NumberToString(pid)).Append("stat");
-  absl::optional<ash::system::SingleProcStat> stat =
-      ash::system::GetSingleProcStat(file_path);
-  if (!stat.has_value())
-    return;
-
-  pid_stat_map->emplace(pid, stat.value());
-  if (stat.value().name == "vm_concierge") {
-    if (*vm_concierge_pid_out != -1) {
-      LOG(ERROR) << "More than one vm_concierge process found: "
-                 << *vm_concierge_pid_out << " and " << pid << ".";
-      *vm_concierge_pid_out = -1;
-      return;
-    }
-    *vm_concierge_pid_out = pid;
-  }
-  auto it = ppid_pids->find(stat.value().ppid);
-  if (it == ppid_pids->end()) {
-    std::set<pid_t> pids({pid});
-    ppid_pids->emplace(stat.value().ppid, std::move(pids));
-  } else {
-    it->second.emplace(pid);
-  }
-}
-
-// Recursively insert |pid| and its children according to |ppid_pids| into
-// |crosvm_pids|.
-void InsertPid(pid_t pid,
-               const std::unordered_map<pid_t, std::set<pid_t>>& ppid_pids,
-               std::set<pid_t>* crosvm_pids) {
-  DCHECK(crosvm_pids);
-  crosvm_pids->insert(pid);
-  auto it = ppid_pids.find(pid);
-  if (it == ppid_pids.end())
-    return;
-  for (pid_t cpid : it->second) {
-    if (crosvm_pids->find(cpid) != crosvm_pids->end())
-      continue;
-    InsertPid(cpid, ppid_pids, crosvm_pids);
-  }
-}
-
-}  // namespace
-
-PidStatMap GetCrosvmPidStatMap(const base::FilePath& slash_proc) {
-  PidStatMap crosvm_process_map;
-  PidStatMap all_process_map;
-  pid_t vm_concierge_pid = -1;
-  std::unordered_map<pid_t, std::set<pid_t>> ppid_pids;
-
-  base::FileEnumerator slash_proc_file_enum(slash_proc, false /* recursive */,
-                                            base::FileEnumerator::DIRECTORIES);
-  for (base::FilePath name = slash_proc_file_enum.Next(); !name.empty();
-       name = slash_proc_file_enum.Next()) {
-    std::string pid_str = name.BaseName().value();
-    pid_t pid;
-    if (!base::StringToInt(pid_str, &pid)) {
-      continue;
-    }
-    ProcessProcStatFile(pid, &all_process_map, &ppid_pids, &vm_concierge_pid,
-                        slash_proc);
-  }
-
-  if (vm_concierge_pid == -1 || all_process_map.empty())
-    return crosvm_process_map;
-
-  std::set<pid_t> crosvm_pids;
-  InsertPid(vm_concierge_pid, ppid_pids, &crosvm_pids);
-
-  for (pid_t pid : crosvm_pids)
-    crosvm_process_map.emplace(pid, all_process_map.at(pid));
-  return crosvm_process_map;
-}
-
-}  // namespace crostini
diff --git a/chrome/browser/ash/crostini/crosvm_process_list.h b/chrome/browser/ash/crostini/crosvm_process_list.h
deleted file mode 100644
index 28c3174..0000000
--- a/chrome/browser/ash/crostini/crosvm_process_list.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2018 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_ASH_CROSTINI_CROSVM_PROCESS_LIST_H_
-#define CHROME_BROWSER_ASH_CROSTINI_CROSVM_PROCESS_LIST_H_
-
-#include <set>
-#include <unordered_map>
-
-#include "base/files/file_path.h"
-#include "chrome/browser/ash/system/procfs_util.h"
-
-namespace crostini {
-
-using PidStatMap = std::unordered_map<pid_t, ash::system::SingleProcStat>;
-
-// Returns a map from crosvm PIDs to their stat map.
-// |slash_proc| is "/proc" for production and is only changed for tests.
-PidStatMap GetCrosvmPidStatMap(
-    const base::FilePath& slash_proc = base::FilePath("/proc"));
-
-}  // namespace crostini
-
-#endif  // CHROME_BROWSER_ASH_CROSTINI_CROSVM_PROCESS_LIST_H_
diff --git a/chrome/browser/ash/crostini/crosvm_process_list_unittest.cc b/chrome/browser/ash/crostini/crosvm_process_list_unittest.cc
deleted file mode 100644
index cc0e5d7..0000000
--- a/chrome/browser/ash/crostini/crosvm_process_list_unittest.cc
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright 2018 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/ash/crostini/crosvm_process_list.h"
-
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/strings/string_number_conversions.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace crostini {
-
-class CrosvmProcessListTest : public testing::Test {
- public:
-  CrosvmProcessListTest() {
-    CHECK(temp_dir_.CreateUniqueTempDir());
-    slash_proc_ = temp_dir_.GetPath().Append("proc");
-    CHECK(base::CreateDirectory(slash_proc_));
-  }
-
-  CrosvmProcessListTest(const CrosvmProcessListTest&) = delete;
-  CrosvmProcessListTest& operator=(const CrosvmProcessListTest&) = delete;
-
-  ~CrosvmProcessListTest() override = default;
-
-  // Create a directory |dir_name| under /proc and write |contents| to file
-  // |file_name| under this directory.
-  void WriteContentsToFileUnderSubdir(const std::string& contents,
-                                      const std::string& dir_name,
-                                      const std::string& file_name) {
-    base::FilePath dir = slash_proc_.Append(dir_name);
-    CHECK(base::CreateDirectory(dir));
-    base::FilePath file = dir.Append(file_name);
-    EXPECT_EQ(static_cast<int>(contents.size()),
-              base::WriteFile(file, contents.c_str(), contents.size()));
-  }
-
- protected:
-  base::FilePath slash_proc_;
-
- private:
-  base::ScopedTempDir temp_dir_;
-};
-
-TEST_F(CrosvmProcessListTest, ConciergeIsTheOnlyCrosvmProcess) {
-  pid_t concierge_pid = 7404;
-  std::string stat_contents =
-      "7404 (vm_concierge) S 1 7403 7403 0 -1 4210944 854 0 0 0 1489 1333 0 0 "
-      "20 0 4 0 67733375 271028224 3149 18446744073709551615 96193202642944 "
-      "96193203221904 140726650459504 140726650457840 134557843346867 0 81920 "
-      "0 0 0 0 0 17 2 0 0 0 0 0 96193203229904 96193203257392 96193216815104 "
-      "140726650461974 140726650461996 140726650461996 140726650462178 0";
-  PidStatMap expected = {{concierge_pid,
-                          {.pid = concierge_pid,
-                           .name = "vm_concierge",
-                           .ppid = 1,
-                           .utime = 1489,
-                           .stime = 1333,
-                           .rss = 3149}}};
-  WriteContentsToFileUnderSubdir(stat_contents,
-                                 base::NumberToString(concierge_pid), "stat");
-  EXPECT_EQ(expected, GetCrosvmPidStatMap(slash_proc_));
-}
-
-TEST_F(CrosvmProcessListTest, ConciergeNotRunning) {
-  EXPECT_TRUE(GetCrosvmPidStatMap(slash_proc_).empty());
-}
-
-TEST_F(CrosvmProcessListTest, SkipOtherDirAndFile) {
-  base::FilePath other_dir = slash_proc_.Append("other_dir");
-  CHECK(base::CreateDirectory(other_dir));
-
-  WriteContentsToFileUnderSubdir("other contents", "other_dir", "other_file");
-  EXPECT_TRUE(GetCrosvmPidStatMap(slash_proc_).empty());
-}
-
-TEST_F(CrosvmProcessListTest, SkipOtherProcess) {
-  pid_t concierge_pid = 7404;
-  std::string stat_contents =
-      "7404 (vm_concierge) S 1 7403 7403 0 -1 4210944 854 0 0 0 1489 1333 0 0 "
-      "20 0 4 0 67733375 271028224 3149 18446744073709551615 96193202642944 "
-      "96193203221904 140726650459504 140726650457840 134557843346867 0 81920 "
-      "0 0 0 0 0 17 2 0 0 0 0 0 96193203229904 96193203257392 96193216815104 "
-      "140726650461974 140726650461996 140726650461996 140726650462178 0";
-  WriteContentsToFileUnderSubdir(stat_contents,
-                                 base::NumberToString(concierge_pid), "stat");
-
-  pid_t other_pid = 1111;
-  std::string other_stat_contents =
-      "1111 (other) S 1 7403 7403 0 -1 4210944 854 0 0 0 1489 1333 0 0 "
-      "20 0 4 0 67733375 271028224 3149 18446744073709551615 96193202642944 "
-      "96193203221904 140726650459504 140726650457840 134557843346867 0 81920 "
-      "0 0 0 0 0 17 2 0 0 0 0 0 96193203229904 96193203257392 96193216815104 "
-      "140726650461974 140726650461996 140726650461996 140726650462178 0";
-  WriteContentsToFileUnderSubdir(other_stat_contents,
-                                 base::NumberToString(other_pid), "stat");
-
-  PidStatMap expected = {{concierge_pid,
-                          {.pid = concierge_pid,
-                           .name = "vm_concierge",
-                           .ppid = 1,
-                           .utime = 1489,
-                           .stime = 1333,
-                           .rss = 3149}}};
-  EXPECT_EQ(expected, GetCrosvmPidStatMap(slash_proc_));
-}
-
-TEST_F(CrosvmProcessListTest, ChildrenAreIncluded) {
-  pid_t concierge_pid = 2222;
-  std::string stat_contents =
-      "2222 (vm_concierge) S 1 7403 7403 0 -1 4210944 854 0 0 0 1489 1333 0 0 "
-      "20 0 4 0 67733375 271028224 3149 18446744073709551615 96193202642944 "
-      "96193203221904 140726650459504 140726650457840 134557843346867 0 81920 "
-      "0 0 0 0 0 17 2 0 0 0 0 0 96193203229904 96193203257392 96193216815104 "
-      "140726650461974 140726650461996 140726650461996 140726650462178 0";
-  WriteContentsToFileUnderSubdir(stat_contents,
-                                 base::NumberToString(concierge_pid), "stat");
-
-  pid_t child_pid = 3333;
-  std::string child_stat_contents =
-      "3333 (child) S 2222 7403 7403 0 -1 4210944 854 0 0 0 1489 1333 0 0 "
-      "20 0 4 0 67733375 271028224 3149 18446744073709551615 96193202642944 "
-      "96193203221904 140726650459504 140726650457840 134557843346867 0 81920 "
-      "0 0 0 0 0 17 2 0 0 0 0 0 96193203229904 96193203257392 96193216815104 "
-      "140726650461974 140726650461996 140726650461996 140726650462178 0";
-  WriteContentsToFileUnderSubdir(child_stat_contents,
-                                 base::NumberToString(child_pid), "stat");
-
-  pid_t grand_child_pid = 4444;
-  std::string grand_child_stat_contents =
-      "4444 (grand_child) S 3333 7403 7403 0 -1 4210944 854 0 0 0 1489 1333 0 "
-      "0 "
-      "20 0 4 0 67733375 271028224 3149 18446744073709551615 96193202642944 "
-      "96193203221904 140726650459504 140726650457840 134557843346867 0 81920 "
-      "0 0 0 0 0 17 2 0 0 0 0 0 96193203229904 96193203257392 96193216815104 "
-      "140726650461974 140726650461996 140726650461996 140726650462178 0";
-  WriteContentsToFileUnderSubdir(grand_child_stat_contents,
-                                 base::NumberToString(grand_child_pid), "stat");
-
-  PidStatMap expected = {{concierge_pid,
-                          {.pid = concierge_pid,
-                           .name = "vm_concierge",
-                           .ppid = 1,
-                           .utime = 1489,
-                           .stime = 1333,
-                           .rss = 3149}},
-                         {child_pid,
-                          {.pid = 3333,
-                           .name = "child",
-                           .ppid = concierge_pid,
-                           .utime = 1489,
-                           .stime = 1333,
-                           .rss = 3149}},
-                         {grand_child_pid,
-                          {.pid = 4444,
-                           .name = "grand_child",
-                           .ppid = child_pid,
-                           .utime = 1489,
-                           .stime = 1333,
-                           .rss = 3149}}};
-  EXPECT_EQ(expected, GetCrosvmPidStatMap(slash_proc_));
-}
-}  // namespace crostini
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest.cc b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
index fd8b23f0..1b36b58 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
@@ -400,15 +400,20 @@
     ZipFiles, /* zip_files.js */
     FilesAppBrowserTest,
     ::testing::Values(
-        TestCase("zipFileOpenDownloads").InGuestMode(),
         TestCase("zipFileOpenDownloads"),
+        TestCase("zipFileOpenDownloads").FilesSwa(),
+        TestCase("zipFileOpenDownloads").InGuestMode(),
+        TestCase("zipFileOpenDownloads").InGuestMode().FilesSwa(),
         TestCase("zipFileOpenDrive"),
+        TestCase("zipFileOpenDrive").FilesSwa(),
         TestCase("zipFileOpenUsb"),
+        TestCase("zipFileOpenUsb").FilesSwa(),
         TestCase("zipNotifyFileTasks"),
-        TestCase("zipCreateFileDownloads").InGuestMode(),
+        TestCase("zipNotifyFileTasks").FilesSwa(),
         TestCase("zipCreateFileDownloads"),
-        TestCase("zipCreateFileDownloads").InGuestMode().FilesSwa(),
         TestCase("zipCreateFileDownloads").FilesSwa(),
+        TestCase("zipCreateFileDownloads").InGuestMode(),
+        TestCase("zipCreateFileDownloads").InGuestMode().FilesSwa(),
         TestCase("zipCreateFileDrive"),
         TestCase("zipCreateFileDrive").FilesSwa(),
         TestCase("zipCreateFileUsb"),
@@ -417,12 +422,19 @@
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     CreateNewFolder, /* create_new_folder.js */
     FilesAppBrowserTest,
-    ::testing::Values(TestCase("selectCreateFolderDownloads").InGuestMode(),
-                      TestCase("selectCreateFolderDownloads"),
-                      TestCase("createFolderDownloads").InGuestMode(),
-                      TestCase("createFolderDownloads"),
-                      TestCase("createFolderNestedDownloads"),
-                      TestCase("createFolderDrive")));
+    ::testing::Values(
+        TestCase("selectCreateFolderDownloads"),
+        TestCase("selectCreateFolderDownloads").FilesSwa(),
+        TestCase("selectCreateFolderDownloads").InGuestMode(),
+        TestCase("selectCreateFolderDownloads").InGuestMode().FilesSwa(),
+        TestCase("createFolderDownloads"),
+        TestCase("createFolderDownloads").FilesSwa(),
+        TestCase("createFolderDownloads").InGuestMode(),
+        TestCase("createFolderDownloads").InGuestMode().FilesSwa(),
+        TestCase("createFolderNestedDownloads"),
+        TestCase("createFolderNestedDownloads").FilesSwa(),
+        TestCase("createFolderDrive"),
+        TestCase("createFolderDrive").FilesSwa()));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     KeyboardOperations, /* keyboard_operations.js */
diff --git a/chrome/browser/ash/file_manager/file_tasks.cc b/chrome/browser/ash/file_manager/file_tasks.cc
index c8641af..f179d85c 100644
--- a/chrome/browser/ash/file_manager/file_tasks.cc
+++ b/chrome/browser/ash/file_manager/file_tasks.cc
@@ -446,16 +446,16 @@
 
   std::string task_id = TaskDescriptorToId(task_descriptor);
   if (!mime_types.empty()) {
-    DictionaryPrefUpdate mime_type_pref(pref_service,
-                                        prefs::kDefaultTasksByMimeType);
+    DictionaryPrefUpdateDeprecated mime_type_pref(
+        pref_service, prefs::kDefaultTasksByMimeType);
     for (const std::string& mime_type : mime_types) {
       mime_type_pref->SetKey(mime_type, base::Value(task_id));
     }
   }
 
   if (!suffixes.empty()) {
-    DictionaryPrefUpdate mime_type_pref(pref_service,
-                                        prefs::kDefaultTasksBySuffix);
+    DictionaryPrefUpdateDeprecated mime_type_pref(pref_service,
+                                                  prefs::kDefaultTasksBySuffix);
     for (const std::string& suffix : suffixes) {
       // Suffixes are case insensitive.
       std::string lower_suffix = base::ToLowerASCII(suffix);
diff --git a/chrome/browser/ash/file_manager/guest_os_file_tasks_unittest.cc b/chrome/browser/ash/file_manager/guest_os_file_tasks_unittest.cc
index 0b32c4d1..f70959cf 100644
--- a/chrome/browser/ash/file_manager/guest_os_file_tasks_unittest.cc
+++ b/chrome/browser/ash/file_manager/guest_os_file_tasks_unittest.cc
@@ -60,8 +60,8 @@
               guest_os::GuestOsRegistryService::VmType vm_type) {
     // crostini.registry {<id>: {container_name: "penguin", name: {"": <name>},
     //                           mime_types: [<mime>,], vm_name: "termina"}}
-    DictionaryPrefUpdate update(profile_.GetPrefs(),
-                                guest_os::prefs::kGuestOsRegistry);
+    DictionaryPrefUpdateDeprecated update(profile_.GetPrefs(),
+                                          guest_os::prefs::kGuestOsRegistry);
     base::DictionaryValue* registry = update.Get();
     base::Value app(base::Value::Type::DICTIONARY);
     app.SetKey("container_name", base::Value("penguin"));
@@ -93,8 +93,8 @@
 
   void AddMime(const std::string& file_ext, const std::string& mime) {
     // crostini.mime_types.termina.penguin.<file_ext>: <mime>
-    DictionaryPrefUpdate update(profile_.GetPrefs(),
-                                guest_os::prefs::kGuestOsMimeTypes);
+    DictionaryPrefUpdateDeprecated update(profile_.GetPrefs(),
+                                          guest_os::prefs::kGuestOsMimeTypes);
     base::DictionaryValue* mimes = update.Get();
     mimes->SetStringPath("termina.penguin." + file_ext, mime);
   }
diff --git a/chrome/browser/ash/file_manager/open_with_browser.cc b/chrome/browser/ash/file_manager/open_with_browser.cc
index 2be0c3ee..5729293 100644
--- a/chrome/browser/ash/file_manager/open_with_browser.cc
+++ b/chrome/browser/ash/file_manager/open_with_browser.cc
@@ -8,6 +8,7 @@
 
 #include "ash/components/drivefs/drivefs_util.h"
 #include "ash/components/drivefs/mojom/drivefs.mojom.h"
+#include "ash/public/cpp/new_window_delegate.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/cxx17_backports.h"
@@ -19,27 +20,18 @@
 #include "chrome/browser/ash/file_manager/filesystem_api_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/fileapi/external_file_url_util.h"
-#include "chrome/browser/plugins/plugin_prefs.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_tabstrip.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
 #include "chrome/common/chrome_content_client.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/drive/drive_api_util.h"
 #include "components/drive/file_system_core_util.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/plugin_service.h"
-#include "content/public/common/pepper_plugin_info.h"
 #include "net/base/filename_util.h"
 #include "pdf/buildflags.h"
 #include "storage/browser/file_system/file_system_url.h"
 
 using content::BrowserThread;
-using content::PluginService;
 
 namespace file_manager {
 namespace util {
@@ -69,22 +61,14 @@
   return false;
 }
 
-void OpenNewTab(Profile* profile, const GURL& url) {
+void OpenNewTab(const GURL& url) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  // Check the validity of the pointer so that the closure from
-  // base::BindOnce(&OpenNewTab, profile) can be passed between threads.
-  if (!g_browser_process->profile_manager()->IsValidProfile(profile))
+  if (!ash::NewWindowDelegate::GetPrimary()) {
     return;
-
-  chrome::ScopedTabbedBrowserDisplayer displayer(profile);
-  chrome::AddSelectedTabWithURL(displayer.browser(), url,
-      ui::PAGE_TRANSITION_LINK);
-
-  // Since the ScopedTabbedBrowserDisplayer does not guarantee that the
-  // browser will be shown on the active desktop, we ensure the visibility.
-  multi_user_util::MoveWindowToCurrentDesktop(
-      displayer.browser()->window()->GetNativeWindow());
+  }
+  ash::NewWindowDelegate::GetPrimary()->OpenUrl(url,
+                                                /*from_user_interaction=*/true);
 }
 
 // Reads the alternate URL from a GDoc file. When it fails, returns a file URL
@@ -98,29 +82,28 @@
 }
 
 // Parse a local file to extract the Docs url and open this url.
-void OpenGDocUrlFromFile(const base::FilePath& file_path, Profile* profile) {
+void OpenGDocUrlFromFile(const base::FilePath& file_path) {
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE, {base::MayBlock()},
       base::BindOnce(&ReadUrlFromGDocAsync, file_path),
-      base::BindOnce(&OpenNewTab, profile));
+      base::BindOnce(&OpenNewTab));
 }
 
 // Open a hosted GDoc, from a path hosted in DriveFS.
 void OpenHostedDriveFsFile(const base::FilePath& file_path,
-                           Profile* profile,
                            drive::FileError error,
                            drivefs::mojom::FileMetadataPtr metadata) {
   if (error != drive::FILE_ERROR_OK)
     return;
   if (drivefs::IsLocal(metadata->type)) {
-    OpenGDocUrlFromFile(file_path, profile);
+    OpenGDocUrlFromFile(file_path);
     return;
   }
   GURL hosted_url(metadata->alternate_url);
   if (!hosted_url.is_valid())
     return;
 
-  OpenNewTab(profile, hosted_url);
+  OpenNewTab(hosted_url);
 }
 
 }  // namespace
@@ -141,7 +124,7 @@
     if (page_url.is_empty())
       page_url = net::FilePathToFileURL(file_path);
 
-    OpenNewTab(profile, page_url);
+    OpenNewTab(page_url);
     return true;
   }
 
@@ -154,7 +137,7 @@
       const GURL url =
           chromeos::FileSystemURLToExternalFileURL(file_system_url);
       DCHECK(!url.is_empty());
-      OpenNewTab(profile, url);
+      OpenNewTab(url);
     } else {
       drive::DriveIntegrationService* integration_service =
           drive::DriveIntegrationServiceFactory::FindForProfile(profile);
@@ -163,10 +146,10 @@
           integration_service->GetDriveFsInterface() &&
           integration_service->GetRelativeDrivePath(file_path, &path)) {
         integration_service->GetDriveFsInterface()->GetMetadata(
-            path, base::BindOnce(&OpenHostedDriveFsFile, file_path, profile));
+            path, base::BindOnce(&OpenHostedDriveFsFile, file_path));
         return true;
       }
-      OpenGDocUrlFromFile(file_path, profile);
+      OpenGDocUrlFromFile(file_path);
     }
     return true;
   }
diff --git a/chrome/browser/ash/file_system_provider/registry.cc b/chrome/browser/ash/file_system_provider/registry.cc
index d6f8f945..a76e1eb 100644
--- a/chrome/browser/ash/file_system_provider/registry.cc
+++ b/chrome/browser/ash/file_system_provider/registry.cc
@@ -91,8 +91,8 @@
   PrefService* const pref_service = profile_->GetPrefs();
   DCHECK(pref_service);
 
-  DictionaryPrefUpdate dict_update(pref_service,
-                                   prefs::kFileSystemProviderMounted);
+  DictionaryPrefUpdateDeprecated dict_update(pref_service,
+                                             prefs::kFileSystemProviderMounted);
 
   base::Value* file_systems_per_extension = dict_update->FindKeyOfType(
       file_system_info.provider_id().ToString(), base::Value::Type::DICTIONARY);
@@ -111,8 +111,8 @@
   PrefService* const pref_service = profile_->GetPrefs();
   DCHECK(pref_service);
 
-  DictionaryPrefUpdate dict_update(pref_service,
-                                   prefs::kFileSystemProviderMounted);
+  DictionaryPrefUpdateDeprecated dict_update(pref_service,
+                                             prefs::kFileSystemProviderMounted);
 
   base::DictionaryValue* file_systems_per_extension = NULL;
   if (!dict_update->GetDictionaryWithoutPathExpansion(
@@ -244,8 +244,8 @@
 
   // TODO(mtomasz): Consider optimizing it by moving information about watchers
   // or even file systems to leveldb.
-  DictionaryPrefUpdate dict_update(pref_service,
-                                   prefs::kFileSystemProviderMounted);
+  DictionaryPrefUpdateDeprecated dict_update(pref_service,
+                                             prefs::kFileSystemProviderMounted);
 
   // All of the following checks should not happen in healthy environment.
   // However, since they rely on storage, DCHECKs can't be used.
diff --git a/chrome/browser/ash/guest_os/guest_os_mime_types_service.cc b/chrome/browser/ash/guest_os/guest_os_mime_types_service.cc
index b63fa4d4..f8d911f 100644
--- a/chrome/browser/ash/guest_os/guest_os_mime_types_service.cc
+++ b/chrome/browser/ash/guest_os/guest_os_mime_types_service.cc
@@ -37,7 +37,7 @@
 // TODO(crbug.com/1015353): Can be removed after M99.
 void GuestOsMimeTypesService::MigrateVerboseMimeTypePrefs(
     PrefService* pref_service) {
-  DictionaryPrefUpdate update(pref_service, prefs::kGuestOsMimeTypes);
+  DictionaryPrefUpdateDeprecated update(pref_service, prefs::kGuestOsMimeTypes);
   base::DictionaryValue* mime_types = update.Get();
   std::map<std::string,
            std::map<std::string, std::map<std::string, std::string>>>
@@ -130,7 +130,7 @@
     const std::string& vm_name,
     const std::string& container_name) {
   VLOG(1) << "ClearMimeTypes(" << vm_name << ", " << container_name << ")";
-  DictionaryPrefUpdate update(prefs_, prefs::kGuestOsMimeTypes);
+  DictionaryPrefUpdateDeprecated update(prefs_, prefs::kGuestOsMimeTypes);
   base::DictionaryValue* mime_types = update.Get();
   base::Value* vm = mime_types->FindDictKey(vm_name);
   if (vm) {
@@ -158,7 +158,7 @@
   }
   VLOG(1) << "UpdateMimeTypes(" << mime_type_mappings.vm_name() << ", "
           << mime_type_mappings.container_name() << ")=" << exts;
-  DictionaryPrefUpdate update(prefs_, prefs::kGuestOsMimeTypes);
+  DictionaryPrefUpdateDeprecated update(prefs_, prefs::kGuestOsMimeTypes);
   base::DictionaryValue* mime_types = update.Get();
   base::Value* vm = mime_types->FindDictKey(mime_type_mappings.vm_name());
   if (!vm) {
diff --git a/chrome/browser/ash/guest_os/guest_os_registry_service.cc b/chrome/browser/ash/guest_os/guest_os_registry_service.cc
index 445df18..53d3725 100644
--- a/chrome/browser/ash/guest_os/guest_os_registry_service.cc
+++ b/chrome/browser/ash/guest_os/guest_os_registry_service.cc
@@ -904,9 +904,11 @@
     const std::string& vm_name,
     const std::string& container_name) {
   std::vector<std::string> removed_apps;
-  // The DictionaryPrefUpdate should be destructed before calling the observer.
+  // The DictionaryPrefUpdateDeprecated should be destructed before calling the
+  // observer.
   {
-    DictionaryPrefUpdate update(prefs_, guest_os::prefs::kGuestOsRegistry);
+    DictionaryPrefUpdateDeprecated update(prefs_,
+                                          guest_os::prefs::kGuestOsRegistry);
     base::DictionaryValue* apps = update.Get();
 
     for (const auto item : apps->DictItems()) {
@@ -965,9 +967,11 @@
   std::vector<std::string> removed_apps;
   std::vector<std::string> inserted_apps;
 
-  // The DictionaryPrefUpdate should be destructed before calling the observer.
+  // The DictionaryPrefUpdateDeprecated should be destructed before calling the
+  // observer.
   {
-    DictionaryPrefUpdate update(prefs_, guest_os::prefs::kGuestOsRegistry);
+    DictionaryPrefUpdateDeprecated update(prefs_,
+                                          guest_os::prefs::kGuestOsRegistry);
     base::DictionaryValue* apps = update.Get();
     for (const App& app : app_list.apps()) {
       if (app.desktop_file_id().empty()) {
@@ -1104,7 +1108,8 @@
 }
 
 void GuestOsRegistryService::AppLaunched(const std::string& app_id) {
-  DictionaryPrefUpdate update(prefs_, guest_os::prefs::kGuestOsRegistry);
+  DictionaryPrefUpdateDeprecated update(prefs_,
+                                        guest_os::prefs::kGuestOsRegistry);
   base::DictionaryValue* apps = update.Get();
 
   base::Value* app = apps->FindKey(app_id);
@@ -1130,7 +1135,8 @@
                                           bool scaled) {
   DCHECK_NE(app_id, crostini::kCrostiniTerminalSystemAppId);
 
-  DictionaryPrefUpdate update(prefs_, guest_os::prefs::kGuestOsRegistry);
+  DictionaryPrefUpdateDeprecated update(prefs_,
+                                        guest_os::prefs::kGuestOsRegistry);
   base::DictionaryValue* apps = update.Get();
 
   base::Value* app = apps->FindKey(app_id);
diff --git a/chrome/browser/ash/guest_os/guest_os_share_path.cc b/chrome/browser/ash/guest_os/guest_os_share_path.cc
index 392894d..e56521fd 100644
--- a/chrome/browser/ash/guest_os/guest_os_share_path.cc
+++ b/chrome/browser/ash/guest_os/guest_os_share_path.cc
@@ -512,7 +512,8 @@
 
   if (unpersist) {
     PrefService* pref_service = profile_->GetPrefs();
-    DictionaryPrefUpdate update(pref_service, prefs::kGuestOSPathsSharedToVms);
+    DictionaryPrefUpdateDeprecated update(pref_service,
+                                          prefs::kGuestOSPathsSharedToVms);
     base::DictionaryValue* shared_paths = update.Get();
     RemovePersistedPathFromPrefs(shared_paths, vm_name, path);
   }
@@ -563,7 +564,8 @@
 void GuestOsSharePath::RegisterPersistedPath(const std::string& vm_name,
                                              const base::FilePath& path) {
   PrefService* pref_service = profile_->GetPrefs();
-  DictionaryPrefUpdate update(pref_service, prefs::kGuestOSPathsSharedToVms);
+  DictionaryPrefUpdateDeprecated update(pref_service,
+                                        prefs::kGuestOSPathsSharedToVms);
   base::DictionaryValue* shared_paths = update.Get();
   // Check if path is already shared so we know whether we need to add it.
   bool already_shared = false;
diff --git a/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc b/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
index 2ff795d..9fc19ba2 100644
--- a/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
+++ b/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
@@ -247,8 +247,8 @@
     share_path_ = root_.Append("path-to-share");
     shared_path_ = root_.Append("already-shared");
     ASSERT_TRUE(base::CreateDirectory(shared_path_));
-    DictionaryPrefUpdate update(profile()->GetPrefs(),
-                                prefs::kGuestOSPathsSharedToVms);
+    DictionaryPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                          prefs::kGuestOSPathsSharedToVms);
     base::DictionaryValue* shared_paths = update.Get();
     base::Value termina(base::Value::Type::LIST);
     termina.Append(base::Value(crostini::kCrostiniDefaultVmName));
@@ -786,8 +786,8 @@
 
 TEST_F(GuestOsSharePathTest, UnsharePathSuccess) {
   SetUpVolume();
-  DictionaryPrefUpdate update(profile()->GetPrefs(),
-                              prefs::kGuestOSPathsSharedToVms);
+  DictionaryPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                        prefs::kGuestOSPathsSharedToVms);
   base::DictionaryValue* shared_paths = update.Get();
   base::Value vms(base::Value::Type::LIST);
   vms.Append(base::Value("vm-running"));
@@ -813,8 +813,8 @@
 
 TEST_F(GuestOsSharePathTest, UnsharePathVmNotRunning) {
   SetUpVolume();
-  DictionaryPrefUpdate update(profile()->GetPrefs(),
-                              prefs::kGuestOSPathsSharedToVms);
+  DictionaryPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                        prefs::kGuestOSPathsSharedToVms);
   base::DictionaryValue* shared_paths = update.Get();
   base::Value vms(base::Value::Type::LIST);
   vms.Append(base::Value("vm-not-running"));
@@ -830,8 +830,8 @@
 
 TEST_F(GuestOsSharePathTest, UnsharePathPluginVmNotRunning) {
   SetUpVolume();
-  DictionaryPrefUpdate update(profile()->GetPrefs(),
-                              prefs::kGuestOSPathsSharedToVms);
+  DictionaryPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                        prefs::kGuestOSPathsSharedToVms);
   base::DictionaryValue* shared_paths = update.Get();
   base::Value vms(base::Value::Type::LIST);
   vms.Append(base::Value("PvmDefault"));
@@ -848,8 +848,8 @@
 // Tests that it cannot unshare path when ARCVM is not running.
 TEST_F(GuestOsSharePathTest, UnsharePathArcvmNotRunning) {
   SetUpVolume();
-  DictionaryPrefUpdate update(profile()->GetPrefs(),
-                              prefs::kGuestOSPathsSharedToVms);
+  DictionaryPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                        prefs::kGuestOSPathsSharedToVms);
   base::DictionaryValue* shared_paths = update.Get();
   base::Value vms(base::Value::Type::LIST);
   vms.Append(base::Value(arc::kArcVmName));
diff --git a/chrome/browser/ash/input_method/emoji_suggester.cc b/chrome/browser/ash/input_method/emoji_suggester.cc
index 826a8306..4d1ac26 100644
--- a/chrome/browser/ash/input_method/emoji_suggester.cc
+++ b/chrome/browser/ash/input_method/emoji_suggester.cc
@@ -335,8 +335,8 @@
 }
 
 int EmojiSuggester::GetPrefValue(const std::string& pref_name) {
-  DictionaryPrefUpdate update(profile_->GetPrefs(),
-                              prefs::kAssistiveInputFeatureSettings);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kAssistiveInputFeatureSettings);
   auto value = update->FindIntKey(pref_name);
   if (!value.has_value()) {
     update->SetIntKey(pref_name, 0);
@@ -349,8 +349,8 @@
                                                  int max_value) {
   int value = GetPrefValue(pref_name);
   if (value < max_value) {
-    DictionaryPrefUpdate update(profile_->GetPrefs(),
-                                prefs::kAssistiveInputFeatureSettings);
+    DictionaryPrefUpdateDeprecated update(
+        profile_->GetPrefs(), prefs::kAssistiveInputFeatureSettings);
     update->SetIntKey(pref_name, value + 1);
   }
 }
diff --git a/chrome/browser/ash/input_method/multi_word_suggester.cc b/chrome/browser/ash/input_method/multi_word_suggester.cc
index a9ebf6f..997318a 100644
--- a/chrome/browser/ash/input_method/multi_word_suggester.cc
+++ b/chrome/browser/ash/input_method/multi_word_suggester.cc
@@ -69,8 +69,8 @@
 }
 
 std::optional<int> GetTimeFirstAcceptedSuggestion(Profile* profile) {
-  DictionaryPrefUpdate update(profile->GetPrefs(),
-                              prefs::kAssistiveInputFeatureSettings);
+  DictionaryPrefUpdateDeprecated update(profile->GetPrefs(),
+                                        prefs::kAssistiveInputFeatureSettings);
   auto value = update->FindIntKey(kMultiWordFirstAcceptTimeDays);
   if (value.has_value())
     return value.value();
@@ -78,8 +78,8 @@
 }
 
 void SetTimeFirstAcceptedSuggestion(Profile* profile) {
-  DictionaryPrefUpdate update(profile->GetPrefs(),
-                              prefs::kAssistiveInputFeatureSettings);
+  DictionaryPrefUpdateDeprecated update(profile->GetPrefs(),
+                                        prefs::kAssistiveInputFeatureSettings);
   auto time_since_epoch = base::Time::Now() - base::Time::UnixEpoch();
   update->SetIntKey(kMultiWordFirstAcceptTimeDays,
                     time_since_epoch.InDaysFloored());
diff --git a/chrome/browser/ash/input_method/multi_word_suggester_unittest.cc b/chrome/browser/ash/input_method/multi_word_suggester_unittest.cc
index d7278ae..58d7f26 100644
--- a/chrome/browser/ash/input_method/multi_word_suggester_unittest.cc
+++ b/chrome/browser/ash/input_method/multi_word_suggester_unittest.cc
@@ -36,16 +36,16 @@
 }
 
 void SetFirstAcceptTimeTo(Profile* profile, int days_ago) {
-  DictionaryPrefUpdate update(profile->GetPrefs(),
-                              prefs::kAssistiveInputFeatureSettings);
+  DictionaryPrefUpdateDeprecated update(profile->GetPrefs(),
+                                        prefs::kAssistiveInputFeatureSettings);
   base::TimeDelta since_epoch = base::Time::Now() - base::Time::UnixEpoch();
   update->SetIntKey("multi_word_first_accept",
                     since_epoch.InDaysFloored() - days_ago);
 }
 
 std::optional<int> GetFirstAcceptTime(Profile* profile) {
-  DictionaryPrefUpdate update(profile->GetPrefs(),
-                              prefs::kAssistiveInputFeatureSettings);
+  DictionaryPrefUpdateDeprecated update(profile->GetPrefs(),
+                                        prefs::kAssistiveInputFeatureSettings);
   auto value = update->FindIntKey("multi_word_first_accept");
   if (value.has_value())
     return value.value();
diff --git a/chrome/browser/ash/input_method/native_input_method_engine.cc b/chrome/browser/ash/input_method/native_input_method_engine.cc
index 065cbbf..eb52a7f 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine.cc
+++ b/chrome/browser/ash/input_method/native_input_method_engine.cc
@@ -34,6 +34,7 @@
 #include "components/prefs/pref_service.h"
 #include "ui/base/ime/ash/extension_ime_util.h"
 #include "ui/base/ime/ash/ime_bridge.h"
+#include "ui/base/ime/ash/ime_keyboard.h"
 #include "ui/base/ime/ash/input_method_manager.h"
 #include "ui/base/ime/ash/input_method_ukm.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
@@ -473,6 +474,25 @@
   };
 }
 
+std::string MojomLayoutToXkbLayout(mojom::PinyinLayout layout) {
+  switch (layout) {
+    case mojom::PinyinLayout::kUsQwerty:
+      return "us";
+    case mojom::PinyinLayout::kDvorak:
+      return "us(dvorak)";
+    case mojom::PinyinLayout::kColemak:
+      return "us(colemak)";
+  }
+}
+
+void OverrideXkbLayoutIfNeeded(ImeKeyboard* keyboard,
+                               const mojom::InputMethodSettingsPtr& settings) {
+  if (settings && settings->is_pinyin_settings()) {
+    keyboard->SetCurrentKeyboardLayoutByName(
+        MojomLayoutToXkbLayout(settings->get_pinyin_settings()->layout));
+  }
+}
+
 }  // namespace
 
 NativeInputMethodEngine::NativeInputMethodEngine()
@@ -705,17 +725,20 @@
           features::IsAssistiveMultiWordEnabled()
               ? CreateInputFieldContext(assistive_suggester_.get())
               : InputFieldContext{};
+      // TODO(b/200611333): Make input_method_->OnFocus return the overriding
+      // XKB layout instead of having the logic here in Chromium.
+      auto settings =
+          CreateSettingsFromPrefs(*prefs_, engine_id, input_field_context);
+      OverrideXkbLayoutIfNeeded(InputMethodManager::Get()->GetImeKeyboard(),
+                                settings);
 
-      input_method_->OnFocus(
-          mojom::InputFieldInfo::New(
-              TextInputTypeToMojoType(context.type),
-              AutocorrectFlagsToMojoType(context.flags),
-              context.should_do_learning
-                  ? mojom::PersonalizationMode::kEnabled
-                  : mojom::PersonalizationMode::kDisabled),
-          prefs_
-              ? CreateSettingsFromPrefs(*prefs_, engine_id, input_field_context)
-              : nullptr);
+      input_method_->OnFocus(mojom::InputFieldInfo::New(
+                                 TextInputTypeToMojoType(context.type),
+                                 AutocorrectFlagsToMojoType(context.flags),
+                                 context.should_do_learning
+                                     ? mojom::PersonalizationMode::kEnabled
+                                     : mojom::PersonalizationMode::kDisabled),
+                             prefs_ ? std::move(settings) : nullptr);
 
       // TODO(b/202224495): Send the surrounding text as part of InputFieldInfo.
       SendSurroundingTextToNativeMojoEngine(last_surrounding_text_);
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc b/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc
index 8ded0be..09d6d92 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc
+++ b/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc
@@ -26,7 +26,9 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/ime/ash/fake_ime_keyboard.h"
 #include "ui/base/ime/ash/ime_bridge.h"
+#include "ui/base/ime/ash/ime_keyboard.h"
 #include "ui/base/ime/ash/input_method_ash.h"
 #include "ui/base/ime/ash/mock_ime_input_context_handler.h"
 #include "ui/base/ime/ash/mock_input_method_manager.h"
@@ -51,6 +53,7 @@
 
 constexpr char kEngineIdUs[] = "xkb:us::eng";
 constexpr char kEngineIdEs[] = "xkb:es::spa";
+constexpr char kEngineIdPinyin[] = "zh-t-i0-pinyin";
 
 class FakeSuggesterSwitch : public AssistiveSuggesterSwitch {
  public:
@@ -131,6 +134,13 @@
                           input_method_setting);
 }
 
+void SetPinyinLayoutPrefs(Profile& profile, const std::string& layout) {
+  base::Value input_method_setting(base::Value::Type::DICTIONARY);
+  input_method_setting.SetStringPath("pinyin.xkbLayout", layout);
+  profile.GetPrefs()->Set(::prefs::kLanguageInputMethodSpecificSettings,
+                          input_method_setting);
+}
+
 class TestInputEngineManager : public ime::mojom::InputEngineManager {
  public:
   explicit TestInputEngineManager(MockInputMethod* mock_input_method)
@@ -172,7 +182,10 @@
     receiver_.Bind(std::move(receiver));
   }
 
+  ImeKeyboard* GetImeKeyboard() override { return &ime_keyboard_; }
+
  private:
+  FakeImeKeyboard ime_keyboard_;
   TestInputEngineManager test_input_engine_manager_;
   mojo::Receiver<ime::mojom::InputEngineManager> receiver_;
 };
@@ -200,6 +213,7 @@
 
   void EnableDefaultFeatureList() {
     EnableFeatureList({
+        features::kSystemChinesePhysicalTyping,
         features::kAssistPersonalInfo,
         features::kAssistPersonalInfoEmail,
         features::kAssistPersonalInfoName,
@@ -434,6 +448,32 @@
   InputMethodManager::Shutdown();
 }
 
+TEST_F(NativeInputMethodEngineTest, FocusUpdatesXkbLayout) {
+  TestingProfile testing_profile;
+  SetPinyinLayoutPrefs(testing_profile, "Colemak");
+
+  testing::StrictMock<MockInputMethod> mock_input_method;
+  InputMethodManager::Initialize(
+      new TestInputMethodManager(&mock_input_method));
+  NativeInputMethodEngine engine;
+  engine.Initialize(std::make_unique<StubInputMethodEngineObserver>(),
+                    /*extension_id=*/"", &testing_profile);
+
+  ui::IMEEngineHandlerInterface::InputContext input_context(
+      ui::TEXT_INPUT_TYPE_TEXT, ui::TEXT_INPUT_MODE_DEFAULT,
+      ui::TEXT_INPUT_FLAG_NONE, ui::TextInputClient::FOCUS_REASON_MOUSE,
+      /*should_do_learning=*/true);
+  engine.Enable(kEngineIdPinyin);
+  engine.FocusIn(input_context);
+
+  EXPECT_EQ(InputMethodManager::Get()
+                ->GetImeKeyboard()
+                ->GetCurrentKeyboardLayoutName(),
+            "us(colemak)");
+
+  InputMethodManager::Shutdown();
+}
+
 TEST_F(NativeInputMethodEngineTest,
        FocusCallsPassPredictiveWritingPrefWhenEnabled) {
   TestingProfile testing_profile;
diff --git a/chrome/browser/ash/input_method/personal_info_suggester.cc b/chrome/browser/ash/input_method/personal_info_suggester.cc
index 7813356..fd27828d 100644
--- a/chrome/browser/ash/input_method/personal_info_suggester.cc
+++ b/chrome/browser/ash/input_method/personal_info_suggester.cc
@@ -393,8 +393,8 @@
 }
 
 int PersonalInfoSuggester::GetPrefValue(const std::string& pref_name) {
-  DictionaryPrefUpdate update(profile_->GetPrefs(),
-                              prefs::kAssistiveInputFeatureSettings);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kAssistiveInputFeatureSettings);
   auto value = update->FindIntKey(pref_name);
   if (!value.has_value()) {
     update->SetIntKey(pref_name, 0);
@@ -408,8 +408,8 @@
     int max_value) {
   int value = GetPrefValue(pref_name);
   if (value < max_value) {
-    DictionaryPrefUpdate update(profile_->GetPrefs(),
-                                prefs::kAssistiveInputFeatureSettings);
+    DictionaryPrefUpdateDeprecated update(
+        profile_->GetPrefs(), prefs::kAssistiveInputFeatureSettings);
     update->SetIntKey(pref_name, value + 1);
   }
 }
diff --git a/chrome/browser/ash/input_method/personal_info_suggester_unittest.cc b/chrome/browser/ash/input_method/personal_info_suggester_unittest.cc
index 7f289a2..d9ba3ca 100644
--- a/chrome/browser/ash/input_method/personal_info_suggester_unittest.cc
+++ b/chrome/browser/ash/input_method/personal_info_suggester_unittest.cc
@@ -585,8 +585,8 @@
       /*enabled_features=*/{features::kAssistPersonalInfoEmail},
       /*disabled_features=*/{});
 
-  DictionaryPrefUpdate update(profile_->GetPrefs(),
-                              prefs::kAssistiveInputFeatureSettings);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kAssistiveInputFeatureSettings);
   update->SetIntKey(kPersonalInfoSuggesterAcceptanceCount, 1);
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
 
@@ -685,8 +685,8 @@
       /*enabled_features=*/{features::kAssistPersonalInfoEmail},
       /*disabled_features=*/{});
 
-  DictionaryPrefUpdate update(profile_->GetPrefs(),
-                              prefs::kAssistiveInputFeatureSettings);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kAssistiveInputFeatureSettings);
   update->RemoveKey(kPersonalInfoSuggesterShowSettingCount);
   update->RemoveKey(kPersonalInfoSuggesterAcceptanceCount);
   for (int i = 0; i < kMaxShowSettingCount; i++) {
@@ -705,8 +705,8 @@
       /*enabled_features=*/{features::kAssistPersonalInfoEmail},
       /*disabled_features=*/{});
 
-  DictionaryPrefUpdate update(profile_->GetPrefs(),
-                              prefs::kAssistiveInputFeatureSettings);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kAssistiveInputFeatureSettings);
   update->SetIntKey(kPersonalInfoSuggesterShowSettingCount, 0);
   suggester_->Suggest(u"my email is ", 12, 12);
   suggestion_handler_->VerifyShowSettingLink(true);
@@ -723,8 +723,8 @@
       /*enabled_features=*/{features::kAssistPersonalInfoEmail},
       /*disabled_features=*/{});
 
-  DictionaryPrefUpdate update(profile_->GetPrefs(),
-                              prefs::kAssistiveInputFeatureSettings);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kAssistiveInputFeatureSettings);
   update->RemoveKey(kPersonalInfoSuggesterShowSettingCount);
   update->RemoveKey(kPersonalInfoSuggesterAcceptanceCount);
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
@@ -744,8 +744,8 @@
       /*enabled_features=*/{features::kAssistPersonalInfoEmail},
       /*disabled_features=*/{});
 
-  DictionaryPrefUpdate update(profile_->GetPrefs(),
-                              prefs::kAssistiveInputFeatureSettings);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kAssistiveInputFeatureSettings);
   update->RemoveKey(kPersonalInfoSuggesterShowSettingCount);
   update->RemoveKey(kPersonalInfoSuggesterAcceptanceCount);
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
diff --git a/chrome/browser/ash/lock_screen_apps/first_app_run_toast_manager.cc b/chrome/browser/ash/lock_screen_apps/first_app_run_toast_manager.cc
index 07295518..0cf6ef2 100644
--- a/chrome/browser/ash/lock_screen_apps/first_app_run_toast_manager.cc
+++ b/chrome/browser/ash/lock_screen_apps/first_app_run_toast_manager.cc
@@ -144,7 +144,7 @@
 void FirstAppRunToastManager::ToastDialogDismissed() {
   {
     const extensions::Extension* app = app_window_->GetExtension();
-    DictionaryPrefUpdate dict_update(
+    DictionaryPrefUpdateDeprecated dict_update(
         profile_->GetPrefs(), prefs::kNoteTakingAppsLockScreenToastShown);
     dict_update->SetBoolean(app->id(), true);
   }
diff --git a/chrome/browser/ash/lock_screen_apps/state_controller_unittest.cc b/chrome/browser/ash/lock_screen_apps/state_controller_unittest.cc
index 13d8fe2b..19cf665 100644
--- a/chrome/browser/ash/lock_screen_apps/state_controller_unittest.cc
+++ b/chrome/browser/ash/lock_screen_apps/state_controller_unittest.cc
@@ -540,7 +540,7 @@
     if (is_first_app_run_test_)
       return;
 
-    DictionaryPrefUpdate dict_update(
+    DictionaryPrefUpdateDeprecated dict_update(
         profile()->GetPrefs(), prefs::kNoteTakingAppsLockScreenToastShown);
     dict_update->SetBoolean(app_id, true);
   }
diff --git a/chrome/browser/ash/login/app_mode/kiosk_browsertest.cc b/chrome/browser/ash/login/app_mode/kiosk_browsertest.cc
index fe9f7ea..ff615b2 100644
--- a/chrome/browser/ash/login/app_mode/kiosk_browsertest.cc
+++ b/chrome/browser/ash/login/app_mode/kiosk_browsertest.cc
@@ -2872,8 +2872,8 @@
   void SetUpLocalState() override {
     // Simulate auto login request from the previous session.
     PrefService* prefs = g_browser_process->local_state();
-    DictionaryPrefUpdate dict_update(prefs,
-                                     KioskAppManager::kKioskDictionaryName);
+    DictionaryPrefUpdateDeprecated dict_update(
+        prefs, KioskAppManager::kKioskDictionaryName);
     // The AutoLoginState is taken from KioskAppManager::AutoLoginState.
     dict_update->SetInteger(
         KioskAppManager::kKeyAutoLoginState,
diff --git a/chrome/browser/ash/login/arc_terms_of_service_browsertest.cc b/chrome/browser/ash/login/arc_terms_of_service_browsertest.cc
index 6a41701..597057d 100644
--- a/chrome/browser/ash/login/arc_terms_of_service_browsertest.cc
+++ b/chrome/browser/ash/login/arc_terms_of_service_browsertest.cc
@@ -20,8 +20,9 @@
 #include "chrome/browser/ash/login/login_wizard.h"
 #include "chrome/browser/ash/login/oobe_screen.h"
 #include "chrome/browser/ash/login/screens/recommend_apps_screen.h"
-#include "chrome/browser/ash/login/test/embedded_policy_test_server_mixin.h"
+#include "chrome/browser/ash/login/test/embedded_test_server_setup_mixin.h"
 #include "chrome/browser/ash/login/test/js_checker.h"
+#include "chrome/browser/ash/login/test/local_policy_test_server_mixin.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
@@ -649,9 +650,9 @@
 
   void UploadDeviceLocalAccountPolicy() {
     BuildDeviceLocalAccountPolicy();
-    policy_test_server_mixin_.UpdatePolicy(
+    ASSERT_TRUE(local_policy_mixin_.server()->UpdatePolicy(
         policy::dm_protocol::kChromePublicAccountPolicyType, kAccountId,
-        device_local_account_policy_.payload().SerializeAsString());
+        device_local_account_policy_.payload().SerializeAsString()));
   }
 
   void UploadAndInstallDeviceLocalAccountPolicy() {
@@ -664,7 +665,7 @@
     em::ChromeDeviceSettingsProto& proto(device_policy()->payload());
     policy::DeviceLocalAccountTestHelper::AddPublicSession(&proto, kAccountId);
     RefreshDevicePolicy();
-    policy_test_server_mixin_.UpdateDevicePolicy(proto);
+    ASSERT_TRUE(local_policy_mixin_.UpdateDevicePolicy(proto));
   }
 
   void WaitForDisplayName() {
@@ -713,7 +714,7 @@
           policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION));
   policy::DevicePolicyCrosTestHelper policy_helper_;
   policy::UserPolicyBuilder device_local_account_policy_;
-  EmbeddedPolicyTestServerMixin policy_test_server_mixin_{&mixin_host_};
+  LocalPolicyTestServerMixin local_policy_mixin_{&mixin_host_};
   DeviceStateMixin device_state_{
       &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
 };
diff --git a/chrome/browser/ash/login/demo_mode/demo_session.cc b/chrome/browser/ash/login/demo_mode/demo_session.cc
index 8309e1e..f4eb2b5 100644
--- a/chrome/browser/ash/login/demo_mode/demo_session.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_session.cc
@@ -377,32 +377,30 @@
   const std::string current_locale = g_browser_process->GetApplicationLocale();
   bool country_selected = false;
 
-  // TODO(b/203105588): Use the new way of base::Value to create the country
-  // list.
   for (const std::string country : kSupportedCountries) {
-    base::DictionaryValue dict;
-    dict.SetString("value", country);
-    dict.SetString(
+    base::Value dict(base::Value::Type::DICTIONARY);
+    dict.SetStringKey("value", country);
+    dict.SetStringKey(
         "title", l10n_util::GetDisplayNameForCountry(country, current_locale));
     if (country == region) {
-      dict.SetBoolean("selected", true);
+      dict.SetBoolKey("selected", true);
       g_browser_process->local_state()->SetString(prefs::kDemoModeCountry,
                                                   country);
       country_selected = true;
     } else {
-      dict.SetBoolean("selected", false);
+      dict.SetBoolKey("selected", false);
     }
     country_list.Append(std::move(dict));
   }
   if (!country_selected) {
-    base::DictionaryValue countryNotSelectedDict;
-    countryNotSelectedDict.SetString("value",
-                                     DemoSession::kCountryNotSelectedId);
-    countryNotSelectedDict.SetString(
+    base::Value countryNotSelectedDict(base::Value::Type::DICTIONARY);
+    countryNotSelectedDict.SetStringKey("value",
+                                        DemoSession::kCountryNotSelectedId);
+    countryNotSelectedDict.SetStringKey(
         "title",
         l10n_util::GetStringUTF16(
             IDS_OOBE_DEMO_SETUP_PREFERENCES_SCREEN_COUNTRY_NOT_SELECTED_TITLE));
-    countryNotSelectedDict.SetBoolean("selected", true);
+    countryNotSelectedDict.SetBoolKey("selected", true);
     country_list.Append(std::move(countryNotSelectedDict));
   }
   return country_list;
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc
index 85e1962..3a9f3371 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service.cc
@@ -152,7 +152,8 @@
   if (!local_state)
     return;
 
-  DictionaryPrefUpdate update(local_state, prefs::kEasyUnlockHardlockState);
+  DictionaryPrefUpdateDeprecated update(local_state,
+                                        prefs::kEasyUnlockHardlockState);
   update->RemoveKey(account_id.GetUserEmail());
 
   EasyUnlockTpmKeyManager::ResetLocalStateForUser(account_id);
@@ -548,7 +549,8 @@
     return;
   }
 
-  DictionaryPrefUpdate update(local_state, prefs::kEasyUnlockHardlockState);
+  DictionaryPrefUpdateDeprecated update(local_state,
+                                        prefs::kEasyUnlockHardlockState);
   update->SetKey(account_id.GetUserEmail(),
                  base::Value(static_cast<int>(state)));
 
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc
index ec09ead..9368a10c 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc
@@ -279,8 +279,8 @@
   JSONStringValueSerializer serializer(&remote_devices_json);
   serializer.Serialize(devices);
 
-  DictionaryPrefUpdate pairing_update(profile()->GetPrefs(),
-                                      prefs::kEasyUnlockPairing);
+  DictionaryPrefUpdateDeprecated pairing_update(profile()->GetPrefs(),
+                                                prefs::kEasyUnlockPairing);
   if (devices.GetList().empty())
     pairing_update->RemoveKey(kKeyDevices);
   else
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager.cc
index 29434dd..ba3e2af 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager.cc
@@ -197,7 +197,8 @@
   if (!local_state)
     return;
 
-  DictionaryPrefUpdate update(local_state, prefs::kEasyUnlockLocalStateTpmKeys);
+  DictionaryPrefUpdateDeprecated update(local_state,
+                                        prefs::kEasyUnlockLocalStateTpmKeys);
   update->RemoveKey(account_id.GetUserEmail());
 }
 
@@ -304,8 +305,8 @@
 
   std::string encoded;
   base::Base64Encode(value, &encoded);
-  DictionaryPrefUpdate update(local_state_,
-                              prefs::kEasyUnlockLocalStateTpmKeys);
+  DictionaryPrefUpdateDeprecated update(local_state_,
+                                        prefs::kEasyUnlockLocalStateTpmKeys);
   update->SetKey(account_id.GetUserEmail(), base::Value(encoded));
 }
 
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager_unittest.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager_unittest.cc
index 1794625..48d5e38b 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager_unittest.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager_unittest.cc
@@ -339,8 +339,8 @@
                                      const std::string& value) {
     std::string encoded;
     base::Base64Encode(value, &encoded);
-    DictionaryPrefUpdate update(g_browser_process->local_state(),
-                                prefs::kEasyUnlockLocalStateTpmKeys);
+    DictionaryPrefUpdateDeprecated update(g_browser_process->local_state(),
+                                          prefs::kEasyUnlockLocalStateTpmKeys);
     update->SetKey(account_id.GetUserEmail(), base::Value(encoded));
   }
 
diff --git a/chrome/browser/ash/login/login_manager_test.cc b/chrome/browser/ash/login/login_manager_test.cc
index b2ef8043..b8b53b64 100644
--- a/chrome/browser/ash/login/login_manager_test.cc
+++ b/chrome/browser/ash/login/login_manager_test.cc
@@ -63,7 +63,8 @@
 }
 
 void LoginManagerTest::RegisterUser(const AccountId& account_id) {
-  ListPrefUpdate users_pref(g_browser_process->local_state(), "LoggedInUsers");
+  ListPrefUpdateDeprecated users_pref(g_browser_process->local_state(),
+                                      "LoggedInUsers");
   base::Value email_value(account_id.GetUserEmail());
   if (!base::Contains(users_pref->GetList(), email_value))
     users_pref->Append(std::move(email_value));
diff --git a/chrome/browser/ash/login/saml/saml_browsertest.cc b/chrome/browser/ash/login/saml/saml_browsertest.cc
index a040ea9..7af8fd5 100644
--- a/chrome/browser/ash/login/saml/saml_browsertest.cc
+++ b/chrome/browser/ash/login/saml/saml_browsertest.cc
@@ -132,16 +132,25 @@
 const test::UIPath kPasswordConfirmInput = {"saml-confirm-password",
                                             "confirmPasswordInput"};
 const test::UIPath kPasswordSubmit = {"saml-confirm-password", "next"};
+const test::UIPath kSigninFrameDialog = {"gaia-signin", "signin-frame-dialog"};
 const test::UIPath kSamlNoticeMessage = {"gaia-signin", "signin-frame-dialog",
                                          "saml-notice-message"};
 const test::UIPath kSamlNoticeContainer = {"gaia-signin", "signin-frame-dialog",
                                            "saml-notice-container"};
 constexpr test::UIPath kBackButton = {"gaia-signin", "signin-frame-dialog",
                                       "signin-back-button"};
+constexpr test::UIPath kChangeIdPLink = {"gaia-signin",
+                                         "interstitial-change-account"};
+constexpr test::UIPath kChangeIdPButton = {"gaia-signin", "signin-frame-dialog",
+                                           "change-account"};
 constexpr test::UIPath kEnterprisePrimaryButton = {
     "enterprise-enrollment", "step-signin", "primary-action-button"};
 constexpr test::UIPath kSamlCloseButton = {"gaia-signin", "signin-frame-dialog",
                                            "saml-close-button"};
+constexpr test::UIPath kSamlBackButton = {"gaia-signin", "signin-frame-dialog",
+                                          "saml-back-button"};
+const test::UIPath kGaiaLoading = {"gaia-signin", "gaia-loading"};
+const test::UIPath kSamlInterstitial = {"gaia-signin", "saml-interstitial"};
 
 constexpr char kGAIASIDCookieName[] = "SID";
 constexpr char kGAIALSIDCookieName[] = "LSID";
@@ -347,26 +356,56 @@
   FakeSamlIdpMixin fake_saml_idp_mixin_{&mixin_host_, &fake_gaia_};
 };
 
-class SamlTest : public SamlTestBase, public testing::WithParamInterface<bool> {
+// The first value of the parameter runs the tests with
+// kUseAuthsessionAuthentication feature and the second value with
+// kRedirectToDefaultIdP feature.
+class SamlTestWithFeatures
+    : public SamlTestBase,
+      public testing::WithParamInterface<std::tuple<bool, bool>> {
  public:
-  SamlTest() {
-    if (GetParam()) {
-      scoped_feature_list_.InitAndEnableFeature(
-          features::kUseAuthsessionAuthentication);
+  SamlTestWithFeatures() {
+    std::vector<base::Feature> enabled_features;
+    std::vector<base::Feature> disabled_features;
+    if (std::get<0>(GetParam())) {
+      enabled_features.push_back(features::kUseAuthsessionAuthentication);
     } else {
-      scoped_feature_list_.InitAndDisableFeature(
-          features::kUseAuthsessionAuthentication);
+      disabled_features.push_back(features::kUseAuthsessionAuthentication);
     }
+    if (std::get<1>(GetParam())) {
+      enabled_features.push_back(features::kRedirectToDefaultIdP);
+    } else {
+      disabled_features.push_back(features::kRedirectToDefaultIdP);
+    }
+    scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
   }
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
+// The value of the parameter runs the tests with kRedirectToDefaultIdP feature.
+class SamlTestWithRedirectToDefaultIdPFeature
+    : public SamlTestBase,
+      public testing::WithParamInterface<bool> {
+ public:
+  SamlTestWithRedirectToDefaultIdPFeature() {
+    if (GetParam()) {
+      scoped_feature_list_.InitAndEnableFeature(
+          features::kRedirectToDefaultIdP);
+    } else {
+      scoped_feature_list_.InitAndDisableFeature(
+          features::kRedirectToDefaultIdP);
+    }
+  }
+
+ protected:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
 // Tests that signin frame should display the SAML notice and the 'back' button
 // when SAML IdP page is loaded. And the 'back' button goes back to gaia on
 // clicking.
-IN_PROC_BROWSER_TEST_P(SamlTest, SamlUI) {
+IN_PROC_BROWSER_TEST_P(SamlTestWithFeatures, SamlUI) {
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
   StartSamlAndWaitForIdpPageLoad(
       saml_test_users::kFirstUserCorpExampleComEmail);
@@ -385,7 +424,8 @@
   content::DOMMessageQueue message_queue;  // Observe before 'close'.
   SetupAuthFlowChangeListener();
   // Click on 'close'.
-  test::OobeJS().ClickOnPath(kSamlCloseButton);
+  test::OobeJS().ClickOnPath(std::get<1>(GetParam()) ? kSamlBackButton
+                                                     : kSamlCloseButton);
 
   // Auth flow should change back to Gaia.
   std::string message;
@@ -399,7 +439,7 @@
 
 // The SAML IdP requires HTTP Protocol-level authentication (Basic in this
 // case).
-IN_PROC_BROWSER_TEST_P(SamlTest, IdpRequiresHttpAuth) {
+IN_PROC_BROWSER_TEST_P(SamlTestWithFeatures, IdpRequiresHttpAuth) {
   fake_saml_idp()->SetRequireHttpBasicAuth(true);
   fake_saml_idp()->SetLoginHTMLTemplate("saml_api_login.html");
   fake_saml_idp()->SetLoginAuthHTMLTemplate("saml_api_login_auth.html");
@@ -458,7 +498,7 @@
 }
 
 // Tests the sign-in flow when the credentials passing API is used.
-IN_PROC_BROWSER_TEST_P(SamlTest, CredentialPassingAPI) {
+IN_PROC_BROWSER_TEST_P(SamlTestWithFeatures, CredentialPassingAPI) {
   base::HistogramTester histogram_tester;
   fake_saml_idp()->SetLoginHTMLTemplate("saml_api_login.html");
   fake_saml_idp()->SetLoginAuthHTMLTemplate("saml_api_login_auth.html");
@@ -497,7 +537,8 @@
 
 // Tests the sign-in flow when the credentials passing API is used w/o 'confirm'
 // call. The password from the last 'add' should be used.
-IN_PROC_BROWSER_TEST_P(SamlTest, CredentialPassingAPIWithoutConfirm) {
+IN_PROC_BROWSER_TEST_P(SamlTestWithFeatures,
+                       CredentialPassingAPIWithoutConfirm) {
   base::HistogramTester histogram_tester;
   fake_saml_idp()->SetLoginHTMLTemplate("saml_api_login.html");
   fake_saml_idp()->SetLoginAuthHTMLTemplate(
@@ -534,7 +575,7 @@
 }
 
 // Tests the single password scraped flow.
-IN_PROC_BROWSER_TEST_P(SamlTest, ScrapedSingle) {
+IN_PROC_BROWSER_TEST_P(SamlTestWithFeatures, ScrapedSingle) {
   base::HistogramTester histogram_tester;
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
   StartSamlAndWaitForIdpPageLoad(
@@ -576,7 +617,7 @@
 }
 
 // Tests password scraping from a dynamically created password field.
-IN_PROC_BROWSER_TEST_P(SamlTest, ScrapedDynamic) {
+IN_PROC_BROWSER_TEST_P(SamlTestWithFeatures, ScrapedDynamic) {
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
   StartSamlAndWaitForIdpPageLoad(
       saml_test_users::kFirstUserCorpExampleComEmail);
@@ -606,7 +647,7 @@
 }
 
 // Tests the multiple password scraped flow.
-IN_PROC_BROWSER_TEST_P(SamlTest, ScrapedMultiple) {
+IN_PROC_BROWSER_TEST_P(SamlTestWithFeatures, ScrapedMultiple) {
   base::HistogramTester histogram_tester;
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login_two_passwords.html");
 
@@ -641,7 +682,7 @@
 }
 
 // Tests the no password scraped flow.
-IN_PROC_BROWSER_TEST_P(SamlTest, ScrapedNone) {
+IN_PROC_BROWSER_TEST_P(SamlTestWithFeatures, ScrapedNone) {
   base::HistogramTester histogram_tester;
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login_no_passwords.html");
 
@@ -679,7 +720,7 @@
 // Types the second user e-mail into the GAIA login form but then authenticates
 // as the first user via SAML. Verifies that the logged-in user is correctly
 // identified as the first user.
-IN_PROC_BROWSER_TEST_P(SamlTest, UseAutenticatedUserEmailAddress) {
+IN_PROC_BROWSER_TEST_P(SamlTestWithFeatures, UseAutenticatedUserEmailAddress) {
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
   // Type the second user e-mail into the GAIA login form.
   StartSamlAndWaitForIdpPageLoad(
@@ -703,7 +744,8 @@
 
 // Verifies that if the authenticated user's e-mail address cannot be retrieved,
 // an error message is shown.
-IN_PROC_BROWSER_TEST_P(SamlTest, FailToRetrieveAutenticatedUserEmailAddress) {
+IN_PROC_BROWSER_TEST_P(SamlTestWithFeatures,
+                       FailToRetrieveAutenticatedUserEmailAddress) {
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
   StartSamlAndWaitForIdpPageLoad(
       saml_test_users::kFirstUserCorpExampleComEmail);
@@ -720,7 +762,7 @@
 
 // Tests the password confirm flow when more than one password is scraped: show
 // error on the first failure and fatal error on the second failure.
-IN_PROC_BROWSER_TEST_P(SamlTest, PasswordConfirmFlow) {
+IN_PROC_BROWSER_TEST_P(SamlTestWithFeatures, PasswordConfirmFlow) {
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login_two_passwords.html");
   StartSamlAndWaitForIdpPageLoad(
       saml_test_users::kFirstUserCorpExampleComEmail);
@@ -754,7 +796,7 @@
 // Verifies that when the login flow redirects from one host to another, the
 // notice shown to the user is updated. This guards against regressions of
 // http://crbug.com/447818.
-IN_PROC_BROWSER_TEST_P(SamlTest, NoticeUpdatedOnRedirect) {
+IN_PROC_BROWSER_TEST_P(SamlTestWithFeatures, NoticeUpdatedOnRedirect) {
   // Start another https server at `kAdditionalIdPHost`.
   net::EmbeddedTestServer saml_https_server(
       net::EmbeddedTestServer::TYPE_HTTPS);
@@ -808,7 +850,7 @@
 
 // Verifies that when GAIA attempts to redirect to a SAML IdP served over http,
 // not https, the redirect is blocked and an error message is shown.
-IN_PROC_BROWSER_TEST_P(SamlTest, HTTPRedirectDisallowed) {
+IN_PROC_BROWSER_TEST_P(SamlTestWithFeatures, HTTPRedirectDisallowed) {
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
 
   WaitForSigninScreen();
@@ -826,7 +868,7 @@
 // Verifies that when GAIA attempts to redirect to a page served over http, not
 // https, via an HTML meta refresh, the redirect is blocked and an error message
 // is shown. This guards against regressions of http://crbug.com/359515.
-IN_PROC_BROWSER_TEST_P(SamlTest, MetaRefreshToHTTPDisallowed) {
+IN_PROC_BROWSER_TEST_P(SamlTestWithFeatures, MetaRefreshToHTTPDisallowed) {
   const GURL url = embedded_test_server()->base_url().Resolve("/SSO");
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login_instant_meta_refresh.html");
   fake_saml_idp()->SetRefreshURL(url);
@@ -842,7 +884,7 @@
       IDS_LOGIN_FATAL_ERROR_TEXT_INSECURE_URL, base::UTF8ToUTF16(url.spec())));
 }
 
-class SAMLEnrollmentTest : public SamlTestBase {
+class SAMLEnrollmentTest : public SamlTestWithRedirectToDefaultIdPFeature {
  public:
   SAMLEnrollmentTest();
 
@@ -891,7 +933,7 @@
   flow_change_waiter->Wait();
 }
 
-IN_PROC_BROWSER_TEST_F(SAMLEnrollmentTest, WithoutCredentialsPassingAPI) {
+IN_PROC_BROWSER_TEST_P(SAMLEnrollmentTest, WithoutCredentialsPassingAPI) {
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
   StartSamlAndWaitForIdpPageLoad(
       saml_test_users::kFirstUserCorpExampleComEmail);
@@ -904,7 +946,7 @@
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepDeviceAttributes);
 }
 
-IN_PROC_BROWSER_TEST_F(SAMLEnrollmentTest, WithCredentialsPassingAPI) {
+IN_PROC_BROWSER_TEST_P(SAMLEnrollmentTest, WithCredentialsPassingAPI) {
   fake_saml_idp()->SetLoginHTMLTemplate("saml_api_login.html");
   fake_saml_idp()->SetLoginAuthHTMLTemplate("saml_api_login_auth.html");
   StartSamlAndWaitForIdpPageLoad(
@@ -918,7 +960,9 @@
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepDeviceAttributes);
 }
 
-class SAMLPolicyTest : public SamlTestBase {
+INSTANTIATE_TEST_SUITE_P(All, SAMLEnrollmentTest, testing::Bool());
+
+class SAMLPolicyTest : public SamlTestWithRedirectToDefaultIdPFeature {
  public:
   SAMLPolicyTest();
 
@@ -941,6 +985,8 @@
   void ShowSAMLInterstitial();
   void ClickBackOnSAMLInterstitialPage();
   void ClickNextOnSAMLInterstitialPage();
+  void ShowSAMLLoginForm();
+  void ClickBackOnSAMLPage();
   void LogInWithSAML(const std::string& user_id,
                      const std::string& auth_sid_cookie,
                      const std::string& auth_lsid_cookie);
@@ -1119,9 +1165,7 @@
   MaybeWaitForLoginScreenLoad();
   ASSERT_TRUE(LoginScreenTestApi::ClickAddUserButton());
   OobeScreenWaiter(GaiaView::kScreenId).Wait();
-  test::OobeJS()
-      .CreateVisibilityWaiter(true, {"gaia-signin", "saml-interstitial"})
-      ->Wait();
+  test::OobeJS().CreateVisibilityWaiter(true, kSamlInterstitial)->Wait();
 }
 
 void SAMLPolicyTest::ClickBackOnSAMLInterstitialPage() {
@@ -1140,6 +1184,19 @@
   } while (message != "\"SamlLoaded\"");
 }
 
+void SAMLPolicyTest::ShowSAMLLoginForm() {
+  MaybeWaitForLoginScreenLoad();
+  ASSERT_TRUE(LoginScreenTestApi::ClickAddUserButton());
+  OobeScreenWaiter(GaiaView::kScreenId).Wait();
+  test::OobeJS().CreateVisibilityWaiter(true, kSigninFrameDialog)->Wait();
+  test::OobeJS().CreateVisibilityWaiter(false, kGaiaLoading)->Wait();
+  test::OobeJS().ExpectAttributeEQ("isSamlForTesting()", {"gaia-signin"}, true);
+}
+
+void SAMLPolicyTest::ClickBackOnSAMLPage() {
+  test::OobeJS().TapOnPath(kSamlBackButton);
+}
+
 void SAMLPolicyTest::LogInWithSAML(const std::string& user_id,
                                    const std::string& auth_sid_cookie,
                                    const std::string& auth_lsid_cookie) {
@@ -1182,7 +1239,7 @@
   run_loop.Run();
 }
 
-IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_NoSAML) {
+IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, PRE_NoSAML) {
   // Set the offline login time limit for SAML users to zero.
   SetSAMLOfflineSigninTimeLimitPolicy(0);
 
@@ -1209,7 +1266,7 @@
 
 // Verifies that the offline login time limit does not affect a user who
 // authenticated without SAML.
-IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, NoSAML) {
+IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, NoSAML) {
   // Verify that offline login is allowed.
   LoginScreenTestApi::SubmitPassword(
       AccountId::FromUserEmail(kNonSAMLUserEmail), "password",
@@ -1217,7 +1274,7 @@
   test::WaitForPrimaryUserSessionStart();
 }
 
-IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_SAMLNoLimit) {
+IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, PRE_SAMLNoLimit) {
   // Remove the offline login time limit for SAML users.
   SetSAMLOfflineSigninTimeLimitPolicy(-1);
 
@@ -1228,7 +1285,7 @@
 
 // Verifies that when no offline login time limit is set, a user who
 // authenticated with SAML is allowed to log in offline.
-IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, SAMLNoLimit) {
+IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, SAMLNoLimit) {
   // Verify that offline login is allowed.
   LoginScreenTestApi::SubmitPassword(
       AccountId::FromUserEmail(saml_test_users::kFirstUserCorpExampleComEmail),
@@ -1236,7 +1293,7 @@
   test::WaitForPrimaryUserSessionStart();
 }
 
-IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_SAMLZeroLimit) {
+IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, PRE_SAMLZeroLimit) {
   // Set the offline login time limit for SAML users to zero.
   SetSAMLOfflineSigninTimeLimitPolicy(0);
 
@@ -1247,13 +1304,13 @@
 
 // Verifies that when the offline login time limit is exceeded for a user who
 // authenticated via SAML, that user is forced to log in online the next time.
-IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, SAMLZeroLimit) {
+IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, SAMLZeroLimit) {
   // Verify that offline login is not allowed.
   ASSERT_TRUE(LoginScreenTestApi::IsForcedOnlineSignin(AccountId::FromUserEmail(
       saml_test_users::kFirstUserCorpExampleComEmail)));
 }
 
-IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_PRE_TransferCookiesAffiliated) {
+IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, PRE_PRE_TransferCookiesAffiliated) {
   fake_saml_idp()->SetCookieValue(kSAMLIdPCookieValue1);
   ShowGAIALoginForm();
   LogInWithSAML(saml_test_users::kFirstUserCorpExampleComEmail,
@@ -1269,7 +1326,7 @@
 // IdP cookies are not transferred to a user's profile on subsequent login, even
 // if the user belongs to the domain that the device is enrolled into. Also
 // verifies that GAIA cookies are not transferred.
-IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_TransferCookiesAffiliated) {
+IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, PRE_TransferCookiesAffiliated) {
   fake_saml_idp()->SetCookieValue(kSAMLIdPCookieValue2);
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
   ShowGAIALoginForm();
@@ -1286,7 +1343,7 @@
 // cookies are transferred to a user's profile on subsequent login when the user
 // belongs to the domain that the device is enrolled into. Also verifies that
 // GAIA cookies are not transferred.
-IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, TransferCookiesAffiliated) {
+IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, TransferCookiesAffiliated) {
   fake_saml_idp()->SetCookieValue(kSAMLIdPCookieValue2);
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
   ShowGAIALoginForm();
@@ -1301,7 +1358,7 @@
   EXPECT_EQ(kSAMLIdPCookieValue2, GetCookieValue(kSAMLIdPCookieName));
 }
 
-IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, PRE_TransferCookiesUnaffiliated) {
+IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, PRE_TransferCookiesUnaffiliated) {
   fake_saml_idp()->SetCookieValue(kSAMLIdPCookieValue1);
   ShowGAIALoginForm();
   LogInWithSAML(saml_test_users::kFifthUserExampleTestEmail,
@@ -1317,7 +1374,7 @@
 // IdP are not transferred to a user's profile on subsequent login if the user
 // does not belong to the domain that the device is enrolled into. Also verifies
 // that GAIA cookies are not transferred.
-IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, TransferCookiesUnaffiliated) {
+IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, TransferCookiesUnaffiliated) {
   fake_saml_idp()->SetCookieValue(kSAMLIdPCookieValue2);
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
   ShowGAIALoginForm();
@@ -1332,38 +1389,56 @@
   EXPECT_EQ(kSAMLIdPCookieValue1, GetCookieValue(kSAMLIdPCookieName));
 }
 
+// When GetParam() is false:
 // Tests that the SAML interstitial page is loaded when the authentication
 // behavior device policy is set to SAML_INTERSTITIAL, and when the user clicks
 // the "change account" link, we go back to the default GAIA signin screen.
-IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, SAMLInterstitialChangeAccount) {
+//
+// When GetParam() is true:
+// Tests that the SAML page is loaded when the authentication behavior device
+// policy is set to SAML_INTERSTITIAL, and when the user clicks the "Enter
+// Google Account info" button, we go to the default GAIA signin screen.
+IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, SAMLInterstitialChangeAccount) {
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
   SetLoginBehaviorPolicyToSAMLInterstitial();
   WaitForSigninScreen();
 
-  ShowSAMLInterstitial();
-  test::OobeJS().ExpectHiddenPath({"gaia-signin", "signin-frame-dialog"});
-  test::OobeJS().ExpectVisiblePath({"gaia-signin", "saml-interstitial"});
+  if (GetParam()) {
+    ShowSAMLLoginForm();
 
-  // Click the "change account" link on the SAML interstitial page.
-  test::OobeJS().TapLinkOnPath({"gaia-signin", "interstitial-change-account"});
+    // Click the "Enter Google Account info" button on the SAML page.
+    test::OobeJS().TapOnPath(kChangeIdPButton);
+  } else {
+    ShowSAMLInterstitial();
+    test::OobeJS().ExpectHiddenPath(kSigninFrameDialog);
+    test::OobeJS().ExpectVisiblePath(kSamlInterstitial);
 
-  // Expects that only the gaia signin frame is visible and shown.
-  test::OobeJS()
-      .CreateVisibilityWaiter(true, {"gaia-signin", "signin-frame-dialog"})
-      ->Wait();
-  test::OobeJS()
-      .CreateVisibilityWaiter(false, {"gaia-signin", "gaia-loading"})
-      ->Wait();
-  test::OobeJS().ExpectHasNoAttribute("transparent",
-                                      {"gaia-signin", "signin-frame-dialog"});
-  test::OobeJS().ExpectHiddenPath({"gaia-signin", "saml-interstitial"});
+    // Click the "change account" link on the SAML interstitial page.
+    test::OobeJS().TapLinkOnPath(kChangeIdPLink);
+  }
+
+  // Expects that the gaia signin frame is shown.
+  test::OobeJS().CreateVisibilityWaiter(true, kSigninFrameDialog)->Wait();
+  test::OobeJS().CreateVisibilityWaiter(false, kGaiaLoading)->Wait();
+  test::OobeJS().ExpectHasNoAttribute("transparent", kSigninFrameDialog);
+  test::OobeJS().ExpectAttributeEQ("isSamlForTesting()", {"gaia-signin"},
+                                   false);
+
+  if (!GetParam())
+    test::OobeJS().ExpectHiddenPath(kSamlInterstitial);
 }
 
+// When GetParam() is false:
 // Tests that clicking "Next" in the SAML interstitial page successfully
 // triggers a SAML redirect request, and the SAML IdP authentication page is
 // loaded and authenticaing there is successful.
 // TODO(https://crbug.com/1102738) flaky test
-IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, DISABLED_SAMLInterstitialNext) {
+//
+// When GetParam() is true:
+// Tests that clicking back on the SAML page successfully closes the oobe
+// dialog. Reopens a dialog and checks that SAML IdP authentication page is
+// loaded and authenticaing there is successful.
+IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, DISABLED_SAMLInterstitialNext) {
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
   fake_gaia_.fake_gaia()->SetFakeMergeSessionParams(
       saml_test_users::kFirstUserCorpExampleComEmail, kTestAuthSIDCookie1,
@@ -1371,14 +1446,23 @@
   SetLoginBehaviorPolicyToSAMLInterstitial();
   WaitForSigninScreen();
 
-  ShowSAMLInterstitial();
-  ClickBackOnSAMLInterstitialPage();
+  if (GetParam()) {
+    ShowSAMLLoginForm();
+    ClickBackOnSAMLPage();
+  } else {
+    ShowSAMLInterstitial();
+    ClickBackOnSAMLInterstitialPage();
+  }
   // Back button should hide OOBE dialog.
   EXPECT_FALSE(LoginScreenTestApi::IsOobeDialogVisible());
   EXPECT_TRUE(LoginScreenTestApi::IsAddUserButtonShown());
 
-  ShowSAMLInterstitial();
-  ClickNextOnSAMLInterstitialPage();
+  if (GetParam()) {
+    ShowSAMLLoginForm();
+  } else {
+    ShowSAMLInterstitial();
+    ClickNextOnSAMLInterstitialPage();
+  }
 
   SigninFrameJS().TypeIntoPath("fake_user", {"Email"});
   SigninFrameJS().TypeIntoPath("fake_password", {"Password"});
@@ -1391,7 +1475,7 @@
 // Ensure that the permission status of getUserMedia requests from SAML login
 // pages is controlled by the kLoginVideoCaptureAllowedUrls pref rather than the
 // underlying user content setting.
-IN_PROC_BROWSER_TEST_F(SAMLPolicyTest, TestLoginMediaPermission) {
+IN_PROC_BROWSER_TEST_P(SAMLPolicyTest, TestLoginMediaPermission) {
   fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
 
   const GURL url1("https://google.com");
@@ -1438,10 +1522,16 @@
       blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
 }
 
-class SAMLPasswordAttributesTest : public SAMLPolicyTest,
-                                   public testing::WithParamInterface<bool> {
+INSTANTIATE_TEST_SUITE_P(All, SAMLPolicyTest, testing::Bool());
+
+class SAMLPasswordAttributesTest : public SAMLPolicyTest {
  public:
-  SAMLPasswordAttributesTest() = default;
+  SAMLPasswordAttributesTest() {
+    // Reset feature list, as we use parameterization here to test against
+    // different policy values and not against enabling/disabling feature
+    // from SamlTestWithRedirectToDefaultIdPFeature.
+    scoped_feature_list_.Reset();
+  }
 
   SAMLPasswordAttributesTest(const SAMLPasswordAttributesTest&) = delete;
   SAMLPasswordAttributesTest& operator=(const SAMLPasswordAttributesTest&) =
@@ -1554,7 +1644,8 @@
     "kTimeoutTaskDelay should be less than kBuildResponseTaskDelay to trigger "
     "timeout error in SAMLDeviceAttestationTest.TimeoutError test.");
 
-class SAMLDeviceAttestationTest : public SamlTestBase {
+class SAMLDeviceAttestationTest
+    : public SamlTestWithRedirectToDefaultIdPFeature {
  public:
   SAMLDeviceAttestationTest() = default;
 
@@ -1619,7 +1710,7 @@
 
 // Verify that device attestation is not available when
 // DeviceWebBasedAttestationAllowedUrls policy is not set.
-IN_PROC_BROWSER_TEST_F(SAMLDeviceAttestationTest, DefaultPolicy) {
+IN_PROC_BROWSER_TEST_P(SAMLDeviceAttestationTest, DefaultPolicy) {
   base::HistogramTester histogram_tester;
 
   // Leave policy unset.
@@ -1637,7 +1728,7 @@
 // Verify that device attestation is not available when
 // DeviceWebBasedAttestationAllowedUrls policy is set to empty list of allowed
 // URLs.
-IN_PROC_BROWSER_TEST_F(SAMLDeviceAttestationTest, EmptyPolicy) {
+IN_PROC_BROWSER_TEST_P(SAMLDeviceAttestationTest, EmptyPolicy) {
   base::HistogramTester histogram_tester;
   SetAllowedUrlsPolicy({/* empty list */});
 
@@ -1653,7 +1744,7 @@
 
 // Verify that device attestation is not available when device is not enterprise
 // enrolled.
-IN_PROC_BROWSER_TEST_F(SAMLDeviceAttestationTest, NotEnterpriseEnrolledError) {
+IN_PROC_BROWSER_TEST_P(SAMLDeviceAttestationTest, NotEnterpriseEnrolledError) {
   base::HistogramTester histogram_tester;
   SetAllowedUrlsPolicy({fake_saml_idp()->GetIdpHost()});
 
@@ -1670,9 +1761,11 @@
       attestation::TpmChallengeKeyResultCode::kNonEnterpriseDeviceError, 1);
 }
 
+INSTANTIATE_TEST_SUITE_P(All, SAMLDeviceAttestationTest, testing::Bool());
+
 // Verify that device attestation is not available when device attestation is
 // not enabled.
-IN_PROC_BROWSER_TEST_F(SAMLDeviceAttestationEnrolledTest,
+IN_PROC_BROWSER_TEST_P(SAMLDeviceAttestationEnrolledTest,
                        DeviceAttestationNotEnabledError) {
   base::HistogramTester histogram_tester;
   SetAllowedUrlsPolicy({fake_saml_idp()->GetIdpHost()});
@@ -1691,7 +1784,7 @@
 }
 
 // Verify that device attestation works when all policies configured correctly.
-IN_PROC_BROWSER_TEST_F(SAMLDeviceAttestationEnrolledTest, Success) {
+IN_PROC_BROWSER_TEST_P(SAMLDeviceAttestationEnrolledTest, Success) {
   base::HistogramTester histogram_tester;
   SetAllowedUrlsPolicy({fake_saml_idp()->GetIdpHost()});
   settings_provider_->SetBoolean(kDeviceAttestationEnabled, true);
@@ -1713,7 +1806,7 @@
 
 // Verify that device attestation is not available for URLs that are not in the
 // allowed URLs list.
-IN_PROC_BROWSER_TEST_F(SAMLDeviceAttestationEnrolledTest, PolicyNoMatchError) {
+IN_PROC_BROWSER_TEST_P(SAMLDeviceAttestationEnrolledTest, PolicyNoMatchError) {
   base::HistogramTester histogram_tester;
   SetAllowedUrlsPolicy({fake_saml_idp()->GetIdpDomain()});
   settings_provider_->SetBoolean(kDeviceAttestationEnabled, true);
@@ -1734,7 +1827,7 @@
 
 // Verify that device attestation is available for URLs that match a pattern
 // from allowed URLs list.
-IN_PROC_BROWSER_TEST_F(SAMLDeviceAttestationEnrolledTest, PolicyRegexSuccess) {
+IN_PROC_BROWSER_TEST_P(SAMLDeviceAttestationEnrolledTest, PolicyRegexSuccess) {
   base::HistogramTester histogram_tester;
   SetAllowedUrlsPolicy({"[*.]" + fake_saml_idp()->GetIdpDomain()});
   settings_provider_->SetBoolean(kDeviceAttestationEnabled, true);
@@ -1756,7 +1849,7 @@
 
 // Verify that device attestation works in case of multiple items in allowed
 // URLs list.
-IN_PROC_BROWSER_TEST_F(SAMLDeviceAttestationEnrolledTest,
+IN_PROC_BROWSER_TEST_P(SAMLDeviceAttestationEnrolledTest,
                        PolicyTwoEntriesSuccess) {
   base::HistogramTester histogram_tester;
   SetAllowedUrlsPolicy({"example2.com", fake_saml_idp()->GetIdpHost()});
@@ -1777,7 +1870,7 @@
       attestation::TpmChallengeKeyResultCode::kSuccess, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(SAMLDeviceAttestationEnrolledTest, TimeoutError) {
+IN_PROC_BROWSER_TEST_P(SAMLDeviceAttestationEnrolledTest, TimeoutError) {
   base::HistogramTester histogram_tester;
   SetAllowedUrlsPolicy({"example2.com", fake_saml_idp()->GetIdpHost()});
   settings_provider_->SetBoolean(kDeviceAttestationEnabled, true);
@@ -1807,6 +1900,13 @@
       attestation::TpmChallengeKeyResultCode::kTimeoutError, 1);
 }
 
-INSTANTIATE_TEST_SUITE_P(All, SamlTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All,
+                         SAMLDeviceAttestationEnrolledTest,
+                         testing::Bool());
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         SamlTestWithFeatures,
+                         ::testing::Combine(::testing::Bool(),
+                                            ::testing::Bool()));
 
 }  // namespace ash
diff --git a/chrome/browser/ash/login/screens/gaia_screen.cc b/chrome/browser/ash/login/screens/gaia_screen.cc
index 1889ed1..6e6e29b7 100644
--- a/chrome/browser/ash/login/screens/gaia_screen.cc
+++ b/chrome/browser/ash/login/screens/gaia_screen.cc
@@ -18,6 +18,7 @@
 constexpr char kUserActionBack[] = "back";
 constexpr char kUserActionCancel[] = "cancel";
 constexpr char kUserActionStartEnrollment[] = "startEnrollment";
+constexpr char kUserActionReloadDefault[] = "reloadDefault";
 
 }  // namespace
 
@@ -96,6 +97,9 @@
     exit_callback_.Run(Result::CANCEL);
   } else if (action_id == kUserActionStartEnrollment) {
     exit_callback_.Run(Result::ENTERPRISE_ENROLL);
+  } else if (action_id == kUserActionReloadDefault) {
+    DCHECK(features::IsRedirectToDefaultIdPEnabled());
+    LoadOnline(EmptyAccountId());
   } else {
     BaseScreen::OnUserAction(action_id);
   }
diff --git a/chrome/browser/ash/login/screens/packaged_license_screen_browsertest.cc b/chrome/browser/ash/login/screens/packaged_license_screen_browsertest.cc
index d5fb74f..24ed170 100644
--- a/chrome/browser/ash/login/screens/packaged_license_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/packaged_license_screen_browsertest.cc
@@ -41,7 +41,8 @@
   }
 
   void SetUpLicense(bool value) {
-    DictionaryPrefUpdate dict(local_state(), prefs::kServerBackedDeviceState);
+    DictionaryPrefUpdateDeprecated dict(local_state(),
+                                        prefs::kServerBackedDeviceState);
     if (value) {
       dict.Get()->SetKey(policy::kDeviceStatePackagedLicense,
                          base::Value(true));
diff --git a/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.cc b/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.cc
index a4bf429..d8a58b8 100644
--- a/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.cc
+++ b/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.cc
@@ -40,9 +40,8 @@
 }  // namespace
 
 EmbeddedPolicyTestServerMixin::EmbeddedPolicyTestServerMixin(
-    InProcessBrowserTestMixinHost* host,
-    std::initializer_list<Capabilities> capabilities)
-    : InProcessBrowserTestMixin(host), capabilities_(capabilities) {}
+    InProcessBrowserTestMixinHost* host)
+    : InProcessBrowserTestMixin(host) {}
 
 EmbeddedPolicyTestServerMixin::~EmbeddedPolicyTestServerMixin() = default;
 
@@ -53,22 +52,13 @@
       FakeGaiaMixin::kFakeAuthCode);
   policy_test_server_->policy_storage()->add_managed_user("*");
 
-  if (!capabilities_.contains(ENABLE_CANNED_SIGNING_KEYS)) {
-    // Create universal signing keys that can sign any domain.
-    std::vector<policy::SignatureProvider::SigningKey> universal_signing_keys;
-    universal_signing_keys.push_back(policy::SignatureProvider::SigningKey(
-        policy::PolicyBuilder::CreateTestSigningKey(),
-        {{"*", policy::PolicyBuilder::GetTestSigningKeySignature()}}));
-    policy_test_server_->policy_storage()
-        ->signature_provider()
-        ->set_signing_keys(std::move(universal_signing_keys));
-  }
-
-  if (capabilities_.contains(ENABLE_AUTOMATIC_ROTATION_OF_SIGNINGKEYS)) {
-    policy_test_server_->policy_storage()
-        ->signature_provider()
-        ->set_rotate_keys(true);
-  }
+  // Create universal signing keys that can sign any domain.
+  std::vector<policy::SignatureProvider::SigningKey> universal_signing_keys;
+  universal_signing_keys.push_back(policy::SignatureProvider::SigningKey(
+      policy::PolicyBuilder::CreateTestSigningKey(),
+      {{"*", policy::PolicyBuilder::GetTestSigningKeySignature()}}));
+  policy_test_server_->policy_storage()->signature_provider()->set_signing_keys(
+      std::move(universal_signing_keys));
 
   // Register default user used in many tests.
   policy::ClientStorage::ClientInfo client_info;
@@ -96,30 +86,16 @@
 
 void EmbeddedPolicyTestServerMixin::UpdateDevicePolicy(
     const enterprise_management::ChromeDeviceSettingsProto& policy) {
-  UpdatePolicy(policy::dm_protocol::kChromeDevicePolicyType,
-               policy.SerializeAsString());
+  policy_test_server_->policy_storage()->SetPolicyPayload(
+      policy::dm_protocol::kChromeDevicePolicyType, policy.SerializeAsString());
 }
 
 void EmbeddedPolicyTestServerMixin::UpdateUserPolicy(
     const enterprise_management::CloudPolicySettings& policy,
     const std::string& policy_user) {
   policy_test_server_->policy_storage()->set_policy_user(policy_user);
-  UpdatePolicy(policy::dm_protocol::kChromeUserPolicyType,
-               policy.SerializeAsString());
-}
-
-void EmbeddedPolicyTestServerMixin::UpdatePolicy(
-    const std::string& type,
-    const std::string& serialized_policy) {
-  UpdatePolicy(type, std::string(), serialized_policy);
-}
-
-void EmbeddedPolicyTestServerMixin::UpdatePolicy(
-    const std::string& type,
-    const std::string& entity_id,
-    const std::string& serialized_policy) {
-  policy_test_server_->policy_storage()->SetPolicyPayload(type, entity_id,
-                                                          serialized_policy);
+  policy_test_server_->policy_storage()->SetPolicyPayload(
+      policy::dm_protocol::kChromeUserPolicyType, policy.SerializeAsString());
 }
 
 void EmbeddedPolicyTestServerMixin::SetUpdateDeviceAttributesPermission(
diff --git a/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.h b/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.h
index f1bd1f43..0c4bcff 100644
--- a/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.h
+++ b/chrome/browser/ash/login/test/embedded_policy_test_server_mixin.h
@@ -5,12 +5,10 @@
 #ifndef CHROME_BROWSER_ASH_LOGIN_TEST_EMBEDDED_POLICY_TEST_SERVER_MIXIN_H_
 #define CHROME_BROWSER_ASH_LOGIN_TEST_EMBEDDED_POLICY_TEST_SERVER_MIXIN_H_
 
-#include <initializer_list>
 #include <memory>
 #include <string>
 
 #include "base/command_line.h"
-#include "base/containers/flat_set.h"
 #include "chrome/browser/ash/policy/server_backed_state/server_backed_state_keys_broker.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "chromeos/system/fake_statistics_provider.h"
@@ -30,18 +28,7 @@
 // Server is started after SetUp execution.
 class EmbeddedPolicyTestServerMixin : public InProcessBrowserTestMixin {
  public:
-  enum Capabilities {
-    // Enables the usage of keys canned into the policy test server, instead of
-    // the default key returned by PolicyBuilder::CreateTestSigningKey().
-    ENABLE_CANNED_SIGNING_KEYS,
-    // Enables the automatic rotation of the policy signing keys with each
-    // policy fetch request.
-    ENABLE_AUTOMATIC_ROTATION_OF_SIGNINGKEYS
-  };
-
-  explicit EmbeddedPolicyTestServerMixin(
-      InProcessBrowserTestMixinHost* host,
-      std::initializer_list<Capabilities> capabilities = {});
+  explicit EmbeddedPolicyTestServerMixin(InProcessBrowserTestMixinHost* host);
 
   EmbeddedPolicyTestServerMixin(const EmbeddedPolicyTestServerMixin&) = delete;
   EmbeddedPolicyTestServerMixin& operator=(
@@ -57,29 +44,16 @@
   void SetUp() override;
   void SetUpCommandLine(base::CommandLine* command_line) override;
 
-  // Updates the device policy blob served by the embedded policy test server.
-  // This does not trigger policy invalidation, hence test authors must manually
-  // trigger a policy fetch.
+  // Updates the device policy blob served by the local policy test server.
   void UpdateDevicePolicy(
       const enterprise_management::ChromeDeviceSettingsProto& policy);
 
   // Updates user policy blob served by the embedded policy test server.
-  // `policy_user` - the policy user's email. This does not trigger policy
-  // invalidation, hence test authors must manually trigger a policy fetch.
+  // `policy_user` - the policy user's email.
   void UpdateUserPolicy(
       const enterprise_management::CloudPolicySettings& policy,
       const std::string& policy_user);
 
-  // Updates policy selected by |type| and optional |entity_id|. The policy is
-  // set to the proto serialized in |serialized_policy|. This does not trigger
-  // policy invalidation, hence test authors must manually trigger a policy
-  // fetch.
-  void UpdatePolicy(const std::string& type,
-                    const std::string& serialized_policy);
-  void UpdatePolicy(const std::string& type,
-                    const std::string& entity_id,
-                    const std::string& serialized_policy);
-
   // Configures whether the server should indicate that the client is
   // allowed to update device attributes in response to
   // DeviceAttributeUpdatePermissionRequest.
@@ -135,7 +109,6 @@
 
  private:
   std::unique_ptr<policy::EmbeddedPolicyTestServer> policy_test_server_;
-  base::flat_set<Capabilities> capabilities_;
 };
 
 }  // namespace ash
diff --git a/chrome/browser/ash/login/test/login_manager_mixin.cc b/chrome/browser/ash/login/test/login_manager_mixin.cc
index e877ea09..c9f6928 100644
--- a/chrome/browser/ash/login/test/login_manager_mixin.cc
+++ b/chrome/browser/ash/login/test/login_manager_mixin.cc
@@ -127,19 +127,19 @@
 
 void LoginManagerMixin::SetUpLocalState() {
   for (const auto& user : initial_users_) {
-    ListPrefUpdate users_pref(g_browser_process->local_state(),
-                              "LoggedInUsers");
+    ListPrefUpdateDeprecated users_pref(g_browser_process->local_state(),
+                                        "LoggedInUsers");
     base::Value email_value(user.account_id.GetUserEmail());
     if (!base::Contains(users_pref->GetList(), email_value))
       users_pref->Append(std::move(email_value));
 
-    DictionaryPrefUpdate user_type_update(g_browser_process->local_state(),
-                                          "UserType");
+    DictionaryPrefUpdateDeprecated user_type_update(
+        g_browser_process->local_state(), "UserType");
     user_type_update->SetKey(user.account_id.GetAccountIdKey(),
                              base::Value(static_cast<int>(user.user_type)));
 
-    DictionaryPrefUpdate user_token_update(g_browser_process->local_state(),
-                                           "OAuthTokenStatus");
+    DictionaryPrefUpdateDeprecated user_token_update(
+        g_browser_process->local_state(), "OAuthTokenStatus");
     user_token_update->SetKey(user.account_id.GetUserEmail(),
                               base::Value(static_cast<int>(user.token_status)));
 
diff --git a/chrome/browser/ash/login/ui/login_display_host_mojo.cc b/chrome/browser/ash/login/ui/login_display_host_mojo.cc
index a5317fa..f01f15c 100644
--- a/chrome/browser/ash/login/ui/login_display_host_mojo.cc
+++ b/chrome/browser/ash/login/ui/login_display_host_mojo.cc
@@ -398,6 +398,7 @@
 
 void LoginDisplayHostMojo::ShowOsInstallScreen() {
   StartWizard(OsInstallScreenView::kScreenId);
+  ShowDialog();
 }
 
 void LoginDisplayHostMojo::ShowGuestTosScreen() {
diff --git a/chrome/browser/ash/login/ui/login_feedback.cc b/chrome/browser/ash/login/ui/login_feedback.cc
index cc5b93e..016a183 100644
--- a/chrome/browser/ash/login/ui/login_feedback.cc
+++ b/chrome/browser/ash/login/ui/login_feedback.cc
@@ -212,7 +212,7 @@
         /*show_questionnaire=*/false,
         /*from_chrome_labs_or_kaleidoscope=*/false);
 
-    FeedbackDialog::CreateOrShow(*info);
+    FeedbackDialog::CreateOrShow(ProfileManager::GetActiveUserProfile(), *info);
   } else {
     api->RequestFeedbackForFlow(
         description_, std::string(), "Login", std::string(), GURL(),
diff --git a/chrome/browser/ash/login/users/avatar/user_image_manager_impl.cc b/chrome/browser/ash/login/users/avatar/user_image_manager_impl.cc
index 49311a7..95bc4c0 100644
--- a/chrome/browser/ash/login/users/avatar/user_image_manager_impl.cc
+++ b/chrome/browser/ash/login/users/avatar/user_image_manager_impl.cc
@@ -465,7 +465,7 @@
     return;
   }
 
-  DictionaryPrefUpdate update(local_state, kUserImageProperties);
+  DictionaryPrefUpdateDeprecated update(local_state, kUserImageProperties);
 
   update->SetKey(account_id().GetUserEmail(), std::move(entry));
 
@@ -855,8 +855,8 @@
 
 void UserImageManagerImpl::DeleteUserImageAndLocalStateEntry(
     const char* prefs_dict_root) {
-  DictionaryPrefUpdate update(g_browser_process->local_state(),
-                              prefs_dict_root);
+  DictionaryPrefUpdateDeprecated update(g_browser_process->local_state(),
+                                        prefs_dict_root);
   const base::DictionaryValue* image_properties;
   if (!update->GetDictionaryWithoutPathExpansion(account_id_.GetUserEmail(),
                                                  &image_properties))
diff --git a/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc b/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc
index 3017d7b..c994652 100644
--- a/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc
+++ b/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc
@@ -143,7 +143,7 @@
   int synced_index;
   if (GetSyncedImageIndex(&synced_index) && (synced_index == local_index))
     return;
-  DictionaryPrefUpdate update(prefs_, kUserImageInfo);
+  DictionaryPrefUpdateDeprecated update(prefs_, kUserImageInfo);
   base::DictionaryValue* dict = update.Get();
   dict->SetInteger(kImageIndex, local_index);
   VLOG(1) << "Saved avatar index " << local_index << " to sync.";
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
index 3eb1ef1..897b2960 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
@@ -790,8 +790,8 @@
   // If ephemeral users are enabled and we are on the login screen, take this
   // opportunity to clean up by removing all regular users except the owner.
   if (GetEphemeralUsersEnabled() && !IsUserLoggedIn()) {
-    ListPrefUpdate prefs_users_update(GetLocalState(),
-                                      user_manager::kRegularUsersPref);
+    ListPrefUpdateDeprecated prefs_users_update(
+        GetLocalState(), user_manager::kRegularUsersPref);
     prefs_users_update->ClearList();
     for (user_manager::UserList::iterator it = users_.begin();
          it != users_.end();) {
@@ -957,7 +957,8 @@
   // TODO(tbarzic): Forward data removal request to HammerDeviceHandler,
   // instead of removing the prefs value here.
   if (GetLocalState()->FindPreference(prefs::kDetachableBaseDevices)) {
-    DictionaryPrefUpdate update(GetLocalState(), prefs::kDetachableBaseDevices);
+    DictionaryPrefUpdateDeprecated update(GetLocalState(),
+                                          prefs::kDetachableBaseDevices);
     update->RemoveKey(account_id.HasAccountIdKey()
                           ? account_id.GetAccountIdKey()
                           : account_id.GetUserEmail());
@@ -1052,7 +1053,7 @@
   // will be loaded in LoadDeviceLocalAccounts() on the next reboot regardless
   // of whether they still exist in kAccountsPrefDeviceLocalAccounts, allowing
   // us to clean up associated data if they disappear from policy.
-  ListPrefUpdate prefs_device_local_accounts_update(
+  ListPrefUpdateDeprecated prefs_device_local_accounts_update(
       GetLocalState(), kDeviceLocalAccountsWithSavedData);
   prefs_device_local_accounts_update->ClearList();
   for (const auto& account : device_local_accounts)
@@ -1349,14 +1350,16 @@
 }
 
 void ChromeUserManagerImpl::AddReportingUser(const AccountId& account_id) {
-  ListPrefUpdate users_update(GetLocalState(), ::prefs::kReportingUsers);
+  ListPrefUpdateDeprecated users_update(GetLocalState(),
+                                        ::prefs::kReportingUsers);
   base::Value email_value(account_id.GetUserEmail());
   if (!base::Contains(users_update->GetList(), email_value))
     users_update->Append(std::move(email_value));
 }
 
 void ChromeUserManagerImpl::RemoveReportingUser(const AccountId& account_id) {
-  ListPrefUpdate users_update(GetLocalState(), ::prefs::kReportingUsers);
+  ListPrefUpdateDeprecated users_update(GetLocalState(),
+                                        ::prefs::kReportingUsers);
   users_update->EraseListIter(
       std::find(users_update->GetList().begin(), users_update->GetList().end(),
                 base::Value(FullyCanonicalize(account_id.GetUserEmail()))));
diff --git a/chrome/browser/ash/login/users/multi_profile_user_controller.cc b/chrome/browser/ash/login/users/multi_profile_user_controller.cc
index fc0a51ed..5f5f54f 100644
--- a/chrome/browser/ash/login/users/multi_profile_user_controller.cc
+++ b/chrome/browser/ash/login/users/multi_profile_user_controller.cc
@@ -161,8 +161,8 @@
 
 void MultiProfileUserController::RemoveCachedValues(
     const std::string& user_email) {
-  DictionaryPrefUpdate update(local_state_,
-                              prefs::kCachedMultiProfileUserBehavior);
+  DictionaryPrefUpdateDeprecated update(local_state_,
+                                        prefs::kCachedMultiProfileUserBehavior);
   update->RemoveKey(user_email);
 }
 
@@ -182,8 +182,8 @@
 
 void MultiProfileUserController::SetCachedValue(const std::string& user_email,
                                                 const std::string& behavior) {
-  DictionaryPrefUpdate update(local_state_,
-                              prefs::kCachedMultiProfileUserBehavior);
+  DictionaryPrefUpdateDeprecated update(local_state_,
+                                        prefs::kCachedMultiProfileUserBehavior);
   update->SetKey(user_email, base::Value(SanitizeBehaviorValue(behavior)));
 }
 
@@ -209,8 +209,8 @@
           ->IsDefaultValue()) {
     // Migration code to clear cached default behavior.
     // TODO(xiyuan): Remove this after M35.
-    DictionaryPrefUpdate update(local_state_,
-                                prefs::kCachedMultiProfileUserBehavior);
+    DictionaryPrefUpdateDeprecated update(
+        local_state_, prefs::kCachedMultiProfileUserBehavior);
     update->RemoveKey(user_email);
   } else {
     const std::string behavior =
diff --git a/chrome/browser/ash/login/users/supervised_user_manager_impl.cc b/chrome/browser/ash/login/users/supervised_user_manager_impl.cc
index b82a2b9..ee19187 100644
--- a/chrome/browser/ash/login/users/supervised_user_manager_impl.cc
+++ b/chrome/browser/ash/login/users/supervised_user_manager_impl.cc
@@ -229,7 +229,7 @@
                                                    const char* key,
                                                    const std::string& value) {
   PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate update(local_state, key);
+  DictionaryPrefUpdateDeprecated update(local_state, key);
   update->SetKey(user_id, base::Value(value));
 }
 
@@ -237,7 +237,7 @@
                                                     const char* key,
                                                     const int value) {
   PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate update(local_state, key);
+  DictionaryPrefUpdateDeprecated update(local_state, key);
   update->SetKey(user_id, base::Value(value));
 }
 
@@ -245,14 +245,15 @@
                                                     const char* key,
                                                     const bool value) {
   PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate update(local_state, key);
+  DictionaryPrefUpdateDeprecated update(local_state, key);
   update->SetKey(user_id, base::Value(value));
 }
 
 void SupervisedUserManagerImpl::RemoveNonCryptohomeData(
     const std::string& user_id) {
   PrefService* prefs = g_browser_process->local_state();
-  ListPrefUpdate prefs_new_users_update(prefs, kSupervisedUsersFirstRun);
+  ListPrefUpdateDeprecated prefs_new_users_update(prefs,
+                                                  kSupervisedUsersFirstRun);
   prefs_new_users_update->EraseListValue(base::Value(user_id));
 
   CleanPref(user_id, kSupervisedUserSyncId);
@@ -269,13 +270,13 @@
 void SupervisedUserManagerImpl::CleanPref(const std::string& user_id,
                                           const char* key) {
   PrefService* prefs = g_browser_process->local_state();
-  DictionaryPrefUpdate dict_update(prefs, key);
+  DictionaryPrefUpdateDeprecated dict_update(prefs, key);
   dict_update->RemoveKey(user_id);
 }
 
 bool SupervisedUserManagerImpl::CheckForFirstRun(const std::string& user_id) {
-  ListPrefUpdate prefs_new_users_update(g_browser_process->local_state(),
-                                        kSupervisedUsersFirstRun);
+  ListPrefUpdateDeprecated prefs_new_users_update(
+      g_browser_process->local_state(), kSupervisedUsersFirstRun);
   return prefs_new_users_update->EraseListValue(base::Value(user_id));
 }
 
diff --git a/chrome/browser/ash/login/wizard_controller_browsertest.cc b/chrome/browser/ash/login/wizard_controller_browsertest.cc
index 041194e7..6029f0a 100644
--- a/chrome/browser/ash/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/ash/login/wizard_controller_browsertest.cc
@@ -2049,8 +2049,15 @@
   net::SpawnedTestServer proxy_server_;
 };
 
+// TODO(crbug.com/1286218): Flakes on CrOS.
+#if defined(OS_CHROMEOS)
+#define MAYBE_ProxyAuthDialogOnSigninScreen \
+  DISABLED_ProxyAuthDialogOnSigninScreen
+#else
+#define MAYBE_ProxyAuthDialogOnSigninScreen ProxyAuthDialogOnSigninScreen
+#endif
 IN_PROC_BROWSER_TEST_F(WizardControllerProxyAuthOnSigninTest,
-                       ProxyAuthDialogOnSigninScreen) {
+                       MAYBE_ProxyAuthDialogOnSigninScreen) {
   content::WindowedNotificationObserver auth_needed_waiter(
       chrome::NOTIFICATION_AUTH_NEEDED,
       content::NotificationService::AllSources());
diff --git a/chrome/browser/ash/platform_keys/key_permissions/key_permissions_pref_util.cc b/chrome/browser/ash/platform_keys/key_permissions/key_permissions_pref_util.cc
index f25b074..a325dd5 100644
--- a/chrome/browser/ash/platform_keys/key_permissions/key_permissions_pref_util.cc
+++ b/chrome/browser/ash/platform_keys/key_permissions/key_permissions_pref_util.cc
@@ -70,7 +70,7 @@
   std::string public_key_spki_der_b64;
   base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
 
-  DictionaryPrefUpdate update(profile_prefs, prefs::kPlatformKeys);
+  DictionaryPrefUpdateDeprecated update(profile_prefs, prefs::kPlatformKeys);
 
   auto new_pref_entry = std::make_unique<base::DictionaryValue>();
   new_pref_entry->SetKey(kPrefKeyUsage, base::Value(kPrefKeyUsageCorporate));
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_installer_unittest.cc b/chrome/browser/ash/plugin_vm/plugin_vm_installer_unittest.cc
index 43ad004d..92f13d9 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_installer_unittest.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_installer_unittest.cc
@@ -203,7 +203,8 @@
   }
 
   void SetPluginVmImagePref(std::string url, std::string hash) {
-    DictionaryPrefUpdate update(profile_->GetPrefs(), prefs::kPluginVmImage);
+    DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                          prefs::kPluginVmImage);
     base::DictionaryValue* plugin_vm_image = update.Get();
     plugin_vm_image->SetKey("url", base::Value(url));
     plugin_vm_image->SetKey("hash", base::Value(hash));
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_util.cc b/chrome/browser/ash/plugin_vm/plugin_vm_util.cc
index 5ca38c49..f452fb6 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_util.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_util.cc
@@ -75,8 +75,8 @@
                            const std::string& image_url,
                            const std::string& image_hash,
                            const std::string& license_key) {
-  DictionaryPrefUpdate update(profile->GetPrefs(),
-                              plugin_vm::prefs::kPluginVmImage);
+  DictionaryPrefUpdateDeprecated update(profile->GetPrefs(),
+                                        plugin_vm::prefs::kPluginVmImage);
   base::DictionaryValue* dict = update.Get();
   dict->SetPath(prefs::kPluginVmImageUrlKeyName, base::Value(image_url));
   dict->SetPath(prefs::kPluginVmImageHashKeyName, base::Value(image_hash));
diff --git a/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc b/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc
index 7fd1306..fac85b5 100644
--- a/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc
+++ b/chrome/browser/ash/policy/affiliation/affiliation_test_helper.cc
@@ -168,7 +168,8 @@
 
 // static
 void AffiliationTestHelper::PreLoginUser(const AccountId& account_id) {
-  ListPrefUpdate users_pref(g_browser_process->local_state(), "LoggedInUsers");
+  ListPrefUpdateDeprecated users_pref(g_browser_process->local_state(),
+                                      "LoggedInUsers");
   base::Value email_value(account_id.GetUserEmail());
   if (!base::Contains(users_pref->GetList(), email_value))
     users_pref->Append(std::move(email_value));
diff --git a/chrome/browser/ash/policy/core/device_cloud_policy_browsertest.cc b/chrome/browser/ash/policy/core/device_cloud_policy_browsertest.cc
index 6e3dafc..52c46c0 100644
--- a/chrome/browser/ash/policy/core/device_cloud_policy_browsertest.cc
+++ b/chrome/browser/ash/policy/core/device_cloud_policy_browsertest.cc
@@ -15,7 +15,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
 #include "base/values.h"
-#include "chrome/browser/ash/login/test/embedded_policy_test_server_mixin.h"
+#include "chrome/browser/ash/login/test/local_policy_test_server_mixin.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/policy/core/device_cloud_policy_store_ash.h"
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
@@ -81,6 +81,8 @@
 
   KeyRotationDeviceCloudPolicyTest() {
     UpdateBuiltTestPolicyValue(kInitialPolicyValue);
+    local_policy_mixin_.EnableCannedSigningKeys();
+    local_policy_mixin_.EnableAutomaticRotationOfSigningKeys();
   }
 
   void SetUpInProcessBrowserTestFixture() override {
@@ -108,7 +110,8 @@
   }
 
   void UpdateServedTestPolicy() {
-    policy_test_server_mixin_.UpdateDevicePolicy(device_policy()->payload());
+    EXPECT_TRUE(
+        local_policy_mixin_.UpdateDevicePolicy(device_policy()->payload()));
   }
 
   void StartDevicePolicyRefresh() {
@@ -188,11 +191,7 @@
     }
   }
 
-  ash::EmbeddedPolicyTestServerMixin policy_test_server_mixin_{
-      &mixin_host_,
-      {ash::EmbeddedPolicyTestServerMixin::ENABLE_CANNED_SIGNING_KEYS,
-       ash::EmbeddedPolicyTestServerMixin::
-           ENABLE_AUTOMATIC_ROTATION_OF_SIGNINGKEYS}};
+  ash::LocalPolicyTestServerMixin local_policy_mixin_{&mixin_host_};
   std::unique_ptr<PolicyChangeRegistrar> policy_change_registrar_;
   int awaited_policy_value_ = -1;
   std::unique_ptr<base::RunLoop> policy_change_waiting_run_loop_;
@@ -286,10 +285,11 @@
     DevicePolicyCrosBrowserTest::SetUpInProcessBrowserTestFixture();
     SetFakeDevicePolicy();
 
-    policy_test_server_mixin_.UpdateDevicePolicy(device_policy()->payload());
-    policy_test_server_mixin_.UpdatePolicy(
+    EXPECT_TRUE(
+        local_policy_mixin_.UpdateDevicePolicy(device_policy()->payload()));
+    EXPECT_TRUE(local_policy_mixin_.server()->UpdatePolicy(
         dm_protocol::kChromeSigninExtensionPolicyType, kTestExtensionId,
-        BuildTestComponentPolicyPayload().SerializeAsString());
+        BuildTestComponentPolicyPayload().SerializeAsString()));
   }
 
   void SetUpOnMainThread() override {
@@ -386,7 +386,7 @@
     builder->Build();
   }
 
-  ash::EmbeddedPolicyTestServerMixin policy_test_server_mixin_{&mixin_host_};
+  ash::LocalPolicyTestServerMixin local_policy_mixin_{&mixin_host_};
 };
 
 }  // namespace
diff --git a/chrome/browser/ash/policy/core/device_local_account_browsertest.cc b/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
index 47c03f57..6afd459 100644
--- a/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
+++ b/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
@@ -832,8 +832,8 @@
 
   // Data for the account is normally added after successful authentication.
   // Shortcut that here.
-  DictionaryPrefUpdate given_name_update(g_browser_process->local_state(),
-                                         "UserGivenName");
+  DictionaryPrefUpdateDeprecated given_name_update(
+      g_browser_process->local_state(), "UserGivenName");
   given_name_update->SetKey(account_id_1_.GetUserEmail(),
                             base::Value("Elaine"));
 
diff --git a/chrome/browser/ash/policy/enrollment/auto_enrollment_client_impl.cc b/chrome/browser/ash/policy/enrollment/auto_enrollment_client_impl.cc
index ac79f7bf..b870ce0 100644
--- a/chrome/browser/ash/policy/enrollment/auto_enrollment_client_impl.cc
+++ b/chrome/browser/ash/policy/enrollment/auto_enrollment_client_impl.cc
@@ -1324,7 +1324,8 @@
   StateDownloadMessageProcessor::ParsedResponse parsed_response =
       std::move(parsed_response_opt.value());
   {
-    DictionaryPrefUpdate dict(local_state_, prefs::kServerBackedDeviceState);
+    DictionaryPrefUpdateDeprecated dict(local_state_,
+                                        prefs::kServerBackedDeviceState);
     UpdateDict(dict.Get(), kDeviceStateManagementDomain,
                parsed_response.management_domain.has_value(),
                std::make_unique<base::Value>(
diff --git a/chrome/browser/ash/policy/networking/policy_cert_service_factory.cc b/chrome/browser/ash/policy/networking/policy_cert_service_factory.cc
index 01f7222..186666e 100644
--- a/chrome/browser/ash/policy/networking/policy_cert_service_factory.cc
+++ b/chrome/browser/ash/policy/networking/policy_cert_service_factory.cc
@@ -81,8 +81,8 @@
 // static
 bool PolicyCertServiceFactory::ClearUsedPolicyCertificates(
     const std::string& user_email) {
-  ListPrefUpdate update(g_browser_process->local_state(),
-                        prefs::kUsedPolicyCertificates);
+  ListPrefUpdateDeprecated update(g_browser_process->local_state(),
+                                  prefs::kUsedPolicyCertificates);
   return (update->EraseListValue(base::Value(user_email)) > 0);
 }
 
diff --git a/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc b/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
index 8307de02..70c48258 100644
--- a/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
+++ b/chrome/browser/ash/policy/networking/policy_certs_browsertest.cc
@@ -20,7 +20,7 @@
 #include "chrome/browser/ash/login/existing_user_controller.h"
 #include "chrome/browser/ash/login/helper.h"
 #include "chrome/browser/ash/login/startup_utils.h"
-#include "chrome/browser/ash/login/test/embedded_policy_test_server_mixin.h"
+#include "chrome/browser/ash/login/test/local_policy_test_server_mixin.h"
 #include "chrome/browser/ash/login/test/login_or_lock_screen_visible_waiter.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
@@ -703,7 +703,7 @@
     command_line->AppendSwitch(chromeos::switches::kOobeSkipPostLogin);
   }
 
-  ash::EmbeddedPolicyTestServerMixin policy_test_server_mixin_{&mixin_host_};
+  ash::LocalPolicyTestServerMixin local_policy_mixin_{&mixin_host_};
 
   const AccountId device_local_account_id_ =
       AccountId::FromUserEmail(GenerateDeviceLocalAccountUserId(
@@ -731,11 +731,9 @@
     account->set_type(
         em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION);
     RefreshDevicePolicy();
-    policy_test_server_mixin_.UpdateDevicePolicy(proto);
+    ASSERT_TRUE(local_policy_mixin_.UpdateDevicePolicy(proto));
   }
 
-  // TODO(crbug/874831): Consider migrating to LoggedInMixin and deprecating
-  // this function.
   void StartLogin() {
     ash::WizardController::SkipPostLoginScreensForTesting();
     auto* const wizard_controller = ash::WizardController::default_controller();
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.cc
index e8a00f0c..9249b34 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler.cc
@@ -151,6 +151,51 @@
     std::move(callback).Run(std::move(metric_data));
   }
 }
+void HandleBootPerformanceResult(
+    MetricCallback callback,
+    CrosHealthdMetricSampler::MetricType metric_type,
+    chromeos::cros_healthd::mojom::TelemetryInfoPtr result) {
+  MetricData metric_data;
+  auto* const boot_info_out = metric_data.mutable_telemetry_data()
+                                  ->mutable_boot_performance_telemetry();
+
+  const auto& boot_performance_result = result->boot_performance_result;
+  if (!boot_performance_result.is_null()) {
+    switch (boot_performance_result->which()) {
+      case chromeos::cros_healthd::mojom::BootPerformanceResult::Tag::ERROR: {
+        DVLOG(1) << "cros_healthd: Error getting Boot Performance info: "
+                 << boot_performance_result->get_error()->msg;
+        break;
+      }
+
+      case chromeos::cros_healthd::mojom::BootPerformanceResult::Tag::
+          BOOT_PERFORMANCE_INFO: {
+        const auto& boot_performance_info =
+            boot_performance_result->get_boot_performance_info();
+        if (boot_performance_info.is_null()) {
+          DVLOG(1) << "Null BootPerformanceInfo from cros_healthd";
+          break;
+        }
+
+        // Gather boot performance info.
+        boot_info_out->set_boot_up_seconds(
+            (int64_t)boot_performance_info->boot_up_seconds);
+        boot_info_out->set_boot_up_timestamp_seconds(
+            (int64_t)boot_performance_info->boot_up_timestamp);
+        boot_info_out->set_shutdown_seconds(
+            (int64_t)boot_performance_info->shutdown_seconds);
+        boot_info_out->set_shutdown_timestamp_seconds(
+            (int64_t)boot_performance_info->shutdown_timestamp);
+        boot_info_out->set_shutdown_reason(
+            boot_performance_info->shutdown_reason);
+
+        std::move(callback).Run(metric_data);
+
+        break;
+      }
+    }
+  }
+}
 
 void HandleAudioResult(MetricCallback callback,
                        CrosHealthdMetricSampler::MetricType metric_type,
@@ -283,6 +328,11 @@
                          std::move(metric_data), std::move(result));
       break;
     }
+    case cros_healthd::ProbeCategoryEnum::kBootPerformance: {
+      HandleBootPerformanceResult(std::move(callback), metric_type,
+                                  std::move(result));
+      break;
+    }
     default: {
       NOTREACHED();
       return;
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc
index 4232883..eaa5f41 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/cros_healthd_metric_sampler_unittest.cc
@@ -39,6 +39,13 @@
 constexpr int64_t kTmeMaxKeys = 2;
 constexpr int64_t kTmeKeysLength = 4;
 
+// Boot Performance constants.
+constexpr int64_t kBootUpSeconds = 5054;
+constexpr int64_t kBootUpTimestampSeconds = 23;
+constexpr int64_t kShutdownSeconds = 44003;
+constexpr int64_t kShutdownTimestampSeconds = 49;
+constexpr char kShutdownReason[] = "user-request";
+
 cros_healthd::KeylockerInfoPtr CreateKeylockerInfo(bool configured) {
   return cros_healthd::KeylockerInfo::New(configured);
 }
@@ -118,6 +125,21 @@
   return telemetry_info;
 }
 
+cros_healthd::TelemetryInfoPtr CreateBootPerformanceResult(
+    int64_t boot_up_seconds,
+    int64_t boot_up_timestamp_seconds,
+    int64_t shutdown_seconds,
+    int64_t shutdown_timestamp_seconds,
+    const std::string& shutdown_reason) {
+  auto telemetry_info = cros_healthd::TelemetryInfo::New();
+  telemetry_info->boot_performance_result =
+      cros_healthd::BootPerformanceResult::NewBootPerformanceInfo(
+          cros_healthd::BootPerformanceInfo::New(
+              boot_up_seconds, boot_up_timestamp_seconds, shutdown_seconds,
+              shutdown_timestamp_seconds, shutdown_reason));
+  return telemetry_info;
+}
+
 MetricData CollectData(cros_healthd::TelemetryInfoPtr telemetry_info,
                        cros_healthd::ProbeCategoryEnum probe_category,
                        CrosHealthdMetricSampler::MetricType metric_type,
@@ -296,6 +318,17 @@
       std::move(telemetry_info), cros_healthd::ProbeCategoryEnum::kAudio,
       CrosHealthdMetricSampler::MetricType::kTelemetry);
   ASSERT_FALSE(audio_data.has_telemetry_data());
+
+  telemetry_info = cros_healthd::TelemetryInfo::New();
+  telemetry_info->boot_performance_result =
+      cros_healthd::BootPerformanceResult::NewError(
+          cros_healthd::ProbeError::New(cros_healthd::ErrorType::kFileReadError,
+                                        ""));
+  const auto& boot_performance_data =
+      CollectError(std::move(telemetry_info),
+                   cros_healthd::ProbeCategoryEnum::kBootPerformance,
+                   CrosHealthdMetricSampler::MetricType::kTelemetry);
+  ASSERT_FALSE(boot_performance_data.has_telemetry_data());
 }
 
 TEST_F(CrosHealthdMetricSamplerTest, TestAudioNormalTest) {
@@ -334,6 +367,35 @@
   ASSERT_THAT(result.telemetry_data().audio_telemetry().output_volume(), Eq(0));
 }
 
+TEST_F(CrosHealthdMetricSamplerTest, BootPerformanceCommonBehavior) {
+  MetricData result = CollectData(
+      CreateBootPerformanceResult(kBootUpSeconds, kBootUpTimestampSeconds,
+                                  kShutdownSeconds, kShutdownTimestampSeconds,
+                                  kShutdownReason),
+      cros_healthd::ProbeCategoryEnum::kBootPerformance,
+      CrosHealthdMetricSampler::MetricType::kTelemetry, MetricData{});
+
+  ASSERT_TRUE(result.has_telemetry_data());
+  ASSERT_TRUE(result.telemetry_data().has_boot_performance_telemetry());
+  ASSERT_THAT(
+      result.telemetry_data().boot_performance_telemetry().boot_up_seconds(),
+      Eq(5054));
+  ASSERT_THAT(result.telemetry_data()
+                  .boot_performance_telemetry()
+                  .boot_up_timestamp_seconds(),
+              Eq(23));
+  ASSERT_THAT(
+      result.telemetry_data().boot_performance_telemetry().shutdown_seconds(),
+      Eq(44003));
+  ASSERT_THAT(result.telemetry_data()
+                  .boot_performance_telemetry()
+                  .shutdown_timestamp_seconds(),
+              Eq(49));
+  EXPECT_EQ(
+      result.telemetry_data().boot_performance_telemetry().shutdown_reason(),
+      "user-request");
+}
+
 INSTANTIATE_TEST_SUITE_P(
     CrosHealthdMetricSamplerTbtTests,
     CrosHealthdMetricSamplerTbtTest,
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc
index 8180784c..e8f6bb1 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc
@@ -6,6 +6,7 @@
 
 #include "ash/components/settings/cros_settings_names.h"
 #include "base/logging.h"
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/audio/audio_events_observer.h"
 #include "chrome/browser/ash/policy/reporting/metrics_reporting/network/https_latency_sampler.h"
 #include "chrome/browser/ash/policy/reporting/metrics_reporting/network/network_events_observer.h"
 #include "chrome/browser/ash/policy/reporting/metrics_reporting/network/network_info_sampler.h"
@@ -33,9 +34,11 @@
 
 constexpr base::TimeDelta kDefaultReportUploadFrequency = base::Hours(3);
 constexpr base::TimeDelta kDefaultNetworkTelemetryCollectionRate =
-    base::Minutes(10);
+    base::Minutes(60);
 constexpr base::TimeDelta kDefaultNetworkTelemetryEventCheckingRate =
-    base::Minutes(2);
+    base::Minutes(10);
+constexpr base::TimeDelta kDefaultAudioTelemetryCollectionRate =
+    base::Minutes(10);
 
 base::TimeDelta GetDefaultRate(base::TimeDelta default_rate,
                                base::TimeDelta testing_rate) {
@@ -178,6 +181,7 @@
 
 void MetricReportingManager::InitOnAffiliatedLogin() {
   InitNetworkCollectors();
+  InitAudioCollectors();
 }
 
 std::unique_ptr<MetricReportQueue>
@@ -321,4 +325,23 @@
       kReportDeviceNetworkStatusDefaultValue);
 }
 
+void MetricReportingManager::InitAudioCollectors() {
+  const bool kReportDeviceAudioStatusDefaultValue = true;
+
+  CreateEventObserverManager(
+      std::make_unique<AudioEventsObserver>(),
+      /*enable_setting_path=*/::ash::kReportDeviceAudioStatus,
+      kReportDeviceAudioStatusDefaultValue);
+
+  auto audio_telemetry_sampler = std::make_unique<CrosHealthdMetricSampler>(
+      chromeos::cros_healthd::mojom::ProbeCategoryEnum::kAudio,
+      CrosHealthdMetricSampler::MetricType::kTelemetry);
+  CreatePeriodicCollector(
+      std::move(audio_telemetry_sampler),
+      /*enable_setting_path=*/::ash::kReportDeviceAudioStatus,
+      kReportDeviceAudioStatusDefaultValue,
+      ::ash::kReportDeviceAudioStatusCheckingRateMs,
+      GetDefaulCollectionRate(kDefaultAudioTelemetryCollectionRate));
+}
+
 }  // namespace reporting
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.h b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.h
index 74dc8e2..e076f2e 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.h
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.h
@@ -126,6 +126,8 @@
 
   void InitNetworkCollectors();
 
+  void InitAudioCollectors();
+
   CrosReportingSettings reporting_settings_;
 
   std::vector<std::unique_ptr<Sampler>> samplers_;
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc
index 2d0c157..86c293e 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc
@@ -587,5 +587,197 @@
     [](const testing::TestParamInfo<HealthdInfoReportingTest::ParamType>&
            info) { return info.param.test_name; });
 
+constexpr int kAudioCheckingRateMs = 60000;
+
+struct AudioReportingTestCase {
+  std::string test_name;
+  absl::optional<bool> is_feature_enabled;
+  bool is_deprovisioned;
+  bool is_affiliated;
+  int expected_telemetry_count;
+  int time_forward;
+};
+
+class AudioReportingTest
+    : public ::testing::TestWithParam<AudioReportingTestCase> {
+ protected:
+  AudioReportingTest() = default;
+
+  AudioReportingTest(const AudioReportingTest&) = delete;
+  AudioReportingTest& operator=(const AudioReportingTest&) = delete;
+
+  ~AudioReportingTest() override = default;
+
+  void SetUp() override {
+    ::ash::CrosHealthdClient::InitializeFake();
+
+    task_runner_ = base::ThreadPool::CreateSequencedTaskRunner({});
+
+    scoped_testing_cros_settings_.device_settings()->SetInteger(
+        ::ash::kReportDeviceAudioStatusCheckingRateMs, kAudioCheckingRateMs);
+
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void TearDown() override {
+    ::ash::CrosHealthdClient::Shutdown();
+    ::ash::cros_healthd::ServiceConnection::GetInstance()->FlushForTesting();
+  }
+
+  void EmitAudioSevereUnderrunEvent() {
+    base::RunLoop run_loop;
+    ::ash::cros_healthd::FakeCrosHealthdClient::Get()
+        ->EmitAudioSevereUnderrunEventForTesting();
+    base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                     run_loop.QuitClosure());
+    run_loop.Run();
+  }
+
+  void SetAudioResult() {
+    auto telemetry_info = chromeos::cros_healthd::mojom::TelemetryInfo::New();
+    telemetry_info->audio_result =
+        chromeos::cros_healthd::mojom::AudioResult::NewAudioInfo(
+            chromeos::cros_healthd::mojom::AudioInfo::New(
+                /*output_mute=*/true,
+                /*input_mute=*/true, /*output_volume=*/25,
+                /*output_device_name=*/"hey",
+                /*input_gain=*/50, /*input_device_name=*/"airpods",
+                /*underruns=*/0,
+                /*severe_underruns=*/1));
+    ::ash::cros_healthd::FakeCrosHealthdClient::Get()
+        ->SetProbeTelemetryInfoResponseForTesting(telemetry_info);
+  }
+
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  ::ash::ScopedTestingCrosSettings scoped_testing_cros_settings_;
+};
+
+TEST_F(AudioReportingTest, AudioEvent) {
+  scoped_testing_cros_settings_.device_settings()->SetBoolean(
+      ::ash::kReportDeviceAudioStatus, true);
+
+  auto fake_delegate = std::make_unique<FakeDelegate>();
+  fake_delegate->SetIsAffiliated(true);
+
+  int event_reported_count = 0;
+  auto event_queue =
+      std::unique_ptr<MockReportQueue, base::OnTaskRunnerDeleter>(
+          new ::testing::NiceMock<MockReportQueue>(),
+          base::OnTaskRunnerDeleter(task_runner_));
+  ON_CALL(*event_queue, AddRecord).WillByDefault([&]() {
+    ++event_reported_count;
+  });
+  fake_delegate->SetEventQueue(std::move(event_queue));
+
+  auto metric_reporting_manager = MetricReportingManager::CreateForTesting(
+      std::move(fake_delegate), nullptr);
+
+  metric_reporting_manager->OnLogin(nullptr);
+  base::RunLoop run_loop;
+  EmitAudioSevereUnderrunEvent();
+  base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                   run_loop.QuitClosure());
+  run_loop.Run();
+
+  EXPECT_EQ(event_reported_count, 1);
+}
+
+TEST_P(AudioReportingTest, Telemetry) {
+  const AudioReportingTestCase& test_case = GetParam();
+
+  if (test_case.is_feature_enabled.has_value()) {
+    scoped_testing_cros_settings_.device_settings()->SetBoolean(
+        ::ash::kReportDeviceAudioStatus, test_case.is_feature_enabled.value());
+  }
+
+  auto fake_delegate = std::make_unique<FakeDelegate>();
+  auto* const fake_delegate_ptr = fake_delegate.get();
+
+  fake_delegate->SetIsAffiliated(test_case.is_affiliated);
+  fake_delegate->SetIsDeprovisioned(test_case.is_deprovisioned);
+
+  SetAudioResult();
+
+  int telemetry_report_count = 0;
+  auto telemetry_queue =
+      std::unique_ptr<MockReportQueue, base::OnTaskRunnerDeleter>(
+          new ::testing::NiceMock<MockReportQueue>(),
+          base::OnTaskRunnerDeleter(task_runner_));
+  ON_CALL(*telemetry_queue, AddRecord).WillByDefault([&]() {
+    ++telemetry_report_count;
+  });
+  fake_delegate->SetTelemetryQueue(std::move(telemetry_queue));
+
+  auto metric_reporting_manager = MetricReportingManager::CreateForTesting(
+      std::move(fake_delegate), nullptr);
+  base::RunLoop run_loop;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                   run_loop.QuitClosure());
+  run_loop.Run();
+
+  task_environment_.FastForwardBy(base::Milliseconds(test_case.time_forward));
+  EXPECT_EQ(telemetry_report_count, 0);
+
+  metric_reporting_manager->OnLogin(nullptr);
+
+  task_environment_.FastForwardBy(base::Milliseconds(test_case.time_forward));
+  EXPECT_EQ(telemetry_report_count, test_case.expected_telemetry_count);
+
+  fake_delegate_ptr->SetIsDeprovisioned(true);
+  metric_reporting_manager->DeviceSettingsUpdated();
+
+  // Device is deprovisioned, so no reporting, reset all counts.
+  telemetry_report_count = 0;
+  task_environment_.FastForwardBy(base::Milliseconds(test_case.time_forward));
+  EXPECT_EQ(telemetry_report_count, 0);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AudioReportingTests,
+    AudioReportingTest,
+    ::testing::ValuesIn<AudioReportingTestCase>(
+        {{"Deprovisioned", /*is_feature_enabled=*/true,
+          /*is_deprovisioned=*/true,
+          /*is_affiliated=*/true,
+          /*expected_telemetry_count=*/0,
+          /*time_forward=*/kAudioCheckingRateMs},
+         {"NotAffiliated", /*is_feature_enabled=*/true,
+          /*is_deprovisioned=*/true,
+          /*is_affiliated=*/false,
+          /*expected_telemetry_count=*/0,
+          /*time_forward=*/kAudioCheckingRateMs},
+         {"Disabled", /*is_feature_enabled=*/false,
+          /*is_deprovisioned=*/false,
+          /*is_affiliated=*/true,
+          /*expected_telemetry_count=*/0,
+          /*time_forward=*/kAudioCheckingRateMs},
+         {"DefaultEnabled", /*is_feature_enabled=*/absl::nullopt,
+          /*is_deprovisioned=*/false,
+          /*is_affiliated=*/true,
+          /*expected_telemetry_count=*/1,
+          /*time_forward=*/kAudioCheckingRateMs},
+         {"Enabled", /*is_feature_enabled=*/true,
+          /*is_deprovisioned=*/false,
+          /*is_affiliated=*/true,
+          /*expected_telemetry_count=*/1,
+          /*time_forward=*/kAudioCheckingRateMs},
+         {"Enabled_DoubleTime", /*is_feature_enabled=*/true,
+          /*is_deprovisioned=*/false,
+          /*is_affiliated=*/true,
+          /*expected_telemetry_count=*/2,
+          /*time_forward=*/kAudioCheckingRateMs * 2},
+         {"Enabled_HalfTime", /*is_feature_enabled=*/true,
+          /*is_deprovisioned=*/false,
+          /*is_affiliated=*/true,
+          /*expected_telemetry_count=*/0,
+          /*time_forward=*/kAudioCheckingRateMs / 2}}),
+    [](const testing::TestParamInfo<AudioReportingTest::ParamType>& info) {
+      return info.param.test_name;
+    });
+
 }  // namespace
 }  // namespace reporting
diff --git a/chrome/browser/ash/policy/scheduled_task_handler/test/fake_scheduled_task_executor.cc b/chrome/browser/ash/policy/scheduled_task_handler/test/fake_scheduled_task_executor.cc
index 7af26533..b2dd4f2d0 100644
--- a/chrome/browser/ash/policy/scheduled_task_handler/test/fake_scheduled_task_executor.cc
+++ b/chrome/browser/ash/policy/scheduled_task_handler/test/fake_scheduled_task_executor.cc
@@ -56,8 +56,8 @@
       FROM_HERE, base::BindOnce(std::move(result_cb), true));
   delayed_task_handle_ =
       base::SequencedTaskRunnerHandle::Get()->PostCancelableDelayedTask(
-          FROM_HERE, base::BindOnce(std::move(timer_expired_cb)),
-          delay.value());
+          base::subtle::PostDelayedTaskPassKeyForTesting(), FROM_HERE,
+          base::BindOnce(std::move(timer_expired_cb)), delay.value());
 }
 
 void FakeScheduledTaskExecutor::Reset() {
diff --git a/chrome/browser/ash/policy/status_collector/activity_storage.cc b/chrome/browser/ash/policy/status_collector/activity_storage.cc
index 8b4e972..16b76c5d 100644
--- a/chrome/browser/ash/policy/status_collector/activity_storage.cc
+++ b/chrome/browser/ash/policy/status_collector/activity_storage.cc
@@ -161,7 +161,7 @@
   DCHECK(!start.is_max());
   DCHECK(!end.is_max());
 
-  DictionaryPrefUpdate update(pref_service_, pref_name_);
+  DictionaryPrefUpdateDeprecated update(pref_service_, pref_name_);
   base::Value* activity_times = update.Get();
 
   // Assign the period to day buckets in local time.
diff --git a/chrome/browser/ash/policy/status_collector/child_activity_storage.cc b/chrome/browser/ash/policy/status_collector/child_activity_storage.cc
index 45f95c31..3a9472b 100644
--- a/chrome/browser/ash/policy/status_collector/child_activity_storage.cc
+++ b/chrome/browser/ash/policy/status_collector/child_activity_storage.cc
@@ -29,7 +29,7 @@
                                              base::Time now) {
   DCHECK(start <= end);
 
-  DictionaryPrefUpdate update(pref_service_, pref_name_);
+  DictionaryPrefUpdateDeprecated update(pref_service_, pref_name_);
   base::DictionaryValue* activity_times = update.Get();
 
   // Assign the period to day buckets in local time.
diff --git a/chrome/browser/ash/policy/status_collector/child_status_collector_browsertest.cc b/chrome/browser/ash/policy/status_collector/child_status_collector_browsertest.cc
index c742d66..8f96c03 100644
--- a/chrome/browser/ash/policy/status_collector/child_status_collector_browsertest.cc
+++ b/chrome/browser/ash/policy/status_collector/child_status_collector_browsertest.cc
@@ -775,8 +775,8 @@
   {
     ash::app_time::AppTimeLimitsPolicyBuilder builder;
     builder.SetAppActivityReportingEnabled(/* enabled */ false);
-    DictionaryPrefUpdate update(testing_profile()->GetPrefs(),
-                                prefs::kPerAppTimeLimitsPolicy);
+    DictionaryPrefUpdateDeprecated update(testing_profile()->GetPrefs(),
+                                          prefs::kPerAppTimeLimitsPolicy);
     base::Value* value = update.Get();
     *value = builder.value().Clone();
   }
diff --git a/chrome/browser/ash/psi_memory_metrics.cc b/chrome/browser/ash/psi_memory_metrics.cc
index 95b4fac..46bcdcfe 100644
--- a/chrome/browser/ash/psi_memory_metrics.cc
+++ b/chrome/browser/ash/psi_memory_metrics.cc
@@ -113,7 +113,7 @@
   }
 
   last_timer_ = runner_->PostCancelableDelayedTask(
-      FROM_HERE,
+      base::subtle::PostDelayedTaskPassKey(), FROM_HERE,
       base::BindOnce(&PSIMemoryMetrics::CollectEventsAndReschedule, this),
       collection_interval_);
 }
diff --git a/chrome/browser/ash/smb_client/smb_persisted_share_registry.cc b/chrome/browser/ash/smb_client/smb_persisted_share_registry.cc
index 52a015de..2e4a4f4d 100644
--- a/chrome/browser/ash/smb_client/smb_persisted_share_registry.cc
+++ b/chrome/browser/ash/smb_client/smb_persisted_share_registry.cc
@@ -97,8 +97,8 @@
     : profile_(profile) {}
 
 void SmbPersistedShareRegistry::Save(const SmbShareInfo& share) {
-  ListPrefUpdate pref(profile_->GetPrefs(),
-                      prefs::kNetworkFileSharesSavedShares);
+  ListPrefUpdateDeprecated pref(profile_->GetPrefs(),
+                                prefs::kNetworkFileSharesSavedShares);
 
   base::Value::ListView share_list = pref->GetList();
   for (auto it = share_list.begin(); it != share_list.end(); ++it) {
@@ -113,8 +113,8 @@
 }
 
 void SmbPersistedShareRegistry::Delete(const SmbUrl& share_url) {
-  ListPrefUpdate pref(profile_->GetPrefs(),
-                      prefs::kNetworkFileSharesSavedShares);
+  ListPrefUpdateDeprecated pref(profile_->GetPrefs(),
+                                prefs::kNetworkFileSharesSavedShares);
 
   base::Value::ListView share_list = pref->GetList();
   for (auto it = share_list.begin(); it != share_list.end(); ++it) {
diff --git a/chrome/browser/ash/smb_client/smb_service.cc b/chrome/browser/ash/smb_client/smb_service.cc
index 3de4f1d..471f87f 100644
--- a/chrome/browser/ash/smb_client/smb_service.cc
+++ b/chrome/browser/ash/smb_client/smb_service.cc
@@ -104,26 +104,6 @@
   UMA_HISTOGRAM_ENUMERATION("NativeSmbFileShare.AuthenticationMethod", method);
 }
 
-base::ScopedFD MakeFdWithContents(const std::string& contents) {
-  const size_t content_size = contents.size();
-
-  base::ScopedFD read_fd;
-  base::ScopedFD write_fd;
-  if (!base::CreatePipe(&read_fd, &write_fd, true /* non_blocking */)) {
-    LOG(ERROR) << "Unable to create pipe";
-    return {};
-  }
-  bool success =
-      base::WriteFileDescriptor(
-          write_fd.get(), base::as_bytes(base::make_span(&content_size, 1))) &&
-      base::WriteFileDescriptor(write_fd.get(), contents);
-  if (!success) {
-    PLOG(ERROR) << "Unable to write contents to pipe";
-    return {};
-  }
-  return read_fd;
-}
-
 }  // namespace
 
 bool SmbService::disable_share_discovery_for_testing_ = false;
diff --git a/chrome/browser/ash/system/device_disabling_manager_unittest.cc b/chrome/browser/ash/system/device_disabling_manager_unittest.cc
index 292c168..8410393 100644
--- a/chrome/browser/ash/system/device_disabling_manager_unittest.cc
+++ b/chrome/browser/ash/system/device_disabling_manager_unittest.cc
@@ -178,7 +178,8 @@
 }
 
 void DeviceDisablingManagerOOBETest::SetDeviceDisabled(bool disabled) {
-  DictionaryPrefUpdate dict(&local_state_, prefs::kServerBackedDeviceState);
+  DictionaryPrefUpdateDeprecated dict(&local_state_,
+                                      prefs::kServerBackedDeviceState);
   if (disabled) {
     dict->SetString(policy::kDeviceStateMode, policy::kDeviceStateModeDisabled);
   } else {
diff --git a/chrome/browser/ash/system/procfs_util.cc b/chrome/browser/ash/system/procfs_util.cc
index 766a22a..ecd1224 100644
--- a/chrome/browser/ash/system/procfs_util.cc
+++ b/chrome/browser/ash/system/procfs_util.cc
@@ -123,36 +123,5 @@
   return absl::nullopt;
 }
 
-absl::optional<int64_t> GetUsedMemTotalKB(const base::FilePath& meminfo_file) {
-  int64_t mem_total = 0;
-  int64_t mem_free = 0;
-  std::string meminfo_contents;
-  if (!base::ReadFileToString(meminfo_file, &meminfo_contents))
-    return absl::nullopt;
-
-  std::vector<base::StringPiece> meminfo_lines = base::SplitStringPiece(
-      meminfo_contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-  for (auto& line : meminfo_lines) {
-    if (base::StartsWith(line, "MemTotal:", base::CompareCase::SENSITIVE)) {
-      std::vector<base::StringPiece> line_items = base::SplitStringPiece(
-          line, " \t", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-      if (line_items.size() != 3)
-        return absl::nullopt;
-      if (!base::StringToInt64(line_items.at(1), &mem_total))
-        return absl::nullopt;
-    }
-    if (base::StartsWith(line, "MemFree:", base::CompareCase::SENSITIVE)) {
-      std::vector<base::StringPiece> line_items = base::SplitStringPiece(
-          line, " \t", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
-      if (line_items.size() != 3)
-        return absl::nullopt;
-      if (!base::StringToInt64(line_items.at(1), &mem_free))
-        return absl::nullopt;
-      break;
-    }
-  }
-  return mem_total - mem_free;
-}
-
 }  // namespace system
 }  // namespace ash
diff --git a/chrome/browser/ash/system/procfs_util.h b/chrome/browser/ash/system/procfs_util.h
index 77aaf805..c2f15ecc 100644
--- a/chrome/browser/ash/system/procfs_util.h
+++ b/chrome/browser/ash/system/procfs_util.h
@@ -52,12 +52,6 @@
 absl::optional<int64_t> GetCpuTimeJiffies(
     const base::FilePath& stat_file = base::FilePath("/proc/stat"));
 
-// Returns the total system memory used at the moment in kBs by reading
-// /proc/meminfo file.
-// The input |meminfo_file| is used for testing.
-absl::optional<int64_t> GetUsedMemTotalKB(
-    const base::FilePath& meminfo_file = base::FilePath("/proc/meminfo"));
-
 }  // namespace system
 }  // namespace ash
 
diff --git a/chrome/browser/ash/system/procfs_util_unittest.cc b/chrome/browser/ash/system/procfs_util_unittest.cc
index d1d63847..91362ff 100644
--- a/chrome/browser/ash/system/procfs_util_unittest.cc
+++ b/chrome/browser/ash/system/procfs_util_unittest.cc
@@ -86,20 +86,5 @@
   EXPECT_EQ(expected, GetCpuTimeJiffies(slash_proc_.Append("stat")).value());
 }
 
-TEST_F(ProcfsUtilTest, GetUsedMemTotalSuccess) {
-  std::string contents =
-      "MemTotal:       65946588 kB\n"
-      "MemFree:         9385800 kB\n"
-      "MemAvailable:   44230668 kB\n"
-      "Buffers:         4646292 kB\n"
-      "Cached:         27859260 kB\n"
-      "SwapCached:          780 kB\n"
-      "Active:         40069592 kB\n"
-      "Inactive:       11098284 kB\n";
-  int64_t expected = 65946588 - 9385800;
-  WriteContentsToFile(contents, "meminfo");
-  EXPECT_EQ(expected, GetUsedMemTotalKB(slash_proc_.Append("meminfo")).value());
-}
-
 }  // namespace system
 }  // namespace ash
diff --git a/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.cc b/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.cc
index 31d708c8..77b4a76 100644
--- a/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.cc
@@ -16,6 +16,14 @@
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/ui_base_features.h"
+#include "ui/display/screen.h"
+
+namespace {
+// The Firmware Update SWA window will be a fixed 600px * 640px portal per
+// the specification.
+constexpr int kFirmwareUpdateAppDefaultWidth = 600;
+constexpr int kFirmwareUpdateAppDefaultHeight = 640;
+}  // namespace
 
 // TODO(michaelcheco): Update to correct icon.
 std::unique_ptr<WebAppInstallInfo>
@@ -34,6 +42,14 @@
   return info;
 }
 
+gfx::Rect GetDefaultBoundsForFirmwareUpdateApp(Browser*) {
+  gfx::Rect bounds =
+      display::Screen::GetScreen()->GetDisplayForNewWindows().work_area();
+  bounds.ClampToCenteredSize(
+      {kFirmwareUpdateAppDefaultWidth, kFirmwareUpdateAppDefaultHeight});
+  return bounds;
+}
+
 FirmwareUpdateSystemAppDelegate::FirmwareUpdateSystemAppDelegate(
     Profile* profile)
     : web_app::SystemWebAppDelegate(web_app::SystemAppType::FIRMWARE_UPDATE,
@@ -50,6 +66,15 @@
   return ash::features::IsFirmwareUpdaterAppEnabled();
 }
 
-gfx::Size FirmwareUpdateSystemAppDelegate::GetMinimumWindowSize() const {
-  return {600, 512};
+bool FirmwareUpdateSystemAppDelegate::ShouldAllowMaximize() const {
+  return false;
+}
+
+bool FirmwareUpdateSystemAppDelegate::ShouldAllowResize() const {
+  return false;
+}
+
+gfx::Rect FirmwareUpdateSystemAppDelegate::GetDefaultBounds(
+    Browser* browser) const {
+  return GetDefaultBoundsForFirmwareUpdateApp(browser);
 }
diff --git a/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.h b/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.h
index be2274b..13eb1686 100644
--- a/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.h
+++ b/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.h
@@ -5,9 +5,14 @@
 #ifndef CHROME_BROWSER_ASH_WEB_APPLICATIONS_FIRMWARE_UPDATE_SYSTEM_WEB_APP_INFO_H_
 #define CHROME_BROWSER_ASH_WEB_APPLICATIONS_FIRMWARE_UPDATE_SYSTEM_WEB_APP_INFO_H_
 
-#include "chrome/browser/web_applications/system_web_apps/system_web_app_delegate.h"
-#include "ui/gfx/geometry/size.h"
+#include <memory>
 
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/web_applications/system_web_apps/system_web_app_delegate.h"
+#include "chrome/browser/web_applications/system_web_apps/system_web_app_types.h"
+#include "ui/gfx/geometry/rect.h"
+
+class Browser;
 struct WebAppInstallInfo;
 
 class FirmwareUpdateSystemAppDelegate : public web_app::SystemWebAppDelegate {
@@ -16,11 +21,16 @@
 
   // web_app::SystemWebAppDelegate overrides:
   std::unique_ptr<WebAppInstallInfo> GetWebAppInfo() const override;
-  gfx::Size GetMinimumWindowSize() const override;
   bool IsAppEnabled() const override;
+  bool ShouldAllowMaximize() const override;
+  bool ShouldAllowResize() const override;
+  gfx::Rect GetDefaultBounds(Browser*) const override;
 };
+
 // Returns a WebAppInstallInfo used to install the app.
 std::unique_ptr<WebAppInstallInfo>
 CreateWebAppInfoForFirmwareUpdateSystemWebApp();
 
+gfx::Rect GetDefaultBoundsForFirmwareUpdateApp(Browser*);
+
 #endif  // CHROME_BROWSER_ASH_WEB_APPLICATIONS_FIRMWARE_UPDATE_SYSTEM_WEB_APP_INFO_H_
diff --git a/chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_theme_provider_unittest.cc b/chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_theme_provider_unittest.cc
new file mode 100644
index 0000000..4842580
--- /dev/null
+++ b/chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_theme_provider_unittest.cc
@@ -0,0 +1,148 @@
+// Copyright 2022 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/ash/web_applications/personalization_app/chrome_personalization_app_theme_provider.h"
+
+#include <memory>
+
+#include "ash/constants/ash_features.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
+#include "ash/style/ash_color_provider.h"
+#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
+#include "chrome/test/base/chrome_ash_test_base.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "chromeos/constants/chromeos_features.h"
+#include "components/user_manager/scoped_user_manager.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/test_web_ui.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+constexpr char kFakeTestEmail[] = "fakeemail@personalization";
+
+class TestThemeObserver
+    : public ash::personalization_app::mojom::ThemeObserver {
+ public:
+  void OnColorModeChanged(bool dark_mode_enabled) override {
+    dark_mode_enabled_ = dark_mode_enabled;
+  }
+
+  mojo::PendingRemote<ash::personalization_app::mojom::ThemeObserver>
+  pending_remote() {
+    DCHECK(!theme_observer_receiver_.is_bound());
+    return theme_observer_receiver_.BindNewPipeAndPassRemote();
+  }
+
+  absl::optional<bool> is_dark_mode_enabled() {
+    if (!theme_observer_receiver_.is_bound())
+      return absl::nullopt;
+
+    theme_observer_receiver_.FlushForTesting();
+    return dark_mode_enabled_;
+  }
+
+ private:
+  mojo::Receiver<ash::personalization_app::mojom::ThemeObserver>
+      theme_observer_receiver_{this};
+
+  bool dark_mode_enabled_ = false;
+};
+
+}  // namespace
+
+class ChromePersonalizationAppThemeProviderTest : public ChromeAshTestBase {
+ public:
+  ChromePersonalizationAppThemeProviderTest()
+      : scoped_user_manager_(std::make_unique<ash::FakeChromeUserManager>()),
+        profile_manager_(TestingBrowserProcess::GetGlobal()) {}
+  ChromePersonalizationAppThemeProviderTest(
+      const ChromePersonalizationAppThemeProviderTest&) = delete;
+  ChromePersonalizationAppThemeProviderTest& operator=(
+      const ChromePersonalizationAppThemeProviderTest&) = delete;
+  ~ChromePersonalizationAppThemeProviderTest() override = default;
+
+ protected:
+  // testing::Test:
+  void SetUp() override {
+    ChromeAshTestBase::SetUp();
+    scoped_feature_list_.InitWithFeatures({ash::features::kPersonalizationHub,
+                                           chromeos::features::kDarkLightMode},
+                                          {});
+    ash::AshColorProvider::Get()->OnActiveUserPrefServiceChanged(
+        ash::Shell::Get()->session_controller()->GetActivePrefService());
+
+    ASSERT_TRUE(profile_manager_.SetUp());
+    profile_ = profile_manager_.CreateTestingProfile(kFakeTestEmail);
+
+    web_contents_ = content::WebContents::Create(
+        content::WebContents::CreateParams(profile_));
+    web_ui_.set_web_contents(web_contents_.get());
+
+    theme_provider_ =
+        std::make_unique<ChromePersonalizationAppThemeProvider>(&web_ui_);
+    theme_provider_->BindInterface(
+        theme_provider_remote_.BindNewPipeAndPassReceiver());
+  }
+
+  void TearDown() override {
+    theme_provider_.reset();
+    ChromeAshTestBase::TearDown();
+  }
+
+  TestingProfile* profile() { return profile_; }
+
+  mojo::Remote<ash::personalization_app::mojom::ThemeProvider>*
+  theme_provider_remote() {
+    return &theme_provider_remote_;
+  }
+
+  ChromePersonalizationAppThemeProvider* theme_provider() {
+    return theme_provider_.get();
+  }
+
+  void SetThemeObserver() {
+    theme_provider_remote_->SetThemeObserver(
+        test_theme_observer_.pending_remote());
+  }
+
+  absl::optional<bool> is_dark_mode_enabled() {
+    theme_provider_remote_.FlushForTesting();
+    return test_theme_observer_.is_dark_mode_enabled();
+  }
+
+ private:
+  user_manager::ScopedUserManager scoped_user_manager_;
+  TestingProfileManager profile_manager_;
+  content::TestWebUI web_ui_;
+  std::unique_ptr<content::WebContents> web_contents_;
+  TestingProfile* profile_;
+  mojo::Remote<ash::personalization_app::mojom::ThemeProvider>
+      theme_provider_remote_;
+  TestThemeObserver test_theme_observer_;
+  std::unique_ptr<ChromePersonalizationAppThemeProvider> theme_provider_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(ChromePersonalizationAppThemeProviderTest, SetColorModePref) {
+  SetThemeObserver();
+  theme_provider()->SetColorModePref(/*dark_mode_enabled=*/false);
+  EXPECT_FALSE(is_dark_mode_enabled().value());
+
+  theme_provider()->SetColorModePref(/*dark_mode_enabled=*/true);
+  EXPECT_TRUE(is_dark_mode_enabled().value());
+}
+
+TEST_F(ChromePersonalizationAppThemeProviderTest, OnColorModeChanged) {
+  SetThemeObserver();
+
+  bool dark_mode_enabled = ash::AshColorProvider::Get()->IsDarkModeEnabled();
+  ash::AshColorProvider::Get()->ToggleColorMode();
+  EXPECT_NE(is_dark_mode_enabled().value(), dark_mode_enabled);
+
+  ash::AshColorProvider::Get()->ToggleColorMode();
+  EXPECT_EQ(is_dark_mode_enabled().value(), dark_mode_enabled);
+}
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_user_provider_impl.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_user_provider_impl.cc
new file mode 100644
index 0000000..0d31a31
--- /dev/null
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_user_provider_impl.cc
@@ -0,0 +1,36 @@
+// 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/ash/web_applications/personalization_app/personalization_app_user_provider_impl.h"
+
+#include "ash/public/cpp/personalization_app/user_display_info.h"
+#include "ash/webui/personalization_app/mojom/personalization_app.mojom.h"
+#include "ash/webui/personalization_app/mojom/personalization_app_mojom_traits.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/user_manager/user_info.h"
+#include "content/public/browser/web_ui.h"
+
+PersonalizationAppUserProviderImpl::PersonalizationAppUserProviderImpl(
+    content::WebUI* web_ui)
+    : profile_(Profile::FromWebUI(web_ui)) {}
+
+PersonalizationAppUserProviderImpl::~PersonalizationAppUserProviderImpl() =
+    default;
+
+void PersonalizationAppUserProviderImpl::BindInterface(
+    mojo::PendingReceiver<ash::personalization_app::mojom::UserProvider>
+        receiver) {
+  user_receiver_.reset();
+  user_receiver_.Bind(std::move(receiver));
+}
+
+void PersonalizationAppUserProviderImpl::GetUserInfo(
+    GetUserInfoCallback callback) {
+  const user_manager::User* user =
+      chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
+  DCHECK(user);
+  std::move(callback).Run(ash::personalization_app::UserDisplayInfo(*user));
+}
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_user_provider_impl.h b/chrome/browser/ash/web_applications/personalization_app/personalization_app_user_provider_impl.h
new file mode 100644
index 0000000..102e1656
--- /dev/null
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_user_provider_impl.h
@@ -0,0 +1,47 @@
+// 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_ASH_WEB_APPLICATIONS_PERSONALIZATION_APP_PERSONALIZATION_APP_USER_PROVIDER_IMPL_H_
+#define CHROME_BROWSER_ASH_WEB_APPLICATIONS_PERSONALIZATION_APP_PERSONALIZATION_APP_USER_PROVIDER_IMPL_H_
+
+#include "ash/webui/personalization_app/mojom/personalization_app.mojom.h"
+#include "ash/webui/personalization_app/personalization_app_user_provider.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+
+class Profile;
+
+namespace content {
+class WebUI;
+}  // namespace content
+
+class PersonalizationAppUserProviderImpl
+    : public ash::PersonalizationAppUserProvider {
+ public:
+  explicit PersonalizationAppUserProviderImpl(content::WebUI* web_ui);
+
+  PersonalizationAppUserProviderImpl(
+      const PersonalizationAppUserProviderImpl&) = delete;
+  PersonalizationAppUserProviderImpl& operator=(
+      const PersonalizationAppUserProviderImpl&) = delete;
+
+  ~PersonalizationAppUserProviderImpl() override;
+
+  // PersonalizationAppUserProvider:
+  void BindInterface(
+      mojo::PendingReceiver<ash::personalization_app::mojom::UserProvider>
+          receiver) override;
+
+  // personalization_app::mojom::UserProvider:
+  void GetUserInfo(GetUserInfoCallback callback) override;
+
+ private:
+  // Pointer to profile of user that opened personalization SWA. Not owned.
+  Profile* const profile_ = nullptr;
+
+  mojo::Receiver<ash::personalization_app::mojom::UserProvider> user_receiver_{
+      this};
+};
+
+#endif  // CHROME_BROWSER_ASH_WEB_APPLICATIONS_PERSONALIZATION_APP_PERSONALIZATION_APP_USER_PROVIDER_IMPL_H_
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_user_provider_impl_unittest.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_user_provider_impl_unittest.cc
new file mode 100644
index 0000000..5e76ccf3
--- /dev/null
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_user_provider_impl_unittest.cc
@@ -0,0 +1,114 @@
+// 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/ash/web_applications/personalization_app/personalization_app_user_provider_impl.h"
+
+#include <memory>
+
+#include "ash/constants/ash_features.h"
+#include "ash/webui/personalization_app/mojom/personalization_app.mojom.h"
+#include "base/callback_helpers.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/bind.h"
+#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/ash/login/users/scoped_test_user_manager.h"
+#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/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/user_manager/scoped_user_manager.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_web_ui.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/webui/web_ui_util.h"
+
+namespace {
+
+constexpr char kFakeTestEmail[] = "fakeemail@personalization";
+constexpr char kFakeTestName[] = "Fake Name";
+constexpr char kTestGaiaId[] = "1234567890";
+
+void AddAndLoginUser(const AccountId& account_id,
+                     const std::string& display_name) {
+  ash::FakeChromeUserManager* user_manager =
+      static_cast<ash::FakeChromeUserManager*>(
+          user_manager::UserManager::Get());
+
+  user_manager->AddUser(account_id);
+  user_manager->SaveUserDisplayName(account_id,
+                                    base::UTF8ToUTF16(display_name));
+  user_manager->LoginUser(account_id);
+  user_manager->SwitchActiveUser(account_id);
+}
+}  // namespace
+
+class PersonalizationAppUserProviderImplTest : public testing::Test {
+ public:
+  PersonalizationAppUserProviderImplTest()
+      : scoped_user_manager_(std::make_unique<ash::FakeChromeUserManager>()),
+        profile_manager_(TestingBrowserProcess::GetGlobal()) {}
+  PersonalizationAppUserProviderImplTest(
+      const PersonalizationAppUserProviderImplTest&) = delete;
+  PersonalizationAppUserProviderImplTest& operator=(
+      const PersonalizationAppUserProviderImplTest&) = delete;
+  ~PersonalizationAppUserProviderImplTest() override = default;
+
+ protected:
+  // testing::Test:
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        ash::features::kPersonalizationHub);
+    ASSERT_TRUE(profile_manager_.SetUp());
+    profile_ = profile_manager_.CreateTestingProfile(kFakeTestEmail);
+
+    AddAndLoginUser(AccountId::FromUserEmailGaiaId(kFakeTestEmail, kTestGaiaId),
+                    kFakeTestName);
+
+    web_contents_ = content::WebContents::Create(
+        content::WebContents::CreateParams(profile_));
+    web_ui_.set_web_contents(web_contents_.get());
+
+    user_provider_ =
+        std::make_unique<PersonalizationAppUserProviderImpl>(&web_ui_);
+
+    user_provider_->BindInterface(
+        user_provider_remote_.BindNewPipeAndPassReceiver());
+  }
+
+  TestingProfile* profile() { return profile_; }
+
+  mojo::Remote<ash::personalization_app::mojom::UserProvider>*
+  user_provider_remote() {
+    return &user_provider_remote_;
+  }
+
+  PersonalizationAppUserProviderImpl* user_provider() {
+    return user_provider_.get();
+  }
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+  user_manager::ScopedUserManager scoped_user_manager_;
+  TestingProfileManager profile_manager_;
+  content::TestWebUI web_ui_;
+  std::unique_ptr<content::WebContents> web_contents_;
+  TestingProfile* profile_;
+  mojo::Remote<ash::personalization_app::mojom::UserProvider>
+      user_provider_remote_;
+  std::unique_ptr<PersonalizationAppUserProviderImpl> user_provider_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(PersonalizationAppUserProviderImplTest, GetsUserInfo) {
+  user_provider_remote()->get()->GetUserInfo(base::BindLambdaForTesting(
+      [](ash::personalization_app::UserDisplayInfo user_display_info) {
+        EXPECT_EQ(kFakeTestEmail, user_display_info.email);
+        EXPECT_EQ(kFakeTestName, user_display_info.name);
+      }));
+  user_provider_remote()->FlushForTesting();
+}
diff --git a/chrome/browser/ash/web_applications/scanning_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/scanning_app_integration_browsertest.cc
index 35fc8e97..e00d7c52 100644
--- a/chrome/browser/ash/web_applications/scanning_app_integration_browsertest.cc
+++ b/chrome/browser/ash/web_applications/scanning_app_integration_browsertest.cc
@@ -33,8 +33,9 @@
 // set to be disabled via the SystemFeaturesDisableList policy.
 IN_PROC_BROWSER_TEST_P(ScanningAppIntegrationTest, ScanningAppDisabled) {
   {
-    ListPrefUpdate update(TestingBrowserProcess::GetGlobal()->local_state(),
-                          policy::policy_prefs::kSystemFeaturesDisableList);
+    ListPrefUpdateDeprecated update(
+        TestingBrowserProcess::GetGlobal()->local_state(),
+        policy::policy_prefs::kSystemFeaturesDisableList);
     base::ListValue* list = update.Get();
     list->Append(policy::SystemFeature::kScanning);
   }
diff --git a/chrome/browser/ash/web_applications/settings_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/settings_app_integration_browsertest.cc
index c6746f0..3861e7f 100644
--- a/chrome/browser/ash/web_applications/settings_app_integration_browsertest.cc
+++ b/chrome/browser/ash/web_applications/settings_app_integration_browsertest.cc
@@ -31,8 +31,9 @@
 // via SystemFeaturesDisableList policy, but doesn't launch.
 IN_PROC_BROWSER_TEST_P(SettingsAppIntegrationTest, SettingsAppDisabled) {
   {
-    ListPrefUpdate update(TestingBrowserProcess::GetGlobal()->local_state(),
-                          policy::policy_prefs::kSystemFeaturesDisableList);
+    ListPrefUpdateDeprecated update(
+        TestingBrowserProcess::GetGlobal()->local_state(),
+        policy::policy_prefs::kSystemFeaturesDisableList);
     base::ListValue* list = update.Get();
     list->Append(policy::SystemFeature::kOsSettings);
   }
diff --git a/chrome/browser/background/background_contents_service.cc b/chrome/browser/background/background_contents_service.cc
index aba9b0b..5a4181d 100644
--- a/chrome/browser/background/background_contents_service.cc
+++ b/chrome/browser/background/background_contents_service.cc
@@ -634,7 +634,8 @@
   // already an entry for this application, no need to do anything.
   // TODO(atwilson): Verify that this is the desired behavior based on developer
   // feedback (http://crbug.com/47118).
-  DictionaryPrefUpdate update(prefs_, prefs::kRegisteredBackgroundContents);
+  DictionaryPrefUpdateDeprecated update(prefs_,
+                                        prefs::kRegisteredBackgroundContents);
   base::DictionaryValue* pref = update.Get();
   const std::string& appid = GetParentApplicationId(background_contents);
   base::DictionaryValue* current;
@@ -663,7 +664,8 @@
     return;
   DCHECK(IsTracked(background_contents));
   const std::string& appid = GetParentApplicationId(background_contents);
-  DictionaryPrefUpdate update(prefs_, prefs::kRegisteredBackgroundContents);
+  DictionaryPrefUpdateDeprecated update(prefs_,
+                                        prefs::kRegisteredBackgroundContents);
   update.Get()->RemoveKey(appid);
 }
 
diff --git a/chrome/browser/bitmap_fetcher/bitmap_fetcher.cc b/chrome/browser/bitmap_fetcher/bitmap_fetcher.cc
index 46944770..da1c42f 100644
--- a/chrome/browser/bitmap_fetcher/bitmap_fetcher.cc
+++ b/chrome/browser/bitmap_fetcher/bitmap_fetcher.cc
@@ -33,19 +33,19 @@
 
 BitmapFetcher::~BitmapFetcher() = default;
 
-void BitmapFetcher::Init(const std::string& referrer,
-                         net::ReferrerPolicy referrer_policy,
+void BitmapFetcher::Init(net::ReferrerPolicy referrer_policy,
                          network::mojom::CredentialsMode credentials_mode,
-                         const net::HttpRequestHeaders& additional_headers) {
+                         const net::HttpRequestHeaders& additional_headers,
+                         const url::Origin& initiator) {
   if (simple_loader_ != nullptr)
     return;
 
   auto resource_request = std::make_unique<network::ResourceRequest>();
   resource_request->url = url_;
-  resource_request->referrer = GURL(referrer);
   resource_request->referrer_policy = referrer_policy;
   resource_request->credentials_mode = credentials_mode;
   resource_request->headers.MergeFrom(additional_headers);
+  resource_request->request_initiator = initiator;
   simple_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
                                                     traffic_annotation_);
 }
diff --git a/chrome/browser/bitmap_fetcher/bitmap_fetcher.h b/chrome/browser/bitmap_fetcher/bitmap_fetcher.h
index c4683421..4051808 100644
--- a/chrome/browser/bitmap_fetcher/bitmap_fetcher.h
+++ b/chrome/browser/bitmap_fetcher/bitmap_fetcher.h
@@ -19,6 +19,7 @@
 #include "services/network/public/mojom/fetch_api.mojom-forward.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 class SkBitmap;
 
@@ -47,10 +48,10 @@
   // |additional_headers| will be merged with default HTTP headers provided by
   // |BitmapFetcher| when fetching the image.
   // TODO(tommycli): Init and Start should likely be combined.
-  virtual void Init(const std::string& referrer,
-                    net::ReferrerPolicy referrer_policy,
+  virtual void Init(net::ReferrerPolicy referrer_policy,
                     network::mojom::CredentialsMode credentials_mode,
-                    const net::HttpRequestHeaders& additional_headers = {});
+                    const net::HttpRequestHeaders& additional_headers = {},
+                    const url::Origin& initiator = url::Origin());
 
   // Start fetching the URL with the fetcher. The delegate is notified
   // asynchronously when done.  Start may be called more than once in some
diff --git a/chrome/browser/bitmap_fetcher/bitmap_fetcher_browsertest.cc b/chrome/browser/bitmap_fetcher/bitmap_fetcher_browsertest.cc
index 7a9c06f..75a571e7 100644
--- a/chrome/browser/bitmap_fetcher/bitmap_fetcher_browsertest.cc
+++ b/chrome/browser/bitmap_fetcher/bitmap_fetcher_browsertest.cc
@@ -20,6 +20,7 @@
 #include "net/test/embedded_test_server/http_response.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/referrer_policy.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/codec/png_codec.h"
@@ -39,9 +40,8 @@
 // Class to catch events from the BitmapFetcher for testing.
 class BitmapFetcherTestDelegate : public BitmapFetcherDelegate {
  public:
-  explicit BitmapFetcherTestDelegate(bool async) : called_(false),
-                                                   success_(false),
-                                                   async_(async) {}
+  explicit BitmapFetcherTestDelegate(bool async)
+      : called_(false), success_(false), async_(async) {}
 
   BitmapFetcherTestDelegate(const BitmapFetcherTestDelegate&) = delete;
   BitmapFetcherTestDelegate& operator=(const BitmapFetcherTestDelegate&) =
@@ -151,7 +151,6 @@
   // We expect that the image decoder will get called and return
   // an image in a callback to OnImageDecoded().
   fetcher.Init(
-      std::string(),
       net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
       network::mojom::CredentialsMode::kInclude);
   fetcher.Start(browser()
@@ -199,7 +198,6 @@
   BitmapFetcher fetcher(url, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
 
   fetcher.Init(
-      std::string(),
       net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
       network::mojom::CredentialsMode::kInclude);
   fetcher.Start(browser()
@@ -220,7 +218,6 @@
   BitmapFetcher fetcher(url, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
 
   fetcher.Init(
-      std::string(),
       net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
       network::mojom::CredentialsMode::kInclude);
   fetcher.Start(browser()
@@ -241,7 +238,6 @@
   BitmapFetcher fetcher(url, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
 
   fetcher.Init(
-      std::string(),
       net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
       network::mojom::CredentialsMode::kInclude);
   fetcher.Start(browser()
@@ -265,7 +261,6 @@
   BitmapFetcher fetcher(url, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
 
   fetcher.Init(
-      std::string(),
       net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
       network::mojom::CredentialsMode::kInclude);
   fetcher.Start(browser()
@@ -279,3 +274,55 @@
   EXPECT_TRUE(delegate.success());
   EXPECT_TRUE(gfx::BitmapsAreEqual(test_bitmap(), delegate.bitmap()));
 }
+
+class BitmapFetcherInitiatorBrowserTest : public InProcessBrowserTest {
+ public:
+  // InProcessBrowserTest:
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+
+    // Set up the test server.
+    embedded_test_server()->RegisterRequestHandler(
+        base::BindRepeating(&BitmapFetcherInitiatorBrowserTest::HandleRequest,
+                            base::Unretained(this)));
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+
+  void TearDownOnMainThread() override {
+    // Tear down the test server.
+    EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
+    InProcessBrowserTest::TearDownOnMainThread();
+  }
+
+  const std::string& all_headers() const { return all_headers_; }
+
+ private:
+  std::unique_ptr<HttpResponse> HandleRequest(const HttpRequest& request) {
+    all_headers_ = request.all_headers;
+    std::unique_ptr<BasicHttpResponse> response(new BasicHttpResponse);
+    response->set_code(net::HTTP_OK);
+    return response;
+  }
+
+  std::string all_headers_;
+};
+
+IN_PROC_BROWSER_TEST_F(BitmapFetcherInitiatorBrowserTest, SameOrigin) {
+  GURL image_url = embedded_test_server()->GetURL(kStartTestURL);
+
+  BitmapFetcherTestDelegate delegate(kAsyncCall);
+  BitmapFetcher fetcher(image_url, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  fetcher.Init(net::ReferrerPolicy::NEVER_CLEAR,
+               network::mojom::CredentialsMode::kInclude,
+               net::HttpRequestHeaders(),
+               /*initiator=*/url::Origin::Create(image_url));
+  fetcher.Start(browser()
+                    ->profile()
+                    ->GetDefaultStoragePartition()
+                    ->GetURLLoaderFactoryForBrowserProcess()
+                    .get());
+  delegate.Wait();
+
+  EXPECT_THAT(all_headers(), testing::HasSubstr("Sec-Fetch-Site: same-origin"));
+}
diff --git a/chrome/browser/bitmap_fetcher/bitmap_fetcher_service.cc b/chrome/browser/bitmap_fetcher/bitmap_fetcher_service.cc
index 01e03d5a..4203c31 100644
--- a/chrome/browser/bitmap_fetcher/bitmap_fetcher_service.cc
+++ b/chrome/browser/bitmap_fetcher/bitmap_fetcher_service.cc
@@ -109,19 +109,16 @@
     BitmapFetcherService::BitmapFetchedCallback callback)
     : request_id_(request_id), callback_(std::move(callback)) {}
 
-BitmapFetcherRequest::~BitmapFetcherRequest() {
-}
+BitmapFetcherRequest::~BitmapFetcherRequest() = default;
 
 void BitmapFetcherRequest::NotifyImageChanged(const SkBitmap* bitmap) {
   if (bitmap && !bitmap->empty())
     std::move(callback_).Run(*bitmap);
 }
 
-BitmapFetcherService::CacheEntry::CacheEntry() {
-}
+BitmapFetcherService::CacheEntry::CacheEntry() = default;
 
-BitmapFetcherService::CacheEntry::~CacheEntry() {
-}
+BitmapFetcherService::CacheEntry::~CacheEntry() = default;
 
 BitmapFetcherService::BitmapFetcherService(content::BrowserContext* context)
     : shared_data_decoder_(
@@ -212,7 +209,6 @@
       url, this, traffic_annotation, shared_data_decoder_.get());
 
   new_fetcher->Init(
-      std::string(),
       net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
       network::mojom::CredentialsMode::kInclude);
   new_fetcher->Start(context_->GetDefaultStoragePartition()
diff --git a/chrome/browser/browser_switcher/browser_switcher_navigation_throttle_unittest.cc b/chrome/browser/browser_switcher/browser_switcher_navigation_throttle_unittest.cc
index 11e0fcad..d54cd043 100644
--- a/chrome/browser/browser_switcher/browser_switcher_navigation_throttle_unittest.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_navigation_throttle_unittest.cc
@@ -42,9 +42,9 @@
   ~MockBrowserSwitcherSitelist() override = default;
 
   MOCK_CONST_METHOD1(GetDecision, Decision(const GURL&));
-  MOCK_METHOD1(SetIeemSitelist, void(ParsedXml&&));
-  MOCK_METHOD1(SetExternalSitelist, void(ParsedXml&&));
-  MOCK_METHOD1(SetExternalGreylist, void(ParsedXml&&));
+  MOCK_METHOD1(SetIeemSitelist, void(RawRuleSet&&));
+  MOCK_METHOD1(SetExternalSitelist, void(RawRuleSet&&));
+  MOCK_METHOD1(SetExternalGreylist, void(RawRuleSet&&));
   MOCK_CONST_METHOD0(GetIeemSitelist, const RuleSet*());
   MOCK_CONST_METHOD0(GetExternalSitelist, const RuleSet*());
   MOCK_CONST_METHOD0(GetExternalGreylist, const RuleSet*());
diff --git a/chrome/browser/browser_switcher/browser_switcher_prefs.cc b/chrome/browser/browser_switcher/browser_switcher_prefs.cc
index 12898ff..6bf5eb6 100644
--- a/chrome/browser/browser_switcher/browser_switcher_prefs.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_prefs.cc
@@ -29,21 +29,40 @@
 
 namespace {
 
-std::vector<std::string> GetCachedRules(PrefService* prefs,
-                                        const std::string& pref_name) {
-  std::vector<std::string> rules;
-  for (const auto& url : prefs->GetList(pref_name)->GetList())
-    rules.push_back(url.GetString());
-  return rules;
+std::vector<std::string> GetListPref(PrefService* prefs,
+                                     const std::string& pref_name) {
+  std::vector<std::string> list;
+  if (pref_name.empty())
+    return list;
+  for (const auto& value : prefs->GetList(pref_name)->GetList())
+    list.push_back(value.GetString());
+  return list;
+}
+
+RawRuleSet GetCachedRules(PrefService* prefs,
+                          const std::string& sitelist_pref_name,
+                          const std::string& greylist_pref_name) {
+  return RawRuleSet(GetListPref(prefs, sitelist_pref_name),
+                    GetListPref(prefs, greylist_pref_name));
+}
+
+void SetListPref(PrefService* prefs,
+                 const std::string& pref_name,
+                 const std::vector<std::string>& list) {
+  if (pref_name.empty())
+    return;
+  base::ListValue list_value;
+  for (const auto& str : list)
+    list_value.Append(base::Value(str));
+  prefs->Set(pref_name, list_value);
 }
 
 void SetCachedRules(PrefService* prefs,
-                    const std::string& pref_name,
-                    const std::vector<std::string>& rules) {
-  base::ListValue rules_val;
-  for (const auto& url : rules)
-    rules_val.Append(base::Value(url));
-  prefs->Set(pref_name, rules_val);
+                    const std::string& sitelist_pref_name,
+                    const std::string& greylist_pref_name,
+                    const RawRuleSet& rules) {
+  SetListPref(prefs, sitelist_pref_name, rules.sitelist);
+  SetListPref(prefs, greylist_pref_name, rules.greylist);
 }
 
 }  // namespace
@@ -170,11 +189,13 @@
   registry->RegisterListPref(prefs::kUrlGreylist);
   registry->RegisterStringPref(prefs::kExternalSitelistUrl, "");
   registry->RegisterListPref(prefs::kCachedExternalSitelist);
+  registry->RegisterListPref(prefs::kCachedExternalSitelistGreylist);
   registry->RegisterStringPref(prefs::kExternalGreylistUrl, "");
   registry->RegisterListPref(prefs::kCachedExternalGreylist);
 #if defined(OS_WIN)
   registry->RegisterBooleanPref(prefs::kUseIeSitelist, false);
   registry->RegisterListPref(prefs::kCachedIeSitelist);
+  registry->RegisterListPref(prefs::kCachedIeSitelistGreylist);
   registry->RegisterStringPref(prefs::kChromePath, "");
   registry->RegisterListPref(prefs::kChromeParameters);
 #endif
@@ -210,34 +231,33 @@
   return rules_;
 }
 
-std::vector<std::string> BrowserSwitcherPrefs::GetCachedExternalSitelist()
-    const {
-  return GetCachedRules(prefs_, prefs::kCachedExternalSitelist);
+RawRuleSet BrowserSwitcherPrefs::GetCachedExternalSitelist() const {
+  return GetCachedRules(prefs_, prefs::kCachedExternalSitelist,
+                        prefs::kCachedExternalSitelistGreylist);
 }
 
-void BrowserSwitcherPrefs::SetCachedExternalSitelist(
-    const std::vector<std::string>& sitelist) {
-  SetCachedRules(prefs_, prefs::kCachedExternalSitelist, sitelist);
+void BrowserSwitcherPrefs::SetCachedExternalSitelist(const RawRuleSet& rules) {
+  SetCachedRules(prefs_, prefs::kCachedExternalSitelist,
+                 prefs::kCachedExternalSitelistGreylist, rules);
 }
 
-std::vector<std::string> BrowserSwitcherPrefs::GetCachedExternalGreylist()
-    const {
-  return GetCachedRules(prefs_, prefs::kCachedExternalGreylist);
+RawRuleSet BrowserSwitcherPrefs::GetCachedExternalGreylist() const {
+  return GetCachedRules(prefs_, std::string(), prefs::kCachedExternalGreylist);
 }
 
-void BrowserSwitcherPrefs::SetCachedExternalGreylist(
-    const std::vector<std::string>& greylist) {
-  SetCachedRules(prefs_, prefs::kCachedExternalGreylist, greylist);
+void BrowserSwitcherPrefs::SetCachedExternalGreylist(const RawRuleSet& rules) {
+  SetCachedRules(prefs_, std::string(), prefs::kCachedExternalGreylist, rules);
 }
 
 #if defined(OS_WIN)
-std::vector<std::string> BrowserSwitcherPrefs::GetCachedIeemSitelist() const {
-  return GetCachedRules(prefs_, prefs::kCachedIeSitelist);
+RawRuleSet BrowserSwitcherPrefs::GetCachedIeemSitelist() const {
+  return GetCachedRules(prefs_, prefs::kCachedIeSitelist,
+                        prefs::kCachedIeSitelistGreylist);
 }
 
-void BrowserSwitcherPrefs::SetCachedIeemSitelist(
-    const std::vector<std::string>& sitelist) {
-  SetCachedRules(prefs_, prefs::kCachedIeSitelist, sitelist);
+void BrowserSwitcherPrefs::SetCachedIeemSitelist(const RawRuleSet& rules) {
+  SetCachedRules(prefs_, prefs::kCachedIeSitelist,
+                 prefs::kCachedIeSitelistGreylist, rules);
 }
 #endif
 
@@ -428,20 +448,27 @@
 // List of hosts that should not trigger a transition in either browser.
 const char kUrlGreylist[] = "browser_switcher.url_greylist";
 
-// URL with an external XML sitelist file to load.
+// URL with an external XML sitelist file to load. The cached ruleset has 2
+// parts (sitelist and greylist).
 const char kExternalSitelistUrl[] = "browser_switcher.external_sitelist_url";
 const char kCachedExternalSitelist[] =
     "browser_switcher.cached_external_sitelist";
+const char kCachedExternalSitelistGreylist[] =
+    "browser_switcher.cached_external_sitelist_greylist";
 
-// URL with an external XML greylist file to load.
+// URL with an external XML greylist file to load. Unlike the other cached XML
+// rulesets, this one is just a greylist (rather than a pair of lists).
 const char kExternalGreylistUrl[] = "browser_switcher.external_greylist_url";
 const char kCachedExternalGreylist[] =
     "browser_switcher.cached_external_greylist";
 
 #if defined(OS_WIN)
-// If set to true, use the IE Enterprise Mode Sitelist policy.
+// If set to true, use the IE Enterprise Mode Sitelist policy. The cached
+// ruleset has 2 parts (sitelist and greylist).
 const char kUseIeSitelist[] = "browser_switcher.use_ie_sitelist";
 const char kCachedIeSitelist[] = "browser_switcher.cached_ie_sitelist";
+const char kCachedIeSitelistGreylist[] =
+    "browser_switcher.cached_ie_sitelist_greylist";
 
 // Path to the Chrome executable for the alternative browser.
 const char kChromePath[] = "browser_switcher.chrome_path";
diff --git a/chrome/browser/browser_switcher/browser_switcher_prefs.h b/chrome/browser/browser_switcher/browser_switcher_prefs.h
index 1973f6a..26c78a2 100644
--- a/chrome/browser/browser_switcher/browser_switcher_prefs.h
+++ b/chrome/browser/browser_switcher/browser_switcher_prefs.h
@@ -171,18 +171,18 @@
 
   // Retrieves or stores the locally cached external sitelist from the
   // PrefStore.
-  std::vector<std::string> GetCachedExternalSitelist() const;
-  void SetCachedExternalSitelist(const std::vector<std::string>& sitelist);
+  RawRuleSet GetCachedExternalSitelist() const;
+  void SetCachedExternalSitelist(const RawRuleSet& sitelist);
 
   // Retrieves or stores the locally cached external greylist from the
   // PrefStore.
-  std::vector<std::string> GetCachedExternalGreylist() const;
-  void SetCachedExternalGreylist(const std::vector<std::string>& greylist);
+  RawRuleSet GetCachedExternalGreylist() const;
+  void SetCachedExternalGreylist(const RawRuleSet& greylist);
 
 #if defined(OS_WIN)
   // Retrieves or stores the locally cached IEEM sitelist from the PrefStore.
-  std::vector<std::string> GetCachedIeemSitelist() const;
-  void SetCachedIeemSitelist(const std::vector<std::string>& sitelist);
+  RawRuleSet GetCachedIeemSitelist() const;
+  void SetCachedIeemSitelist(const RawRuleSet& sitelist);
 #endif
 
   // Returns the URL to download for an external XML sitelist. If the pref is
@@ -285,12 +285,14 @@
 extern const char kUrlGreylist[];
 extern const char kExternalSitelistUrl[];
 extern const char kCachedExternalSitelist[];
+extern const char kCachedExternalSitelistGreylist[];
 extern const char kExternalGreylistUrl[];
 extern const char kCachedExternalGreylist[];
 
 #if defined(OS_WIN)
 extern const char kUseIeSitelist[];
 extern const char kCachedIeSitelist[];
+extern const char kCachedIeSitelistGreylist[];
 extern const char kChromePath[];
 extern const char kChromeParameters[];
 #endif
diff --git a/chrome/browser/browser_switcher/browser_switcher_service.cc b/chrome/browser/browser_switcher/browser_switcher_service.cc
index 1b8cc8e..8e3dd19 100644
--- a/chrome/browser/browser_switcher/browser_switcher_service.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_service.cc
@@ -25,6 +25,7 @@
 #include "net/base/load_flags.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/resource_request.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace browser_switcher {
 
@@ -109,7 +110,7 @@
 
   for (auto& source : sources_) {
     if (!source.url.is_valid())
-      DoneParsing(&source, ParsedXml({}));
+      DoneParsing(&source, ParsedXml({}, {}, absl::nullopt));
   }
 
   // Fetch in 1 minute.
@@ -166,7 +167,7 @@
 void XmlDownloader::ParseXml(RulesetSource* source,
                              std::unique_ptr<std::string> bytes) {
   if (!bytes) {
-    DoneParsing(source, ParsedXml({}, "could not fetch XML"));
+    DoneParsing(source, ParsedXml({}, {}, "could not fetch XML"));
     return;
   }
   ParseIeemXml(*bytes, base::BindOnce(&XmlDownloader::DoneParsing,
@@ -177,9 +178,20 @@
 void XmlDownloader::DoneParsing(RulesetSource* source, ParsedXml xml) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  // Greylists can't contain any negative rules, so remove the leading "!".
+  // Special processing for "greylist" XML.
   if (source->contains_inverted_rules) {
-    for (auto& rule : xml.rules) {
+    // BrowserSwitcherExternalGreylistUrl is special: all the rules are part of
+    // the greylist, regardless of what <open-in> says in the XML.
+    //
+    // Merge all the rules into |greylist|, and clear |sitelist|.
+    xml.rules.greylist.insert(xml.rules.greylist.end(),
+                              xml.rules.sitelist.begin(),
+                              xml.rules.sitelist.end());
+    xml.rules.sitelist.clear();
+
+    // Greylists can't contain any negative rules either, so remove the leading
+    // "!".
+    for (auto& rule : xml.rules.greylist) {
       if (base::StartsWith(rule, "!", base::CompareCase::SENSITIVE))
         rule.erase(0, 1);
     }
@@ -318,11 +330,9 @@
 
 void BrowserSwitcherService::LoadRulesFromPrefs() {
   if (prefs().GetExternalSitelistUrl().is_valid())
-    sitelist()->SetExternalSitelist(
-        ParsedXml(prefs().GetCachedExternalSitelist(), absl::nullopt));
+    sitelist()->SetExternalSitelist(prefs().GetCachedExternalSitelist());
   if (prefs().GetExternalGreylistUrl().is_valid())
-    sitelist()->SetExternalGreylist(
-        ParsedXml(prefs().GetCachedExternalGreylist(), absl::nullopt));
+    sitelist()->SetExternalGreylist(prefs().GetCachedExternalGreylist());
 }
 
 void BrowserSwitcherService::OnAllRulesetsParsed() {
@@ -378,7 +388,7 @@
     if (prefs().GetExternalSitelistUrl().is_valid())
       prefs().SetCachedExternalSitelist(xml.rules);
 
-    sitelist()->SetExternalSitelist(std::move(xml));
+    sitelist()->SetExternalSitelist(std::move(xml.rules));
   }
 }
 
@@ -388,11 +398,13 @@
   } else {
     VLOG(2) << "Done parsing external SiteList for greylist rules. "
             << "Applying rules to future navigations.";
+    DCHECK(xml.rules.sitelist.empty());
 
-    if (prefs().GetExternalGreylistUrl().is_valid())
+    if (prefs().GetExternalGreylistUrl().is_valid()) {
       prefs().SetCachedExternalGreylist(xml.rules);
+    }
 
-    sitelist()->SetExternalGreylist(std::move(xml));
+    sitelist()->SetExternalGreylist(std::move(xml.rules));
   }
 }
 
diff --git a/chrome/browser/browser_switcher/browser_switcher_service_win.cc b/chrome/browser/browser_switcher/browser_switcher_service_win.cc
index ce18993..1225fb97 100644
--- a/chrome/browser/browser_switcher/browser_switcher_service_win.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_service_win.cc
@@ -196,8 +196,7 @@
 void BrowserSwitcherServiceWin::LoadRulesFromPrefs() {
   BrowserSwitcherService::LoadRulesFromPrefs();
   if (prefs().UseIeSitelist())
-    sitelist()->SetIeemSitelist(
-        ParsedXml(prefs().GetCachedIeemSitelist(), absl::nullopt));
+    sitelist()->SetIeemSitelist(prefs().GetCachedIeemSitelist());
 }
 
 base::FilePath BrowserSwitcherServiceWin::GetCacheDir() {
@@ -252,7 +251,7 @@
     if (prefs().UseIeSitelist())
       prefs().SetCachedIeemSitelist(xml.rules);
 
-    sitelist()->SetIeemSitelist(std::move(xml));
+    sitelist()->SetIeemSitelist(std::move(xml.rules));
   }
 }
 
diff --git a/chrome/browser/browser_switcher/browser_switcher_sitelist.cc b/chrome/browser/browser_switcher/browser_switcher_sitelist.cc
index 336d398..4c9f5c77 100644
--- a/chrome/browser/browser_switcher/browser_switcher_sitelist.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_sitelist.cc
@@ -446,34 +446,26 @@
     return {kStay, kGreylist, reason_to_stay};
 }
 
-void BrowserSwitcherSitelistImpl::SetIeemSitelist(ParsedXml&& parsed_xml) {
-  DCHECK(!parsed_xml.error);
-
+void BrowserSwitcherSitelistImpl::SetIeemSitelist(RawRuleSet&& rules) {
   UMA_HISTOGRAM_COUNTS_100000("BrowserSwitcher.IeemSitelistSize",
-                              parsed_xml.rules.size());
-
-  StoreRules(&ieem_sitelist_.sitelist, parsed_xml.rules);
-  original_ieem_sitelist_ = std::move(parsed_xml.rules);
+                              rules.sitelist.size());
+  StoreRules(ieem_sitelist_, rules);
+  original_ieem_sitelist_ = std::move(rules);
 }
 
-void BrowserSwitcherSitelistImpl::SetExternalSitelist(ParsedXml&& parsed_xml) {
-  DCHECK(!parsed_xml.error);
-
+void BrowserSwitcherSitelistImpl::SetExternalSitelist(RawRuleSet&& rules) {
   UMA_HISTOGRAM_COUNTS_100000("BrowserSwitcher.ExternalSitelistSize",
-                              parsed_xml.rules.size());
-
-  StoreRules(&external_sitelist_.sitelist, parsed_xml.rules);
-  original_external_sitelist_ = std::move(parsed_xml.rules);
+                              rules.sitelist.size());
+  StoreRules(external_sitelist_, rules);
+  original_external_sitelist_ = std::move(rules);
 }
 
-void BrowserSwitcherSitelistImpl::SetExternalGreylist(ParsedXml&& parsed_xml) {
-  DCHECK(!parsed_xml.error);
-
+void BrowserSwitcherSitelistImpl::SetExternalGreylist(RawRuleSet&& rules) {
   UMA_HISTOGRAM_COUNTS_100000("BrowserSwitcher.ExternalGreylistSize",
-                              parsed_xml.rules.size());
-
-  StoreRules(&external_greylist_.greylist, parsed_xml.rules);
-  original_external_greylist_ = std::move(parsed_xml.rules);
+                              rules.sitelist.size());
+  DCHECK(rules.sitelist.empty());
+  StoreRules(external_greylist_, rules);
+  original_external_greylist_ = std::move(rules);
 }
 
 const RuleSet* BrowserSwitcherSitelistImpl::GetIeemSitelist() const {
@@ -488,15 +480,20 @@
   return &external_greylist_;
 }
 
-void BrowserSwitcherSitelistImpl::StoreRules(
-    std::vector<std::unique_ptr<Rule>>* dst,
-    const std::vector<std::string>& src) {
-  dst->clear();
+void BrowserSwitcherSitelistImpl::StoreRules(RuleSet& dst,
+                                             const RawRuleSet& src) {
+  dst.sitelist.clear();
+  dst.greylist.clear();
   ParsingMode parsing_mode = prefs_->GetParsingMode();
-  for (const std::string& original_rule : src) {
+  for (const std::string& original_rule : src.sitelist) {
     std::unique_ptr<Rule> rule = CanonicalizeRule(original_rule, parsing_mode);
     if (rule)
-      dst->push_back(std::move(rule));
+      dst.sitelist.push_back(std::move(rule));
+  }
+  for (const std::string& original_rule : src.greylist) {
+    std::unique_ptr<Rule> rule = CanonicalizeRule(original_rule, parsing_mode);
+    if (rule)
+      dst.greylist.push_back(std::move(rule));
   }
 }
 
@@ -506,9 +503,9 @@
   auto it = base::ranges::find(changed_prefs, prefs::kParsingMode);
   if (it != changed_prefs.end()) {
     // ParsingMode changed, re-canonicalize rules.
-    StoreRules(&ieem_sitelist_.sitelist, original_ieem_sitelist_);
-    StoreRules(&external_sitelist_.sitelist, original_external_sitelist_);
-    StoreRules(&external_greylist_.greylist, original_external_greylist_);
+    StoreRules(ieem_sitelist_, original_ieem_sitelist_);
+    StoreRules(external_sitelist_, original_external_sitelist_);
+    StoreRules(external_greylist_, original_external_greylist_);
   }
 }
 
diff --git a/chrome/browser/browser_switcher/browser_switcher_sitelist.h b/chrome/browser/browser_switcher/browser_switcher_sitelist.h
index 4c68c65..ab317807 100644
--- a/chrome/browser/browser_switcher/browser_switcher_sitelist.h
+++ b/chrome/browser/browser_switcher/browser_switcher_sitelist.h
@@ -73,17 +73,17 @@
   // Set the Internet Explorer Enterprise Mode sitelist to use, in addition to
   // Chrome's sitelist/greylist policies. Consumes the object, and performs no
   // copy.
-  virtual void SetIeemSitelist(ParsedXml&& sitelist) = 0;
+  virtual void SetIeemSitelist(RawRuleSet&& sitelist) = 0;
 
   // Set the XML sitelist file to use, in addition to Chrome's sitelist/greylist
   // policies. This XML file is in the same format as an IEEM sitelist.
   // Consumes the object, and performs no copy.
-  virtual void SetExternalSitelist(ParsedXml&& sitelist) = 0;
+  virtual void SetExternalSitelist(RawRuleSet&& sitelist) = 0;
 
   // Set the XML sitelist file to use, in addition to Chrome's sitelist/greylist
   // policies. This XML file is in the same format as an IEEM sitelist.
   // Consumes the object, and performs no copy.
-  virtual void SetExternalGreylist(ParsedXml&& sitelist) = 0;
+  virtual void SetExternalGreylist(RawRuleSet&& sitelist) = 0;
 
   virtual const RuleSet* GetIeemSitelist() const = 0;
   virtual const RuleSet* GetExternalSitelist() const = 0;
@@ -104,9 +104,9 @@
 
   // BrowserSwitcherSitelist
   Decision GetDecision(const GURL& url) const override;
-  void SetIeemSitelist(ParsedXml&& sitelist) override;
-  void SetExternalSitelist(ParsedXml&& sitelist) override;
-  void SetExternalGreylist(ParsedXml&& greylist) override;
+  void SetIeemSitelist(RawRuleSet&& rules) override;
+  void SetExternalSitelist(RawRuleSet&& rules) override;
+  void SetExternalGreylist(RawRuleSet&& rules) override;
   const RuleSet* GetIeemSitelist() const override;
   const RuleSet* GetExternalSitelist() const override;
   const RuleSet* GetExternalGreylist() const override;
@@ -119,8 +119,7 @@
 
   // Stores the rules from |src| in |dst|, by first calling CanonicalizeRule()
   // on them.
-  void StoreRules(std::vector<std::unique_ptr<Rule>>* dst,
-                  const std::vector<std::string>& src);
+  void StoreRules(RuleSet& dst, const RawRuleSet& src);
 
   // CanonicalizeRule() has different output based on ParsingMode, so we need to
   // re-canonicalize them if the parsing mode changes.
@@ -129,12 +128,12 @@
 
   RuleSet ieem_sitelist_;
   RuleSet external_sitelist_;
-  RuleSet external_greylist_;
+  RuleSet external_greylist_;  // |external_greylist_.sitelist| is always empty.
 
   // Original values used for canonicalization.
-  std::vector<std::string> original_ieem_sitelist_;
-  std::vector<std::string> original_external_sitelist_;
-  std::vector<std::string> original_external_greylist_;
+  RawRuleSet original_ieem_sitelist_;
+  RawRuleSet original_external_sitelist_;
+  RawRuleSet original_external_greylist_;
 
   base::CallbackListSubscription prefs_changed_subscription_;
 
diff --git a/chrome/browser/browser_switcher/browser_switcher_sitelist_unittest.cc b/chrome/browser/browser_switcher/browser_switcher_sitelist_unittest.cc
index d1ad1da4..658d4e5 100644
--- a/chrome/browser/browser_switcher/browser_switcher_sitelist_unittest.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_sitelist_unittest.cc
@@ -424,9 +424,8 @@
 
 TEST_P(BrowserSwitcherSitelistTest, SetIeemSitelist) {
   Initialize({}, {});
-  ParsedXml ieem;
-  ieem.rules = {"example.com"};
-  sitelist()->SetIeemSitelist(std::move(ieem));
+  // XXX: Also do some tests that use ieem.rules.greylist.
+  sitelist()->SetIeemSitelist(RawRuleSet({"example.com"}, {}));
   EXPECT_TRUE(ShouldSwitch(GURL("http://example.com/")));
   EXPECT_TRUE(ShouldSwitch(GURL("http://bar.example.com/")));
   EXPECT_FALSE(ShouldSwitch(GURL("http://google.com/")));
@@ -434,9 +433,7 @@
 
 TEST_P(BrowserSwitcherSitelistTest, SetExternalSitelist) {
   Initialize({}, {});
-  ParsedXml external;
-  external.rules = {"example.com"};
-  sitelist()->SetExternalSitelist(std::move(external));
+  sitelist()->SetExternalSitelist(RawRuleSet({"example.com"}, {}));
   EXPECT_TRUE(ShouldSwitch(GURL("http://example.com/")));
   EXPECT_TRUE(ShouldSwitch(GURL("http://bar.example.com/")));
   EXPECT_FALSE(ShouldSwitch(GURL("http://google.com/")));
@@ -444,9 +441,7 @@
 
 TEST_P(BrowserSwitcherSitelistTest, SetExternalGreylist) {
   Initialize({"example.com"}, {});
-  ParsedXml external;
-  external.rules = {"foo.example.com"};
-  sitelist()->SetExternalGreylist(std::move(external));
+  sitelist()->SetExternalGreylist(RawRuleSet({}, {"foo.example.com"}));
   EXPECT_TRUE(ShouldSwitch(GURL("http://example.com/")));
   EXPECT_TRUE(ShouldSwitch(GURL("http://bar.example.com/")));
   EXPECT_FALSE(ShouldSwitch(GURL("http://foo.example.com/")));
@@ -455,12 +450,8 @@
 
 TEST_P(BrowserSwitcherSitelistTest, All3Sources) {
   Initialize({"google.com"}, {"mail.google.com"});
-  ParsedXml ieem;
-  ieem.rules = {"!maps.google.com"};
-  sitelist()->SetIeemSitelist(std::move(ieem));
-  ParsedXml external;
-  external.rules = {"yahoo.com"};
-  sitelist()->SetExternalSitelist(std::move(external));
+  sitelist()->SetIeemSitelist(RawRuleSet({"!maps.google.com"}, {}));
+  sitelist()->SetExternalSitelist(RawRuleSet({"yahoo.com"}, {}));
   EXPECT_TRUE(ShouldSwitch(GURL("http://google.com/")));
   EXPECT_TRUE(ShouldSwitch(GURL("http://drive.google.com/")));
   EXPECT_FALSE(ShouldSwitch(GURL("http://mail.google.com/")));
@@ -472,19 +463,13 @@
 TEST_P(BrowserSwitcherSitelistTest, ReCanonicalizeWhenParsingModeChanges) {
   // Configure all sitelists/greylists at the same time.
   Initialize({"google.com"}, {"mail.google.com"});
-  ParsedXml ieem;
-  ieem.rules = {"example.com"};
-  sitelist()->SetIeemSitelist(std::move(ieem));
-  ParsedXml external_sitelist;
-  external_sitelist.rules = {"yahoo.com"};
-  sitelist()->SetExternalSitelist(std::move(external_sitelist));
-  ParsedXml external_greylist;
-  external_greylist.rules = {"duckduckgo.com"};
-  sitelist()->SetExternalGreylist(std::move(external_greylist));
+  sitelist()->SetIeemSitelist(RawRuleSet({"example.com"}, {"grey.com"}));
+  sitelist()->SetExternalSitelist(RawRuleSet({"yahoo.com"}, {}));
+  sitelist()->SetExternalGreylist(RawRuleSet({}, {"duckduckgo.com"}));
 
   // Check initial state.
   CheckRuleSetSize(1u, 1u, &prefs()->GetRules());
-  CheckRuleSetSize(1u, 0u, sitelist()->GetIeemSitelist());
+  CheckRuleSetSize(1u, 1u, sitelist()->GetIeemSitelist());
   CheckRuleSetSize(1u, 0u, sitelist()->GetExternalSitelist());
   CheckRuleSetSize(0u, 1u, sitelist()->GetExternalGreylist());
   if (parsing_mode() == ParsingMode::kDefault) {
@@ -492,6 +477,8 @@
     EXPECT_EQ("mail.google.com", prefs()->GetRules().greylist[0]->ToString());
     EXPECT_EQ("example.com",
               sitelist()->GetIeemSitelist()->sitelist[0]->ToString());
+    EXPECT_EQ("grey.com",
+              sitelist()->GetIeemSitelist()->greylist[0]->ToString());
     EXPECT_EQ("yahoo.com",
               sitelist()->GetExternalSitelist()->sitelist[0]->ToString());
     EXPECT_EQ("duckduckgo.com",
@@ -502,6 +489,8 @@
               prefs()->GetRules().greylist[0]->ToString());
     EXPECT_EQ("*://example.com/",
               sitelist()->GetIeemSitelist()->sitelist[0]->ToString());
+    EXPECT_EQ("*://grey.com/",
+              sitelist()->GetIeemSitelist()->greylist[0]->ToString());
     EXPECT_EQ("*://yahoo.com/",
               sitelist()->GetExternalSitelist()->sitelist[0]->ToString());
     EXPECT_EQ("*://duckduckgo.com/",
@@ -521,7 +510,7 @@
 
   // Check that canonicalization changed.
   CheckRuleSetSize(1u, 1u, &prefs()->GetRules());
-  CheckRuleSetSize(1u, 0u, sitelist()->GetIeemSitelist());
+  CheckRuleSetSize(1u, 1u, sitelist()->GetIeemSitelist());
   CheckRuleSetSize(1u, 0u, sitelist()->GetExternalSitelist());
   CheckRuleSetSize(0u, 1u, sitelist()->GetExternalGreylist());
   if (new_parsing_mode == ParsingMode::kDefault) {
@@ -529,6 +518,8 @@
     EXPECT_EQ("mail.google.com", prefs()->GetRules().greylist[0]->ToString());
     EXPECT_EQ("example.com",
               sitelist()->GetIeemSitelist()->sitelist[0]->ToString());
+    EXPECT_EQ("grey.com",
+              sitelist()->GetIeemSitelist()->greylist[0]->ToString());
     EXPECT_EQ("yahoo.com",
               sitelist()->GetExternalSitelist()->sitelist[0]->ToString());
     EXPECT_EQ("duckduckgo.com",
@@ -539,6 +530,8 @@
               prefs()->GetRules().greylist[0]->ToString());
     EXPECT_EQ("*://example.com/",
               sitelist()->GetIeemSitelist()->sitelist[0]->ToString());
+    EXPECT_EQ("*://grey.com/",
+              sitelist()->GetIeemSitelist()->greylist[0]->ToString());
     EXPECT_EQ("*://yahoo.com/",
               sitelist()->GetExternalSitelist()->sitelist[0]->ToString());
     EXPECT_EQ("*://duckduckgo.com/",
diff --git a/chrome/browser/browser_switcher/ieem_sitelist_parser.cc b/chrome/browser/browser_switcher/ieem_sitelist_parser.cc
index 7686f4eb..2011e017 100644
--- a/chrome/browser/browser_switcher/ieem_sitelist_parser.cc
+++ b/chrome/browser/browser_switcher/ieem_sitelist_parser.cc
@@ -85,7 +85,7 @@
       Entry domain = ParseDomainOrPath(*domain_node, result);
       if (!domain.text.empty() && !domain.exclude) {
         std::string prefix = (domain.do_not_transition ? "!" : "");
-        result->rules.push_back(prefix + domain.text);
+        result->rules.sitelist.push_back(prefix + domain.text);
       }
       // Loop over <path> elements.
       for (const base::Value* path_node :
@@ -93,7 +93,7 @@
         Entry path = ParseDomainOrPath(*path_node, result);
         if (!path.text.empty() && !domain.text.empty() && !path.exclude) {
           std::string prefix = (path.do_not_transition ? "!" : "");
-          result->rules.push_back(prefix + domain.text + path.text);
+          result->rules.sitelist.push_back(prefix + domain.text + path.text);
         }
       }
     }
@@ -122,7 +122,7 @@
     std::string prefix =
         (mode.empty() || !base::CompareCaseInsensitiveASCII(mode, "none")) ? "!"
                                                                            : "";
-    result->rules.push_back(prefix + url);
+    result->rules.sitelist.push_back(prefix + url);
   }
 }
 
@@ -130,7 +130,7 @@
                   data_decoder::DataDecoder::ValueOrError xml) {
   if (!xml.value) {
     // Copies the string, but it should only be around 20 characters.
-    std::move(callback).Run(ParsedXml({}, *xml.error));
+    std::move(callback).Run(ParsedXml({}, {}, *xml.error));
     return;
   }
   DCHECK(data_decoder::IsXmlElementOfType(
@@ -153,9 +153,13 @@
 
 ParsedXml::ParsedXml() = default;
 ParsedXml::ParsedXml(ParsedXml&&) = default;
-ParsedXml::ParsedXml(std::vector<std::string>&& rules_,
-                     absl::optional<std::string>&& error_)
+ParsedXml::ParsedXml(RawRuleSet&& rules_, absl::optional<std::string>&& error_)
     : rules(std::move(rules_)), error(std::move(error_)) {}
+ParsedXml::ParsedXml(std::vector<std::string>&& sitelist,
+                     std::vector<std::string>&& greylist,
+                     absl::optional<std::string>&& error)
+    : ParsedXml(RawRuleSet(std::move(sitelist), std::move(greylist)),
+                std::move(error)) {}
 ParsedXml::~ParsedXml() = default;
 
 void ParseIeemXml(const std::string& xml,
diff --git a/chrome/browser/browser_switcher/ieem_sitelist_parser.h b/chrome/browser/browser_switcher/ieem_sitelist_parser.h
index 8332db9..0f185a4 100644
--- a/chrome/browser/browser_switcher/ieem_sitelist_parser.h
+++ b/chrome/browser/browser_switcher/ieem_sitelist_parser.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "chrome/browser/browser_switcher/browser_switcher_prefs.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
@@ -19,14 +20,18 @@
  public:
   ParsedXml();
   ParsedXml(ParsedXml&&);
-  ParsedXml(std::vector<std::string>&& rules,
+  ParsedXml(RawRuleSet&& rules, absl::optional<std::string>&& error);
+  ParsedXml(std::vector<std::string>&& sitelist,
+            std::vector<std::string>&& greylist,
             absl::optional<std::string>&& error);
   ~ParsedXml();
 
   ParsedXml(const ParsedXml&) = delete;
   ParsedXml& operator=(const ParsedXml&) = delete;
 
-  std::vector<std::string> rules;
+  ParsedXml& operator=(ParsedXml&&) = default;
+
+  RawRuleSet rules;
   absl::optional<std::string> error;
 };
 
diff --git a/chrome/browser/browser_switcher/ieem_sitelist_parser_unittest.cc b/chrome/browser/browser_switcher/ieem_sitelist_parser_unittest.cc
index 14bd1d6..1e6e855 100644
--- a/chrome/browser/browser_switcher/ieem_sitelist_parser_unittest.cc
+++ b/chrome/browser/browser_switcher/ieem_sitelist_parser_unittest.cc
@@ -21,7 +21,8 @@
                  ParsedXml expected,
                  ParsedXml actual) {
   base::ScopedClosureRunner runner(std::move(quit_run_loop));
-  EXPECT_EQ(expected.rules, actual.rules);
+  EXPECT_EQ(expected.rules.sitelist, actual.rules.sitelist);
+  EXPECT_EQ(expected.rules.greylist, actual.rules.greylist);
   EXPECT_EQ(expected.error.has_value(), actual.error.has_value());
   if (expected.error.has_value() && actual.error.has_value())
     EXPECT_EQ(*expected.error, *actual.error);
@@ -47,14 +48,15 @@
 };
 
 TEST_F(IeemSitelistParserTest, BadXml) {
-  TestParseXml("", ParsedXml({}, "Invalid XML: bad content"));
-  TestParseXml("thisisnotxml", ParsedXml({}, "Invalid XML: bad content"));
+  TestParseXml("", ParsedXml({}, {}, "Invalid XML: bad content"));
+  TestParseXml("thisisnotxml", ParsedXml({}, {}, "Invalid XML: bad content"));
 }
 
 TEST_F(IeemSitelistParserTest, BadXmlParsed) {
-  TestParseXml("<bogus></bogus>", ParsedXml({}, "Invalid XML root element"));
+  TestParseXml("<bogus></bogus>",
+               ParsedXml({}, {}, "Invalid XML root element"));
   TestParseXml("<rules version=\"424\"><unknown></unknown></rules>",
-               ParsedXml({}, absl::nullopt));
+               ParsedXml({}, {}, absl::nullopt));
 }
 
 TEST_F(IeemSitelistParserTest, V1OnlyBogusElements) {
@@ -64,7 +66,7 @@
       "</more><emie><domain>ignoretoo.com<path>/ignored_path</path>"
       "</domain></emie><domain>onemoreignored.com</domain>"
       "<path>/ignore_outside_of_domain></path></unknown></rules>";
-  TestParseXml(xml, ParsedXml({}, absl::nullopt));
+  TestParseXml(xml, ParsedXml({}, {}, absl::nullopt));
 }
 
 TEST_F(IeemSitelistParserTest, V1Full) {
@@ -135,7 +137,7 @@
       "!yes.com/actuallyno",
       "!no.com",
   };
-  TestParseXml(xml, ParsedXml(std::move(expected_sitelist), absl::nullopt));
+  TestParseXml(xml, ParsedXml(std::move(expected_sitelist), {}, absl::nullopt));
 }
 
 TEST_F(IeemSitelistParserTest, V2Full) {
@@ -164,7 +166,7 @@
       "!google.com",  "!good.site",     "www.cpandl.com",
       "!contoso.com", "!relecloud.com", "!relecloud.com/about",
   };
-  TestParseXml(xml, ParsedXml(std::move(expected_sitelist), absl::nullopt));
+  TestParseXml(xml, ParsedXml(std::move(expected_sitelist), {}, absl::nullopt));
 }
 
 }  // namespace browser_switcher
diff --git a/chrome/browser/cart/BUILD.gn b/chrome/browser/cart/BUILD.gn
index 1889d2f8..0fa3533 100644
--- a/chrome/browser/cart/BUILD.gn
+++ b/chrome/browser/cart/BUILD.gn
@@ -7,4 +7,5 @@
 mojom("mojo_bindings") {
   sources = [ "chrome_cart.mojom" ]
   public_deps = [ "//url/mojom:url_mojom_gurl" ]
+  webui_module_path = "/"
 }
diff --git a/chrome/browser/cart/cart_service.cc b/chrome/browser/cart/cart_service.cc
index 5d191ae..a354832 100644
--- a/chrome/browser/cart/cart_service.cc
+++ b/chrome/browser/cart/cart_service.cc
@@ -946,7 +946,8 @@
     VLOG(1) << "Empty rule based discounts, cache nothing";
     return;
   }
-  DictionaryPrefUpdate update(profile_->GetPrefs(), prefs::kCartUsedDiscounts);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kCartUsedDiscounts);
   for (auto discount_info : proto.discount_info().rule_discount_info()) {
     update->SetBoolKey(discount_info.rule_id(), true);
   }
diff --git a/chrome/browser/cart/cart_service_unittest.cc b/chrome/browser/cart/cart_service_unittest.cc
index 955b0eb..4571721 100644
--- a/chrome/browser/cart/cart_service_unittest.cc
+++ b/chrome/browser/cart/cart_service_unittest.cc
@@ -1898,13 +1898,13 @@
   profile_->GetPrefs()->SetBoolean(prefs::kCartDiscountEnabled, true);
 
   EXPECT_CALL(coupon_service_, MaybeFeatureStatusChanged(false)).Times(2);
-  ListPrefUpdate(profile_->GetPrefs(), prefs::kNtpDisabledModules)
+  ListPrefUpdateDeprecated(profile_->GetPrefs(), prefs::kNtpDisabledModules)
       ->Append(base::Value("chrome_cart"));
-  ListPrefUpdate(profile_->GetPrefs(), prefs::kNtpDisabledModules)
+  ListPrefUpdateDeprecated(profile_->GetPrefs(), prefs::kNtpDisabledModules)
       ->Append(base::Value("something_unrelated"));
 
   EXPECT_CALL(coupon_service_, MaybeFeatureStatusChanged(true)).Times(1);
-  ListPrefUpdate(profile_->GetPrefs(), prefs::kNtpDisabledModules)
+  ListPrefUpdateDeprecated(profile_->GetPrefs(), prefs::kNtpDisabledModules)
       ->EraseListValue(base::Value("chrome_cart"));
 }
 
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index e792281..4760e7ac 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -979,6 +979,10 @@
     RegisterWebUIControllerInterfaceBinder<
         ash::personalization_app::mojom::ThemeProvider,
         ash::PersonalizationAppUI>(map);
+
+    RegisterWebUIControllerInterfaceBinder<
+        ash::personalization_app::mojom::UserProvider,
+        ash::PersonalizationAppUI>(map);
   }
 
   RegisterWebUIControllerInterfaceBinder<
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 712138e..3169461 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -400,8 +400,8 @@
     // Clear kProfilesLastActive since the user only wants to launch a specific
     // profile. Don't clear it if the user launched a web app, in order to not
     // break any subsequent multi-profile session restore.
-    ListPrefUpdate update(g_browser_process->local_state(),
-                          prefs::kProfilesLastActive);
+    ListPrefUpdateDeprecated update(g_browser_process->local_state(),
+                                    prefs::kProfilesLastActive);
     base::ListValue* profile_list = update.Get();
     profile_list->ClearList();
   }
@@ -1643,7 +1643,7 @@
 
     // Only read and update the persisted sets when First-Party Sets component
     // will be installed.
-    if (base::FeatureList::IsEnabled(net::features::kFirstPartySets)) {
+    if (base::FeatureList::IsEnabled(features::kFirstPartySets)) {
       FirstPartySetsUtil::GetInstance()->SendAndUpdatePersistedSets(
           user_data_dir_,
           /*send_sets=*/
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 2ff56f7..e10d222f 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -56,6 +56,7 @@
 #include "chrome/browser/federated_learning/floc_id_provider.h"
 #include "chrome/browser/federated_learning/floc_id_provider_factory.h"
 #include "chrome/browser/first_party_sets/first_party_sets_pref_names.h"
+#include "chrome/browser/first_party_sets/first_party_sets_util.h"
 #include "chrome/browser/font_access/chrome_font_access_delegate.h"
 #include "chrome/browser/font_family_cache.h"
 #include "chrome/browser/gpu/chrome_browser_main_extra_parts_gpu.h"
@@ -241,7 +242,6 @@
 #include "components/site_isolation/preloaded_isolated_origins.h"
 #include "components/site_isolation/site_isolation_policy.h"
 #include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
-#include "components/subresource_redirect/common/subresource_redirect_features.h"
 #include "components/translate/core/common/translate_switches.h"
 #include "components/variations/variations_associated_data.h"
 #include "components/variations/variations_switches.h"
@@ -3601,12 +3601,6 @@
     web_prefs->text_track_window_radius = style->window_radius;
   }
 
-  if (subresource_redirect::ShouldEnablePublicImageHintsBasedCompression() ||
-      subresource_redirect::ShouldEnableLoginRobotsCheckedImageCompression()) {
-    web_prefs->litepage_subresource_redirect_origin =
-        subresource_redirect::GetSubresourceRedirectOrigin();
-  }
-
 #if defined(OS_ANDROID)
   // If the pref is not set, the default value (true) will be used:
   web_prefs->webxr_immersive_ar_allowed =
@@ -4397,7 +4391,7 @@
   auto* pref_service = profile->GetPrefs();
   DCHECK(pref_service);
 
-  DictionaryPrefUpdate pref_update(
+  DictionaryPrefUpdateDeprecated pref_update(
       pref_service, prefs::kDevToolsBackgroundServicesExpirationDict);
   base::DictionaryValue* exp_dict = pref_update.Get();
 
@@ -6432,22 +6426,5 @@
 }
 
 bool ChromeContentBrowserClient::IsFirstPartySetsEnabled() {
-  // TODO(https://crbug.com/1269360): move this logic into
-  // FirstPartySetsUtil::IsFirstPartySetsEnabled
-  if (!base::FeatureList::IsEnabled(net::features::kFirstPartySets)) {
-    return false;
-  }
-
-  PrefService* local_state;
-  if (g_browser_process) {
-    local_state = g_browser_process->local_state();
-  } else {
-    local_state = startup_data_.chrome_feature_list_creator()->local_state();
-  }
-
-  if (!local_state ||
-      !local_state->HasPrefPath(first_party_sets::kFirstPartySetsEnabled)) {
-    return true;
-  }
-  return local_state->GetBoolean(first_party_sets::kFirstPartySetsEnabled);
+  return FirstPartySetsUtil::GetInstance()->IsFirstPartySetsEnabled();
 }
diff --git a/chrome/browser/chrome_content_browser_client_receiver_bindings.cc b/chrome/browser/chrome_content_browser_client_receiver_bindings.cc
index f601f116..69376a84 100644
--- a/chrome/browser/chrome_content_browser_client_receiver_bindings.cc
+++ b/chrome/browser/chrome_content_browser_client_receiver_bindings.cc
@@ -24,7 +24,6 @@
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/predictors/loading_predictor.h"
 #include "chrome/browser/predictors/loading_predictor_factory.h"
-#include "chrome/browser/subresource_redirect/subresource_redirect_observer.h"
 #include "chrome/browser/ui/search_engines/search_engine_tab_helper.h"
 #include "chrome/common/buildflags.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
@@ -587,16 +586,6 @@
               BindReceiver(std::move(receiver), render_frame_host);
         },
         &render_frame_host));
-    associated_registry.AddInterface(base::BindRepeating(
-        [](content::RenderFrameHost* render_frame_host,
-           mojo::PendingAssociatedReceiver<
-               subresource_redirect::mojom::SubresourceRedirectService>
-               receiver) {
-          subresource_redirect::SubresourceRedirectObserver::
-              BindSubresourceRedirectService(std::move(receiver),
-                                             render_frame_host);
-        },
-        &render_frame_host));
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
     associated_registry.AddInterface(base::BindRepeating(
         [](content::RenderFrameHost* render_frame_host,
diff --git a/chrome/browser/chrome_origin_trials_browsertest.cc b/chrome/browser/chrome_origin_trials_browsertest.cc
index 8e0fe0d..4b130a6 100644
--- a/chrome/browser/chrome_origin_trials_browsertest.cc
+++ b/chrome/browser/chrome_origin_trials_browsertest.cc
@@ -61,7 +61,7 @@
     for (const std::string& feature : features) {
       disabled_feature_list.Append(feature);
     }
-    ListPrefUpdate update(
+    ListPrefUpdateDeprecated update(
         local_state(), embedder_support::prefs::kOriginTrialDisabledFeatures);
     update->Swap(&disabled_feature_list);
   }
@@ -71,8 +71,8 @@
     for (const std::string& token : tokens) {
       disabled_token_list.Append(token);
     }
-    ListPrefUpdate update(local_state(),
-                          embedder_support::prefs::kOriginTrialDisabledTokens);
+    ListPrefUpdateDeprecated update(
+        local_state(), embedder_support::prefs::kOriginTrialDisabledTokens);
     update->Swap(&disabled_token_list);
   }
 
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 19f0da4..df6adea 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1320,10 +1320,6 @@
     "../ash/crostini/crostini_upgrader_ui_delegate.h",
     "../ash/crostini/crostini_util.cc",
     "../ash/crostini/crostini_util.h",
-    "../ash/crostini/crosvm_metrics.cc",
-    "../ash/crostini/crosvm_metrics.h",
-    "../ash/crostini/crosvm_process_list.cc",
-    "../ash/crostini/crosvm_process_list.h",
     "../ash/crostini/fake_crostini_installer_ui_delegate.cc",
     "../ash/crostini/fake_crostini_installer_ui_delegate.h",
     "../ash/crostini/termina_installer.cc",
@@ -3199,6 +3195,8 @@
     "../ash/web_applications/personalization_app/chrome_personalization_app_wallpaper_provider.h",
     "../ash/web_applications/personalization_app/personalization_app_info.cc",
     "../ash/web_applications/personalization_app/personalization_app_info.h",
+    "../ash/web_applications/personalization_app/personalization_app_user_provider_impl.cc",
+    "../ash/web_applications/personalization_app/personalization_app_user_provider_impl.h",
     "../ash/web_applications/print_management_web_app_info.cc",
     "../ash/web_applications/print_management_web_app_info.h",
     "../ash/web_applications/projector_app/untrusted_projector_ui_config.cc",
@@ -4104,8 +4102,6 @@
     "../ash/crostini/crostini_unsupported_action_notifier_unittest.cc",
     "../ash/crostini/crostini_upgrade_available_notification_unittest.cc",
     "../ash/crostini/crostini_util_unittest.cc",
-    "../ash/crostini/crosvm_metrics_unittest.cc",
-    "../ash/crostini/crosvm_process_list_unittest.cc",
     "../ash/crostini/termina_installer_unittest.cc",
     "../ash/crostini/throttle/crostini_active_window_throttle_observer_unittest.cc",
     "../ash/crostini/throttle/crostini_throttle_unittest.cc",
@@ -4588,7 +4584,9 @@
     "../ash/usb/cros_usb_detector_unittest.cc",
     "../ash/web_applications/help_app/help_app_discover_tab_notification_unittest.cc",
     "../ash/web_applications/help_app/help_app_notification_controller_unittest.cc",
+    "../ash/web_applications/personalization_app/chrome_personalization_app_theme_provider_unittest.cc",
     "../ash/web_applications/personalization_app/chrome_personalization_app_wallpaper_provider_unittest.cc",
+    "../ash/web_applications/personalization_app/personalization_app_user_provider_impl_unittest.cc",
     "../ash/wilco_dtc_supportd/testing_wilco_dtc_supportd_bridge_wrapper.cc",
     "../ash/wilco_dtc_supportd/testing_wilco_dtc_supportd_bridge_wrapper.h",
     "../ash/wilco_dtc_supportd/testing_wilco_dtc_supportd_network_context.cc",
diff --git a/chrome/browser/chromeos/extensions/echo_private_api.cc b/chrome/browser/chromeos/extensions/echo_private_api.cc
index d2c2894..4617be4 100644
--- a/chrome/browser/chromeos/extensions/echo_private_api.cc
+++ b/chrome/browser/chromeos/extensions/echo_private_api.cc
@@ -100,7 +100,8 @@
       params->offer_info.additional_properties.DeepCopyWithoutEmptyChildren();
 
   PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate offer_update(local_state, prefs::kEchoCheckedOffers);
+  DictionaryPrefUpdateDeprecated offer_update(local_state,
+                                              prefs::kEchoCheckedOffers);
   offer_update->SetKey("echo." + service_id,
                        base::Value::FromUniquePtrValue(std::move(dict)));
   return RespondNow(NoArguments());
diff --git a/chrome/browser/chromeos/extensions/input_method_api.cc b/chrome/browser/chromeos/extensions/input_method_api.cc
index 89ba5ce3..8151083 100644
--- a/chrome/browser/chromeos/extensions/input_method_api.cc
+++ b/chrome/browser/chromeos/extensions/input_method_api.cc
@@ -393,7 +393,7 @@
   const auto params = SetSettings::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
-  DictionaryPrefUpdate update(
+  DictionaryPrefUpdateDeprecated update(
       Profile::FromBrowserContext(browser_context())->GetPrefs(),
       prefs::kLanguageInputMethodSpecificSettings);
   update->SetPath(params->engine_id, params->settings.ToValue()->Clone());
diff --git a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer.h b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer.h
index 2d0b736a..5fd220f 100644
--- a/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer.h
+++ b/chrome/browser/chromeos/extensions/speech/speech_recognition_private_recognizer.h
@@ -48,6 +48,7 @@
   void OnSpeechSoundLevelChanged(int16_t level) override {}
   void OnSpeechRecognitionStateChanged(
       SpeechRecognizerStatus new_state) override;
+  void OnSpeechRecognitionStopped() override {}
 
   // Handles a call to start speech recognition.
   void HandleStart(absl::optional<std::string> locale,
diff --git a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc
index 63223a6..10757b6 100644
--- a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc
+++ b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_browsertest.cc
@@ -393,8 +393,8 @@
       *DlpRulesManagerFactory::GetForPrimaryProfile(), &helper);
 
   {
-    ListPrefUpdate update(g_browser_process->local_state(),
-                          policy_prefs::kDlpRulesList);
+    ListPrefUpdateDeprecated update(g_browser_process->local_state(),
+                                    policy_prefs::kDlpRulesList);
     base::Value rule(base::Value::Type::DICTIONARY);
     base::Value src_urls(base::Value::Type::DICTIONARY);
     base::Value src_urls_list(base::Value::Type::LIST);
@@ -478,8 +478,8 @@
   SetupCrostini();
 
   {
-    ListPrefUpdate update(g_browser_process->local_state(),
-                          policy_prefs::kDlpRulesList);
+    ListPrefUpdateDeprecated update(g_browser_process->local_state(),
+                                    policy_prefs::kDlpRulesList);
     base::Value rule(base::Value::Type::DICTIONARY);
     base::Value src_urls(base::Value::Type::DICTIONARY);
     base::Value src_urls_list(base::Value::Type::LIST);
@@ -596,8 +596,8 @@
 
   // TODO(1276069): Refactor duplicated code below.
   {
-    ListPrefUpdate update(g_browser_process->local_state(),
-                          policy_prefs::kDlpRulesList);
+    ListPrefUpdateDeprecated update(g_browser_process->local_state(),
+                                    policy_prefs::kDlpRulesList);
     base::Value rule(base::Value::Type::DICTIONARY);
     base::Value src_urls(base::Value::Type::DICTIONARY);
     base::Value src_urls_list(base::Value::Type::LIST);
@@ -690,8 +690,8 @@
 
   // TODO(1276069): Refactor duplicated code below.
   {
-    ListPrefUpdate update(g_browser_process->local_state(),
-                          policy_prefs::kDlpRulesList);
+    ListPrefUpdateDeprecated update(g_browser_process->local_state(),
+                                    policy_prefs::kDlpRulesList);
     base::Value rule(base::Value::Type::DICTIONARY);
     base::Value src_urls(base::Value::Type::DICTIONARY);
     base::Value src_urls_list(base::Value::Type::LIST);
@@ -781,8 +781,8 @@
 
   // TODO(1276069): Refactor duplicated code below.
   {
-    ListPrefUpdate update(g_browser_process->local_state(),
-                          policy_prefs::kDlpRulesList);
+    ListPrefUpdateDeprecated update(g_browser_process->local_state(),
+                                    policy_prefs::kDlpRulesList);
     base::Value rule(base::Value::Type::DICTIONARY);
     base::Value src_urls(base::Value::Type::DICTIONARY);
     base::Value src_urls_list(base::Value::Type::LIST);
@@ -861,8 +861,8 @@
 
   // TODO(1276069): Refactor duplicated code below.
   {
-    ListPrefUpdate update(g_browser_process->local_state(),
-                          policy_prefs::kDlpRulesList);
+    ListPrefUpdateDeprecated update(g_browser_process->local_state(),
+                                    policy_prefs::kDlpRulesList);
     base::Value rule(base::Value::Type::DICTIONARY);
     base::Value src_urls(base::Value::Type::DICTIONARY);
     base::Value src_urls_list(base::Value::Type::LIST);
diff --git a/chrome/browser/client_hints/client_hints_browsertest.cc b/chrome/browser/client_hints/client_hints_browsertest.cc
index 94ec9e6..cb4fca0 100644
--- a/chrome/browser/client_hints/client_hints_browsertest.cc
+++ b/chrome/browser/client_hints/client_hints_browsertest.cc
@@ -2961,7 +2961,7 @@
       accept_ch_frame_observer_interceptor_;
 };
 
-#if defined(OS_CHROMEOS)
+#if defined(OS_CHROMEOS) || defined(OS_LINUX)
 // Flaky: https://crbug.com/1195790
 #define MAYBE_AcceptCHFrame DISABLED_AcceptCHFrame
 #else
diff --git a/chrome/browser/commerce/price_tracking/android/DEPS b/chrome/browser/commerce/price_tracking/android/DEPS
index 2da1d46..26a8e4a 100644
--- a/chrome/browser/commerce/price_tracking/android/DEPS
+++ b/chrome/browser/commerce/price_tracking/android/DEPS
@@ -2,10 +2,11 @@
   "+chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/PriceTrackingUtilities.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java",
+  "+chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/notifications/channels/ChromeChannelDefinitions.java",
   "+components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/ImageFetcher.java",
   "+components/image_fetcher/android/java/src/org/chromium/components/image_fetcher/ImageFetcherFactory.java",
-]
\ No newline at end of file
+]
diff --git a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotificationManager.java b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotificationManager.java
index 893cfa9a..9c96877e 100644
--- a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotificationManager.java
+++ b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceDropNotificationManager.java
@@ -26,12 +26,14 @@
 import org.chromium.base.IntentUtils;
 import org.chromium.base.Log;
 import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.chrome.browser.bookmarks.BookmarkBridge;
 import org.chromium.chrome.browser.browserservices.intents.WebappConstants;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.notifications.NotificationIntentInterceptor;
 import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
 import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.subscriptions.CommerceSubscription;
 import org.chromium.chrome.browser.subscriptions.CommerceSubscription.CommerceSubscriptionType;
 import org.chromium.chrome.browser.subscriptions.CommerceSubscription.SubscriptionManagementType;
@@ -231,19 +233,40 @@
                         String.format(
                                 Locale.US, "Failed to remove subscriptions. Status: %d", status));
             };
-            if (offerId != null) {
-                subscriptionsManager.unsubscribe(
-                        new CommerceSubscription(CommerceSubscriptionType.PRICE_TRACK, offerId,
-                                SubscriptionManagementType.CHROME_MANAGED, TrackingIdType.OFFER_ID),
-                        callback);
+            BookmarkBridge bookmarkBridge = new BookmarkBridge(Profile.getLastUsedRegularProfile());
+
+            Runnable unsubscribeRunnable = () -> {
+                if (offerId != null) {
+                    subscriptionsManager.unsubscribe(
+                            new CommerceSubscription(CommerceSubscriptionType.PRICE_TRACK, offerId,
+                                    SubscriptionManagementType.CHROME_MANAGED,
+                                    TrackingIdType.OFFER_ID),
+                            callback);
+                }
+                if (clusterId != null) {
+                    subscriptionsManager.unsubscribe(
+                            new CommerceSubscription(CommerceSubscriptionType.PRICE_TRACK,
+                                    clusterId, SubscriptionManagementType.USER_MANAGED,
+                                    TrackingIdType.PRODUCT_CLUSTER_ID),
+                            callback);
+                }
+            };
+
+            // Only attempt to unsubscribe once the corresponding bookmarks can also be updated.
+            if (bookmarkBridge.isBookmarkModelLoaded()) {
+                unsubscribeRunnable.run();
+            } else {
+                bookmarkBridge.addObserver(new BookmarkBridge.BookmarkModelObserver() {
+                    @Override
+                    public void bookmarkModelLoaded() {
+                        unsubscribeRunnable.run();
+                    }
+
+                    @Override
+                    public void bookmarkModelChanged() {}
+                });
             }
-            if (clusterId != null) {
-                subscriptionsManager.unsubscribe(
-                        new CommerceSubscription(CommerceSubscriptionType.PRICE_TRACK, clusterId,
-                                SubscriptionManagementType.USER_MANAGED,
-                                TrackingIdType.PRODUCT_CLUSTER_ID),
-                        callback);
-            }
+
             if (recordMetrics) {
                 NotificationUmaTracker.getInstance().onNotificationActionClick(
                         NotificationUmaTracker.ActionType.PRICE_DROP_TURN_OFF_ALERT,
diff --git a/chrome/browser/component_updater/first_party_sets_component_installer.cc b/chrome/browser/component_updater/first_party_sets_component_installer.cc
index 7451e1f..023a351 100644
--- a/chrome/browser/component_updater/first_party_sets_component_installer.cc
+++ b/chrome/browser/component_updater/first_party_sets_component_installer.cc
@@ -21,11 +21,12 @@
 #include "base/version.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/first_party_sets/first_party_sets_pref_names.h"
+#include "chrome/browser/first_party_sets/first_party_sets_util.h"
 #include "components/component_updater/component_installer.h"
 #include "components/component_updater/component_updater_paths.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/network_service_instance.h"
-#include "net/base/features.h"
+#include "content/public/common/content_features.h"
 #include "net/cookies/cookie_util.h"
 #include "services/network/public/mojom/network_service.mojom.h"
 
@@ -60,26 +61,6 @@
   return *instance;
 }
 
-// Determine if First-Party Sets is enabled by checking both the feature
-// and the enterprise policy.
-bool IsFirstPartySetsEnabled() {
-  // TODO(https://crbug.com/1269360): move this logic into
-  // FirstPartySetsUtil
-  if (!base::FeatureList::IsEnabled(net::features::kFirstPartySets)) {
-    return false;
-  }
-  // Some java tests start the browser in minimal mode, in which
-  // `g_browser_process` is unset.
-  if (!g_browser_process)
-    return false;
-  auto* local_state = g_browser_process->local_state();
-  if (!local_state ||
-      !local_state->HasPrefPath(first_party_sets::kFirstPartySetsEnabled)) {
-    return true;
-  }
-  return local_state->GetBoolean(first_party_sets::kFirstPartySetsEnabled);
-}
-
 // Invokes `on_sets_ready`, if:
 // * First-Party Sets is enabled; and
 // * `on_sets_ready` is not null.
@@ -87,7 +68,8 @@
 // If the component has been installed and can be read, we pass the component
 // file; otherwise, we pass an invalid file.
 void SetFirstPartySetsConfig(SetsReadyOnceCallback on_sets_ready) {
-  if (!IsFirstPartySetsEnabled() || on_sets_ready.is_null()) {
+  if (!FirstPartySetsUtil::GetInstance()->IsFirstPartySetsEnabled() ||
+      on_sets_ready.is_null()) {
     return;
   }
 
@@ -205,12 +187,12 @@
   return {
       {
           kDogfoodInstallerAttributeName,
-          BoolToString(net::features::kFirstPartySetsIsDogfooder.Get()),
+          BoolToString(features::kFirstPartySetsIsDogfooder.Get()),
       },
       {
           kV2FormatOptIn,
           BoolToString(base::FeatureList::IsEnabled(
-              net::features::kFirstPartySetsV2ComponentFormat)),
+              features::kFirstPartySetsV2ComponentFormat)),
       },
   };
 }
diff --git a/chrome/browser/component_updater/first_party_sets_component_installer.h b/chrome/browser/component_updater/first_party_sets_component_installer.h
index 6ccdfe5de..72f4807f 100644
--- a/chrome/browser/component_updater/first_party_sets_component_installer.h
+++ b/chrome/browser/component_updater/first_party_sets_component_installer.h
@@ -60,27 +60,27 @@
                                        base::StringPiece contents);
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerTest,
+  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
                            NonexistentFile_OnComponentReady);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerTest,
+  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
                            NonexistentFile_OnRegistrationComplete);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerTest,
+  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
                            LoadsSets_OnComponentReady);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerTest,
+  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
                            LoadsSets_OnNetworkRestart);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerTest,
+  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
                            IgnoreNewSets_NoInitialComponent);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerTest,
+  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
                            IgnoreNewSets_OnComponentReady);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerTest,
+  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureEnabledTest,
                            IgnoreNewSets_OnNetworkRestart);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerTest,
+  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerFeatureDisabledTest,
                            GetInstallerAttributes_Disabled);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerTest,
+  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerNonDogFooderTest,
                            GetInstallerAttributes_NonDogfooder);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerTest,
+  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerDogFooderTest,
                            GetInstallerAttributes_Dogfooder);
-  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerTest,
+  FRIEND_TEST_ALL_PREFIXES(FirstPartySetsComponentInstallerV2FormatTest,
                            GetInstallerAttributes_V2OptOut);
 
   // The following methods override ComponentInstallerPolicy.
diff --git a/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc b/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc
index df54ca7..65161249 100644
--- a/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc
+++ b/chrome/browser/component_updater/first_party_sets_component_installer_unittest.cc
@@ -16,10 +16,11 @@
 #include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/version.h"
+#include "chrome/browser/first_party_sets/first_party_sets_pref_names.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "components/component_updater/mock_component_updater_service.h"
-#include "net/base/features.h"
+#include "content/public/common/content_features.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -44,13 +45,15 @@
  public:
   FirstPartySetsComponentInstallerTest() {
     CHECK(component_install_dir_.CreateUniqueTempDir());
-    scoped_feature_list_.InitAndEnableFeature(net::features::kFirstPartySets);
   }
 
   void TearDown() override {
     FirstPartySetsComponentInstallerPolicy::ResetForTesting();
   }
 
+  // Subclasses are expected to call this in their constructors.
+  virtual void InitializeFeatureList() = 0;
+
  protected:
   base::test::TaskEnvironment env_;
 
@@ -58,9 +61,33 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-TEST_F(FirstPartySetsComponentInstallerTest, FeatureDisabled) {
+class FirstPartySetsComponentInstallerFeatureEnabledTest
+    : public FirstPartySetsComponentInstallerTest {
+ public:
+  FirstPartySetsComponentInstallerFeatureEnabledTest() {
+    InitializeFeatureList();
+  }
+
+  void InitializeFeatureList() override {
+    scoped_feature_list_.InitAndEnableFeature(features::kFirstPartySets);
+  }
+};
+
+class FirstPartySetsComponentInstallerFeatureDisabledTest
+    : public FirstPartySetsComponentInstallerTest {
+ public:
+  FirstPartySetsComponentInstallerFeatureDisabledTest() {
+    InitializeFeatureList();
+  }
+
+  void InitializeFeatureList() override {
+    scoped_feature_list_.InitAndDisableFeature(features::kFirstPartySets);
+  }
+};
+
+TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest, FeatureDisabled) {
   scoped_feature_list_.Reset();
-  scoped_feature_list_.InitAndDisableFeature(net::features::kFirstPartySets);
+  scoped_feature_list_.InitAndDisableFeature(features::kFirstPartySets);
   auto service =
       std::make_unique<component_updater::MockComponentUpdateService>();
 
@@ -73,7 +100,8 @@
   env_.RunUntilIdle();
 }
 
-TEST_F(FirstPartySetsComponentInstallerTest, NonexistentFile_OnComponentReady) {
+TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest,
+       NonexistentFile_OnComponentReady) {
   SEQUENCE_CHECKER(sequence_checker);
 
   ASSERT_TRUE(
@@ -88,7 +116,7 @@
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(FirstPartySetsComponentInstallerTest,
+TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest,
        NonexistentFile_OnRegistrationComplete) {
   ASSERT_TRUE(
       base::DeleteFile(FirstPartySetsComponentInstallerPolicy::GetInstalledPath(
@@ -105,7 +133,8 @@
   run_loop.Run();
 }
 
-TEST_F(FirstPartySetsComponentInstallerTest, LoadsSets_OnComponentReady) {
+TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest,
+       LoadsSets_OnComponentReady) {
   SEQUENCE_CHECKER(sequence_checker);
   const std::string expectation = "some first party sets";
   base::RunLoop run_loop;
@@ -131,7 +160,8 @@
 // Test that when the first version of the component is installed,
 // ComponentReady is a no-op, because OnRegistrationComplete already executed
 // the OnceCallback.
-TEST_F(FirstPartySetsComponentInstallerTest, IgnoreNewSets_NoInitialComponent) {
+TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest,
+       IgnoreNewSets_NoInitialComponent) {
   SEQUENCE_CHECKER(sequence_checker);
 
   int callback_calls = 0;
@@ -165,7 +195,8 @@
 
 // Test if a component has been installed, ComponentReady will be no-op when
 // newer versions are installed.
-TEST_F(FirstPartySetsComponentInstallerTest, IgnoreNewSets_OnComponentReady) {
+TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest,
+       IgnoreNewSets_OnComponentReady) {
   SEQUENCE_CHECKER(sequence_checker);
   const std::string sets_v1 = "first party sets v1";
   const std::string sets_v2 = "first party sets v2";
@@ -206,7 +237,8 @@
   EXPECT_EQ(callback_calls, 1);
 }
 
-TEST_F(FirstPartySetsComponentInstallerTest, LoadsSets_OnNetworkRestart) {
+TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest,
+       LoadsSets_OnNetworkRestart) {
   SEQUENCE_CHECKER(sequence_checker);
   const std::string expectation = "some first party sets";
 
@@ -250,7 +282,8 @@
 // Test ReconfigureAfterNetworkRestart calls the callback with the correct
 // version, i.e. the first installed component, even if there are newer versions
 // installed after browser startup.
-TEST_F(FirstPartySetsComponentInstallerTest, IgnoreNewSets_OnNetworkRestart) {
+TEST_F(FirstPartySetsComponentInstallerFeatureEnabledTest,
+       IgnoreNewSets_OnNetworkRestart) {
   SEQUENCE_CHECKER(sequence_checker);
   const std::string sets_v1 = "first party sets v1";
   const std::string sets_v2 = "first party sets v2";
@@ -299,10 +332,8 @@
   EXPECT_EQ(callback_calls, 1);
 }
 
-TEST_F(FirstPartySetsComponentInstallerTest, GetInstallerAttributes_Disabled) {
-  scoped_feature_list_.Reset();
-  scoped_feature_list_.InitAndDisableFeature(net::features::kFirstPartySets);
-
+TEST_F(FirstPartySetsComponentInstallerFeatureDisabledTest,
+       GetInstallerAttributes_Disabled) {
   FirstPartySetsComponentInstallerPolicy policy(base::DoNothing());
 
   EXPECT_THAT(policy.GetInstallerAttributes(),
@@ -314,13 +345,22 @@
                        "true")));
 }
 
-TEST_F(FirstPartySetsComponentInstallerTest,
+class FirstPartySetsComponentInstallerNonDogFooderTest
+    : public FirstPartySetsComponentInstallerTest {
+ public:
+  FirstPartySetsComponentInstallerNonDogFooderTest() {
+    InitializeFeatureList();
+  }
+
+  void InitializeFeatureList() override {
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(
+        features::kFirstPartySets,
+        {{features::kFirstPartySetsIsDogfooder.name, "false"}});
+  }
+};
+
+TEST_F(FirstPartySetsComponentInstallerNonDogFooderTest,
        GetInstallerAttributes_NonDogfooder) {
-  scoped_feature_list_.Reset();
-  scoped_feature_list_.InitAndEnableFeatureWithParameters(
-      net::features::kFirstPartySets,
-      {{net::features::kFirstPartySetsIsDogfooder.name, "false"}});
-
   FirstPartySetsComponentInstallerPolicy policy(base::DoNothing());
 
   EXPECT_THAT(policy.GetInstallerAttributes(),
@@ -332,12 +372,20 @@
                        "true")));
 }
 
-TEST_F(FirstPartySetsComponentInstallerTest, GetInstallerAttributes_Dogfooder) {
-  scoped_feature_list_.Reset();
-  scoped_feature_list_.InitAndEnableFeatureWithParameters(
-      net::features::kFirstPartySets,
-      {{net::features::kFirstPartySetsIsDogfooder.name, "true"}});
+class FirstPartySetsComponentInstallerDogFooderTest
+    : public FirstPartySetsComponentInstallerTest {
+ public:
+  FirstPartySetsComponentInstallerDogFooderTest() { InitializeFeatureList(); }
 
+  void InitializeFeatureList() override {
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(
+        features::kFirstPartySets,
+        {{features::kFirstPartySetsIsDogfooder.name, "true"}});
+  }
+};
+
+TEST_F(FirstPartySetsComponentInstallerDogFooderTest,
+       GetInstallerAttributes_Dogfooder) {
   FirstPartySetsComponentInstallerPolicy policy(base::DoNothing());
 
   EXPECT_THAT(policy.GetInstallerAttributes(),
@@ -349,12 +397,20 @@
                        "true")));
 }
 
-TEST_F(FirstPartySetsComponentInstallerTest, GetInstallerAttributes_V2OptOut) {
-  scoped_feature_list_.Reset();
-  scoped_feature_list_.InitWithFeatures(
-      {}, {net::features::kFirstPartySets,
-           net::features::kFirstPartySetsV2ComponentFormat});
+class FirstPartySetsComponentInstallerV2FormatTest
+    : public FirstPartySetsComponentInstallerTest {
+ public:
+  FirstPartySetsComponentInstallerV2FormatTest() { InitializeFeatureList(); }
 
+  void InitializeFeatureList() override {
+    scoped_feature_list_.InitWithFeatures(
+        {}, {features::kFirstPartySets,
+             features::kFirstPartySetsV2ComponentFormat});
+  }
+};
+
+TEST_F(FirstPartySetsComponentInstallerV2FormatTest,
+       GetInstallerAttributes_V2OptOut) {
   FirstPartySetsComponentInstallerPolicy policy(base::DoNothing());
 
   EXPECT_THAT(policy.GetInstallerAttributes(),
diff --git a/chrome/browser/component_updater/metadata_table_chromeos.cc b/chrome/browser/component_updater/metadata_table_chromeos.cc
index 46cbb9d..25855ba 100644
--- a/chrome/browser/component_updater/metadata_table_chromeos.cc
+++ b/chrome/browser/component_updater/metadata_table_chromeos.cc
@@ -171,7 +171,7 @@
   DCHECK(pref_service_);
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  DictionaryPrefUpdate update(pref_service_, kMetadataPrefPath);
+  DictionaryPrefUpdateDeprecated update(pref_service_, kMetadataPrefPath);
   update->SetKey(kMetadataContentKey, installed_items_.Clone());
 }
 
diff --git a/chrome/browser/content_settings/content_settings_policy_provider_unittest.cc b/chrome/browser/content_settings/content_settings_policy_provider_unittest.cc
index a9f6b2cf..12456c0c 100644
--- a/chrome/browser/content_settings/content_settings_policy_provider_unittest.cc
+++ b/chrome/browser/content_settings/content_settings_policy_provider_unittest.cc
@@ -175,10 +175,10 @@
   // certificates.
   std::string pattern_str("\"pattern\":\"[*.]google.com\"");
   std::string filter_str("\"filter\":{\"ISSUER\":{\"CN\":\"issuer name\"}}");
-  auto value = std::make_unique<base::ListValue>();
-  value->Append("{" + pattern_str + "," + filter_str + "}");
+  base::Value value(base::Value::Type::LIST);
+  value.Append("{" + pattern_str + "," + filter_str + "}");
   prefs->SetManagedPref(prefs::kManagedAutoSelectCertificateForUrls,
-                        std::move(value));
+                        base::Value::ToUniquePtrValue(std::move(value)));
   GURL youtube_url("https://www.youtube.com");
   EXPECT_EQ(base::Value(),
             TestUtils::GetContentSettingValue(
diff --git a/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc b/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc
index 09b8d09b..b4961a2 100644
--- a/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc
+++ b/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc
@@ -382,7 +382,7 @@
 
   DeadlockCheckerObserver observer(&prefs, &provider);
   {
-    DictionaryPrefUpdate update(&prefs, info->pref_name());
+    DictionaryPrefUpdateDeprecated update(&prefs, info->pref_name());
     base::Value* mutable_settings = update.Get();
     mutable_settings->SetKey("www.example.com,*",
                              base::Value(base::Value::Type::DICTIONARY));
@@ -529,7 +529,7 @@
 
   // Expect the prefs are not empty before we trigger clearing them.
   for (const char* pref : cleared_prefs) {
-    DictionaryPrefUpdate update(&prefs, pref);
+    DictionaryPrefUpdateDeprecated update(&prefs, pref);
     const base::Value* dictionary = update.Get();
     ASSERT_FALSE(dictionary->DictEmpty());
   }
@@ -539,7 +539,7 @@
 
   // Ensure they become empty afterwards.
   for (const char* pref : cleared_prefs) {
-    DictionaryPrefUpdate update(&prefs, pref);
+    DictionaryPrefUpdateDeprecated update(&prefs, pref);
     const base::Value* dictionary = update.Get();
     EXPECT_TRUE(dictionary->DictEmpty());
   }
@@ -551,7 +551,7 @@
   };
 
   for (const char* pref : nonempty_prefs) {
-    DictionaryPrefUpdate update(&prefs, pref);
+    DictionaryPrefUpdateDeprecated update(&prefs, pref);
     const base::Value* dictionary = update.Get();
     EXPECT_EQ(1u, dictionary->DictSize());
   }
diff --git a/chrome/browser/content_settings/host_content_settings_map_unittest.cc b/chrome/browser/content_settings/host_content_settings_map_unittest.cc
index 877cc104..cfc9ec2 100644
--- a/chrome/browser/content_settings/host_content_settings_map_unittest.cc
+++ b/chrome/browser/content_settings/host_content_settings_map_unittest.cc
@@ -1089,8 +1089,8 @@
 
   // Set utf-8 data.
   {
-    DictionaryPrefUpdate update(prefs,
-                                GetPrefName(ContentSettingsType::COOKIES));
+    DictionaryPrefUpdateDeprecated update(
+        prefs, GetPrefName(ContentSettingsType::COOKIES));
     base::Value* all_settings_dictionary = update.Get();
     ASSERT_TRUE(all_settings_dictionary);
 
diff --git a/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.cc b/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.cc
index 753b3fc..642e0d4 100644
--- a/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.cc
+++ b/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.cc
@@ -24,9 +24,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/renderer_host/chrome_navigation_ui_data.h"
-#include "chrome/browser/subresource_redirect/https_image_compression_infobar_decider.h"
-#include "chrome/browser/subresource_redirect/litepages_service_bypass_decider.h"
-#include "chrome/browser/subresource_redirect/origin_robots_rules_cache.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/pref_names.h"
@@ -42,7 +39,6 @@
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
 #include "components/proxy_config/proxy_prefs.h"
-#include "components/subresource_redirect/common/subresource_redirect_features.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
@@ -275,21 +271,4 @@
   // unable to browse non-SSL sites for the most part (see
   // http://crbug.com/476610).
   MigrateDataReductionProxyOffProxyPrefs(profile_prefs);
-  if (subresource_redirect::ShouldEnablePublicImageHintsBasedCompression() ||
-      subresource_redirect::ShouldEnableLoginRobotsCheckedImageCompression()) {
-    https_image_compression_infobar_decider_ =
-        std::make_unique<HttpsImageCompressionInfoBarDecider>(profile_prefs,
-                                                              this);
-  }
-  if (subresource_redirect::ShouldEnablePublicImageHintsBasedCompression() ||
-      subresource_redirect::ShouldEnableLoginRobotsCheckedImageCompression() ||
-      subresource_redirect::ShouldEnableRobotsRulesFetching()) {
-    litepages_service_bypass_decider_ =
-        std::make_unique<LitePagesServiceBypassDecider>();
-  }
-  if (subresource_redirect::ShouldEnableRobotsRulesFetching()) {
-    origin_robots_rules_cache_ =
-        std::make_unique<subresource_redirect::OriginRobotsRulesCache>(
-            url_loader_factory, litepages_service_bypass_decider_->AsWeakPtr());
-  }
 }
diff --git a/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h b/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h
index 43da6de..4b7b906 100644
--- a/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h
+++ b/chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h
@@ -22,12 +22,6 @@
 class DataStore;
 }  // namespace data_reduction_proxy
 
-namespace subresource_redirect {
-class OriginRobotsRulesCache;
-}
-
-class HttpsImageCompressionInfoBarDecider;
-class LitePagesServiceBypassDecider;
 class PrefService;
 
 // Data reduction proxy settings class suitable for use with a Chrome browser.
@@ -75,20 +69,6 @@
   // Public for testing.
   void MigrateDataReductionProxyOffProxyPrefs(PrefService* prefs);
 
-  HttpsImageCompressionInfoBarDecider*
-  https_image_compression_infobar_decider() {
-    return https_image_compression_infobar_decider_.get();
-  }
-
-  LitePagesServiceBypassDecider* litepages_service_bypass_decider() const {
-    return litepages_service_bypass_decider_.get();
-  }
-
-  subresource_redirect::OriginRobotsRulesCache* origin_robots_rules_cache()
-      const {
-    return origin_robots_rules_cache_.get();
-  }
-
  private:
   // Helper method for migrating the Data Reduction Proxy away from using the
   // proxy pref. Returns the ProxyPrefMigrationResult value indicating the
@@ -96,20 +76,6 @@
   ProxyPrefMigrationResult MigrateDataReductionProxyOffProxyPrefsHelper(
       PrefService* prefs);
 
-  // Maintains the decider for this profile that decides whether to show infobar
-  // before triggering https image compression.
-  std::unique_ptr<HttpsImageCompressionInfoBarDecider>
-      https_image_compression_infobar_decider_;
-
-  // Maintains the decider for this profile to contain logic for LitePages
-  // service bypass.
-  std::unique_ptr<LitePagesServiceBypassDecider>
-      litepages_service_bypass_decider_;
-
-  // Maintains the cache of robots rules.
-  std::unique_ptr<subresource_redirect::OriginRobotsRulesCache>
-      origin_robots_rules_cache_;
-
   // Null before InitDataReductionProxySettings is called.
   raw_ptr<Profile> profile_;
 };
diff --git a/chrome/browser/data_saver/login_robots_src_video_browsertest.cc b/chrome/browser/data_saver/login_robots_src_video_browsertest.cc
deleted file mode 100644
index 99dd66e4..0000000
--- a/chrome/browser/data_saver/login_robots_src_video_browsertest.cc
+++ /dev/null
@@ -1,377 +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 <set>
-#include <string>
-
-#include "base/containers/contains.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
-#include "chrome/browser/login_detection/login_detection_type.h"
-#include "chrome/browser/login_detection/login_detection_util.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/subresource_redirect/subresource_redirect_observer.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/subresource_redirect/common/subresource_redirect_result.h"
-#include "components/subresource_redirect/subresource_redirect_browser_test_util.h"
-#include "components/subresource_redirect/subresource_redirect_test_util.h"
-#include "components/ukm/test_ukm_recorder.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "net/http/http_status_code.h"
-#include "net/test/embedded_test_server/http_request.h"
-#include "net/test/embedded_test_server/request_handler_util.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/features.h"
-#include "url/gurl.h"
-
-namespace subresource_redirect {
-
-class LoginRobotsSrcVideoBrowserTest : public InProcessBrowserTest {
- public:
-  explicit LoginRobotsSrcVideoBrowserTest(
-      bool enable_lite_mode = true,
-      bool enable_src_video_compression_metrics_feature = true)
-      : enable_lite_mode_(enable_lite_mode),
-        enable_src_video_compression_metrics_feature_(
-            enable_src_video_compression_metrics_feature),
-        https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
-
-  ~LoginRobotsSrcVideoBrowserTest() override = default;
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitchASCII("host-rules", "MAP * 127.0.0.1");
-    if (enable_lite_mode_)
-      command_line->AppendSwitch("enable-spdy-proxy-auth");
-
-    // Disable infobar shown check to actually compress the pages.
-    command_line->AppendSwitch("override-https-image-compression-infobar");
-  }
-
-  void SetUp() override {
-    ASSERT_TRUE(robots_rules_server_.Start());
-    https_test_server_.ServeFilesFromSourceDirectory("chrome/test/data");
-    https_test_server_.RegisterRequestMonitor(base::BindRepeating(
-        &LoginRobotsSrcVideoBrowserTest::OnHttpsTestServerRequestMonitor,
-        base::Unretained(this)));
-    ASSERT_TRUE(https_test_server_.Start());
-
-    std::vector<base::test::ScopedFeatureList::FeatureAndParams>
-        enabled_features;
-    if (enable_src_video_compression_metrics_feature_) {
-      base::FieldTrialParams params, login_detection_params;
-      params["lite_page_robots_origin"] = robots_rules_server_.GetURL();
-      enabled_features.emplace_back(
-          blink::features::kSubresourceRedirectSrcVideo, params);
-      login_detection_params["logged_in_sites"] = "https://loggedin.com";
-      enabled_features.emplace_back(login_detection::kLoginDetection,
-                                    login_detection_params);
-    }
-    scoped_feature_list_.InitWithFeaturesAndParameters(enabled_features, {});
-    InProcessBrowserTest::SetUp();
-  }
-
-  void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-    ResetMetricsRecorders();
-  }
-
-  GURL GetHttpsTestURL(const std::string& path) const {
-    return https_test_server_.GetURL("test_https_server.com", path);
-  }
-
-  // Navigatest to the page with <video> element, starts the play and waits for
-  // it to complete.
-  void NavigateAndWaitForLoad(Browser* browser, const GURL& url) {
-    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser, url));
-    EXPECT_TRUE(ExecJs(browser->tab_strip_model()->GetActiveWebContents(),
-                       "playVideo()"));
-    RetryForHistogramUntilCountReached(
-        histogram_tester_.get(), "Media.WatchTime.AudioVideo.Discarded.SRC", 1);
-    FetchHistogramsFromChildProcesses();
-  }
-
-  bool RunScriptExtractBool(const std::string& script,
-                            content::WebContents* web_contents = nullptr) {
-    if (!web_contents)
-      web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-    return EvalJs(web_contents, script).ExtractBool();
-  }
-
-  void ResetMetricsRecorders() {
-    histogram_tester_ = std::make_unique<base::HistogramTester>();
-    ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
-  }
-
-  std::map<uint64_t, size_t> GetSrcVideoCompressionUkmMetrics() {
-    base::RunLoop().RunUntilIdle();
-    using SrcVideoUkm =
-        ukm::builders::SubresourceRedirect_PublicSrcVideoCompression;
-    std::map<uint64_t, size_t> merged_metrics;
-    // Flatten the metrics from multiple ukm sources.
-    for (const auto* metrics :
-         ukm_recorder_->GetEntriesByName(SrcVideoUkm::kEntryName)) {
-      for (const auto& metric : metrics->metrics) {
-        if (merged_metrics.find(metric.first) == merged_metrics.end())
-          merged_metrics[metric.first] = metric.second;
-      }
-    }
-    return merged_metrics;
-  }
-
-  // Verifies image compression page info is not shown when src video coverage
-  // metrics recording is enabled.
-  void VerifyImageCompressionPageInfoNotShown(
-      content::WebContents* web_contents = nullptr) {
-    if (!web_contents)
-      web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-    EXPECT_FALSE(subresource_redirect::SubresourceRedirectObserver::
-                     IsHttpsImageCompressionApplied(web_contents));
-  }
-
- protected:
-  // Called for the main origin server requests, to track requests that are
-  // being made directly instead of redirecting to LitePages.
-  void OnHttpsTestServerRequestMonitor(
-      const net::test_server::HttpRequest& request) {
-    received_https_test_server_requests_.insert(request.GetURL());
-  }
-
-  bool enable_lite_mode_;
-  bool enable_src_video_compression_metrics_feature_;
-
-  base::test::ScopedFeatureList scoped_feature_list_;
-
-  // Simulates the LitePages servers that return the robots rules and compress
-  // images.
-  RobotsRulesTestServer robots_rules_server_;
-  net::EmbeddedTestServer https_test_server_;
-
-  // All the origins the robots rules are requested for.
-  std::set<GURL> received_https_test_server_requests_;
-
-  std::unique_ptr<base::HistogramTester> histogram_tester_;
-  std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_;
-};
-
-// Enable tests for linux since LiteMode is enabled only for Android.
-#if defined(OS_WIN) || defined(OS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
-#define DISABLE_ON_WIN_MAC_CHROMEOS(x) DISABLED_##x
-#else
-#define DISABLE_ON_WIN_MAC_CHROMEOS(x) x
-#endif
-
-IN_PROC_BROWSER_TEST_F(
-    LoginRobotsSrcVideoBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestSrcVideoAllowedByRobots)) {
-  robots_rules_server_.AddRobotsRules(
-      GetHttpsTestURL("/"), {{kRuleTypeAllow, ""}, {kRuleTypeDisallow, ""}});
-  NavigateAndWaitForLoad(browser(),
-                         GetHttpsTestURL("/load_image/src_video.html"));
-
-  // The first video load should fetch the robots rules, but the robots rules
-  // check for the src video may timeout. So, do not check for coverage metrics.
-  histogram_tester_->ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRulesFetcher.ResponseCode", net::HTTP_OK, 1);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.SrcVideo.SubresourceRedirectResult", 1);
-  histogram_tester_->ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit", false, 1);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  // Verify the coverage metrics for the subsequent video load.
-  ResetMetricsRecorders();
-
-  EXPECT_TRUE(ExecJs(browser()->tab_strip_model()->GetActiveWebContents(),
-                     "playVideo('/android/media/test.webm?second')"));
-  RetryForHistogramUntilCountReached(
-      histogram_tester_.get(), "Media.WatchTime.AudioVideo.Discarded.SRC", 1);
-  FetchHistogramsFromChildProcesses();
-
-  histogram_tester_->ExpectUniqueSample(
-      "SubresourceRedirect.SrcVideo.SubresourceRedirectResult",
-      SubresourceRedirectResult::kRedirectable, 1);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.SrcVideo.CompressibleFullContentBytes", 1);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  auto full_content_length = histogram_tester_->GetAllSamples(
-      "SubresourceRedirect.SrcVideo.CompressibleFullContentBytes");
-  EXPECT_EQ(1U, full_content_length.size());
-  EXPECT_EQ(1, full_content_length[0].count);
-  EXPECT_LT(50000, full_content_length[0].min);
-
-  using SrcVideoUkm =
-      ukm::builders::SubresourceRedirect_PublicSrcVideoCompression;
-  auto ukm_metrics = GetSrcVideoCompressionUkmMetrics();
-  EXPECT_EQ(SubresourceRedirectResult::kRedirectable,
-            static_cast<SubresourceRedirectResult>(
-                ukm_metrics[SrcVideoUkm::kSubresourceRedirectResultNameHash]));
-  EXPECT_LT(50000U, ukm_metrics[SrcVideoUkm::kFullContentLengthNameHash]);
-
-  robots_rules_server_.VerifyRequestedOrigins({GetHttpsTestURL("/").spec()});
-
-  // Verify the video requests were fetched from the origin, and no compression
-  // redirect happened.
-  EXPECT_TRUE(
-      base::Contains(received_https_test_server_requests_,
-                     https_test_server_.GetURL("/android/media/test.webm")));
-  EXPECT_TRUE(base::Contains(
-      received_https_test_server_requests_,
-      https_test_server_.GetURL("/android/media/test.webm?second")));
-
-  VerifyImageCompressionPageInfoNotShown();
-}
-
-IN_PROC_BROWSER_TEST_F(LoginRobotsSrcVideoBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(NoTriggerWhenDataSaverOff)) {
-  data_reduction_proxy::DataReductionProxySettings::
-      SetDataSaverEnabledForTesting(browser()->profile()->GetPrefs(), false);
-  base::RunLoop().RunUntilIdle();
-
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeAllow, ""}});
-  NavigateAndWaitForLoad(browser(),
-                         GetHttpsTestURL("/load_image/src_video.html"));
-
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.SrcVideo.SubresourceRedirectResult", 0);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.SrcVideo.CompressibleFullContentBytes", 0);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.RobotsRulesFetcher.ResponseCode", 0);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit", 0);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  EXPECT_TRUE(GetSrcVideoCompressionUkmMetrics().empty());
-  robots_rules_server_.VerifyRequestedOrigins({});
-
-  VerifyImageCompressionPageInfoNotShown();
-}
-
-IN_PROC_BROWSER_TEST_F(LoginRobotsSrcVideoBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(NoTriggerInIncognito)) {
-  auto* incognito_browser = CreateIncognitoBrowser();
-
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeAllow, ""}});
-  NavigateAndWaitForLoad(incognito_browser,
-                         GetHttpsTestURL("/load_image/src_video.html"));
-
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.SrcVideo.SubresourceRedirectResult", 0);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.SrcVideo.CompressibleFullContentBytes", 0);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.RobotsRulesFetcher.ResponseCode", 0);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit", 0);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  EXPECT_TRUE(GetSrcVideoCompressionUkmMetrics().empty());
-  robots_rules_server_.VerifyRequestedOrigins({});
-
-  VerifyImageCompressionPageInfoNotShown(
-      incognito_browser->tab_strip_model()->GetActiveWebContents());
-}
-
-IN_PROC_BROWSER_TEST_F(
-    LoginRobotsSrcVideoBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestSrcVideoDisallowedByRobots)) {
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeDisallow, ""}});
-  NavigateAndWaitForLoad(browser(),
-                         GetHttpsTestURL("/load_image/src_video.html"));
-
-  // The first video load should fetch the robots rules, but the robots rules
-  // check for the src video may timeout. So, do not check for coverage metrics.
-  histogram_tester_->ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRulesFetcher.ResponseCode", net::HTTP_OK, 1);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.SrcVideo.SubresourceRedirectResult", 1);
-  histogram_tester_->ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit", false, 1);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  // Verify the for the subsequent video load is ineligible for compression.
-  ResetMetricsRecorders();
-
-  EXPECT_TRUE(ExecJs(browser()->tab_strip_model()->GetActiveWebContents(),
-                     "playVideo('/android/media/test.webm?second')"));
-  RetryForHistogramUntilCountReached(
-      histogram_tester_.get(), "Media.WatchTime.AudioVideo.Discarded.SRC", 1);
-  FetchHistogramsFromChildProcesses();
-
-  histogram_tester_->ExpectUniqueSample(
-      "SubresourceRedirect.SrcVideo.SubresourceRedirectResult",
-      SubresourceRedirectResult::kIneligibleRobotsDisallowed, 1);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.SrcVideo.CompressibleFullContentBytes", 0);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.RobotsRulesFetcher.ResponseCode", 0);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit", 0);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  using SrcVideoUkm =
-      ukm::builders::SubresourceRedirect_PublicSrcVideoCompression;
-  auto ukm_metrics = GetSrcVideoCompressionUkmMetrics();
-  EXPECT_EQ(SubresourceRedirectResult::kIneligibleRobotsDisallowed,
-            static_cast<SubresourceRedirectResult>(
-                ukm_metrics[SrcVideoUkm::kSubresourceRedirectResultNameHash]));
-  EXPECT_LT(50000U, ukm_metrics[SrcVideoUkm::kFullContentLengthNameHash]);
-
-  robots_rules_server_.VerifyRequestedOrigins({GetHttpsTestURL("/").spec()});
-  VerifyImageCompressionPageInfoNotShown();
-}
-
-IN_PROC_BROWSER_TEST_F(
-    LoginRobotsSrcVideoBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(NoCompressionOnLoggedInPage)) {
-  robots_rules_server_.AddRobotsRules(
-      https_test_server_.GetURL("loggedin.com", "/"), {{kRuleTypeAllow, ""}});
-  NavigateAndWaitForLoad(
-      browser(),
-      https_test_server_.GetURL("loggedin.com", "/load_image/src_video.html"));
-
-  histogram_tester_->ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleLoginDetected, 1);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.SrcVideo.CompressibleFullContentBytes", 0);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.RobotsRulesFetcher.ResponseCode", 0);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit", 0);
-  histogram_tester_->ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  using SrcVideoUkm =
-      ukm::builders::SubresourceRedirect_PublicSrcVideoCompression;
-  auto ukm_metrics = GetSrcVideoCompressionUkmMetrics();
-  EXPECT_EQ(SubresourceRedirectResult::kIneligibleLoginDetected,
-            static_cast<SubresourceRedirectResult>(
-                ukm_metrics[SrcVideoUkm::kSubresourceRedirectResultNameHash]));
-  EXPECT_LT(50000U, ukm_metrics[SrcVideoUkm::kFullContentLengthNameHash]);
-
-  robots_rules_server_.VerifyRequestedOrigins({});
-  VerifyImageCompressionPageInfoNotShown();
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/browser/data_saver/subresource_redirect_browsertest.cc b/chrome/browser/data_saver/subresource_redirect_browsertest.cc
deleted file mode 100644
index 7fdf0d2..0000000
--- a/chrome/browser/data_saver/subresource_redirect_browsertest.cc
+++ /dev/null
@@ -1,1685 +0,0 @@
-// 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.
-
-#include "base/files/file_util.h"
-#include "base/path_service.h"
-#include "base/task/thread_pool/thread_pool_instance.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
-#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
-#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/subresource_redirect/https_image_compression_infobar_decider.h"
-#include "chrome/browser/subresource_redirect/litepages_service_bypass_decider.h"
-#include "chrome/browser/subresource_redirect/subresource_redirect_observer.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/base32/base32.h"
-#include "components/metrics/content/subprocess_metrics_provider.h"
-#include "components/optimization_guide/content/browser/optimization_guide_decider.h"
-#include "components/optimization_guide/core/optimization_guide_constants.h"
-#include "components/optimization_guide/core/optimization_guide_features.h"
-#include "components/optimization_guide/core/optimization_guide_switches.h"
-#include "components/optimization_guide/core/optimization_metadata.h"
-#include "components/optimization_guide/proto/hints.pb.h"
-#include "components/optimization_guide/proto/public_image_metadata.pb.h"
-#include "components/ukm/test_ukm_recorder.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/network_connection_change_simulator.h"
-#include "crypto/sha2.h"
-#include "net/base/escape.h"
-#include "net/test/embedded_test_server/http_request.h"
-#include "net/test/embedded_test_server/http_response.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
-#include "services/network/public/cpp/network_quality_tracker.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/features.h"
-#include "url/gurl.h"
-
-namespace {
-
-enum CompressionServerFailureMode {
-  NONE = 0,
-  EMPTY_RESPONSE,
-  LOADSHED_503_RETRY_AFTER_RESPONSE,
-  TIMEOUT,
-};
-
-// Retries fetching |histogram_name| until it contains at least |count| samples.
-// TODO(rajendrant): Convert the tests to wait for image load to complete or the
-// page load complete, instead of waiting on the histograms.
-void RetryForHistogramUntilCountReached(base::HistogramTester* histogram_tester,
-                                        const std::string& histogram_name,
-                                        size_t count) {
-  while (true) {
-    base::ThreadPoolInstance::Get()->FlushForTesting();
-    base::RunLoop().RunUntilIdle();
-
-    content::FetchHistogramsFromChildProcesses();
-    metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-    const std::vector<base::Bucket> buckets =
-        histogram_tester->GetAllSamples(histogram_name);
-    size_t total_count = 0;
-    for (const auto& bucket : buckets) {
-      total_count += bucket.count;
-    }
-    if (total_count >= count) {
-      break;
-    }
-  }
-}
-
-class SubresourceRedirectBrowserTest : public InProcessBrowserTest {
- public:
-  explicit SubresourceRedirectBrowserTest(
-      bool enable_subresource_server_redirect = true,
-      bool override_https_image_compression_infobar = true)
-      : enable_subresource_server_redirect_(enable_subresource_server_redirect),
-        override_https_image_compression_infobar_(
-            override_https_image_compression_infobar),
-        https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
-        compression_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
-
-  SubresourceRedirectBrowserTest(const SubresourceRedirectBrowserTest&) =
-      delete;
-  SubresourceRedirectBrowserTest& operator=(
-      const SubresourceRedirectBrowserTest&) = delete;
-
-  void SetUp() override {
-    // |http_server| setup.
-    http_server_.ServeFilesFromSourceDirectory("chrome/test/data");
-    ASSERT_TRUE(http_server_.Start());
-    http_url_ = http_server_.GetURL("insecure.com", "/");
-    ASSERT_TRUE(http_url_.SchemeIs(url::kHttpScheme));
-
-    // |https_server| setup.
-    https_server_.ServeFilesFromSourceDirectory("chrome/test/data");
-    https_server_.RegisterRequestHandler(base::BindRepeating(
-        &SubresourceRedirectBrowserTest::HandleHTTPSServerRequest,
-        base::Unretained(this)));
-    ASSERT_TRUE(https_server_.Start());
-    https_url_ = https_server_.GetURL("secure.com", "/");
-    ASSERT_TRUE(https_url_.SchemeIs(url::kHttpsScheme));
-
-    // |compression_server| setup.
-    compression_server_.ServeFilesFromSourceDirectory("chrome/test/data");
-    compression_server_.RegisterRequestHandler(base::BindRepeating(
-        &SubresourceRedirectBrowserTest::HandleCompressionServerRequest,
-        base::Unretained(this)));
-    ASSERT_TRUE(compression_server_.Start());
-    compression_url_ = compression_server_.GetURL("compression.com", "/");
-    ASSERT_TRUE(compression_url_.SchemeIs(url::kHttpsScheme));
-
-    scoped_feature_list_.InitWithFeaturesAndParameters(
-        {{blink::features::kSubresourceRedirect,
-          {{"enable_subresource_server_redirect",
-            enable_subresource_server_redirect_ ? "true" : "false"},
-           {"lite_page_subresource_origin", compression_url_.spec()}}},
-         {optimization_guide::features::kOptimizationHints, {}},
-         {optimization_guide::features::kRemoteOptimizationGuideFetching, {}}},
-        {});
-
-    InProcessBrowserTest::SetUp();
-  }
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    // Need to resolve all 3 of the above servers to 127.0.0.1:port, and
-    // the servers themselves can't serve using 127.0.0.1:port as the
-    // compressed resource URLs rely on subdomains, and subdomains
-    // do not function properly when using 127.0.0.1:port
-    command_line->AppendSwitchASCII("host-rules", "MAP * 127.0.0.1");
-    command_line->AppendSwitch("enable-spdy-proxy-auth");
-    command_line->AppendSwitch("optimization-guide-disable-installer");
-    command_line->AppendSwitch("purge_hint_cache_store");
-    if (override_https_image_compression_infobar_) {
-      // Disable infobar shown check to actually compress the pages.
-      command_line->AppendSwitch("override-https-image-compression-infobar");
-    }
-  }
-
-  void EnableDataSaver(bool enabled) {
-    data_reduction_proxy::DataReductionProxySettings::
-        SetDataSaverEnabledForTesting(browser()->profile()->GetPrefs(),
-                                      enabled);
-    base::RunLoop().RunUntilIdle();
-  }
-
-  bool RunScriptExtractBool(const std::string& script,
-                            content::WebContents* web_contents = nullptr) {
-    if (!web_contents)
-      web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-    return EvalJs(web_contents, script).ExtractBool();
-  }
-
-  std::string RunScriptExtractString(
-      const std::string& script,
-      content::WebContents* web_contents = nullptr) {
-    if (!web_contents)
-      web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-    return content::EvalJs(web_contents, script,
-                           content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
-        .ExtractString();
-  }
-
-  // Sets up public image URL hint data.
-  void SetUpPublicImageURLPaths(
-      const GURL& url,
-      const std::vector<std::string>& public_image_paths) {
-    auto* optimization_guide_decider =
-        OptimizationGuideKeyedServiceFactory::GetForProfile(
-            browser()->profile());
-    optimization_guide::proto::PublicImageMetadata public_image_metadata;
-    for (const auto& image_path : public_image_paths) {
-      public_image_metadata.add_url(
-          https_server_.GetURL("secure.com", image_path).spec());
-    }
-
-    optimization_guide::OptimizationMetadata optimization_metadata;
-    optimization_metadata.set_public_image_metadata(public_image_metadata);
-    optimization_guide_decider->AddHintForTesting(
-        url, optimization_guide::proto::COMPRESS_PUBLIC_IMAGES,
-        optimization_metadata);
-  }
-
-  void CreateUkmRecorder() {
-    ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
-  }
-
-  std::map<uint64_t, size_t> GetImageCompressionUkmMetrics() {
-    using ImageCompressionUkm = ukm::builders::PublicImageCompressionDataUse;
-    std::map<uint64_t, size_t> metric_bytes;
-    // Flatten the metrics from multiple ukm sources.
-    for (const auto* metrics :
-         ukm_recorder_->GetEntriesByName(ImageCompressionUkm::kEntryName)) {
-      for (const auto& metric : metrics->metrics) {
-        if (metric_bytes.find(metric.first) == metric_bytes.end())
-          metric_bytes[metric.first] = 0;
-        metric_bytes[metric.first] += metric.second;
-      }
-    }
-    return metric_bytes;
-  }
-
-  void WaitForImageCompressionUkmMetrics(size_t count) {
-    while (ukm_recorder_
-               ->GetEntriesByName(
-                   ukm::builders::PublicImageCompressionDataUse::kEntryName)
-               .size() < count) {
-      base::RunLoop().RunUntilIdle();
-    }
-  }
-
-  void VerifyPublicImageCompressionUkm(uint64_t hash, size_t num_images) {
-    const auto metrics = GetImageCompressionUkmMetrics();
-    if (num_images) {
-      EXPECT_THAT(metrics, testing::Contains(testing::Pair(
-                               hash, testing::Gt(num_images * 500))));
-    } else {
-      EXPECT_EQ(metrics.find(hash), metrics.end());
-    }
-  }
-
-  void VerifyCompressibleImageUkm(size_t num_images) {
-    VerifyPublicImageCompressionUkm(
-        ukm::builders::PublicImageCompressionDataUse::
-            kCompressibleImageBytesNameHash,
-        num_images);
-  }
-
-  void VerifyIneligibleImageHintsUnavailableUkm(size_t num_images) {
-    VerifyPublicImageCompressionUkm(
-        ukm::builders::PublicImageCompressionDataUse::
-            kIneligibleImageHintsUnavailableBytesNameHash,
-        num_images);
-  }
-
-  void VerifyIneligibleImageHintsUnavailableUkmButCompressible(
-      size_t num_images) {
-    VerifyPublicImageCompressionUkm(
-        ukm::builders::PublicImageCompressionDataUse::
-            kIneligibleImageHintsUnavailableButCompressibleBytesNameHash,
-        num_images);
-  }
-
-  void VerifyIneligibleImageHintsUnavailableAndMissingInHintsUkm(
-      size_t num_images) {
-    VerifyPublicImageCompressionUkm(
-        ukm::builders::PublicImageCompressionDataUse::
-            kIneligibleImageHintsUnavailableAndMissingInHintsBytesNameHash,
-        num_images);
-  }
-
-  void VerifyIneligibleMissingInImageHintsUkm(size_t num_images) {
-    VerifyPublicImageCompressionUkm(
-        ukm::builders::PublicImageCompressionDataUse::
-            kIneligibleMissingInImageHintsBytesNameHash,
-        num_images);
-  }
-
-  void VerifyIneligibleOtherImageUkm(size_t num_images) {
-    VerifyPublicImageCompressionUkm(
-        ukm::builders::PublicImageCompressionDataUse::
-            kIneligibleOtherImageBytesNameHash,
-        num_images);
-  }
-
-  void VerifyImageCompressionPageInfoState(
-      bool is_https_image_compression_applied,
-      content::WebContents* web_contents = nullptr) {
-    if (!web_contents)
-      web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-    EXPECT_EQ(is_https_image_compression_applied,
-              subresource_redirect::SubresourceRedirectObserver::
-                  IsHttpsImageCompressionApplied(web_contents));
-  }
-
-  GURL GetSubresourceURLForURL(const std::string& path) {
-    GURL compressed_url = compression_url();
-    std::string origin_hash = base::ToLowerASCII(base32::Base32Encode(
-        crypto::SHA256HashString(
-            https_url().scheme() + "://" + https_url().host() + ":" +
-            base::NumberToString(https_url().EffectiveIntPort())),
-        base32::Base32EncodePolicy::OMIT_PADDING));
-    std::string host_str = origin_hash + "." + compressed_url.host();
-    GURL::Replacements replacements;
-    replacements.SetHostStr(host_str);
-    replacements.SetPathStr(path);
-    return compressed_url.ReplaceComponents(replacements);
-  }
-
-  void VerifyAndClearBypassTimeout(base::TimeDelta minimum_bypass_until) {
-    auto* litepages_service_bypass_decider =
-        DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
-            browser()
-                ->tab_strip_model()
-                ->GetActiveWebContents()
-                ->GetBrowserContext())
-            ->litepages_service_bypass_decider();
-    EXPECT_LE(base::TimeTicks::Now() + minimum_bypass_until,
-              litepages_service_bypass_decider->GetBypassUntilTimeForTesting()
-                  .value());
-    litepages_service_bypass_decider->SetBypassUntilTimeForTesting(
-        base::TimeTicks::Now());
-  }
-
-  GURL http_url() const { return http_url_; }
-  GURL https_url() const { return https_url_; }
-  GURL compression_url() const { return compression_url_; }
-  GURL request_url() const { return request_url_; }
-
-  GURL HttpURLWithPath(const std::string& path) {
-    return http_server_.GetURL("insecure.com", path);
-  }
-  GURL HttpsURLWithPath(const std::string& path) {
-    return https_server_.GetURL("secure.com", path);
-  }
-
-  void SetHttpsServerImageToFail() { https_server_image_fail_ = true; }
-  void SetCompressionServerToFail(CompressionServerFailureMode mode) {
-    compression_server_failure_mode_ = mode;
-  }
-
-  base::HistogramTester* histogram_tester() { return &histogram_tester_; }
-
- private:
-  void TearDownOnMainThread() override {
-    EXPECT_TRUE(https_server_.ShutdownAndWaitUntilComplete());
-
-    InProcessBrowserTest::TearDownOnMainThread();
-  }
-
-  // Handles the https server request.
-  std::unique_ptr<net::test_server::HttpResponse> HandleHTTPSServerRequest(
-      const net::test_server::HttpRequest& request) {
-    if (https_server_image_fail_ &&
-        request.GetURL().path() == "/load_image/image.png") {
-      return std::make_unique<net::test_server::RawHttpResponse>("", "");
-    }
-    return nullptr;
-  }
-
-  // Handles the compression server request.
-  std::unique_ptr<net::test_server::HttpResponse>
-  HandleCompressionServerRequest(const net::test_server::HttpRequest& request) {
-    std::unique_ptr<net::test_server::BasicHttpResponse> response =
-        std::make_unique<net::test_server::BasicHttpResponse>();
-    request_url_ = request.GetURL();
-
-    if (compression_server_failure_mode_ ==
-        CompressionServerFailureMode::EMPTY_RESPONSE) {
-      return std::make_unique<net::test_server::RawHttpResponse>("", "");
-    } else if (compression_server_failure_mode_ ==
-               CompressionServerFailureMode::
-                   LOADSHED_503_RETRY_AFTER_RESPONSE) {
-      response->set_code(net::HTTP_SERVICE_UNAVAILABLE);
-      response->AddCustomHeader("Retry-After", "5");
-      return response;
-    } else if (compression_server_failure_mode_ ==
-               CompressionServerFailureMode::TIMEOUT) {
-      return std::make_unique<net::test_server::DelayedHttpResponse>(
-          base::Seconds(10));
-    }
-
-    // For the purpose of this browsertest, a redirect to the compression server
-    // that is looking to access image.png will be treated as though it is
-    // compressed.  All other redirects will be assumed failures to retrieve the
-    // requested resource and return a redirect to private_url_image.png.
-    if (request_url_.query().find(
-            net::EscapeQueryParamValue("/image.png", true /* use_plus */), 0) !=
-        std::string::npos) {
-      // Serve the correct image file.
-      std::string file_contents;
-      base::FilePath test_data_directory;
-      base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory);
-      if (base::ReadFileToString(
-              test_data_directory.AppendASCII("load_image/image.png"),
-              &file_contents)) {
-        response->set_content(file_contents);
-        response->set_code(net::HTTP_OK);
-      }
-    } else if (request_url_.query().find(
-                   net::EscapeQueryParamValue("/fail_image.png",
-                                              true /* use_plus */),
-                   0) != std::string::npos) {
-      response->set_code(net::HTTP_NOT_FOUND);
-    } else if (base::StartsWith(request_url_.path(), "/load_image/",
-                                base::CompareCase::SENSITIVE)) {
-      // Let the page be served directly
-      return nullptr;
-    } else {
-      response->set_code(net::HTTP_TEMPORARY_REDIRECT);
-      response->AddCustomHeader(
-          "Location",
-          HttpsURLWithPath("/load_image/private_url_image.png").spec());
-    }
-    return std::move(response);
-  }
-
-  base::test::ScopedFeatureList scoped_feature_list_;
-  std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_;
-
-  bool enable_subresource_server_redirect_ = false;
-
-  // Image compression is allowed only after the infobar has been shown. This
-  // bool overrides it and disabled the image compression infobar check.
-  bool override_https_image_compression_infobar_ = true;
-
-  GURL compression_url_;
-  GURL http_url_;
-  GURL https_url_;
-  GURL request_url_;
-
-  net::EmbeddedTestServer http_server_;
-  net::EmbeddedTestServer https_server_;
-  net::EmbeddedTestServer compression_server_;
-
-  base::HistogramTester histogram_tester_;
-
-  // Whether the embedded test servers should return failure.
-  bool https_server_image_fail_ = false;
-  CompressionServerFailureMode compression_server_failure_mode_ =
-      CompressionServerFailureMode::NONE;
-};
-
-class RedirectDisabledSubresourceRedirectBrowserTest
-    : public SubresourceRedirectBrowserTest {
- public:
-  RedirectDisabledSubresourceRedirectBrowserTest()
-      : SubresourceRedirectBrowserTest(false) {}
-};
-
-class InfoBarEnabledSubresourceRedirectBrowserTest
-    : public SubresourceRedirectBrowserTest {
- public:
-  InfoBarEnabledSubresourceRedirectBrowserTest()
-      : SubresourceRedirectBrowserTest(true, false) {}
-
-  // Verifies whether the infobar needs to be shown for the next navigation or
-  // not.
-  void VerifyNeedToShowInfoBarState(bool expected_need_to_show_infobar) {
-    EXPECT_EQ(expected_need_to_show_infobar,
-              DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
-                  browser()->profile())
-                  ->https_image_compression_infobar_decider()
-                  ->NeedToShowInfoBar());
-  }
-};
-
-#if defined(OS_WIN) || defined(OS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
-#define DISABLE_ON_WIN_MAC_CHROMEOS(x) DISABLED_##x
-#else
-#define DISABLE_ON_WIN_MAC_CHROMEOS(x) x
-#endif
-
-//  NOTE: It is indirectly verified that correct requests are being sent to
-//  the mock compression server by the counts in the histogram bucket for
-//  HTTP_TEMPORARY_REDIRECTs.
-
-//  This test loads image.html, which triggers a subresource request
-//  for image.png.  This triggers an internal redirect to the mocked
-//  compression server, which responds with HTTP_OK.
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestHTMLLoadRedirectSuccess)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-
-  GURL url = HttpsURLWithPath("/load_image/image_delayed_load.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      2);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  EXPECT_EQ(request_url().port(), compression_url().port());
-  VerifyCompressibleImageUkm(1);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(true);
-}
-
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(TestBypassOnFetchTimeout)) {
-  g_browser_process->network_quality_tracker()
-      ->ReportEffectiveConnectionTypeForTesting(
-          net::EFFECTIVE_CONNECTION_TYPE_2G);
-
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/image_delayed_load.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-
-  // The first navigation will attempt to fetch image and timeout, which will
-  // trigger bypass.
-  SetCompressionServerToFail(CompressionServerFailureMode::TIMEOUT);
-  base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      2);
-  RetryForHistogramUntilCountReached(histogram_tester(),
-                                     "SubresourceRedirect.BypassDuration", 1);
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-  histogram_tester()->ExpectUniqueSample(
-      "SubresourceRedirect.CompressionFetchTimeout", true, 1);
-  histogram_tester()->ExpectUniqueSample(
-      "SubresourceRedirect.LitePagesService.BypassResult", false, 1);
-  histogram_tester()->ExpectTotalCount("SubresourceRedirect.BypassDuration", 1);
-
-  // The second navigation should not attempt subresource redirect.
-  SetCompressionServerToFail(CompressionServerFailureMode::NONE);
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(),
-      HttpsURLWithPath("/load_image/image_delayed_load.html?second")));
-
-  base::RunLoop().RunUntilIdle();
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.LitePagesService.BypassResult",
-      2);
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.LitePagesService.BypassResult", true, 1);
-
-  // The third navigation should attempt subresource redirect, once the bypass
-  // is cleared.
-  VerifyAndClearBypassTimeout(base::Seconds(4));
-  url = HttpsURLWithPath("/load_image/image_delayed_load.html?third");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  EXPECT_EQ(request_url().port(), compression_url().port());
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      2);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-}
-
-//  This test loads private_url_image.html, which triggers a subresource
-//  request for private_url_image.png.  This triggers an internal redirect
-//  to the mock compression server, which bypasses the request. The
-//  mock compression server creates a redirect to the original resource.
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestHTMLLoadRedirectBypass)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/private_url_image.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/private_url_image.png"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      2);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 2);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-  // The image will be marked as compressible and page info will show even
-  // though the private image redirect was bypassed.
-  VerifyCompressibleImageUkm(1);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(true);
-}
-
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
-                       NoTriggerWhenDataSaverOff) {
-  EnableDataSaver(false);
-  CreateUkmRecorder();
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), HttpsURLWithPath("/load_image/image_delayed_load.html")));
-
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-
-  // No coverage metrics recorded.
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(false);
-}
-
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest, NoTriggerInIncognito) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  auto* incognito_browser = CreateIncognitoBrowser();
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      incognito_browser,
-      HttpsURLWithPath("/load_image/image_delayed_load.html")));
-
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  EXPECT_TRUE(RunScriptExtractBool(
-      "checkImage()",
-      incognito_browser->tab_strip_model()->GetActiveWebContents()));
-
-  EXPECT_EQ(
-      GURL(RunScriptExtractString(
-               "imageSrc()",
-               incognito_browser->tab_strip_model()->GetActiveWebContents()))
-          .port(),
-      https_url().port());
-
-  // No coverage metrics recorded.
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(
-      false, incognito_browser->tab_strip_model()->GetActiveWebContents());
-}
-
-//  This test loads image.html, from a non secure site. This triggers a
-//  subresource request, but no internal redirect should be created for
-//  non-secure sites.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
-                       NoTriggerOnNonSecureSite) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpURLWithPath("/load_image/image_delayed_load.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            http_url().port());
-
-  // No coverage metrics recorded.
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(true);
-}
-
-//  This test loads page_with_favicon.html, which creates a subresource
-//  request for icon.png.  There should be no internal redirect as favicons
-//  are not considered images by chrome.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest, NoTriggerOnNonImage) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/favicon/page_with_favicon.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-
-  // No coverage metrics recorded.
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(true);
-}
-
-}  // namespace
-
-// This test loads a resource that will return a 404 from the server, this
-// should trigger the fallback logic back to the original resource. In total
-// This results in 2 redirects (to the compression server, and back to the
-// original resource), 1 404 not-found from the compression server, and 1
-// 200 ok from the original resource.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(FallbackOnServerNotFound)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/fail_image.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/fail_image.png"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 3);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 2);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_NOT_FOUND, 1);
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-
-  // Ineligible Other bucket ukm recorded.
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(1);
-  VerifyImageCompressionPageInfoState(true);
-}
-
-//  This test verifies that the client will utilize the fallback logic if the
-//  compression server fails and returns nothing.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(FallbackOnServerFailure)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/image_delayed_load.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  SetCompressionServerToFail(CompressionServerFailureMode::EMPTY_RESPONSE);
-
-  base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  RetryForHistogramUntilCountReached(
-      histogram_tester(),
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 1);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", false, 1);
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-
-  // Ineligible Other bucket ukm recorded.
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(1);
-  VerifyImageCompressionPageInfoState(true);
-}
-
-//  This test verifies that the client will utilize the fallback logic if the
-//  compression server and the main https server fails and returns nothing.
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(FallbackOnBothServerFailure)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/image_delayed_load.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  SetHttpsServerImageToFail();
-  SetCompressionServerToFail(CompressionServerFailureMode::EMPTY_RESPONSE);
-
-  base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  // The image should failed to load, but some redirect metrics are recorded.
-  EXPECT_FALSE(RunScriptExtractBool("checkImage()"));
-  RetryForHistogramUntilCountReached(
-      histogram_tester(),
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 1);
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", false, 1);
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-
-  // No coverage ukm is recorded, since the image load failed.
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(true);
-}
-
-// This test verifies that accessing the compression server directly will not do
-// any additional redirection.
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestDirectCompressionServerPage)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/image_delayed_load.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-
-  base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(),
-      GetSubresourceURLForURL("/load_image/image_delayed_load.html")));
-
-  // The image should failed to load, but some redirect
-  // metrics are recorded.
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt."
-      "ServerResponded",
-      0);
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            compression_url().port());
-
-  // The coverage ukm still gets recorded.
-  VerifyCompressibleImageUkm(1);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(false);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestTwoPublicImagesAreRedirected)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/two_images.html");
-  SetUpPublicImageURLPaths(
-      url, {"/load_image/image.png", "/load_image/image.png?foo"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      4);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 2);
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 2);
-  EXPECT_TRUE(RunScriptExtractBool("checkBothImagesLoaded()"));
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-
-  VerifyCompressibleImageUkm(2);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(true);
-}
-
-// This test verifies that only the images in the public image URL list are
-// is_subresource_redirect_feature_enabled. In this test both images should load
-// but only one image should be redirected.
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestOnlyPublicImageIsRedirected)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/two_images.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      2);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkBothImagesLoaded()"));
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-
-  VerifyCompressibleImageUkm(1);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(1);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(true);
-}
-
-// This test verifies that the fragments in the image URL are removed before
-// checking against the public image URL list.
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestImageURLFragmentAreRemoved)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/image_with_fragment.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      2);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-
-  VerifyCompressibleImageUkm(1);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(true);
-}
-
-//  This test loads image_js.html, which triggers a javascript request
-//  for image.png for which subresource redirect will not be attempted.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
-                       NoTriggerOnJavaScriptImageRequest) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/image_js.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-
-  VerifyIneligibleOtherImageUkm(1);
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyImageCompressionPageInfoState(true);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.Blink.Ineligibility", 1);
-}
-
-// This test verifies images restricted via CSP img-src directive will not be
-// redirected.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(
-                           NoTriggerOnContentSecurityPolicyRestrictedImgSrc)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/image_csp_img_src.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-
-  VerifyIneligibleOtherImageUkm(1);
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyImageCompressionPageInfoState(true);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.Blink.Ineligibility", 2);
-}
-
-// This test verifies images restricted via CSP default-src directive will not
-// be redirected.
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(
-        NoTriggerOnContentSecurityPolicyRestrictedDefaultSrc)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/image_csp_default_src.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-
-  VerifyIneligibleOtherImageUkm(1);
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyImageCompressionPageInfoState(true);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.Blink.Ineligibility", 2);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(
-        ImageRedirectedOnContentSecurityPolicyImgNotRestricted)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-
-  GURL url = HttpsURLWithPath("/load_image/image_csp_img_allowed.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      2);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  EXPECT_EQ(request_url().port(), compression_url().port());
-  VerifyCompressibleImageUkm(1);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(true);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.Blink.Ineligibility", 0);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(
-        TestOnlyImageWithoutCrossOriginAttributeIsRedirected)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/image_crossorigin_attribute.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png?nocrossorgin"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      2);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkAllImagesLoaded()"));
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-
-  // 3 images are not compressible, 1 image is compressible.
-  VerifyCompressibleImageUkm(1);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(3);
-  VerifyImageCompressionPageInfoState(true);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.Blink.Ineligibility", 6);
-}
-
-// This test verifies that no image redirect happens when empty hints is sent.
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestNoRedirectWithEmptyHints)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/image_delayed_load.html");
-  SetUpPublicImageURLPaths(url, {});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.DidCompress.CompressionPercent", 0);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-
-  VerifyIneligibleMissingInImageHintsUkm(1);
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  // Empty hints will not show up in page info.
-  VerifyImageCompressionPageInfoState(false);
-}
-
-// This test verifies that no image redirect happens when hints are not yet
-// received.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
-                       TestNoRedirectWithoutHints) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), HttpsURLWithPath("/load_image/image_delayed_load.html")));
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.DidCompress.CompressionPercent", 0);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-  WaitForImageCompressionUkmMetrics(1);
-  VerifyIneligibleImageHintsUnavailableUkm(1);
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  // Page info not updated when hints are missing.
-  VerifyImageCompressionPageInfoState(false);
-}
-
-// This test verifies that two images in a page are not redirected, when hints
-// are missing.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
-                       TestNoRedirectWithoutHintsTwoImages) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), HttpsURLWithPath("/load_image/two_images.html")));
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.DidCompress.CompressionPercent", 0);
-  EXPECT_TRUE(RunScriptExtractBool("checkBothImagesLoaded()"));
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-  WaitForImageCompressionUkmMetrics(2);
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(2);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  // Page info not updated when hints are missing.
-  VerifyImageCompressionPageInfoState(false);
-}
-
-// This test initiates same-origin navigation and verifies the hints from the
-// previous navigation are not used.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(TestSameOriginNavigation)) {
-  g_browser_process->network_quality_tracker()
-      ->ReportEffectiveConnectionTypeForTesting(
-          net::EFFECTIVE_CONNECTION_TYPE_2G);
-
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/image_delayed_load.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      2);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  EXPECT_EQ(request_url().port(), compression_url().port());
-  VerifyCompressibleImageUkm(1);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(true);
-
-  // Initiate a same-origin navigation without hints, and let the timeout ukm be
-  // recorded.
-  CreateUkmRecorder();
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), HttpsURLWithPath("/load_image/two_images.html")));
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 2);
-  EXPECT_TRUE(RunScriptExtractBool("checkBothImagesLoaded()"));
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-  WaitForImageCompressionUkmMetrics(2);
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(2);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(false);
-}
-
-// This used to be DISABLE_ON_WIN_MAC_CHROMEOS(), but now it is disabled on all
-// platforms, since the test is also flaky on Linux. crbug.com/1114038
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
-                       DISABLED_TestBypassOn503LoadShedFailure) {
-  g_browser_process->network_quality_tracker()
-      ->ReportEffectiveConnectionTypeForTesting(
-          net::EFFECTIVE_CONNECTION_TYPE_2G);
-
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/image_delayed_load.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-
-  // The first navigation will atttempt to fetch image and fail with 503, which
-  // will trigger bypass.
-  SetCompressionServerToFail(
-      CompressionServerFailureMode::LOADSHED_503_RETRY_AFTER_RESPONSE);
-  base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      1);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 503, 1);
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-  base::RunLoop().RunUntilIdle();
-
-  RetryForHistogramUntilCountReached(histogram_tester(),
-                                     "SubresourceRedirect.BypassDuration", 1);
-  histogram_tester()->ExpectUniqueSample(
-      "SubresourceRedirect.LitePagesService.BypassResult", false, 1);
-  histogram_tester()->ExpectTotalCount("SubresourceRedirect.BypassDuration", 1);
-
-  // The second navigation should not attempt subresource redirect.
-  SetCompressionServerToFail(CompressionServerFailureMode::NONE);
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(),
-      HttpsURLWithPath("/load_image/image_delayed_load.html?second")));
-
-  base::RunLoop().RunUntilIdle();
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.LitePagesService.BypassResult",
-      2);
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.LitePagesService.BypassResult", true, 1);
-
-  // The third navigation should attempt subresource redirect, once the bypass
-  // is cleared.
-  VerifyAndClearBypassTimeout(base::Seconds(4));
-  url = HttpsURLWithPath("/load_image/image_delayed_load.html?third");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  base::RunLoop().RunUntilIdle();
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      2);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-}
-
-// This test verifies that the image redirect to lite page is disabled via
-// finch, and only the coverage metrics are recorded.
-IN_PROC_BROWSER_TEST_F(RedirectDisabledSubresourceRedirectBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(ImagesNotRedirected)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/image_delayed_load.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.DidCompress.CompressionPercent", 0);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(1);
-  VerifyImageCompressionPageInfoState(false);
-}
-
-IN_PROC_BROWSER_TEST_F(InfoBarEnabledSubresourceRedirectBrowserTest,
-                       InfoBarNotShownWhenDataSaverOff) {
-  EnableDataSaver(false);
-  CreateUkmRecorder();
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), HttpsURLWithPath("/load_image/image_delayed_load.html")));
-
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  VerifyNeedToShowInfoBarState(true);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-
-  // No coverage metrics recorded.
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyNeedToShowInfoBarState(true);
-}
-
-IN_PROC_BROWSER_TEST_F(InfoBarEnabledSubresourceRedirectBrowserTest,
-                       InfoBarNotShownInIncognito) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  auto* incognito_browser = CreateIncognitoBrowser();
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      incognito_browser,
-      HttpsURLWithPath("/load_image/image_delayed_load.html")));
-
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  VerifyNeedToShowInfoBarState(true);
-
-  EXPECT_TRUE(RunScriptExtractBool(
-      "checkImage()",
-      incognito_browser->tab_strip_model()->GetActiveWebContents()));
-
-  EXPECT_EQ(
-      GURL(RunScriptExtractString(
-               "imageSrc()",
-               incognito_browser->tab_strip_model()->GetActiveWebContents()))
-          .port(),
-      https_url().port());
-
-  // No coverage metrics recorded.
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyNeedToShowInfoBarState(true);
-}
-
-// This test verifies that the image compression is enabled only after the
-// infobar has been shown.
-IN_PROC_BROWSER_TEST_F(
-    InfoBarEnabledSubresourceRedirectBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(ImagesCompressedAfterInfoBarShown)) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  VerifyNeedToShowInfoBarState(true);
-
-  // The first navigation will not enable image compression but show the
-  // infobar.
-  GURL url = HttpsURLWithPath("/load_image/image_delayed_load.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.DidCompress.CompressionPercent", 0);
-  // InfoBar had been shown.
-  VerifyNeedToShowInfoBarState(false);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-
-  // No coverage metrics will be recorded as well.
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-
-  // The second navigation will enable image compression, and infobar should not
-  // be shown.
-  url = HttpsURLWithPath("/load_image/image_delayed_load.html?second");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
-
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      2);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-  VerifyNeedToShowInfoBarState(false);
-
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  EXPECT_EQ(request_url().port(), compression_url().port());
-  VerifyCompressibleImageUkm(1);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-}
-
-// This class sets up a hints server where image hints are fetched from to test
-// the page level hint fetches.
-class SubresourceRedirectWithHintsServerBrowserTest
-    : public SubresourceRedirectBrowserTest {
- public:
-  // How the hints server should respond to the get hints request.
-  enum HintFetchMode {
-    // Delay the hints fetch until images are loaded. Delay 3 seconds before
-    // sending response. This delay is chosen such that the server sends the
-    // hints after the image has completed loading but before the hints receive
-    // timeout(5 seconds).
-    HINT_FETCH_AFTER_IMAGES_LOADED,
-
-    // Do not send response.
-    HINT_FETCH_HUNG,
-  };
-
-  SubresourceRedirectWithHintsServerBrowserTest()
-      : SubresourceRedirectBrowserTest(true),
-        hints_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
-
-  void SetUp() override {
-    hints_server_.ServeFilesFromSourceDirectory("chrome/test/data/previews");
-    hints_server_.RegisterRequestHandler(base::BindRepeating(
-        &SubresourceRedirectWithHintsServerBrowserTest::HandleGetHintsRequest,
-        base::Unretained(this)));
-    ASSERT_TRUE(hints_server_.Start());
-    SubresourceRedirectBrowserTest::SetUp();
-  }
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch("ignore-certificate-errors");
-    command_line->AppendSwitch("purge_hint_cache_store");
-    command_line->AppendSwitch(optimization_guide::switches::
-                                   kDisableCheckingUserPermissionsForTesting);
-    command_line->AppendSwitchASCII(
-        optimization_guide::switches::kOptimizationGuideServiceGetHintsURL,
-        hints_server_.base_url().spec());
-    command_line->AppendSwitchASCII(
-        optimization_guide::switches::kFetchHintsOverride, "secure.com");
-    command_line->AppendSwitch(
-        optimization_guide::switches::kFetchHintsOverrideTimer);
-    SubresourceRedirectBrowserTest::SetUpCommandLine(command_line);
-  }
-
-  void SetUpOnMainThread() override {
-    content::NetworkConnectionChangeSimulator().SetConnectionType(
-        network::mojom::ConnectionType::CONNECTION_2G);
-    SubresourceRedirectBrowserTest::SetUpOnMainThread();
-  }
-
-  // Sets up public image URL hint data that is returned by the hints server.
-  void SetUpPublicImageURLPaths(
-      const std::string& url,
-      const std::vector<std::string>& public_image_paths,
-      HintFetchMode hint_fetch_mode) {
-    hint_fetch_mode_ = hint_fetch_mode;
-    optimization_guide::proto::GetHintsResponse get_hints_response;
-    optimization_guide::proto::Hint* hint = get_hints_response.add_hints();
-    hint->set_key_representation(optimization_guide::proto::FULL_URL);
-    hint->set_key(HttpsURLWithPath(url).spec());
-    optimization_guide::proto::PageHint* page_hint = hint->add_page_hints();
-    page_hint->set_page_pattern(HttpsURLWithPath(url).spec());
-    auto* optimization = page_hint->add_allowlisted_optimizations();
-    optimization->set_optimization_type(
-        optimization_guide::proto::OptimizationType::COMPRESS_PUBLIC_IMAGES);
-    auto* public_image_metadata = optimization->mutable_public_image_metadata();
-
-    for (const auto& public_image_path : public_image_paths) {
-      public_image_metadata->add_url(
-          HttpsURLWithPath(public_image_path).spec());
-    }
-
-    base::AutoLock lock(lock_);
-    get_hints_response.SerializeToString(&get_hints_response_);
-  }
-
- private:
-  std::unique_ptr<net::test_server::HttpResponse> HandleGetHintsRequest(
-      const net::test_server::HttpRequest& request) {
-    switch (hint_fetch_mode_) {
-      case HintFetchMode::HINT_FETCH_AFTER_IMAGES_LOADED: {
-        auto response = std::make_unique<net::test_server::DelayedHttpResponse>(
-            base::Seconds(3));
-        response->set_content(get_hints_response_);
-        response->set_code(net::HTTP_OK);
-        return std::move(response);
-      }
-      case HintFetchMode::HINT_FETCH_HUNG:
-        return std::make_unique<net::test_server::HungResponse>();
-    }
-    return nullptr;
-  }
-
-  net::EmbeddedTestServer hints_server_;
-  std::string get_hints_response_;
-  base::Lock lock_;
-  HintFetchMode hint_fetch_mode_ =
-      HintFetchMode::HINT_FETCH_AFTER_IMAGES_LOADED;
-};
-
-// This test verifies that two images in a page are not redirected, when hints
-// are received delayed.
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectWithHintsServerBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestNoRedirectWithDelayedHintsTwoImages)) {
-  g_browser_process->network_quality_tracker()
-      ->ReportEffectiveConnectionTypeForTesting(
-          net::EFFECTIVE_CONNECTION_TYPE_2G);
-
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  SetUpPublicImageURLPaths("/load_image/two_images.html",
-                           {"/load_image/image.png"},
-                           HintFetchMode::HINT_FETCH_AFTER_IMAGES_LOADED);
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), HttpsURLWithPath("/load_image/two_images.html")));
-
-  // Let the images load.
-  EXPECT_TRUE(RunScriptExtractBool("checkBothImagesLoaded()"));
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.DidCompress.CompressionPercent", 0);
-  EXPECT_EQ(GURL(RunScriptExtractString("imageSrc()")).port(),
-            https_url().port());
-  VerifyImageCompressionPageInfoState(false);
-
-  // One image will be recorded as compressible, but image hints not received in
-  // time. Another image is recorded as not compressible, and image hints not
-  // received in time.
-  WaitForImageCompressionUkmMetrics(2);
-  VerifyIneligibleImageHintsUnavailableUkmButCompressible(1);
-  VerifyIneligibleImageHintsUnavailableAndMissingInHintsUkm(1);
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(true);
-}
-
-// Tests CSS background images are redirected.
-// TODO(crbug.com/1274972): Make this work with NavigationThreadingOptimizations
-// enabled.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectBrowserTest,
-                       DISABLED_TestCSSBackgroundImageRedirect) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/css_background_image.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
-
-  RetryForHistogramUntilCountReached(
-      histogram_tester(), "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      2);
-  base::RunLoop().RunUntilIdle();
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-
-  histogram_tester()->ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-
-  EXPECT_EQ(request_url().port(), compression_url().port());
-  WaitForImageCompressionUkmMetrics(1);
-  VerifyCompressibleImageUkm(1);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(0);
-  VerifyImageCompressionPageInfoState(true);
-}
-
-// Tests CSS background image coverage metrics is recorded but not redirected,
-// when redirect is disabled.
-// TODO(crbug.com/1274972): Make this work with NavigationThreadingOptimizations
-// enabled.
-IN_PROC_BROWSER_TEST_F(RedirectDisabledSubresourceRedirectBrowserTest,
-                       DISABLED_TestCSSBackgroundImageRedirect) {
-  EnableDataSaver(true);
-  CreateUkmRecorder();
-  GURL url = HttpsURLWithPath("/load_image/css_background_image.html");
-  SetUpPublicImageURLPaths(url, {"/load_image/image.png"});
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
-
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-  base::RunLoop().RunUntilIdle();
-
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester()->ExpectTotalCount(
-      "SubresourceRedirect.DidCompress.CompressionPercent", 0);
-
-  WaitForImageCompressionUkmMetrics(1);
-  VerifyCompressibleImageUkm(0);
-  VerifyIneligibleImageHintsUnavailableUkm(0);
-  VerifyIneligibleMissingInImageHintsUkm(0);
-  VerifyIneligibleOtherImageUkm(1);
-  VerifyImageCompressionPageInfoState(false);
-}
diff --git a/chrome/browser/data_saver/subresource_redirect_login_robots_browsertest.cc b/chrome/browser/data_saver/subresource_redirect_login_robots_browsertest.cc
deleted file mode 100644
index 3b4f14f..0000000
--- a/chrome/browser/data_saver/subresource_redirect_login_robots_browsertest.cc
+++ /dev/null
@@ -1,1446 +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.
-
-#include <string>
-
-#include "base/strings/strcat.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/timer/elapsed_timer.h"
-#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
-#include "chrome/browser/login_detection/login_detection_type.h"
-#include "chrome/browser/login_detection/login_detection_util.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/subresource_redirect/https_image_compression_infobar_decider.h"
-#include "chrome/browser/subresource_redirect/subresource_redirect_observer.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/subresource_redirect/common/subresource_redirect_result.h"
-#include "components/subresource_redirect/subresource_redirect_browser_test_util.h"
-#include "components/subresource_redirect/subresource_redirect_test_util.h"
-#include "components/ukm/test_ukm_recorder.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "net/http/http_status_code.h"
-#include "net/test/embedded_test_server/request_handler_util.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/features.h"
-#include "url/gurl.h"
-
-namespace subresource_redirect {
-
-const int kMaxRobotsRulesParsersCacheSize = 20;
-
-class SubresourceRedirectLoginRobotsBrowserTest : public InProcessBrowserTest {
- public:
-  explicit SubresourceRedirectLoginRobotsBrowserTest(
-      const std::vector<std::pair<std::string, std::string>>&
-          additional_feature_params = {},
-      bool enable_lite_mode = true,
-      bool enable_login_robots_compression_feature = true)
-      : enable_lite_mode_(enable_lite_mode),
-        enable_login_robots_compression_feature_(
-            enable_login_robots_compression_feature),
-        additional_feature_params_(additional_feature_params),
-        https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
-
-  ~SubresourceRedirectLoginRobotsBrowserTest() override = default;
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitchASCII("host-rules", "MAP * 127.0.0.1");
-    if (enable_lite_mode_)
-      command_line->AppendSwitch("enable-spdy-proxy-auth");
-
-    // Disable infobar shown check to actually compress the pages.
-    command_line->AppendSwitch("override-https-image-compression-infobar");
-  }
-
-  void SetUp() override {
-    ASSERT_TRUE(robots_rules_server_.Start());
-    ASSERT_TRUE(image_compression_server_.Start());
-    https_test_server_.ServeFilesFromSourceDirectory("chrome/test/data");
-    ASSERT_TRUE(https_test_server_.Start());
-
-    std::vector<base::test::ScopedFeatureList::FeatureAndParams>
-        enabled_features;
-    if (enable_login_robots_compression_feature_) {
-      base::FieldTrialParams params, login_detection_params;
-      params["enable_public_image_hints_based_compression"] = "false";
-      params["enable_login_robots_based_compression"] = "true";
-      params["lite_page_robots_origin"] = robots_rules_server_.GetURL();
-      params["lite_page_subresource_origin"] =
-          image_compression_server_.GetURL();
-      // This rules fetch timeout is chosen such that the tests would have
-      // enough time to fetch the rules without causing a timeout.
-      params["robots_rules_receive_timeout_ms"] = "3000";
-      // Allow first 3 images to be loaded faster.
-      params["first_k_subresource_limit"] = "3";
-      params["robots_rules_receive_first_k_timeout_ms"] = "2000";
-      for (const auto& param : additional_feature_params_) {
-        params[param.first] = param.second;
-      }
-      params["max_robots_rules_parsers_cache_size"] =
-          base::NumberToString(kMaxRobotsRulesParsersCacheSize);
-      enabled_features.emplace_back(blink::features::kSubresourceRedirect,
-                                    params);
-      login_detection_params["logged_in_sites"] = "https://loggedin.com";
-      enabled_features.emplace_back(login_detection::kLoginDetection,
-                                    login_detection_params);
-    }
-    scoped_feature_list_.InitWithFeaturesAndParameters(enabled_features, {});
-    InProcessBrowserTest::SetUp();
-  }
-
-  GURL GetHttpsTestURL(const std::string& path) const {
-    return https_test_server_.GetURL("test_https_server.com", path);
-  }
-
-  void NavigateAndWaitForLoad(Browser* browser, const GURL& url) {
-    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser, url));
-    EXPECT_EQ(true, EvalJs(browser->tab_strip_model()->GetActiveWebContents(),
-                           "checkImage()"));
-    FetchHistogramsFromChildProcesses();
-  }
-
-  bool RunScriptExtractBool(const std::string& script,
-                            content::WebContents* web_contents = nullptr) {
-    if (!web_contents)
-      web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-    return EvalJs(web_contents, script).ExtractBool();
-  }
-
-  void VerifyImageCompressionPageInfoState(
-      bool is_https_image_compression_applied,
-      content::WebContents* web_contents = nullptr) {
-    if (!web_contents)
-      web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-    EXPECT_EQ(is_https_image_compression_applied,
-              subresource_redirect::SubresourceRedirectObserver::
-                  IsHttpsImageCompressionApplied(web_contents));
-  }
-
-  void CreateUkmRecorder() {
-    ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
-  }
-
-  std::map<uint64_t, size_t> GetImageCompressionUkmMetrics() {
-    base::RunLoop().RunUntilIdle();
-    using ImageCompressionUkm = ukm::builders::PublicImageCompressionImageLoad;
-    std::map<uint64_t, size_t> merged_metrics;
-    // Flatten the metrics from multiple ukm sources.
-    for (const auto* metrics :
-         ukm_recorder_->GetEntriesByName(ImageCompressionUkm::kEntryName)) {
-      for (const auto& metric : metrics->metrics) {
-        if (merged_metrics.find(metric.first) == merged_metrics.end())
-          merged_metrics[metric.first] = metric.second;
-      }
-    }
-    return merged_metrics;
-  }
-
-  void VerifyRobotsRulesFetch(
-      const std::set<std::string>& expected_robots_requests) {
-    if (!expected_robots_requests.empty()) {
-      histogram_tester_.ExpectBucketCount(
-          "SubresourceRedirect.RobotsRulesFetcher.ResponseCode", net::HTTP_OK,
-          expected_robots_requests.size());
-      histogram_tester_.ExpectBucketCount(
-          "SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit", false,
-          expected_robots_requests.size());
-    } else {
-      histogram_tester_.ExpectTotalCount(
-          "SubresourceRedirect.RobotsRulesFetcher.ResponseCode", 0);
-      histogram_tester_.ExpectTotalCount(
-          "SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit", 0);
-    }
-    robots_rules_server_.VerifyRequestedOrigins(expected_robots_requests);
-  }
-
-  void VerifyCompressedImageFetch(
-      const std::set<std::string>& expected_image_requests) {
-    if (!expected_image_requests.empty()) {
-      histogram_tester_.ExpectUniqueSample(
-          "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-          SubresourceRedirectResult::kRedirectable, 1);
-      histogram_tester_.ExpectBucketCount(
-          "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK,
-          1);
-      histogram_tester_.ExpectBucketCount(
-          "SubresourceRedirect.CompressionAttempt.ResponseCode",
-          net::HTTP_TEMPORARY_REDIRECT, 1);
-      histogram_tester_.ExpectUniqueSample(
-          "SubresourceRedirect.CompressionAttempt.ServerResponded", true, 1);
-    } else {
-      histogram_tester_.ExpectTotalCount(
-          "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 0);
-      histogram_tester_.ExpectTotalCount(
-          "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-      histogram_tester_.ExpectTotalCount(
-          "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-    }
-    histogram_tester_.ExpectTotalCount(
-        "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-    VerifyImageCompressionPageInfoState(true);
-    image_compression_server_.VerifyRequestedImagePaths(
-        expected_image_requests);
-
-    using ImageCompressionUkm = ukm::builders::PublicImageCompressionImageLoad;
-    auto ukm_metrics = GetImageCompressionUkmMetrics();
-    EXPECT_LT(100U, ukm_metrics[ImageCompressionUkm::kOriginalBytesNameHash]);
-    EXPECT_THAT(ukm_metrics,
-                testing::Contains(testing::Key(
-                    ImageCompressionUkm::kNavigationToRequestStartNameHash)));
-    EXPECT_THAT(ukm_metrics,
-                testing::Contains(testing::Key(
-                    ImageCompressionUkm::kNavigationToRequestSentNameHash)));
-    EXPECT_THAT(
-        ukm_metrics,
-        testing::Contains(testing::Key(
-            ImageCompressionUkm::kNavigationToResponseReceivedNameHash)));
-    EXPECT_THAT(ukm_metrics,
-                testing::Contains(testing::Key(
-                    ImageCompressionUkm::kRobotsRulesFetchLatencyNameHash)));
-    if (!expected_image_requests.empty()) {
-      EXPECT_EQ(SubresourceRedirectResult::kRedirectable,
-                static_cast<SubresourceRedirectResult>(
-                    ukm_metrics[ImageCompressionUkm::kRedirectResultNameHash]));
-      EXPECT_LT(
-          10U,
-          ukm_metrics[ImageCompressionUkm::kCompressionPercentageNameHash]);
-    } else {
-      EXPECT_EQ(SubresourceRedirectResult::kIneligibleBlinkDisallowed,
-                static_cast<SubresourceRedirectResult>(
-                    ukm_metrics[ImageCompressionUkm::kRedirectResultNameHash]));
-    }
-  }
-
- protected:
-  bool enable_lite_mode_;
-  bool enable_login_robots_compression_feature_;
-
-  // Additional feature params that are set for the subresource redirect
-  // feature.
-  std::vector<std::pair<std::string, std::string>> additional_feature_params_;
-
-  base::test::ScopedFeatureList scoped_feature_list_;
-
-  // Simulates the LitePages servers that return the robots rules and compress
-  // images.
-  RobotsRulesTestServer robots_rules_server_;
-  ImageCompressionTestServer image_compression_server_;
-  net::EmbeddedTestServer https_test_server_;
-
-  base::HistogramTester histogram_tester_;
-  std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_;
-};
-
-class SubresourceRedirectLoginRobotsLowMemoryBrowserTest
-    : public SubresourceRedirectLoginRobotsBrowserTest,
-      public testing::WithParamInterface<std::tuple<bool, bool>> {
- public:
-  SubresourceRedirectLoginRobotsLowMemoryBrowserTest()
-      : SubresourceRedirectLoginRobotsBrowserTest(
-            {{"enable_login_robots_for_low_memory",
-              is_login_robots_for_low_memory_feature_enabled() ? "true"
-                                                               : "false"}},
-            true, /* enable_lite_mode */
-            true  /* enable_login_robots_compression_feature */
-        ) {}
-
-  ~SubresourceRedirectLoginRobotsLowMemoryBrowserTest() override = default;
-
-  bool is_low_end_device() const { return std::get<0>(GetParam()); }
-
-  bool is_login_robots_for_low_memory_feature_enabled() const {
-    return std::get<1>(GetParam());
-  }
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    SubresourceRedirectLoginRobotsBrowserTest::SetUpCommandLine(command_line);
-    if (is_low_end_device())
-      command_line->AppendSwitch("enable-low-end-device-mode");
-  }
-};
-
-// Enable tests for linux since LiteMode is enabled only for Android.
-#if defined(OS_WIN) || defined(OS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
-#define DISABLE_ON_WIN_MAC_CHROMEOS(x) DISABLED_##x
-#else
-#define DISABLE_ON_WIN_MAC_CHROMEOS(x) x
-#endif
-
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectLoginRobotsBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(TestImageAllowedByRobots)) {
-  CreateUkmRecorder();
-  robots_rules_server_.AddRobotsRules(
-      GetHttpsTestURL("/"),
-      {{kRuleTypeAllow, "/load_image/image.png"}, {kRuleTypeDisallow, ""}});
-  NavigateAndWaitForLoad(browser(), GetHttpsTestURL("/load_image/image.html"));
-
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kRedirectable, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", true, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png"});
-
-  using ImageCompressionUkm = ukm::builders::PublicImageCompressionImageLoad;
-  auto ukm_metrics = GetImageCompressionUkmMetrics();
-  EXPECT_LT(100U, ukm_metrics[ImageCompressionUkm::kOriginalBytesNameHash]);
-  EXPECT_LT(10U,
-            ukm_metrics[ImageCompressionUkm::kCompressionPercentageNameHash]);
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToRequestStartNameHash)));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToRequestSentNameHash)));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToResponseReceivedNameHash)));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kRobotsRulesFetchLatencyNameHash)));
-  EXPECT_EQ(SubresourceRedirectResult::kRedirectable,
-            static_cast<SubresourceRedirectResult>(
-                ukm_metrics[ImageCompressionUkm::kRedirectResultNameHash]));
-  VerifyImageCompressionPageInfoState(true);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectLoginRobotsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestImageDisallowedByRobots)) {
-  CreateUkmRecorder();
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeDisallow, ""}});
-  NavigateAndWaitForLoad(browser(), GetHttpsTestURL("/load_image/image.html"));
-
-  // The image will start redirect and pause when robots rules are getting
-  // fetched. But when the robots rules disallows the image, it will reset and
-  // fetch the original URL.
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleRobotsDisallowed, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths({});
-
-  using ImageCompressionUkm = ukm::builders::PublicImageCompressionImageLoad;
-  auto ukm_metrics = GetImageCompressionUkmMetrics();
-  EXPECT_LT(100U, ukm_metrics[ImageCompressionUkm::kOriginalBytesNameHash]);
-  EXPECT_THAT(ukm_metrics,
-              testing::Not(testing::Contains(testing::Key(
-                  ImageCompressionUkm::kCompressionPercentageNameHash))));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToRequestStartNameHash)));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToRequestSentNameHash)));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToResponseReceivedNameHash)));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kRobotsRulesFetchLatencyNameHash)));
-  EXPECT_EQ(SubresourceRedirectResult::kIneligibleRobotsDisallowed,
-            static_cast<SubresourceRedirectResult>(
-                ukm_metrics[ImageCompressionUkm::kRedirectResultNameHash]));
-
-  // Page info would still show compression is enabled even when no image got
-  // compressed.
-  VerifyImageCompressionPageInfoState(true);
-}
-
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectLoginRobotsBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(NoTriggerWhenDataSaverOff)) {
-  data_reduction_proxy::DataReductionProxySettings::
-      SetDataSaverEnabledForTesting(browser()->profile()->GetPrefs(), false);
-  base::RunLoop().RunUntilIdle();
-
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeAllow, ""}});
-  NavigateAndWaitForLoad(browser(), GetHttpsTestURL("/load_image/image.html"));
-
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  VerifyRobotsRulesFetch({});
-  image_compression_server_.VerifyRequestedImagePaths({});
-  VerifyImageCompressionPageInfoState(false);
-}
-
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectLoginRobotsBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(NoTriggerInIncognito)) {
-  auto* incognito_browser = CreateIncognitoBrowser();
-
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeAllow, ""}});
-  NavigateAndWaitForLoad(incognito_browser,
-                         GetHttpsTestURL("/load_image/image.html"));
-
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  VerifyRobotsRulesFetch({});
-  image_compression_server_.VerifyRequestedImagePaths({});
-  VerifyImageCompressionPageInfoState(false);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectLoginRobotsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestRobotsRulesFetchTimeout)) {
-  CreateUkmRecorder();
-  robots_rules_server_.set_failure_mode(
-      RobotsRulesTestServer::FailureMode::kTimeout);
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeAllow, ""}});
-  NavigateAndWaitForLoad(browser(), GetHttpsTestURL("/load_image/image.html"));
-
-  // The image will start redirect and pause when robots rules are getting
-  // fetched. But when the fetch timesout, it will reset and fetch the original
-  // URL.
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-
-  // Wait until the robots rules fetch times-out.
-  RetryForHistogramUntilCountReached(
-      &histogram_tester_, "SubresourceRedirect.RobotsRulesFetcher.ResponseCode",
-      1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleRobotsTimeout, 1);
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths({});
-
-  using ImageCompressionUkm = ukm::builders::PublicImageCompressionImageLoad;
-  auto ukm_metrics = GetImageCompressionUkmMetrics();
-  EXPECT_LT(100U, ukm_metrics[ImageCompressionUkm::kOriginalBytesNameHash]);
-  EXPECT_THAT(ukm_metrics,
-              testing::Not(testing::Contains(testing::Key(
-                  ImageCompressionUkm::kCompressionPercentageNameHash))));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToRequestStartNameHash)));
-  // Verify robots rules fetch took closer to the 1 second timeout.
-  EXPECT_LT(900U,
-            ukm_metrics[ImageCompressionUkm::kRobotsRulesFetchLatencyNameHash]);
-  EXPECT_LT(900U,
-            ukm_metrics[ImageCompressionUkm::kNavigationToRequestSentNameHash]);
-  EXPECT_LT(
-      900U,
-      ukm_metrics[ImageCompressionUkm::kNavigationToResponseReceivedNameHash]);
-  EXPECT_EQ(SubresourceRedirectResult::kIneligibleRobotsTimeout,
-            static_cast<SubresourceRedirectResult>(
-                ukm_metrics[ImageCompressionUkm::kRedirectResultNameHash]));
-  VerifyImageCompressionPageInfoState(true);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectLoginRobotsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestOneImageAllowedOneDisallowed)) {
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeDisallow, "*foo"}});
-  NavigateAndWaitForLoad(browser(),
-                         GetHttpsTestURL("/load_image/two_images.html"));
-
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kRedirectable, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleRobotsDisallowed, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 2);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png"});
-  VerifyImageCompressionPageInfoState(true);
-}
-
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectLoginRobotsBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(TestTwoImagesAllowed)) {
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeAllow, ""}});
-  NavigateAndWaitForLoad(browser(),
-                         GetHttpsTestURL("/load_image/two_images.html"));
-
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kRedirectable, 2);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 2);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 2);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 2);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png", "/load_image/image.png?foo"});
-  VerifyImageCompressionPageInfoState(true);
-}
-
-// Verify an new image loads fine after robots rules fetch is complete.
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectLoginRobotsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestImageLoadAfterRobotsFetch)) {
-  robots_rules_server_.AddRobotsRules(
-      GetHttpsTestURL("/"),
-      {{kRuleTypeAllow, "/load_image/image.png"}, {kRuleTypeDisallow, ""}});
-  NavigateAndWaitForLoad(browser(), GetHttpsTestURL("/load_image/image.html"));
-
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kRedirectable, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", true, 1);
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png"});
-
-  // Load another image and that will be immediately redirected as well.
-  EXPECT_TRUE(RunScriptExtractBool(R"(loadNewImage("image.png?foo"))"));
-  FetchHistogramsFromChildProcesses();
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kRedirectable, 2);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 2);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 2);
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-
-  // No more new robots rules fetches.
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit", 1);
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png", "/load_image/image.png?foo"});
-  VerifyImageCompressionPageInfoState(true);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectLoginRobotsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestDifferentOriginImageLoad)) {
-  robots_rules_server_.AddRobotsRules(
-      GetHttpsTestURL("/"),
-      {{kRuleTypeAllow, "/load_image/image.png"}, {kRuleTypeDisallow, ""}});
-  NavigateAndWaitForLoad(browser(), GetHttpsTestURL("/load_image/image.html"));
-
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kRedirectable, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", true, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.RobotRulesDecider.ApplyDuration", 1);
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png"});
-
-  // Load a compressible image from different origin and that will trigger
-  // robots rules fetch.
-  robots_rules_server_.AddRobotsRules(
-      https_test_server_.GetURL("differentorigin.com", "/"),
-      {{kRuleTypeDisallow, "*disallowed*"}});
-  EXPECT_TRUE(RunScriptExtractBool(content::JsReplace(
-      "loadNewImage($1)",
-      https_test_server_.GetURL("differentorigin.com",
-                                "/load_image/image.png?allowed"))));
-  FetchHistogramsFromChildProcesses();
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kRedirectable, 2);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 2);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 2);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.RobotRulesDecider.ApplyDuration", 2);
-
-  // Another robots rules fetch happened.
-  VerifyRobotsRulesFetch(
-      {GetHttpsTestURL("/").spec(),
-       https_test_server_.GetURL("differentorigin.com", "/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png", "/load_image/image.png?allowed"});
-
-  // Load a disallowed image from the different origin.
-  EXPECT_TRUE(RunScriptExtractBool(content::JsReplace(
-      "loadNewImage($1)",
-      https_test_server_.GetURL("differentorigin.com",
-                                "/load_image/image.png?disallowed"))));
-  FetchHistogramsFromChildProcesses();
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleRobotsDisallowed, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 4);
-
-  // No more new robots rules fetches.
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit", 2);
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png", "/load_image/image.png?allowed"});
-  VerifyImageCompressionPageInfoState(true);
-}
-
-// Verifies that LitePages gets blocked due to robots fetch failure, and
-// subsequent robots rules fetch does not happen.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectLoginRobotsBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(TestRobotsFetchLoadshed)) {
-  robots_rules_server_.set_failure_mode(
-      RobotsRulesTestServer::FailureMode::kLoadshed503RetryAfterResponse);
-  NavigateAndWaitForLoad(browser(), GetHttpsTestURL("/load_image/image.html"));
-  RetryForHistogramUntilCountReached(
-      &histogram_tester_,
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 1);
-
-  // One robots rules fetch failure should result in LitePages block.
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRulesFetcher.ResponseCode",
-      net::HTTP_SERVICE_UNAVAILABLE, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit", false, 1);
-  // Bypass check happens twice - once for pageload, and once for robots
-  // fetch.
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LitePagesService.BypassResult", false, 2);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.RobotRulesDecider.ApplyDuration", 0);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleRobotsDisallowed, 1);
-
-  robots_rules_server_.VerifyRequestedOrigins({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths({});
-
-  // Load an image from different origin and that should not trigger robots
-  // rules fetch, since LitePages is blocked.
-  EXPECT_TRUE(RunScriptExtractBool(content::JsReplace(
-      "loadNewImage($1)",
-      https_test_server_.GetURL("differentorigin.com",
-                                "/load_image/image.png?allowed"))));
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  RetryForHistogramUntilCountReached(
-      &histogram_tester_,
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 2);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LitePagesService.BypassResult", true, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.RobotRulesDecider.ApplyDuration", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit", 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleRobotsDisallowed, 2);
-
-  // No more additional fetches.
-  robots_rules_server_.VerifyRequestedOrigins({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths({});
-  VerifyImageCompressionPageInfoState(true);
-}
-
-// Verifies that when an image load fails, LitePages gets blocked, and
-// subsequent robots rules fetch, LitePages image loads does not happen.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectLoginRobotsBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(TestImageFetchLoadshed)) {
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeAllow, ""}});
-  image_compression_server_.set_failure_mode(
-      ImageCompressionTestServer::FailureMode::kLoadshed503RetryAfterResponse);
-  NavigateAndWaitForLoad(browser(), GetHttpsTestURL("/load_image/image.html"));
-
-  // Robots rules fetch was success.
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.RobotRulesDecider.ApplyDuration", 1);
-
-  // One compressed image fetch failed and then loaded directly.
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kRedirectable, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 2);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_SERVICE_UNAVAILABLE, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-
-  // Bypass check happens twice - once for pageload, and once for robots
-  // fetch.
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LitePagesService.BypassResult", false, 2);
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png"});
-
-  // Load an image from different origin and that should not trigger robots
-  // rules fetch, since LitePages is blocked.
-  EXPECT_TRUE(RunScriptExtractBool(content::JsReplace(
-      "loadNewImage($1)",
-      https_test_server_.GetURL("differentorigin.com",
-                                "/load_image/image.png?allowed"))));
-  FetchHistogramsFromChildProcesses();
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleRobotsDisallowed, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LitePagesService.BypassResult", true, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.RobotRulesDecider.ApplyDuration", 1);
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-
-  // No more additional fetches.
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png"});
-  VerifyImageCompressionPageInfoState(true);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectLoginRobotsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestNoCompressionOnLoggedInPage)) {
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeAllow, "*"}});
-  // Trigger OAuth login by triggering OAuth start and complete.
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), GetHttpsTestURL("/simple.html?initial")));
-  histogram_tester_.ExpectUniqueSample(
-      "Login.PageLoad.DetectionType",
-      login_detection::LoginDetectionType::kNoLogin, 1);
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), https_test_server_.GetURL("oauth_server.com",
-                                           "/simple.html?client_id=user")));
-  histogram_tester_.ExpectBucketCount(
-      "Login.PageLoad.DetectionType",
-      login_detection::LoginDetectionType::kNoLogin, 2);
-
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), GetHttpsTestURL("/simple.html?code=123")));
-  histogram_tester_.ExpectBucketCount(
-      "Login.PageLoad.DetectionType",
-      login_detection::LoginDetectionType::kOauthFirstTimeLoginFlow, 1);
-
-  // The next navigation will be treated as logged-in.
-  NavigateAndWaitForLoad(browser(), GetHttpsTestURL("/load_image/image.html"));
-  histogram_tester_.ExpectBucketCount(
-      "Login.PageLoad.DetectionType",
-      login_detection::LoginDetectionType::kOauthLogin, 1);
-
-  // No image compression will be triggered.
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleLoginDetected, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  VerifyRobotsRulesFetch({});
-  image_compression_server_.VerifyRequestedImagePaths({});
-  VerifyImageCompressionPageInfoState(false);
-}
-
-// Tests images in subframe are compressed.
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectLoginRobotsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestSubframeImageAllowedByRobots)) {
-  robots_rules_server_.AddRobotsRules(
-      GetHttpsTestURL("/"),
-      {{kRuleTypeAllow, "/load_image/image.png"}, {kRuleTypeDisallow, ""}});
-  NavigateAndWaitForLoad(browser(),
-                         GetHttpsTestURL("/load_image/page_with_iframe.html"));
-  EXPECT_EQ(true, EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
-                         "checkSubframeImage()"));
-  FetchHistogramsFromChildProcesses();
-
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kRedirectable, 2);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 2);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 2);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", true, 2);
-  // The robots rules are fetched once, since both images are from the same
-  // origin.
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png?mainframe", "/load_image/image.png"});
-  VerifyImageCompressionPageInfoState(true);
-}
-
-// Tests images in cross-origin subframe are compressed.
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectLoginRobotsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestCrossOriginSubframeImageAllowedByRobots)) {
-  robots_rules_server_.AddRobotsRules(
-      GetHttpsTestURL("/"),
-      {{kRuleTypeAllow, "/load_image/image.png"}, {kRuleTypeDisallow, ""}});
-  NavigateAndWaitForLoad(
-      browser(), GetHttpsTestURL(net::test_server::GetFilePathWithReplacements(
-                     "/load_image/page_with_crossorigin_iframe.html",
-                     {{"REPLACE_WITH_BASE_URL",
-                       https_test_server_.GetURL("foo.com", "/").spec()}})));
-
-  // Wait for the histograms, since javascript cannot be used to wait for
-  // loading of the image in the subframe.
-  RetryForHistogramUntilCountReached(
-      &histogram_tester_,
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 2);
-
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kRedirectable, 2);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 2);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 2);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", true, 2);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec(),
-                          https_test_server_.GetURL("foo.com", "/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png?mainframe", "/load_image/image.png"});
-  VerifyImageCompressionPageInfoState(true);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectLoginRobotsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestLoggedInSubframeDisallowed)) {
-  robots_rules_server_.AddRobotsRules(
-      GetHttpsTestURL("/"),
-      {{kRuleTypeAllow, "/load_image/image.png"}, {kRuleTypeDisallow, ""}});
-  NavigateAndWaitForLoad(
-      browser(),
-      GetHttpsTestURL(net::test_server::GetFilePathWithReplacements(
-          "/load_image/page_with_crossorigin_iframe.html",
-          {{"REPLACE_WITH_BASE_URL",
-            https_test_server_.GetURL("loggedin.com", "/").spec()}})));
-
-  // Wait for the histograms, since javascript cannot be used to wait for
-  // loading of the image in the crossorigin subframe.
-  RetryForHistogramUntilCountReached(&histogram_tester_,
-                                     "Blink.DecodedImageType", 2);
-  RetryForHistogramUntilCountReached(
-      &histogram_tester_,
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 2);
-
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kRedirectable, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleLoginDetected, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", true, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png?mainframe"});
-  // Main frame still enables image compression.
-  VerifyImageCompressionPageInfoState(true);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectLoginRobotsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestLoggedInMainframeDisallowsSubframe)) {
-  robots_rules_server_.AddRobotsRules(
-      https_test_server_.GetURL("loggedin.com", "/"),
-      {{kRuleTypeAllow, "/load_image/image.png"}, {kRuleTypeDisallow, ""}});
-  NavigateAndWaitForLoad(
-      browser(), https_test_server_.GetURL(
-                     "loggedin.com", "/load_image/page_with_iframe.html"));
-
-  // Wait for the histograms, since javascript cannot be used to wait for
-  // loading of the image in the crossorigin subframe.
-  RetryForHistogramUntilCountReached(&histogram_tester_,
-                                     "Blink.DecodedImageType", 2);
-  RetryForHistogramUntilCountReached(
-      &histogram_tester_,
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 2);
-
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleLoginDetected, 2);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  VerifyRobotsRulesFetch({});
-  image_compression_server_.VerifyRequestedImagePaths({});
-  VerifyImageCompressionPageInfoState(false);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectLoginRobotsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestFirstKImagesLoadFaster)) {
-  robots_rules_server_.set_failure_mode(
-      RobotsRulesTestServer::FailureMode::kTimeout);
-  NavigateAndWaitForLoad(browser(), GetHttpsTestURL("/load_image/image.html"));
-
-  // Load 2 more images from different domain, so that they will fetch different
-  // robots rules and will all timeout with the shorter first k timeout
-  // duration.
-  for (const char* image_origin : {
-           "foo1.com",
-           "foo2.com",
-       }) {
-    base::ElapsedTimer elapsed_timer;
-    std::string load_image_url = base::StrCat(
-        {"loadNewImage('",
-         https_test_server_.GetURL(image_origin, "/load_image/image.png")
-             .spec(),
-         "')"});
-    EXPECT_TRUE(RunScriptExtractBool(load_image_url));
-    EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-    // The image should load closer to 2 seconds.
-    EXPECT_LT(base::Seconds(0.9), elapsed_timer.Elapsed());
-    EXPECT_GT(base::Seconds(2.6), elapsed_timer.Elapsed());
-  }
-
-  FetchHistogramsFromChildProcesses();
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 3);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 3);
-
-  // The next image should use longer robots rules fetch timeout duration,
-  // since this is past the first K limit.
-  base::ElapsedTimer elapsed_timer;
-  std::string load_image_url = base::StrCat(
-      {"loadNewImage('",
-       https_test_server_.GetURL("bar1.com", "/load_image/image.png").spec(),
-       "')"});
-  EXPECT_TRUE(RunScriptExtractBool(load_image_url));
-  EXPECT_TRUE(RunScriptExtractBool("checkImage()"));
-  // The image should load closer to 3 seconds.
-  EXPECT_LT(base::Seconds(2.9), elapsed_timer.Elapsed());
-
-  FetchHistogramsFromChildProcesses();
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 4);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 4);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  VerifyImageCompressionPageInfoState(true);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectLoginRobotsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestRobotsRulesFetchedInPreloadScanner)) {
-  CreateUkmRecorder();
-  robots_rules_server_.AddRobotsRules(
-      GetHttpsTestURL("/"),
-      {{kRuleTypeAllow, "/load_image/image.png"}, {kRuleTypeDisallow, ""}});
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), GetHttpsTestURL("/load_image/preload_scanner_image.html")));
-
-  // The robots rules will be fetched, but the image will not load.
-  RetryForHistogramUntilCountReached(
-      &histogram_tester_, "SubresourceRedirect.RobotsRulesFetcher.ResponseCode",
-      1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths({});
-
-  // Now start loading the image.
-  EXPECT_TRUE(
-      content::ExecJs(browser()->tab_strip_model()->GetActiveWebContents(),
-                      "loadBelowViewportImage()"));
-  EXPECT_EQ(true, EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
-                         "checkImage()"));
-  FetchHistogramsFromChildProcesses();
-
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kRedirectable, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", true, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png"});
-}
-
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectLoginRobotsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestRobotsRulesInMemoryCacheEviction)) {
-  std::set<std::string> expected_robots_rules_requests;
-  std::set<std::string> expected_compressed_image_requests;
-  robots_rules_server_.set_failure_mode(
-      RobotsRulesTestServer::FailureMode::kTimeout);
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), GetHttpsTestURL("/load_image/image.html")));
-  expected_robots_rules_requests.emplace(GetHttpsTestURL("/").spec());
-  expected_compressed_image_requests.emplace(
-      GetHttpsTestURL("/load_image/image.html").spec());
-
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit", false, 1);
-
-  // Load images from lot of different origins which will hit the robots
-  // rules in-memory cache limit specified by feature param
-  // max_robots_rules_parsers_cache_size.
-  for (int i = 0; i < kMaxRobotsRulesParsersCacheSize + 5; i++) {
-    GURL image_url = https_test_server_.GetURL(
-        base::StringPrintf("foo%d.com", i), "/load_image/image.png?allowed");
-    ASSERT_TRUE(
-        ExecJs(browser()->tab_strip_model()->GetActiveWebContents(),
-               content::JsReplace("document.images[0].src = $1;", image_url)));
-    expected_robots_rules_requests.emplace(image_url.GetWithEmptyPath().spec());
-    if (expected_compressed_image_requests.size() <=
-        kMaxRobotsRulesParsersCacheSize) {
-      expected_compressed_image_requests.emplace(image_url.spec());
-    }
-  }
-
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit", false,
-      kMaxRobotsRulesParsersCacheSize + 5 + 1);
-}
-
-// Verifies that the image is only compressed in low memory device with the low
-// memory feature flag enabled, and in non-low memory devices.
-IN_PROC_BROWSER_TEST_P(SubresourceRedirectLoginRobotsLowMemoryBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(TestLowMemoryDisableMode)) {
-  CreateUkmRecorder();
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeAllow, ""}});
-  NavigateAndWaitForLoad(browser(), GetHttpsTestURL("/load_image/image.html"));
-
-  if (!is_login_robots_for_low_memory_feature_enabled() &&
-      is_low_end_device()) {
-    // No compression when the low memory feature is disabled on low memory
-    // device
-    histogram_tester_.ExpectTotalCount(
-        "SubresourceRedirect.CompressionAttempt.ResponseCode", 0);
-    histogram_tester_.ExpectTotalCount(
-        "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-    histogram_tester_.ExpectTotalCount(
-        "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-    VerifyRobotsRulesFetch({});
-    image_compression_server_.VerifyRequestedImagePaths({});
-    VerifyImageCompressionPageInfoState(false);
-    return;
-  }
-
-  // Compression happens in all the other cases:
-  //  1. Low end device with low memory feature flag enabled.
-  //  3. Non-low end device.
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", true, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png"});
-
-  using ImageCompressionUkm = ukm::builders::PublicImageCompressionImageLoad;
-  auto ukm_metrics = GetImageCompressionUkmMetrics();
-  EXPECT_LT(100U, ukm_metrics[ImageCompressionUkm::kOriginalBytesNameHash]);
-  EXPECT_LT(10U,
-            ukm_metrics[ImageCompressionUkm::kCompressionPercentageNameHash]);
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToRequestStartNameHash)));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToRequestSentNameHash)));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToResponseReceivedNameHash)));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kRobotsRulesFetchLatencyNameHash)));
-  EXPECT_THAT(ukm_metrics, testing::Contains(testing::Key(
-                               ImageCompressionUkm::kRedirectResultNameHash)));
-  VerifyImageCompressionPageInfoState(true);
-}
-
-INSTANTIATE_TEST_SUITE_P(All,
-                         SubresourceRedirectLoginRobotsLowMemoryBrowserTest,
-                         testing::Combine(testing::Bool(), testing::Bool()));
-
-class SubresourceRedirectLoginRobotsFirstKDisableBrowserTest
-    : public SubresourceRedirectLoginRobotsBrowserTest,
-      public testing::WithParamInterface<std::tuple<bool, bool>> {
- public:
-  SubresourceRedirectLoginRobotsFirstKDisableBrowserTest()
-      : SubresourceRedirectLoginRobotsBrowserTest(
-            {{"first_k_disable_subresource_redirect_limit", "1"}},
-            true, /* enable_lite_mode */
-            true  /* enable_login_robots_compression_feature */
-        ) {}
-};
-
-// Test that first image is disallowed for compression and only the second image
-// is compressed.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectLoginRobotsFirstKDisableBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMEOS(TestFirstKImageDisallowed)) {
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeAllow, ""}});
-  NavigateAndWaitForLoad(browser(),
-                         GetHttpsTestURL("/load_image/two_images.html"));
-
-  EXPECT_TRUE(RunScriptExtractBool("checkBothImagesLoaded()"));
-  RetryForHistogramUntilCountReached(
-      &histogram_tester_,
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 2);
-
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kRedirectable, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleFirstKDisableSubresourceRedirect,
-      1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode", net::HTTP_OK, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths(
-      {"/load_image/image.png?foo"});
-  VerifyImageCompressionPageInfoState(true);
-}
-
-class SubresourceRedirectLoginRobotsJavascriptImageBrowserTest
-    : public SubresourceRedirectLoginRobotsBrowserTest,
-      public testing::WithParamInterface<
-          std::tuple<bool /* allow_javascript_crossorigin_images */,
-                     bool /* is_crossorigin_image */>> {
- public:
-  SubresourceRedirectLoginRobotsJavascriptImageBrowserTest()
-      : SubresourceRedirectLoginRobotsBrowserTest(
-            {{"allow_javascript_crossorigin_images",
-              allow_javascript_crossorigin_images() ? "true" : "false"}},
-            true, /* enable_lite_mode */
-            true  /* enable_login_robots_compression_feature */
-        ) {}
-
-  bool allow_javascript_crossorigin_images() const {
-    return std::get<0>(GetParam());
-  }
-  bool is_crossorigin_image() const { return std::get<1>(GetParam()); }
-};
-
-IN_PROC_BROWSER_TEST_P(
-    SubresourceRedirectLoginRobotsJavascriptImageBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestJavascriptCrossOriginImageAllowed)) {
-  bool is_compression_expected =
-      is_crossorigin_image() && allow_javascript_crossorigin_images();
-  GURL image_url =
-      is_crossorigin_image()
-          ? https_test_server_.GetURL("foo.com", "/load_image/image.png")
-          : GetHttpsTestURL("/load_image/image.png");
-
-  CreateUkmRecorder();
-  robots_rules_server_.AddRobotsRules(
-      image_url.GetWithEmptyPath(),
-      {{kRuleTypeAllow, "/load_image/image.png"}, {kRuleTypeDisallow, ""}});
-  ASSERT_TRUE(
-      ui_test_utils::NavigateToURL(browser(), GetHttpsTestURL("/simple.html")));
-
-  EXPECT_EQ(true, EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
-                         content::JsReplace(
-                             R"(
-    new Promise(resolve => {
-        const img = document.createElement("img");
-        img.onload = () => {
-            resolve(true);
-        }
-        img.src = $1;
-        document.body.appendChild(img);
-    });)",
-                             image_url)));
-  FetchHistogramsFromChildProcesses();
-
-  if (is_compression_expected) {
-    VerifyRobotsRulesFetch({image_url.GetWithEmptyPath().spec()});
-    VerifyCompressedImageFetch({"/load_image/image.png"});
-  } else {
-    VerifyRobotsRulesFetch({});
-    VerifyCompressedImageFetch({});
-  }
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    SubresourceRedirectLoginRobotsJavascriptImageBrowserTest,
-    testing::Combine(testing::Bool() /* allow_javascript_crossorigin_images */,
-                     testing::Bool() /* is_crossorigin_image */));
-
-// Disables the actual subresource redirect and enables only recording metrics.
-class SubresourceRedirectLoginRobotsRedirectDisabledBrowserTest
-    : public SubresourceRedirectLoginRobotsBrowserTest {
- public:
-  SubresourceRedirectLoginRobotsRedirectDisabledBrowserTest()
-      : SubresourceRedirectLoginRobotsBrowserTest(
-            {{"enable_subresource_server_redirect", "false"}},
-            true, /* enable_lite_mode */
-            true  /* enable_login_robots_compression_feature */
-        ) {}
-};
-
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectLoginRobotsRedirectDisabledBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestImageAllowedByRobots)) {
-  CreateUkmRecorder();
-  robots_rules_server_.AddRobotsRules(
-      GetHttpsTestURL("/"),
-      {{kRuleTypeAllow, "/load_image/image.png"}, {kRuleTypeDisallow, ""}});
-  NavigateAndWaitForLoad(browser(), GetHttpsTestURL("/load_image/image.html"));
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-
-  // The image will not be redirected.
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleCompressionDisabled, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-  image_compression_server_.VerifyRequestedImagePaths({});
-
-  // Image load UKM should get recorded.
-  using ImageCompressionUkm = ukm::builders::PublicImageCompressionImageLoad;
-  auto ukm_metrics = GetImageCompressionUkmMetrics();
-  EXPECT_LT(100U, ukm_metrics[ImageCompressionUkm::kOriginalBytesNameHash]);
-  EXPECT_THAT(ukm_metrics,
-              testing::Not(testing::Contains(testing::Key(
-                  ImageCompressionUkm::kCompressionPercentageNameHash))));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToRequestStartNameHash)));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToRequestSentNameHash)));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToResponseReceivedNameHash)));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kRobotsRulesFetchLatencyNameHash)));
-  EXPECT_EQ(SubresourceRedirectResult::kIneligibleCompressionDisabled,
-            static_cast<SubresourceRedirectResult>(
-                ukm_metrics[ImageCompressionUkm::kRedirectResultNameHash]));
-  VerifyImageCompressionPageInfoState(false);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    SubresourceRedirectLoginRobotsRedirectDisabledBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestImageDisallowedByRobots)) {
-  CreateUkmRecorder();
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeDisallow, ""}});
-  NavigateAndWaitForLoad(browser(), GetHttpsTestURL("/load_image/image.html"));
-
-  VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-
-  // The image will not be redirected.
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleRobotsDisallowed, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.ImageCompressionNotificationInfoBar", 0);
-  image_compression_server_.VerifyRequestedImagePaths({});
-
-  // Image load UKM should get recorded.
-  using ImageCompressionUkm = ukm::builders::PublicImageCompressionImageLoad;
-  auto ukm_metrics = GetImageCompressionUkmMetrics();
-  EXPECT_LT(100U, ukm_metrics[ImageCompressionUkm::kOriginalBytesNameHash]);
-  EXPECT_THAT(ukm_metrics,
-              testing::Not(testing::Contains(testing::Key(
-                  ImageCompressionUkm::kCompressionPercentageNameHash))));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToRequestStartNameHash)));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToRequestSentNameHash)));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kNavigationToResponseReceivedNameHash)));
-  EXPECT_THAT(ukm_metrics,
-              testing::Contains(testing::Key(
-                  ImageCompressionUkm::kRobotsRulesFetchLatencyNameHash)));
-  EXPECT_EQ(SubresourceRedirectResult::kIneligibleRobotsDisallowed,
-            static_cast<SubresourceRedirectResult>(
-                ukm_metrics[ImageCompressionUkm::kRedirectResultNameHash]));
-  VerifyImageCompressionPageInfoState(false);
-}
-
-class SubresourceRedirectLoginRobotsCSPRestrictedImageBrowserTest
-    : public SubresourceRedirectLoginRobotsBrowserTest,
-      public testing::WithParamInterface<bool> {
- public:
-  SubresourceRedirectLoginRobotsCSPRestrictedImageBrowserTest()
-      : SubresourceRedirectLoginRobotsBrowserTest(
-            {{"allow_csp_restricted_images",
-              allow_csp_restricted_images() ? "true" : "false"}},
-            true, /* enable_lite_mode */
-            true  /* enable_login_robots_compression_feature */
-        ) {}
-
-  bool allow_csp_restricted_images() const { return GetParam(); }
-};
-
-IN_PROC_BROWSER_TEST_P(
-    SubresourceRedirectLoginRobotsCSPRestrictedImageBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMEOS(TestDefaultSrcRestrictedImageAllowed)) {
-  CreateUkmRecorder();
-  robots_rules_server_.AddRobotsRules(
-      GetHttpsTestURL("/"),
-      {{kRuleTypeAllow, "/load_image/image.png"}, {kRuleTypeDisallow, ""}});
-  NavigateAndWaitForLoad(browser(),
-                         GetHttpsTestURL("/load_image/image_csp_img_src.html"));
-
-  if (allow_csp_restricted_images()) {
-    VerifyRobotsRulesFetch({GetHttpsTestURL("/").spec()});
-    VerifyCompressedImageFetch({"/load_image/image.png"});
-  } else {
-    VerifyRobotsRulesFetch({});
-    VerifyCompressedImageFetch({});
-  }
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    SubresourceRedirectLoginRobotsCSPRestrictedImageBrowserTest,
-    testing::Bool() /* allow_csp_restricted_images */);
-
-}  // namespace subresource_redirect
diff --git a/chrome/browser/data_saver/subresource_redirect_login_robots_unittest.cc b/chrome/browser/data_saver/subresource_redirect_login_robots_unittest.cc
deleted file mode 100644
index aae683d5..0000000
--- a/chrome/browser/data_saver/subresource_redirect_login_robots_unittest.cc
+++ /dev/null
@@ -1,107 +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 <string>
-
-#include "base/test/scoped_command_line.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
-#include "chrome/browser/data_use_measurement/chrome_data_use_measurement.h"
-#include "chrome/browser/login_detection/login_detection_util.h"
-#include "chrome/browser/subresource_redirect/subresource_redirect_observer.h"
-#include "chrome/test/base/chrome_render_view_host_test_harness.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
-#include "components/data_reduction_proxy/core/browser/data_store_impl.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
-#include "content/public/test/navigation_simulator.h"
-#include "content/public/test/test_renderer_host.h"
-#include "third_party/blink/public/common/features.h"
-#include "url/gurl.h"
-
-namespace subresource_redirect {
-
-class SubresourceRedirectLoginRobotsTest
-    : public ChromeRenderViewHostTestHarness {
- protected:
-  void SetUp() override {
-    command_line_.GetProcessCommandLine()->AppendSwitch(
-        data_reduction_proxy::switches::kOverrideHttpsImageCompressionInfobar);
-    feature_list_.InitWithFeaturesAndParameters(
-        {{blink::features::kSubresourceRedirect,
-          {{"enable_public_image_hints_based_compression", "false"},
-           {"enable_login_robots_based_compression", "true"},
-           {"enable_login_robots_for_low_memory", "true"}}},
-         {login_detection::kLoginDetection,
-          {{"logged_in_sites", "https://loggedin.test"}}}},
-        {});
-
-    ChromeRenderViewHostTestHarness::SetUp();
-
-    data_use_measurement::ChromeDataUseMeasurement::CreateInstance(
-        g_browser_process->local_state());
-    data_reduction_proxy::DataReductionProxySettings::
-        SetDataSaverEnabledForTesting(profile()->GetPrefs(), true);
-    auto* drp_settings =
-        DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
-            profile());
-    drp_settings->InitDataReductionProxySettings(
-        profile(),
-        std::make_unique<data_reduction_proxy::DataStoreImpl>(
-            profile()->GetPath()),
-        task_environment()->GetMainThreadTaskRunner());
-  }
-
-  std::unique_ptr<content::NavigationSimulator> CreateAndNavigateFrame(
-      const std::string& name,
-      const GURL& url) {
-    content::RenderFrameHost* rfh =
-        content::RenderFrameHostTester::For(main_rfh())->AppendChild(name);
-    return content::NavigationSimulator::CreateRendererInitiated(url, rfh);
-  }
-
-  bool CompressionWasAppliedTo(content::RenderFrameHost* rfh) {
-    return ImageCompressionAppliedDocument::GetState(rfh) ==
-           ImageCompressionAppliedDocument::kLoginRobotsCheckedEnabled;
-  }
-
- private:
-  base::test::ScopedCommandLine command_line_;
-  base::test::ScopedFeatureList feature_list_;
-};
-
-TEST_F(SubresourceRedirectLoginRobotsTest, RaceBetweenFrames) {
-  SetContents(CreateTestWebContents());
-  SubresourceRedirectObserver::MaybeCreateForWebContents(web_contents());
-  ASSERT_TRUE(SubresourceRedirectObserver::FromWebContents(web_contents()))
-      << "subresource redirect was not enabled";
-  content::NavigationSimulator::NavigateAndCommitFromBrowser(
-      web_contents(), GURL("https://main.test/"));
-  auto simulator_logged_in =
-      CreateAndNavigateFrame("loggedin", GURL("https://loggedin.test/"));
-  auto simulator_logged_out =
-      CreateAndNavigateFrame("loggedout", GURL("https://loggedout.test/"));
-
-  // Since these navigations are occurring at the same time, and the
-  // "did finish navigation" signal needs to wait for a renderer ack, it is
-  // possible though unlikely for these to arrive at nearly the same time.
-  //
-  // We deterministically force this to happen here, which requires the
-  // implementation to track per-navigation data, and not simply assume that
-  // each DidFinishNavigation corresponds to the last ReadyToCommit.
-  simulator_logged_in->ReadyToCommit();
-  simulator_logged_out->ReadyToCommit();
-  simulator_logged_in->Commit();
-  simulator_logged_out->Commit();
-
-  // Despite this race, compression should always be applied only to eligible
-  // documents.
-  EXPECT_FALSE(
-      CompressionWasAppliedTo(simulator_logged_in->GetFinalRenderFrameHost()));
-  EXPECT_TRUE(
-      CompressionWasAppliedTo(simulator_logged_out->GetFinalRenderFrameHost()));
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/browser/device_api/OWNERS b/chrome/browser/device_api/OWNERS
index a9f985c4..ebd242a 100644
--- a/chrome/browser/device_api/OWNERS
+++ b/chrome/browser/device_api/OWNERS
@@ -1,5 +1,9 @@
+# Primary owners
+bfranz@chromium.org
+
+# Backup owners
 apotapchuk@chromium.org
 anqing@chromium.org
 
-# Policy stack owners.
+# Policy stack owners
 file://chrome/browser/ash/policy/OWNERS
diff --git a/chrome/browser/device_api/device_service_unittest.cc b/chrome/browser/device_api/device_service_unittest.cc
index a0732ec..8c5f477 100644
--- a/chrome/browser/device_api/device_service_unittest.cc
+++ b/chrome/browser/device_api/device_service_unittest.cc
@@ -59,16 +59,16 @@
   }
 
   void InstallTrustedApp() {
-    ListPrefUpdate update(profile()->GetPrefs(),
-                          prefs::kWebAppInstallForceList);
+    ListPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                    prefs::kWebAppInstallForceList);
     base::DictionaryValue app_policy;
     app_policy.SetString(web_app::kUrlKey, kDefaultAppInstallUrl);
     update->Append(std::move(app_policy));
   }
 
   void RemoveTrustedApp() {
-    ListPrefUpdate update(profile()->GetPrefs(),
-                          prefs::kWebAppInstallForceList);
+    ListPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                    prefs::kWebAppInstallForceList);
     update->ClearList();
   }
 
@@ -81,8 +81,8 @@
   }
 
   void RemoveAllowedOrigin() {
-    ListPrefUpdate update(profile()->GetPrefs(),
-                          prefs::kDeviceAttributesAllowedForOrigins);
+    ListPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                    prefs::kDeviceAttributesAllowedForOrigins);
     update->ClearList();
   }
 
diff --git a/chrome/browser/device_api/managed_configuration_api.cc b/chrome/browser/device_api/managed_configuration_api.cc
index 3693259..1aaab000 100644
--- a/chrome/browser/device_api/managed_configuration_api.cc
+++ b/chrome/browser/device_api/managed_configuration_api.cc
@@ -315,8 +315,8 @@
     PostStoreConfiguration(origin, base::DictionaryValue());
     return;
   }
-  DictionaryPrefUpdate update(profile_->GetPrefs(),
-                              prefs::kLastManagedConfigurationHashForOrigin);
+  DictionaryPrefUpdateDeprecated update(
+      profile_->GetPrefs(), prefs::kLastManagedConfigurationHashForOrigin);
   update.Get()->SetStringKey(GetOriginEncoded(origin), url_hash);
 
   // We need to transform each value into a string.
diff --git a/chrome/browser/devtools/devtools_file_helper.cc b/chrome/browser/devtools/devtools_file_helper.cc
index 8ffee4fb..dff7c97 100644
--- a/chrome/browser/devtools/devtools_file_helper.cc
+++ b/chrome/browser/devtools/devtools_file_helper.cc
@@ -306,8 +306,8 @@
   *g_last_save_path.Pointer() = path;
   saved_files_[url] = path;
 
-  DictionaryPrefUpdate update(profile_->GetPrefs(),
-                              prefs::kDevToolsEditedFiles);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kDevToolsEditedFiles);
   base::DictionaryValue* files_map = update.Get();
   files_map->SetKey(base::MD5String(url), base::FilePathToValue(path));
   std::string file_system_path = path.AsUTF8Unsafe();
@@ -374,8 +374,8 @@
   std::string file_system_id = RegisterFileSystem(web_contents_, path);
   std::string file_system_path = path.AsUTF8Unsafe();
 
-  DictionaryPrefUpdate update(profile_->GetPrefs(),
-                              prefs::kDevToolsFileSystemPaths);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kDevToolsFileSystemPaths);
   base::DictionaryValue* file_systems_paths_value = update.Get();
   file_systems_paths_value->SetKey(file_system_path, base::Value(type));
 }
@@ -426,8 +426,8 @@
   base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system_path);
   isolated_context()->RevokeFileSystemByPath(path);
 
-  DictionaryPrefUpdate update(profile_->GetPrefs(),
-                              prefs::kDevToolsFileSystemPaths);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kDevToolsFileSystemPaths);
   base::DictionaryValue* file_systems_paths_value = update.Get();
   file_systems_paths_value->RemoveKey(file_system_path);
 }
diff --git a/chrome/browser/devtools/devtools_settings.cc b/chrome/browser/devtools/devtools_settings.cc
index a725c992..c183558c 100644
--- a/chrome/browser/devtools/devtools_settings.cc
+++ b/chrome/browser/devtools/devtools_settings.cc
@@ -54,13 +54,13 @@
       prefs->GetDictionary(dictionary_to_insert_into)->FindStringKey(name);
   if (dictionary_to_insert_into == prefs::kDevToolsPreferences ||
       !already_synced_value) {
-    DictionaryPrefUpdate insert_update(profile_->GetPrefs(),
-                                       dictionary_to_insert_into);
+    DictionaryPrefUpdateDeprecated insert_update(profile_->GetPrefs(),
+                                                 dictionary_to_insert_into);
     insert_update.Get()->SetKey(name, base::Value(*settings_value));
   }
 
-  DictionaryPrefUpdate remove_update(profile_->GetPrefs(),
-                                     dictionary_to_remove_from);
+  DictionaryPrefUpdateDeprecated remove_update(profile_->GetPrefs(),
+                                               dictionary_to_remove_from);
   remove_update.Get()->RemoveKey(name);
 }
 
@@ -101,8 +101,8 @@
     return;
   }
 
-  DictionaryPrefUpdate update(profile_->GetPrefs(),
-                              GetDictionaryNameForSettingsName(name));
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        GetDictionaryNameForSettingsName(name));
   update.Get()->SetKey(name, base::Value(value));
 }
 
@@ -113,21 +113,21 @@
     return;
   }
 
-  DictionaryPrefUpdate update(profile_->GetPrefs(),
-                              GetDictionaryNameForSettingsName(name));
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        GetDictionaryNameForSettingsName(name));
   update.Get()->RemoveKey(name);
 }
 
 void DevToolsSettings::Clear() {
   profile_->GetPrefs()->SetBoolean(prefs::kDevToolsSyncPreferences,
                                    kSyncDevToolsPreferencesDefault);
-  DictionaryPrefUpdate unsynced_update(profile_->GetPrefs(),
-                                       prefs::kDevToolsPreferences);
+  DictionaryPrefUpdateDeprecated unsynced_update(profile_->GetPrefs(),
+                                                 prefs::kDevToolsPreferences);
   unsynced_update.Get()->DictClear();
-  DictionaryPrefUpdate sync_enabled_update(
+  DictionaryPrefUpdateDeprecated sync_enabled_update(
       profile_->GetPrefs(), prefs::kDevToolsSyncedPreferencesSyncEnabled);
   sync_enabled_update.Get()->DictClear();
-  DictionaryPrefUpdate sync_disabled_update(
+  DictionaryPrefUpdateDeprecated sync_disabled_update(
       profile_->GetPrefs(), prefs::kDevToolsSyncedPreferencesSyncDisabled);
   sync_disabled_update.Get()->DictClear();
 }
@@ -169,9 +169,11 @@
       sync_enabled ? prefs::kDevToolsSyncedPreferencesSyncDisabled
                    : prefs::kDevToolsSyncedPreferencesSyncEnabled;
 
-  DictionaryPrefUpdate target_update(profile_->GetPrefs(), target_dictionary);
+  DictionaryPrefUpdateDeprecated target_update(profile_->GetPrefs(),
+                                               target_dictionary);
   target_update.Get()->MergeDictionary(
       profile_->GetPrefs()->GetDictionary(source_dictionary));
-  DictionaryPrefUpdate source_update(profile_->GetPrefs(), source_dictionary);
+  DictionaryPrefUpdateDeprecated source_update(profile_->GetPrefs(),
+                                               source_dictionary);
   source_update.Get()->DictClear();
 }
diff --git a/chrome/browser/devtools/devtools_settings_unittest.cc b/chrome/browser/devtools/devtools_settings_unittest.cc
index fe612d5..cde2148 100644
--- a/chrome/browser/devtools/devtools_settings_unittest.cc
+++ b/chrome/browser/devtools/devtools_settings_unittest.cc
@@ -102,8 +102,8 @@
   // 2) Simulate the update to synced plus setting of a new value on a
   //    different device.
   {
-    DictionaryPrefUpdate update(profile_.GetPrefs(),
-                                prefs::kDevToolsSyncedPreferencesSyncEnabled);
+    DictionaryPrefUpdateDeprecated update(
+        profile_.GetPrefs(), prefs::kDevToolsSyncedPreferencesSyncEnabled);
     update.Get()->SetKey("setting", base::Value("overwritten synced value"));
   }
 
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index 909bac7..42a76e66 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -133,7 +133,8 @@
   absl::optional<base::Value> parsed = base::JSONReader::Read(json);
   if (!parsed || !parsed->is_dict())
     return;
-  DictionaryPrefUpdate update(profile->GetPrefs(), prefs::kDevToolsPreferences);
+  DictionaryPrefUpdateDeprecated update(profile->GetPrefs(),
+                                        prefs::kDevToolsPreferences);
   for (auto dict_value : parsed->DictItems()) {
     if (!dict_value.second.is_string())
       continue;
@@ -1657,7 +1658,7 @@
            ->FindKey(kDevToolsApp)) {
     // Ensure there is always a default size so that
     // BrowserFrame::InitBrowserFrame can retrieve it later.
-    DictionaryPrefUpdate update(prefs, prefs::kAppWindowPlacement);
+    DictionaryPrefUpdateDeprecated update(prefs, prefs::kAppWindowPlacement);
     base::Value* wp_prefs = update.Get();
     base::Value dev_tools_defaults(base::Value::Type::DICTIONARY);
     dev_tools_defaults.SetIntKey("left", 100);
diff --git a/chrome/browser/download/download_prefs.cc b/chrome/browser/download/download_prefs.cc
index d352852..9182051d 100644
--- a/chrome/browser/download/download_prefs.cc
+++ b/chrome/browser/download/download_prefs.cc
@@ -579,6 +579,27 @@
   if (drivefs_mounted && drivefs.IsParent(path))
     return path;
 
+  // Allow paths for removable media devices.
+  base::FilePath removable_media_path;
+  if (chrome::GetRemovableMediaPath(&removable_media_path) &&
+      removable_media_path.IsParent(path)) {
+    return path;
+  }
+
+  // Allow paths under the Android files mount point.
+  base::FilePath android_files_path;
+  if (chrome::GetAndroidFilesPath(&android_files_path) &&
+      android_files_path.IsParent(path)) {
+    return path;
+  }
+
+  // Allow Linux files mount point and subdirs.
+  base::FilePath linux_files_path;
+  if (chrome::GetLinuxFilesPath(&linux_files_path) &&
+      (linux_files_path == path || linux_files_path.IsParent(path))) {
+    return path;
+  }
+
   // Otherwise, return the safe default.
   return default_downloads_path;
 #elif BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/download/download_prefs_unittest.cc b/chrome/browser/download/download_prefs_unittest.cc
index b26bdc0..780ca2d 100644
--- a/chrome/browser/download/download_prefs_unittest.cc
+++ b/chrome/browser/download/download_prefs_unittest.cc
@@ -22,8 +22,10 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/drive/drive_integration_service.h"
+#include "chrome/browser/ash/file_manager/path_util.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chromeos/dbus/cros_disks/cros_disks_client.h"
 #include "components/drive/drive_pref_names.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
@@ -192,8 +194,8 @@
 
   content::BrowserTaskEnvironment task_environment_;
   TestingProfile profile;
-  ListPrefUpdate update(profile.GetPrefs(),
-                        prefs::kDownloadExtensionsToOpenByPolicy);
+  ListPrefUpdateDeprecated update(profile.GetPrefs(),
+                                  prefs::kDownloadExtensionsToOpenByPolicy);
   update->Append("txt");
   DownloadPrefs prefs(&profile);
 
@@ -210,8 +212,8 @@
 
   content::BrowserTaskEnvironment task_environment_;
   TestingProfile profile;
-  ListPrefUpdate update(profile.GetPrefs(),
-                        prefs::kDownloadExtensionsToOpenByPolicy);
+  ListPrefUpdateDeprecated update(profile.GetPrefs(),
+                                  prefs::kDownloadExtensionsToOpenByPolicy);
   update->Append("exe");
   DownloadPrefs prefs(&profile);
   EXPECT_TRUE(prefs.EnableAutoOpenByUserBasedOnExtension(kFilePathType1));
@@ -229,8 +231,8 @@
 
   content::BrowserTaskEnvironment task_environment_;
   TestingProfile profile;
-  ListPrefUpdate update(profile.GetPrefs(),
-                        prefs::kDownloadExtensionsToOpenByPolicy);
+  ListPrefUpdateDeprecated update(profile.GetPrefs(),
+                                  prefs::kDownloadExtensionsToOpenByPolicy);
   update->Append("swf");
   DownloadPrefs prefs(&profile);
 
@@ -256,16 +258,16 @@
 
   // Update the policy preference.
   {
-    ListPrefUpdate update(profile.GetPrefs(),
-                          prefs::kDownloadExtensionsToOpenByPolicy);
+    ListPrefUpdateDeprecated update(profile.GetPrefs(),
+                                    prefs::kDownloadExtensionsToOpenByPolicy);
     update->Append("swf");
   }
   EXPECT_TRUE(prefs.IsAutoOpenEnabled(kURL, kDangerousFilePath));
 
   // Remove the policy and ensure the file stops auto-opening.
   {
-    ListPrefUpdate update(profile.GetPrefs(),
-                          prefs::kDownloadExtensionsToOpenByPolicy);
+    ListPrefUpdateDeprecated update(profile.GetPrefs(),
+                                    prefs::kDownloadExtensionsToOpenByPolicy);
     update->ClearList();
   }
   EXPECT_FALSE(prefs.IsAutoOpenEnabled(kURL, kDangerousFilePath));
@@ -278,11 +280,11 @@
 
   content::BrowserTaskEnvironment task_environment_;
   TestingProfile profile;
-  ListPrefUpdate update_type(profile.GetPrefs(),
-                             prefs::kDownloadExtensionsToOpenByPolicy);
+  ListPrefUpdateDeprecated update_type(
+      profile.GetPrefs(), prefs::kDownloadExtensionsToOpenByPolicy);
   update_type->Append("txt");
-  ListPrefUpdate update_url(profile.GetPrefs(),
-                            prefs::kDownloadAllowedURLsForOpenByPolicy);
+  ListPrefUpdateDeprecated update_url(
+      profile.GetPrefs(), prefs::kDownloadAllowedURLsForOpenByPolicy);
   update_url->Append("basic.com");
   DownloadPrefs prefs(&profile);
 
@@ -298,8 +300,8 @@
 
   content::BrowserTaskEnvironment task_environment_;
   TestingProfile profile;
-  ListPrefUpdate update_type(profile.GetPrefs(),
-                             prefs::kDownloadExtensionsToOpenByPolicy);
+  ListPrefUpdateDeprecated update_type(
+      profile.GetPrefs(), prefs::kDownloadExtensionsToOpenByPolicy);
   update_type->Append("txt");
   DownloadPrefs prefs(&profile);
 
@@ -309,8 +311,8 @@
 
   // Update the policy preference to only allow |kAllowedURL|.
   {
-    ListPrefUpdate update_url(profile.GetPrefs(),
-                              prefs::kDownloadAllowedURLsForOpenByPolicy);
+    ListPrefUpdateDeprecated update_url(
+        profile.GetPrefs(), prefs::kDownloadAllowedURLsForOpenByPolicy);
     update_url->Append("basic.com");
   }
 
@@ -319,8 +321,8 @@
 
   // Remove the policy and ensure both auto-open again.
   {
-    ListPrefUpdate update_url(profile.GetPrefs(),
-                              prefs::kDownloadAllowedURLsForOpenByPolicy);
+    ListPrefUpdateDeprecated update_url(
+        profile.GetPrefs(), prefs::kDownloadAllowedURLsForOpenByPolicy);
     update_url->ClearList();
   }
   EXPECT_TRUE(prefs.IsAutoOpenByPolicy(kAllowedURL, kFilePath));
@@ -339,8 +341,8 @@
 
   content::BrowserTaskEnvironment task_environment_;
   TestingProfile profile;
-  ListPrefUpdate update_type(profile.GetPrefs(),
-                             prefs::kDownloadExtensionsToOpenByPolicy);
+  ListPrefUpdateDeprecated update_type(
+      profile.GetPrefs(), prefs::kDownloadExtensionsToOpenByPolicy);
   update_type->Append("txt");
   DownloadPrefs prefs(&profile);
 
@@ -352,8 +354,8 @@
 
   // Update the policy preference to only allow |kAllowedURL|.
   {
-    ListPrefUpdate update_url(profile.GetPrefs(),
-                              prefs::kDownloadAllowedURLsForOpenByPolicy);
+    ListPrefUpdateDeprecated update_url(
+        profile.GetPrefs(), prefs::kDownloadAllowedURLsForOpenByPolicy);
     update_url->Append("basic.com");
   }
 
@@ -427,6 +429,32 @@
   DownloadPrefs prefs(&profile);
   const base::FilePath default_dir =
       prefs.GetDefaultDownloadDirectoryForProfile();
+  AccountId account_id =
+      AccountId::FromUserEmailGaiaId(profile.GetProfileUserName(), "12345");
+  const std::string drivefs_profile_salt = "a";
+  base::FilePath removable_media_dir;
+  base::FilePath android_files_dir;
+  base::FilePath linux_files_dir;
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  removable_media_dir = chromeos::CrosDisksClient::GetRemovableDiskMountPoint();
+  android_files_dir = base::FilePath(file_manager::util::kAndroidFilesPath);
+  linux_files_dir = file_manager::util::GetCrostiniMountDirectory(&profile);
+#elif BUILDFLAG(IS_CHROMEOS_LACROS)
+  // These values would normally be sent by ash during lacros startup.
+  base::FilePath documents_path =
+      base::PathService::CheckedGet(chrome::DIR_USER_DOCUMENTS);
+  removable_media_dir = base::FilePath("/media/removable");
+  android_files_dir = base::FilePath("/run/arc/sdcard/write/emulated/0");
+  linux_files_dir =
+      base::FilePath("/media/fuse/crostini_0123456789abcdef_termina_penguin");
+  const base::FilePath drivefs_dir = base::FilePath(
+      "/media/fuse/drivefs-" + base::MD5String(drivefs_profile_salt + "-" +
+                                               account_id.GetAccountIdKey()));
+  chrome::SetLacrosDefaultPaths(documents_path, default_dir, drivefs_dir,
+                                removable_media_dir, android_files_dir,
+                                linux_files_dir);
+#endif
 
   // Test a valid subdirectory of downloads.
   ExpectValidDownloadDir(&profile, &prefs, default_dir.AppendASCII("testdir"));
@@ -435,13 +463,11 @@
   // Test a valid subdirectory of documents. This isn't tested for ash because
   // these tests run on the linux "emulator", where ash uses ~/Documents, but
   // the ash path sanitization code doesn't handle that path.
-  base::FilePath documents_path =
-      base::PathService::CheckedGet(chrome::DIR_USER_DOCUMENTS);
   ExpectValidDownloadDir(&profile, &prefs,
                          documents_path.AppendASCII("testdir"));
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
-  // Test with an invalid path outside the download directory or drivefs.
+  // Test with an invalid path outside the permitted paths.
   profile.GetPrefs()->SetString(prefs::kDownloadDefaultDirectory,
                                 "/home/chronos");
   EXPECT_EQ(prefs.DownloadPath(), default_dir);
@@ -452,32 +478,35 @@
                                 parent_reference.value());
   EXPECT_EQ(prefs.DownloadPath(), default_dir);
 
-  // TODO(https://crbug.com/1148848): Sort out path sanitization for Lacros.
-  // Once the ash-only code can be shared the tests below can be enabled.
-#if BUILDFLAG(IS_CHROMEOS_ASH)
   // Test a valid path for Android files.
-  ExpectValidDownloadDir(
-      &profile, &prefs,
-      base::FilePath("/run/arc/sdcard/write/emulated/0/Documents"));
+  ExpectValidDownloadDir(&profile, &prefs,
+                         android_files_dir.AppendASCII("Documents"));
+  // Test with an invalid path for Android files (can't directly download to
+  // "Android Files").
+  profile.GetPrefs()->SetString(prefs::kDownloadDefaultDirectory,
+                                android_files_dir.value());
+  EXPECT_EQ(prefs.DownloadPath(), default_dir);
 
   // Linux files root.
-  ExpectValidDownloadDir(
-      &profile, &prefs,
-      base::FilePath("/media/fuse/crostini_0123456789abcdef_termina_penguin"));
+  ExpectValidDownloadDir(&profile, &prefs, linux_files_dir);
   // Linux files/testdir.
-  ExpectValidDownloadDir(
-      &profile, &prefs,
-      base::FilePath(
-          "/media/fuse/crostini_0123456789abcdef_termina_penguin/testdir"));
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+  ExpectValidDownloadDir(&profile, &prefs,
+                         linux_files_dir.AppendASCII("testdir"));
+
+  // Test with a valid path for Removable media.
+  ExpectValidDownloadDir(&profile, &prefs,
+                         removable_media_dir.AppendASCII("MY_USB_KEY"));
+  // Test with an invalid path for Removable media (must have a disk
+  // sub-directory).
+  profile.GetPrefs()->SetString(prefs::kDownloadDefaultDirectory,
+                                removable_media_dir.value());
+  EXPECT_EQ(prefs.DownloadPath(), default_dir);
+
   // DriveFS
   {
     // Create new profile for enabled feature to work.
     TestingProfile profile2(base::FilePath("/home/chronos/u-0123456789abcdef"));
     DownloadPrefs prefs2(&profile2);
-    AccountId account_id =
-        AccountId::FromUserEmailGaiaId(profile2.GetProfileUserName(), "12345");
-    const std::string drivefs_profile_salt = "a";
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     ash::FakeChromeUserManager user_manager;
     const auto* user = user_manager.AddUser(account_id);
@@ -490,17 +519,7 @@
     auto* integration_service =
         drive::DriveIntegrationServiceFactory::GetForProfile(&profile2);
     integration_service->SetEnabled(true);
-#elif BUILDFLAG(IS_CHROMEOS_LACROS)
-    base::FilePath documents_dir;
-    base::PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir);
-    base::FilePath downloads_dir;
-    base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &downloads_dir);
-    const base::FilePath drivefs = base::FilePath(
-        "/media/fuse/drivefs-" + base::MD5String(drivefs_profile_salt + "-" +
-                                                 account_id.GetAccountIdKey()));
-    chrome::SetLacrosDefaultPaths(/*documents_dir=*/documents_dir,
-                                  /*downloads_dir=*/downloads_dir, drivefs);
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
     // My Drive root.
     ExpectValidDownloadDir(
diff --git a/chrome/browser/engagement/important_sites_util.cc b/chrome/browser/engagement/important_sites_util.cc
index 01bdb48..65fcb76 100644
--- a/chrome/browser/engagement/important_sites_util.cc
+++ b/chrome/browser/engagement/important_sites_util.cc
@@ -410,7 +410,8 @@
 
 bool ImportantSitesUtil::IsDialogDisabled(Profile* profile) {
   PrefService* service = profile->GetPrefs();
-  DictionaryPrefUpdate update(service, prefs::kImportantSitesDialogHistory);
+  DictionaryPrefUpdateDeprecated update(service,
+                                        prefs::kImportantSitesDialogHistory);
 
   return ShouldSuppressItem(update.Get());
 }
@@ -551,7 +552,8 @@
   } else {
     // Record that the user did not interact with the dialog.
     PrefService* service = profile->GetPrefs();
-    DictionaryPrefUpdate update(service, prefs::kImportantSitesDialogHistory);
+    DictionaryPrefUpdateDeprecated update(service,
+                                          prefs::kImportantSitesDialogHistory);
     RecordIgnore(update.Get());
   }
 
diff --git a/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.cc b/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.cc
index 402094f..9c21d7f 100644
--- a/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.cc
+++ b/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.cc
@@ -94,6 +94,14 @@
       custom_message_data_[*tag] = data;
     }
   }
+
+  const base::Value* require_justification_tags =
+      settings_value.FindListKey(kKeyRequireJustificationTags);
+  if (require_justification_tags && require_justification_tags->is_list()) {
+    for (const base::Value& tag : require_justification_tags->GetList()) {
+      tags_requiring_justification_.insert(tag.GetString());
+    }
+  }
 }
 
 // static
@@ -142,6 +150,7 @@
   DCHECK(settings.analysis_url.is_valid());
   settings.minimum_data_size = minimum_data_size_;
   settings.custom_message_data = custom_message_data_;
+  settings.tags_requiring_justification = tags_requiring_justification_;
 
   return settings;
 }
diff --git a/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.h b/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.h
index 6a96719f..0670ce0 100644
--- a/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.h
+++ b/chrome/browser/enterprise/connectors/analysis/analysis_service_settings.h
@@ -110,6 +110,7 @@
   // A map from tag (dlp, malware, etc) to the custom message and "learn more"
   // link associated with it.
   std::map<std::string, CustomMessageData> custom_message_data_;
+  std::set<std::string> tags_requiring_justification_;
   std::string service_provider_name_;
 };
 
diff --git a/chrome/browser/enterprise/connectors/analysis/analysis_service_settings_unittest.cc b/chrome/browser/enterprise/connectors/analysis/analysis_service_settings_unittest.cc
index 3db5eab..917680c 100644
--- a/chrome/browser/enterprise/connectors/analysis/analysis_service_settings_unittest.cc
+++ b/chrome/browser/enterprise/connectors/analysis/analysis_service_settings_unittest.cc
@@ -114,6 +114,25 @@
   ],
 })";
 
+constexpr char kNormalSettingsDlpRequiresBypassJustification[] = R"({
+  "service_provider": "google",
+  "enable": [
+    {"url_list": ["*"], "tags": ["dlp", "malware"]},
+  ],
+  "disable": [
+    {"url_list": ["no.dlp.com", "no.dlp.or.malware.ca"], "tags": ["dlp"]},
+    {"url_list": ["no.malware.com", "no.dlp.or.malware.ca"],
+         "tags": ["malware"]},
+    {"url_list": ["scan2.com"], "tags": ["dlp", "malware"]},
+  ],
+  "block_until_verdict": 1,
+  "block_password_protected": true,
+  "block_large_files": true,
+  "block_unsupported_file_types": true,
+  "minimum_data_size": 123,
+  "require_justification_tags": ["dlp"],
+})";
+
 constexpr char kScan1DotCom[] = "https://scan1.com";
 constexpr char kScan2DotCom[] = "https://scan2.com";
 constexpr char kNoDlpDotCom[] = "https://no.dlp.com";
@@ -175,6 +194,15 @@
   return settings.get();
 }
 
+AnalysisSettings* NormalSettingsDlpRequiresBypassJustification() {
+  static base::NoDestructor<AnalysisSettings> settings([]() {
+    AnalysisSettings settings = NormalSettingsWithTags({"dlp", "malware"});
+    settings.tags_requiring_justification = {"dlp"};
+    return settings;
+  }());
+  return settings.get();
+}
+
 AnalysisSettings* NoSettings() {
   return nullptr;
 }
@@ -237,6 +265,9 @@
       ASSERT_EQ(kExpectedLearnMoreUrlSpecs.at(entry.first),
                 service_settings.GetLearnMoreUrl(entry.first).value().spec());
     }
+
+    ASSERT_EQ(analysis_settings.value().tags_requiring_justification,
+              expected_settings()->tags_requiring_justification);
   }
 }
 
@@ -281,6 +312,9 @@
                   NoSettings()),
         TestParam(kScan1DotCom,
                   kNormalSettingsWithCustomMessage,
-                  NormalSettingsWithCustomMessage())));
+                  NormalSettingsWithCustomMessage()),
+        TestParam(kScan1DotCom,
+                  kNormalSettingsDlpRequiresBypassJustification,
+                  NormalSettingsDlpRequiresBypassJustification())));
 
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
index b4a25ef..68293003 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
@@ -164,7 +164,8 @@
 
 ContentAnalysisDelegate::~ContentAnalysisDelegate() = default;
 
-void ContentAnalysisDelegate::BypassWarnings() {
+void ContentAnalysisDelegate::BypassWarnings(
+    absl::optional<std::u16string> user_justification) {
   if (callback_.is_null())
     return;
 
@@ -237,6 +238,16 @@
   return absl::nullopt;
 }
 
+bool ContentAnalysisDelegate::BypassRequiresJustification() const {
+  return data_.settings.tags_requiring_justification.count(final_result_tag_) >
+         0;
+}
+
+std::u16string ContentAnalysisDelegate::GetBypassJustificationLabel() const {
+  return l10n_util::GetStringUTF16(
+      IDS_DEEP_SCANNING_DIALOG_UPLOAD_BYPASS_JUSTIFICATION_LABEL);
+}
+
 absl::optional<std::u16string>
 ContentAnalysisDelegate::OverrideCancelButtonText() const {
   return absl::nullopt;
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h
index 628ddbe8..f1811168 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h
@@ -158,7 +158,8 @@
 
   // Called when the user decides to bypass the verdict they obtained from DLP.
   // This will allow the upload of files marked as DLP warnings.
-  void BypassWarnings() override;
+  void BypassWarnings(
+      absl::optional<std::u16string> user_justification) override;
 
   // Called when the user decides to cancel the file upload. This will stop the
   // upload to Chrome since the scan wasn't allowed to complete. If |warning| is
@@ -170,6 +171,10 @@
 
   absl::optional<GURL> GetCustomLearnMoreUrl() const override;
 
+  bool BypassRequiresJustification() const override;
+
+  std::u16string GetBypassJustificationLabel() const override;
+
   absl::optional<std::u16string> OverrideCancelButtonText() const override;
 
   // Returns true if the deep scanning feature is enabled in the upload
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_base.h b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_base.h
index f6ed2059..3959c45 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_base.h
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate_base.h
@@ -34,7 +34,8 @@
   virtual ~ContentAnalysisDelegateBase() = default;
 
   // Called when the user decides to bypass the verdict they obtained from DLP.
-  virtual void BypassWarnings() = 0;
+  virtual void BypassWarnings(
+      absl::optional<std::u16string> user_justification) = 0;
 
   // Called when the user hits "cancel" on the dialog, typically cancelling a
   // pending file transfer.
@@ -48,6 +49,12 @@
   // the dialog, or absl::nullopt if there isn't any.
   virtual absl::optional<GURL> GetCustomLearnMoreUrl() const = 0;
 
+  // Returns true if the final verdict is from a type of analysis that requires
+  // user justification to bypass, as per the connector policy.
+  virtual bool BypassRequiresJustification() const = 0;
+
+  virtual std::u16string GetBypassJustificationLabel() const = 0;
+
   // Returns the text to display on the "cancel" button in the dialog, or
   // absl::nullopt if no text is specified by this delegate. Takes precedence
   // over any other text that would be chosen by the dialog.
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc
index 5687457..5178c975 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc
@@ -37,7 +37,7 @@
 #include "ui/views/bubble/bubble_frame_view.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/link.h"
-#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/controls/textarea/textarea.h"
 #include "ui/views/controls/throbber.h"
 #include "ui/views/layout/box_layout_view.h"
 #include "ui/views/layout/fill_layout.h"
@@ -62,6 +62,7 @@
 constexpr int kMessageAndIconRowLeadingPadding = 32;
 constexpr int kMessageAndIconRowTrailingPadding = 48;
 constexpr int kSideIconBetweenChildSpacing = 16;
+constexpr int kPaddingBeforeBypassJustification = 16;
 
 // These time values are non-const in order to be overridden in test so they
 // complete faster.
@@ -232,7 +233,10 @@
 void ContentAnalysisDialog::AcceptButtonCallback() {
   DCHECK(delegate_);
   DCHECK(is_warning());
-  delegate_->BypassWarnings();
+  absl::optional<std::u16string> justification = absl::nullopt;
+  if (delegate_->BypassRequiresJustification())
+    justification = bypass_justification_->GetText();
+  delegate_->BypassWarnings(justification);
 }
 
 void ContentAnalysisDialog::CancelButtonCallback() {
@@ -263,6 +267,15 @@
 #endif
 }
 
+void ContentAnalysisDialog::ContentsChanged(
+    views::Textfield* sender,
+    const std::u16string& new_contents) {
+  if (new_contents.size() == 0)
+    DialogDelegate::SetButtonEnabled(ui::DIALOG_BUTTON_OK, false);
+  else
+    DialogDelegate::SetButtonEnabled(ui::DIALOG_BUTTON_OK, true);
+}
+
 bool ContentAnalysisDialog::ShouldShowCloseButton() const {
   return false;
 }
@@ -297,7 +310,7 @@
                    views::TableLayout::ColumnSize::kUsePreferred, 0, 0)
         .AddPaddingColumn(views::TableLayout::kFixedSize,
                           kMessageAndIconRowTrailingPadding)
-        .AddRows(2, views::TableLayout::kFixedSize);
+        .AddRows(4, views::TableLayout::kFixedSize);
 
     // Add the side icon.
     message_container->AddChildView(CreateSideIcon());
@@ -324,6 +337,28 @@
         base::Unretained(this)));
     learn_more_link_->SetVisible(false);
 
+    message_container->AddChildView(
+        std::make_unique<views::View>());  // Skip a column
+    justification_text_label_ =
+        message_container->AddChildView(std::make_unique<views::Label>());
+    justification_text_label_->SetText(
+        delegate_->GetBypassJustificationLabel());
+    justification_text_label_->SetBorder(
+        views::CreateEmptyBorder(kPaddingBeforeBypassJustification, 0, 0, 0));
+    justification_text_label_->SetLineHeight(kLineHeight);
+    justification_text_label_->SetMultiLine(true);
+    justification_text_label_->SetVerticalAlignment(gfx::ALIGN_MIDDLE);
+    justification_text_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    justification_text_label_->SetVisible(false);
+
+    message_container->AddChildView(
+        std::make_unique<views::View>());  // Skip a column
+    bypass_justification_ =
+        message_container->AddChildView(std::make_unique<views::Textarea>());
+    bypass_justification_->SetAssociatedLabel(justification_text_label_);
+    bypass_justification_->SetController(this);
+    bypass_justification_->SetVisible(false);
+
     // If the dialog was started in a state other than pending, setup the views
     // accordingly.
     if (!is_pending())
@@ -422,6 +457,10 @@
   // display.
   learn_more_link_->SetVisible((is_failure() || is_warning()) &&
                                has_learn_more_url());
+  justification_text_label_->SetVisible(is_warning() &&
+                                        bypass_requires_justification());
+  bypass_justification_->SetVisible(is_warning() &&
+                                    bypass_requires_justification());
 }
 
 void ContentAnalysisDialog::UpdateDialog() {
@@ -519,6 +558,9 @@
     DialogDelegate::SetAcceptCallback(
         base::BindOnce(&ContentAnalysisDialog::AcceptButtonCallback,
                        weak_ptr_factory_.GetWeakPtr()));
+
+    if (delegate_->BypassRequiresJustification())
+      DialogDelegate::SetButtonEnabled(ui::DIALOG_BUTTON_OK, false);
   } else if (is_failure() || is_pending()) {
     // Include the Cancel button when the scan is pending or failing.
     DialogDelegate::SetButtons(ui::DIALOG_BUTTON_CANCEL);
@@ -754,4 +796,14 @@
   return message_;
 }
 
+views::Label* ContentAnalysisDialog::GetBypassJustificationLabelForTesting()
+    const {
+  return justification_text_label_;
+}
+
+views::Textarea*
+ContentAnalysisDialog::GetBypassJustificationTextareaForTesting() const {
+  return bypass_justification_;
+}
+
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.h b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.h
index 731ec42..f8010c12 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.h
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.h
@@ -15,6 +15,7 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "ui/views/animation/bounds_animator.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/controls/textfield/textfield_controller.h"
 #include "ui/views/window/dialog_delegate.h"
 
 namespace content {
@@ -30,6 +31,7 @@
 class ImageView;
 class Label;
 class Link;
+class Textarea;
 class Throbber;
 class Widget;
 }  // namespace views
@@ -42,7 +44,8 @@
 // Dialog shown for Deep Scanning to offer the possibility of cancelling the
 // upload to the user.
 class ContentAnalysisDialog : public views::DialogDelegate,
-                              public content::WebContentsObserver {
+                              public content::WebContentsObserver,
+                              public views::TextfieldController {
  public:
   // TestObserver should be implemented by tests that need to track when certain
   // ContentAnalysisDialog functions are called. The test can add itself as an
@@ -127,6 +130,10 @@
     return delegate_->GetCustomLearnMoreUrl().has_value();
   }
 
+  bool bypass_requires_justification() const {
+    return delegate_->BypassRequiresJustification();
+  }
+
   // Returns the side image's logo color depending on |dialog_state_|.
   SkColor GetSideImageLogoColor() const;
 
@@ -141,6 +148,8 @@
   views::ImageView* GetTopImageForTesting() const;
   views::Throbber* GetSideIconSpinnerForTesting() const;
   views::Label* GetMessageForTesting() const;
+  views::Label* GetBypassJustificationLabelForTesting() const;
+  views::Textarea* GetBypassJustificationTextareaForTesting() const;
 
  private:
   // Friend the unit test class for this so it can call the private dtor.
@@ -228,6 +237,10 @@
   // ensure the auto-closing success dialog handles focus correctly.
   void SuccessCallback();
 
+  // views::TextfieldController:
+  void ContentsChanged(views::Textfield* sender,
+                       const std::u16string& new_contents) override;
+
   std::unique_ptr<ContentAnalysisDelegateBase> delegate_;
 
   raw_ptr<content::WebContents> web_contents_;
@@ -239,6 +252,8 @@
   raw_ptr<DeepScanningSideIconSpinnerView> side_icon_spinner_ = nullptr;
   raw_ptr<views::Label> message_ = nullptr;
   raw_ptr<views::Link> learn_more_link_ = nullptr;
+  raw_ptr<views::Label> justification_text_label_ = nullptr;
+  raw_ptr<views::Textarea> bypass_justification_ = nullptr;
 
   base::TimeTicks first_shown_timestamp_;
 
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc
index 48b6cd24..68bd0389 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog_browsertest.cc
@@ -24,6 +24,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/views/controls/image_view.h"
+#include "ui/views/controls/textarea/textarea.h"
 #include "ui/views/controls/throbber.h"
 #include "ui/views/test/ax_event_counter.h"
 
@@ -729,7 +730,8 @@
   class MockDelegate : public ContentAnalysisDelegateBase {
    public:
     ~MockDelegate() override = default;
-    void BypassWarnings() override {}
+    void BypassWarnings(
+        absl::optional<std::u16string> user_justification) override {}
     void Cancel(bool warning) override {}
 
     absl::optional<std::u16string> GetCustomMessage() const override {
@@ -740,9 +742,24 @@
       return absl::nullopt;
     }
 
+    bool BypassRequiresJustification() const override {
+      return bypass_requires_justification_;
+    }
+
+    std::u16string GetBypassJustificationLabel() const override {
+      return u"MOCK_BYPASS_JUSTIFICATION_LABEL";
+    }
+
     absl::optional<std::u16string> OverrideCancelButtonText() const override {
       return absl::nullopt;
     }
+
+    void SetBypassRequiresJustification(bool value) {
+      bypass_requires_justification_ = value;
+    }
+
+   private:
+    bool bypass_requires_justification_ = false;
   };
 
   class MockCustomMessageDelegate : public ContentAnalysisDelegateBase {
@@ -752,7 +769,8 @@
 
     ~MockCustomMessageDelegate() override = default;
 
-    void BypassWarnings() override {}
+    void BypassWarnings(
+        absl::optional<std::u16string> user_justification) override {}
     void Cancel(bool warning) override {}
 
     absl::optional<std::u16string> GetCustomMessage() const override {
@@ -763,6 +781,11 @@
       return learn_more_url_;
     }
 
+    bool BypassRequiresJustification() const override { return false; }
+    std::u16string GetBypassJustificationLabel() const override {
+      return u"MOCK_BYPASS_JUSTIFICATION_LABEL";
+    }
+
     absl::optional<std::u16string> OverrideCancelButtonText() const override {
       return absl::nullopt;
     }
@@ -807,10 +830,28 @@
       std::move(delegate), ContentAnalysisDelegateBase::FinalResult::SUCCESS);
   dialog->ShowResult(ContentAnalysisDelegateBase::FinalResult::WARNING);
 
+  EXPECT_TRUE(dialog->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
   EXPECT_EQ(dialog->GetMessageForTesting()->GetText(), u"Test");
 }
 
 IN_PROC_BROWSER_TEST_F(ContentAnalysisDialogPlainTests,
+                       TestBypassJustification) {
+  enterprise_connectors::ContentAnalysisDialog::
+      SetMinimumPendingDialogTimeForTesting(base::Milliseconds(0));
+
+  std::unique_ptr<MockDelegate> delegate = std::make_unique<MockDelegate>();
+  delegate->SetBypassRequiresJustification(true);
+  ContentAnalysisDialog* dialog = CreateContentAnalysisDialog(
+      std::move(delegate), ContentAnalysisDelegateBase::FinalResult::SUCCESS);
+  dialog->ShowResult(ContentAnalysisDelegateBase::FinalResult::WARNING);
+
+  EXPECT_FALSE(dialog->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
+  dialog->GetBypassJustificationTextareaForTesting()->InsertOrReplaceText(
+      u"test");
+  EXPECT_TRUE(dialog->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
+}
+
+IN_PROC_BROWSER_TEST_F(ContentAnalysisDialogPlainTests,
                        TestOpenInDefaultPendingState) {
   ContentAnalysisDialog* dialog =
       CreateContentAnalysisDialog(std::make_unique<MockDelegate>());
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_downloads_delegate.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_downloads_delegate.cc
index 7f9d803..c142d638 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_downloads_delegate.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_downloads_delegate.cc
@@ -25,7 +25,8 @@
 
 ContentAnalysisDownloadsDelegate::~ContentAnalysisDownloadsDelegate() = default;
 
-void ContentAnalysisDownloadsDelegate::BypassWarnings() {
+void ContentAnalysisDownloadsDelegate::BypassWarnings(
+    absl::optional<std::u16string> user_justification) {
   if (open_file_callback_)
     std::move(open_file_callback_).Run();
   ResetCallbacks();
@@ -58,6 +59,19 @@
   return custom_learn_more_url_;
 }
 
+bool ContentAnalysisDownloadsDelegate::BypassRequiresJustification() const {
+  return false;
+}
+
+std::u16string ContentAnalysisDownloadsDelegate::GetBypassJustificationLabel()
+    const {
+  // TODO(crbug.com/1278000): Change this to a downloads-specific string when
+  // this feature is supported for downloads. Returning a non-empty string is
+  // required to avoid a DCHECK in the a11y code that looks for an a11y label.
+  return l10n_util::GetStringUTF16(
+      IDS_DEEP_SCANNING_DIALOG_UPLOAD_BYPASS_JUSTIFICATION_LABEL);
+}
+
 absl::optional<std::u16string>
 ContentAnalysisDownloadsDelegate::OverrideCancelButtonText() const {
   return l10n_util::GetStringUTF16(
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_downloads_delegate.h b/chrome/browser/enterprise/connectors/analysis/content_analysis_downloads_delegate.h
index 3d8bcf1..b861422 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_downloads_delegate.h
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_downloads_delegate.h
@@ -26,7 +26,8 @@
   // Called when the user opts to keep the download and open it. Should not be
   // called if the result was a "block" since the option shouldn't be available
   // in that case.
-  void BypassWarnings() override;
+  void BypassWarnings(
+      absl::optional<std::u16string> user_justification) override;
 
   // Called when the user opts to delete the downloaded file and not open it.
   void Cancel(bool warning) override;
@@ -35,6 +36,9 @@
 
   absl::optional<GURL> GetCustomLearnMoreUrl() const override;
 
+  bool BypassRequiresJustification() const override;
+  std::u16string GetBypassJustificationLabel() const override;
+
   absl::optional<std::u16string> OverrideCancelButtonText() const override;
 
  private:
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_downloads_delegate_unittest.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_downloads_delegate_unittest.cc
index a39bf01..52cb82c 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_downloads_delegate_unittest.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_downloads_delegate_unittest.cc
@@ -26,12 +26,12 @@
       base::BindOnce(&ContentAnalysisDownloadsDelegateTest::DiscardCallback,
                      base::Unretained(this)));
 
-  delegate.BypassWarnings();
+  delegate.BypassWarnings(absl::nullopt);
   EXPECT_EQ(1, times_open_called_);
   EXPECT_EQ(0, times_discard_called_);
 
   // Attempting any action after one has been performed is a no-op.
-  delegate.BypassWarnings();
+  delegate.BypassWarnings(absl::nullopt);
   EXPECT_EQ(1, times_open_called_);
   EXPECT_EQ(0, times_discard_called_);
 
@@ -65,7 +65,7 @@
   EXPECT_EQ(0, times_open_called_);
   EXPECT_EQ(1, times_discard_called_);
 
-  delegate.BypassWarnings();
+  delegate.BypassWarnings(absl::nullopt);
   EXPECT_EQ(0, times_open_called_);
   EXPECT_EQ(1, times_discard_called_);
 }
@@ -91,7 +91,7 @@
   EXPECT_EQ(0, times_open_called_);
   EXPECT_EQ(1, times_discard_called_);
 
-  delegate.BypassWarnings();
+  delegate.BypassWarnings(absl::nullopt);
   EXPECT_EQ(0, times_open_called_);
   EXPECT_EQ(1, times_discard_called_);
 }
diff --git a/chrome/browser/enterprise/connectors/common.h b/chrome/browser/enterprise/connectors/common.h
index 2b9517e9..89626269 100644
--- a/chrome/browser/enterprise/connectors/common.h
+++ b/chrome/browser/enterprise/connectors/common.h
@@ -33,6 +33,7 @@
 constexpr char kKeyMinimumDataSize[] = "minimum_data_size";
 constexpr char kKeyEnabledEventNames[] = "enabled_event_names";
 constexpr char kKeyCustomMessages[] = "custom_messages";
+constexpr char kKeyRequireJustificationTags[] = "require_justification_tags";
 constexpr char kKeyCustomMessagesTag[] = "tag";
 constexpr char kKeyCustomMessagesMessage[] = "message";
 constexpr char kKeyCustomMessagesLearnMoreUrl[] = "learn_more_url";
@@ -81,6 +82,7 @@
   bool block_large_files = false;
   bool block_unsupported_file_types = false;
   std::map<std::string, CustomMessageData> custom_message_data;
+  std::set<std::string> tags_requiring_justification;
 
   // Minimum text size for BulkDataEntry scans. 0 means no minimum.
   size_t minimum_data_size = 100;
diff --git a/chrome/browser/enterprise/reporting/extension_request/extension_request_observer.cc b/chrome/browser/enterprise/reporting/extension_request/extension_request_observer.cc
index 480a9db..d9dfc24 100644
--- a/chrome/browser/enterprise/reporting/extension_request/extension_request_observer.cc
+++ b/chrome/browser/enterprise/reporting/extension_request/extension_request_observer.cc
@@ -161,7 +161,7 @@
 
 void ExtensionRequestObserver::RemoveExtensionsFromPendingList(
     const std::vector<std::string>& extension_ids) {
-  DictionaryPrefUpdate pending_requests_update(
+  DictionaryPrefUpdateDeprecated pending_requests_update(
       Profile::FromBrowserContext(profile_)->GetPrefs(),
       prefs::kCloudExtensionRequestIds);
   for (auto& id : extension_ids)
diff --git a/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc b/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc
index b6170a5..4b1c248 100644
--- a/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc
+++ b/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc
@@ -133,7 +133,7 @@
   }
 
   // Update the preference in the end.
-  DictionaryPrefUpdate uploaded_requests_update(
+  DictionaryPrefUpdateDeprecated uploaded_requests_update(
       profile->GetPrefs(), kCloudExtensionRequestUploadedIds);
 
   for (const auto& report : reports) {
diff --git a/chrome/browser/error_reporting/webui_js_error_reporting_browsertest.cc b/chrome/browser/error_reporting/webui_js_error_reporting_browsertest.cc
index 5b9c742..32f2148 100644
--- a/chrome/browser/error_reporting/webui_js_error_reporting_browsertest.cc
+++ b/chrome/browser/error_reporting/webui_js_error_reporting_browsertest.cc
@@ -5,7 +5,6 @@
 #include <memory>
 
 #include "base/containers/contains.h"
-#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/error_reporting/mock_chrome_js_error_report_processor.h"
@@ -25,7 +24,6 @@
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/content_features.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -141,20 +139,11 @@
     InProcessBrowserTest::SetUpOnMainThread();
   }
 
-  void SetUpInProcessBrowserTestFixture() override {
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(
-        features::kSendWebUIJavaScriptErrorReports,
-        {{features::kSendWebUIJavaScriptErrorReportsSendToProductionVariation,
-          "false"}});
-    InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
-  }
-
  protected:
   // NoErrorsAfterNavigation needs a second embedded test server to serve up
   // its error page, since embedded_test_server() is in use by the
   // MockCrashEndpoint.
   net::test_server::EmbeddedTestServer error_page_test_server_;
-  base::test::ScopedFeatureList scoped_feature_list_;
   const GURL error_url_;
 };
 
diff --git a/chrome/browser/extensions/active_tab_permission_granter.cc b/chrome/browser/extensions/active_tab_permission_granter.cc
index f005e5b..32e3494 100644
--- a/chrome/browser/extensions/active_tab_permission_granter.cc
+++ b/chrome/browser/extensions/active_tab_permission_granter.cc
@@ -235,9 +235,6 @@
 void ActiveTabPermissionGranter::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
   // Important: sub-frames don't get granted!
-  // TODO(https://crbug.com/1218946): With MPArch there may be multiple main
-  // frames. This caller was converted automatically to the primary main frame
-  // to preserve its semantics. Follow up to confirm correctness.
   if (!navigation_handle->IsInPrimaryMainFrame() ||
       !navigation_handle->HasCommitted() ||
       navigation_handle->IsSameDocument()) {
@@ -245,16 +242,8 @@
   }
 
   // Only clear the granted permissions for cross-origin navigations.
-  // TODO(crbug.com/698985),  TODO(devlin): We likely shouldn't be using the
-  // visible entry. Instead, we should use WebContents::GetLastCommittedURL().
-  content::NavigationEntry* navigation_entry =
-      web_contents()->GetController().GetVisibleEntry();
-  if (navigation_entry &&
-      navigation_entry->GetURL().DeprecatedGetOriginAsURL() ==
-          navigation_handle->GetPreviousMainFrameURL()
-              .DeprecatedGetOriginAsURL()) {
+  if (navigation_handle->IsSameOrigin())
     return;
-  }
 
   ClearActiveExtensionsAndNotify();
 }
diff --git a/chrome/browser/extensions/active_tab_permission_granter.h b/chrome/browser/extensions/active_tab_permission_granter.h
index f7fb4b6..a9b9e0c 100644
--- a/chrome/browser/extensions/active_tab_permission_granter.h
+++ b/chrome/browser/extensions/active_tab_permission_granter.h
@@ -62,6 +62,9 @@
   void RevokeForTesting();
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(ExtensionActionRunnerFencedFrameBrowserTest,
+                           FencedFrameDoesNotClearActiveExtensions);
+
   // content::WebContentsObserver implementation.
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
diff --git a/chrome/browser/extensions/api/commands/command_service.cc b/chrome/browser/extensions/api/commands/command_service.cc
index d86acae1..4a82592 100644
--- a/chrome/browser/extensions/api/commands/command_service.cc
+++ b/chrome/browser/extensions/api/commands/command_service.cc
@@ -186,8 +186,8 @@
           command_name != manifest_values::kBrowserActionCommandEvent &&
           command_name != manifest_values::kActionCommandEvent));
 
-  DictionaryPrefUpdate updater(profile_->GetPrefs(),
-                               prefs::kExtensionCommands);
+  DictionaryPrefUpdateDeprecated updater(profile_->GetPrefs(),
+                                         prefs::kExtensionCommands);
   base::DictionaryValue* bindings = updater.Get();
 
   std::string key = GetPlatformKeybindingKeyForAccelerator(accelerator,
@@ -629,7 +629,8 @@
 
 void CommandService::RemoveKeybindingPrefs(const std::string& extension_id,
                                            const std::string& command_name) {
-  DictionaryPrefUpdate updater(profile_->GetPrefs(), prefs::kExtensionCommands);
+  DictionaryPrefUpdateDeprecated updater(profile_->GetPrefs(),
+                                         prefs::kExtensionCommands);
   base::DictionaryValue* bindings = updater.Get();
 
   typedef std::vector<std::string> KeysToRemove;
diff --git a/chrome/browser/extensions/api/commands/command_service_browsertest.cc b/chrome/browser/extensions/api/commands/command_service_browsertest.cc
index df92388..b848c62 100644
--- a/chrome/browser/extensions/api/commands/command_service_browsertest.cc
+++ b/chrome/browser/extensions/api/commands/command_service_browsertest.cc
@@ -109,8 +109,8 @@
   const Extension* extension = InstallExtension(extension_dir, 1);
   ASSERT_TRUE(extension);
 
-  DictionaryPrefUpdate updater(browser()->profile()->GetPrefs(),
-                               prefs::kExtensionCommands);
+  DictionaryPrefUpdateDeprecated updater(browser()->profile()->GetPrefs(),
+                                         prefs::kExtensionCommands);
   base::DictionaryValue* bindings = updater.Get();
 
   // Simulate command |toggle-feature| has been assigned with a shortcut on
diff --git a/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc b/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
index a37c1b4..7da4d5e 100644
--- a/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
+++ b/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
@@ -150,7 +150,7 @@
 
   DesktopMediaPickerController::DoneCallback callback = base::BindOnce(
       &DesktopCaptureChooseDesktopMediaFunctionBase::OnPickerDialogResults,
-      this, origin, web_contents);
+      this, origin, render_frame_host->GetGlobalId());
   DesktopMediaPickerController::Params picker_params;
   picker_params.web_contents = web_contents;
   picker_params.context = parent_window;
@@ -179,7 +179,7 @@
 
 void DesktopCaptureChooseDesktopMediaFunctionBase::OnPickerDialogResults(
     const GURL& origin,
-    content::WebContents* web_contents,
+    const content::GlobalRenderFrameHostId& render_frame_host_id,
     const std::string& err,
     DesktopMediaID source) {
   picker_controller_.reset();
@@ -196,15 +196,9 @@
   }
 
   std::string result;
-  if (source.type != DesktopMediaID::TYPE_NONE && web_contents) {
-    // TODO(miu): Once render_frame_host() is being set, we should register the
-    // exact RenderFrame requesting the stream, not the main RenderFrame.  With
-    // that change, also update
-    // MediaCaptureDevicesDispatcher::ProcessDesktopCaptureAccessRequest().
-    // http://crbug.com/304341
-    content::RenderFrameHost* const main_frame = web_contents->GetMainFrame();
+  if (source.type != DesktopMediaID::TYPE_NONE) {
     result = content::DesktopStreamsRegistry::GetInstance()->RegisterStream(
-        main_frame->GetProcess()->GetID(), main_frame->GetRoutingID(),
+        render_frame_host_id.child_id, render_frame_host_id.frame_routing_id,
         url::Origin::Create(origin), source, extension()->name(),
         content::kRegistryStreamTypeDesktop);
   }
diff --git a/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.h b/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.h
index f54da5d..b1f067d7 100644
--- a/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.h
+++ b/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.h
@@ -53,10 +53,11 @@
   int request_id_;
 
  private:
-  void OnPickerDialogResults(const GURL& origin,
-                             content::WebContents* web_contents,
-                             const std::string& err,
-                             content::DesktopMediaID source);
+  void OnPickerDialogResults(
+      const GURL& origin,
+      const content::GlobalRenderFrameHostId& render_frame_host_id,
+      const std::string& err,
+      content::DesktopMediaID source);
 
   std::unique_ptr<DesktopMediaPickerController> picker_controller_;
 };
diff --git a/chrome/browser/extensions/api/preference/preference_api.cc b/chrome/browser/extensions/api/preference/preference_api.cc
index 9d4dcd9..2717d52 100644
--- a/chrome/browser/extensions/api/preference/preference_api.cc
+++ b/chrome/browser/extensions/api/preference/preference_api.cc
@@ -364,9 +364,7 @@
   }
 
   struct PrefMapData {
-    PrefMapData()
-        : read_permission(APIPermissionID::kInvalid),
-          write_permission(APIPermissionID::kInvalid) {}
+    PrefMapData() = default;
 
     PrefMapData(const std::string& pref_name,
                 APIPermissionID read,
@@ -379,10 +377,10 @@
     std::string pref_name;
 
     // Permission needed to read the preference.
-    APIPermissionID read_permission;
+    APIPermissionID read_permission = APIPermissionID::kInvalid;
 
     // Permission needed to write the preference.
-    APIPermissionID write_permission;
+    APIPermissionID write_permission = APIPermissionID::kInvalid;
   };
 
   using PrefMap = std::map<std::string, PrefMapData>;
@@ -619,16 +617,16 @@
                                             bool incognito) {
   if (incognito) {
     extension_prefs()->UpdateExtensionPref(
-        extension_id,
-        pref_names::kPrefIncognitoContentSettings,
-        content_settings_store()->GetSettingsForExtension(
-            extension_id, kExtensionPrefsScopeIncognitoPersistent));
+        extension_id, pref_names::kPrefIncognitoContentSettings,
+        base::Value::ToUniquePtrValue(
+            base::Value(content_settings_store()->GetSettingsForExtension(
+                extension_id, kExtensionPrefsScopeIncognitoPersistent))));
   } else {
     extension_prefs()->UpdateExtensionPref(
-        extension_id,
-        pref_names::kPrefContentSettings,
-        content_settings_store()->GetSettingsForExtension(
-            extension_id, kExtensionPrefsScopeRegular));
+        extension_id, pref_names::kPrefContentSettings,
+        base::Value::ToUniquePtrValue(
+            base::Value(content_settings_store()->GetSettingsForExtension(
+                extension_id, kExtensionPrefsScopeRegular))));
   }
 }
 
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
index 10167b9..bba5a80 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
@@ -192,6 +192,8 @@
 const char
     SafeBrowsingPrivateEventRouter::kKeyPasswordBreachIdentitiesUsername[] =
         "username";
+const char SafeBrowsingPrivateEventRouter::kKeyUserJustification[] =
+    "userJustification";
 
 // All new event names should be added to the kAllEvents array below!
 const char SafeBrowsingPrivateEventRouter::kKeyPasswordReuseEvent[] =
@@ -597,7 +599,8 @@
     const std::string& scan_id,
     safe_browsing::DeepScanAccessPoint access_point,
     const enterprise_connectors::ContentAnalysisResponse::Result& result,
-    const int64_t content_size) {
+    const int64_t content_size,
+    absl::optional<std::u16string> user_justification) {
   absl::optional<enterprise_connectors::ReportingSettings> settings =
       GetReportingSettings();
   if (!settings.has_value() ||
@@ -625,6 +628,9 @@
                        result.evidence_locker_filepath());
   }
   event.SetStringKey(kKeyScanId, scan_id);
+  if (user_justification) {
+    event.SetStringKey(kKeyUserJustification, *user_justification);
+  }
 
   AddAnalysisConnectorVerdictToEvent(result, &event);
 
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
index 6e4174a..5c15d1b 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
@@ -92,6 +92,7 @@
   static const char kKeyPasswordBreachIdentities[];
   static const char kKeyPasswordBreachIdentitiesUrl[];
   static const char kKeyPasswordBreachIdentitiesUsername[];
+  static const char kKeyUserJustification[];
 
   static const char kKeyPasswordReuseEvent[];
   static const char kKeyPasswordChangedEvent[];
@@ -171,7 +172,8 @@
       const std::string& scan_id,
       safe_browsing::DeepScanAccessPoint access_point,
       const enterprise_connectors::ContentAnalysisResponse::Result& result,
-      const int64_t content_size);
+      const int64_t content_size,
+      absl::optional<std::u16string> user_justification = absl::nullopt);
 
   // Notifies listeners that deep scanning failed, for the given |reason|.
   void OnUnscannedFileEvent(const GURL& url,
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
index 5e5bdb5..a757032 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
@@ -305,7 +305,7 @@
     return status;
   }
 
-  DictionaryPrefUpdate pending_requests_update(
+  DictionaryPrefUpdateDeprecated pending_requests_update(
       profile->GetPrefs(), prefs::kCloudExtensionRequestIds);
   DCHECK(!pending_requests_update->FindKey(id));
   base::Value request_data(base::Value::Type::DICTIONARY);
diff --git a/chrome/browser/extensions/chrome_extension_cookies.cc b/chrome/browser/extensions/chrome_extension_cookies.cc
index 1570cfb..e1358eb 100644
--- a/chrome/browser/extensions/chrome_extension_cookies.cc
+++ b/chrome/browser/extensions/chrome_extension_cookies.cc
@@ -5,13 +5,18 @@
 #include "chrome/browser/extensions/chrome_extension_cookies.h"
 
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/extensions/chrome_extension_cookies_factory.h"
+#include "chrome/browser/first_party_sets/first_party_sets_pref_names.h"
+#include "chrome/browser/first_party_sets/first_party_sets_util.h"
 #include "chrome/browser/net/profile_network_context_service.h"
+#include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_constants.h"
 #include "components/cookie_config/cookie_store_util.h"
+#include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/cookie_store_factory.h"
@@ -22,7 +27,9 @@
 namespace extensions {
 
 ChromeExtensionCookies::ChromeExtensionCookies(Profile* profile)
-    : profile_(profile) {
+    : profile_(profile),
+      first_party_sets_enabled_(
+          FirstPartySetsUtil::GetInstance()->IsFirstPartySetsEnabled()) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   cookie_settings_ = CookieSettingsFactory::GetForProfile(profile);
   cookie_settings_observation_.Observe(cookie_settings_.get());
@@ -35,7 +42,7 @@
     creation_config = std::make_unique<content::CookieStoreConfig>(
         profile_->GetPath().Append(chrome::kExtensionsCookieFilename),
         profile_->ShouldRestoreOldSessionCookies(),
-        profile_->ShouldPersistSessionCookies());
+        profile_->ShouldPersistSessionCookies(), first_party_sets_enabled_);
     creation_config->crypto_delegate = cookie_config::GetCookieCryptoDelegate();
   }
   creation_config->cookieable_schemes.push_back(extensions::kExtensionScheme);
@@ -69,9 +76,10 @@
 
   // Safe since |io_data_| is non-null so no IOData deletion is queued.
   content::GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE, base::BindOnce(&IOData::CreateRestrictedCookieManager,
-                                base::Unretained(io_data_.get()), origin,
-                                isolation_info, std::move(receiver)));
+      FROM_HERE,
+      base::BindOnce(&IOData::CreateRestrictedCookieManager,
+                     base::Unretained(io_data_.get()), origin, isolation_info,
+                     first_party_sets_enabled_, std::move(receiver)));
 }
 
 void ChromeExtensionCookies::ClearCookies(const GURL& origin,
@@ -118,6 +126,7 @@
 void ChromeExtensionCookies::IOData::CreateRestrictedCookieManager(
     const url::Origin& origin,
     const net::IsolationInfo& isolation_info,
+    const bool first_party_sets_enabled,
     mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
 
@@ -127,7 +136,7 @@
           GetOrCreateCookieStore(), network_cookie_settings_, origin,
           isolation_info,
           /* null cookies_observer disables logging */
-          mojo::NullRemote()),
+          mojo::NullRemote(), first_party_sets_enabled),
       std::move(receiver));
 }
 
diff --git a/chrome/browser/extensions/chrome_extension_cookies.h b/chrome/browser/extensions/chrome_extension_cookies.h
index 3dc2dd3..96cf18d1 100644
--- a/chrome/browser/extensions/chrome_extension_cookies.h
+++ b/chrome/browser/extensions/chrome_extension_cookies.h
@@ -86,6 +86,7 @@
     void CreateRestrictedCookieManager(
         const url::Origin& origin,
         const net::IsolationInfo& isolation_info,
+        bool first_party_sets_enabled,
         mojo::PendingReceiver<network::mojom::RestrictedCookieManager>
             receiver);
 
@@ -143,6 +144,8 @@
   base::ScopedObservation<content_settings::CookieSettings,
                           content_settings::CookieSettings::Observer>
       cookie_settings_observation_{this};
+
+  const bool first_party_sets_enabled_;
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_action_runner.cc b/chrome/browser/extensions/extension_action_runner.cc
index 18a84d9..d866cfa 100644
--- a/chrome/browser/extensions/extension_action_runner.cc
+++ b/chrome/browser/extensions/extension_action_runner.cc
@@ -475,21 +475,20 @@
   declarative_net_request::RulesMonitorService* rules_monitor_service =
       declarative_net_request::RulesMonitorService::Get(browser_context_);
 
-  // TODO(https://crbug.com/1218946): With MPArch there may be multiple main
-  // frames. This caller was converted automatically to the primary main frame
-  // to preserve its semantics. Follow up to confirm correctness.
-  const bool is_main_frame = navigation_handle->IsInPrimaryMainFrame();
   const bool has_committed = navigation_handle->HasCommitted();
 
-  if (is_main_frame && !has_committed && rules_monitor_service) {
+  if (navigation_handle->IsInMainFrame() && !has_committed &&
+      rules_monitor_service) {
     // Clean up any pending actions recorded in the action tracker for this
     // navigation.
     rules_monitor_service->action_tracker().ClearPendingNavigation(
         navigation_handle->GetNavigationId());
   }
 
-  if (!is_main_frame || !has_committed || navigation_handle->IsSameDocument())
+  if (!navigation_handle->IsInPrimaryMainFrame() || !has_committed ||
+      navigation_handle->IsSameDocument()) {
     return;
+  }
 
   LogUMA();
   num_page_requests_ = 0;
diff --git a/chrome/browser/extensions/extension_action_runner.h b/chrome/browser/extensions/extension_action_runner.h
index c42a407..08525600e 100644
--- a/chrome/browser/extensions/extension_action_runner.h
+++ b/chrome/browser/extensions/extension_action_runner.h
@@ -135,6 +135,9 @@
 #endif  // defined(UNIT_TEST)
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(ExtensionActionRunnerFencedFrameBrowserTest,
+                           DoNotResetExtensionActionRunner);
+
   struct PendingScript {
     PendingScript(mojom::RunLocation run_location,
                   ScriptInjectionCallback permit_script);
diff --git a/chrome/browser/extensions/extension_action_runner_browsertest.cc b/chrome/browser/extensions/extension_action_runner_browsertest.cc
index 227b803..6e4a74e 100644
--- a/chrome/browser/extensions/extension_action_runner_browsertest.cc
+++ b/chrome/browser/extensions/extension_action_runner_browsertest.cc
@@ -24,10 +24,12 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/fenced_frame_test_util.h"
 #include "extensions/browser/extension_action.h"
 #include "extensions/common/extension_features.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/test_extension_dir.h"
+#include "net/dns/mock_host_resolver.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace extensions {
@@ -523,4 +525,133 @@
                        DONT_WITHHOLD_PERMISSIONS, DOES_NOT_REQUIRE_CONSENT);
 }
 
+class ExtensionActionRunnerFencedFrameBrowserTest
+    : public ExtensionActionRunnerBrowserTest {
+ public:
+  ExtensionActionRunnerFencedFrameBrowserTest() = default;
+  ~ExtensionActionRunnerFencedFrameBrowserTest() override = default;
+
+  ExtensionActionRunnerFencedFrameBrowserTest(
+      const ExtensionActionRunnerFencedFrameBrowserTest&) = delete;
+  ExtensionActionRunnerFencedFrameBrowserTest& operator=(
+      const ExtensionActionRunnerFencedFrameBrowserTest&) = delete;
+
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    ASSERT_TRUE(embedded_test_server()->Start());
+    ExtensionActionRunnerBrowserTest::SetUpOnMainThread();
+  }
+
+ protected:
+  content::test::FencedFrameTestHelper fenced_frame_helper_;
+};
+
+// Tests that a fenced frame doesn't clear active extensions.
+IN_PROC_BROWSER_TEST_F(ExtensionActionRunnerFencedFrameBrowserTest,
+                       FencedFrameDoesNotClearActiveExtensions) {
+  // Set a situation that |granted_extensions_| of ActiveTabPermissionGranter is
+  // not empty to test a fenced frame doesn't clear active extensions.
+  const Extension* extension = LoadExtension(
+      test_data_dir_.AppendASCII("blocked_actions/content_scripts"));
+  ASSERT_TRUE(extension);
+  ScriptingPermissionsModifier(profile(), extension)
+      .SetWithholdHostPermissions(true);
+
+  GURL initial_url = embedded_test_server()->GetURL("a.com", "/simple.html");
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  ExtensionActionRunner* runner =
+      ExtensionActionRunner::GetForWebContents(web_contents);
+  ASSERT_TRUE(runner);
+
+  runner->set_default_bubble_close_action_for_testing(
+      std::make_unique<ToolbarActionsBarBubbleDelegate::CloseAction>(
+          ToolbarActionsBarBubbleDelegate::CLOSE_EXECUTE));
+
+  content::NavigationEntry* entry =
+      web_contents->GetController().GetLastCommittedEntry();
+  ASSERT_TRUE(entry);
+  const int first_nav_id = entry->GetUniqueID();
+
+  runner->RunAction(extension, true);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents));
+  entry = web_contents->GetController().GetLastCommittedEntry();
+  ASSERT_TRUE(entry);
+  EXPECT_GE(entry->GetUniqueID(), first_nav_id);
+  EXPECT_EQ("success", GetValue(web_contents));
+  EXPECT_FALSE(runner->WantsToRun(extension));
+
+  ActiveTabPermissionGranter* active_tab_granter =
+      TabHelper::FromWebContents(web_contents)->active_tab_permission_granter();
+  ASSERT_TRUE(active_tab_granter);
+  EXPECT_EQ(active_tab_granter->granted_extensions_.size(), 1U);
+
+  // The origin of |url| and |fenced_frame_url| should be different because
+  // ActiveTabPermissionGranter::DidFinishNavigation is only able to clear
+  // active extensions when the origins are different.
+  GURL fenced_frame_url =
+      embedded_test_server()->GetURL("b.com", "/fenced_frames/title1.html");
+  // Create a fenced frame and load the test url. Active extensions should not
+  // be cleared by the fenced frame navigation.
+  content::RenderFrameHost* fenced_frame_host =
+      fenced_frame_helper_.CreateFencedFrame(web_contents->GetMainFrame(),
+                                             fenced_frame_url);
+  ASSERT_TRUE(fenced_frame_host);
+  EXPECT_EQ(active_tab_granter->granted_extensions_.size(), 1U);
+
+  // Active extensions should be cleared after navigating a test url on the
+  // primary main frame.
+  GURL test_url = embedded_test_server()->GetURL("c.com", "/simple.html");
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url));
+  EXPECT_EQ(active_tab_granter->granted_extensions_.size(), 0U);
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionActionRunnerFencedFrameBrowserTest,
+                       DoNotResetExtensionActionRunner) {
+  // Loadup an extension and navigate to test that a fenced frame doesn't reset
+  // ExtensionActionRunner's member variables.
+  const Extension* extension =
+      CreateExtension(ALL_HOSTS, CONTENT_SCRIPT, WITHHOLD_PERMISSIONS);
+  ASSERT_TRUE(extension);
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(web_contents);
+  ExtensionActionRunner* action_runner =
+      ExtensionActionRunner::GetForWebContents(web_contents);
+  ASSERT_TRUE(action_runner);
+
+  ExtensionTestMessageListener inject_success_listener(kInjectSucceeded,
+                                                       false /* won't reply */);
+  inject_success_listener.set_extension_id(extension->id());
+
+  GURL url = embedded_test_server()->GetURL("/extensions/test_file.html");
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+  ScriptingPermissionsModifier modifier(profile(), extension);
+  modifier.SetWithholdHostPermissions(false);
+  EXPECT_TRUE(RunAllPendingInRenderer(web_contents));
+
+  // Create a fenced frame and navigate the fenced frame url.
+  GURL fenced_frame_url =
+      embedded_test_server()->GetURL("/fenced_frames/title1.html");
+  content::RenderFrameHost* fenced_frame_host =
+      fenced_frame_helper_.CreateFencedFrame(web_contents->GetMainFrame(),
+                                             fenced_frame_url);
+  ASSERT_TRUE(fenced_frame_host);
+  // Fenced frame doesn't clear pending script injection requests and the
+  // scripts.
+  EXPECT_EQ(1, action_runner->num_page_requests());
+  EXPECT_EQ(1U, action_runner->pending_scripts_.size());
+
+  // Navigate again on the primary main frame. Pending script injection requests
+  // and scripts should be cleared.
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+  EXPECT_EQ(0, action_runner->num_page_requests());
+  EXPECT_EQ(0U, action_runner->pending_scripts_.size());
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_assets_manager_chromeos.cc b/chrome/browser/extensions/extension_assets_manager_chromeos.cc
index 5009b5d..48df683 100644
--- a/chrome/browser/extensions/extension_assets_manager_chromeos.cc
+++ b/chrome/browser/extensions/extension_assets_manager_chromeos.cc
@@ -215,7 +215,8 @@
   if (!local_state)
     return false;
 
-  DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions);
+  DictionaryPrefUpdateDeprecated shared_extensions(local_state,
+                                                   kSharedExtensions);
   std::vector<std::string> extensions;
   extensions.reserve(shared_extensions->DictSize());
   for (base::DictionaryValue::Iterator it(*shared_extensions);
@@ -300,7 +301,8 @@
   }
 
   PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions);
+  DictionaryPrefUpdateDeprecated shared_extensions(local_state,
+                                                   kSharedExtensions);
   base::DictionaryValue* extension_info = NULL;
   base::DictionaryValue* version_info = NULL;
   base::ListValue* users = NULL;
@@ -387,7 +389,8 @@
   }
 
   PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions);
+  DictionaryPrefUpdateDeprecated shared_extensions(local_state,
+                                                   kSharedExtensions);
   base::DictionaryValue* extension_info_weak = NULL;
   if (!shared_extensions->GetDictionary(id, &extension_info_weak)) {
     auto extension_info = std::make_unique<base::DictionaryValue>();
@@ -432,7 +435,8 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   PrefService* local_state = g_browser_process->local_state();
-  DictionaryPrefUpdate shared_extensions(local_state, kSharedExtensions);
+  DictionaryPrefUpdateDeprecated shared_extensions(local_state,
+                                                   kSharedExtensions);
   base::Value* extension_info = shared_extensions->FindDictKey(id);
   if (!extension_info) {
     NOTREACHED();
diff --git a/chrome/browser/extensions/extension_cookies_browsertest.cc b/chrome/browser/extensions/extension_cookies_browsertest.cc
index b418b24f..3b586bb 100644
--- a/chrome/browser/extensions/extension_cookies_browsertest.cc
+++ b/chrome/browser/extensions/extension_cookies_browsertest.cc
@@ -26,6 +26,7 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/network_session_configurator/common/network_switches.h"
 #include "content/public/browser/storage_partition.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
@@ -36,7 +37,6 @@
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/test_extension_dir.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "net/base/features.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/http/http_request_headers.h"
 #include "net/test/embedded_test_server/controllable_http_response.h"
@@ -976,7 +976,7 @@
                             std::end(kSamePartyCookies)),
         no_same_party_cookies_(std::begin(kNoSamePartyCookies),
                                std::end(kNoSamePartyCookies)) {
-    feature_list_.InitAndEnableFeature(net::features::kFirstPartySets);
+    feature_list_.InitAndEnableFeature(features::kFirstPartySets);
   }
   ~ExtensionSamePartyCookiesTest() override = default;
   ExtensionSamePartyCookiesTest(const ExtensionSamePartyCookiesTest&) = delete;
diff --git a/chrome/browser/extensions/extension_garbage_collector_chromeos_unittest.cc b/chrome/browser/extensions/extension_garbage_collector_chromeos_unittest.cc
index 62ea7f0..4e89aaa 100644
--- a/chrome/browser/extensions/extension_garbage_collector_chromeos_unittest.cc
+++ b/chrome/browser/extensions/extension_garbage_collector_chromeos_unittest.cc
@@ -90,7 +90,8 @@
                                   const std::string& version,
                                   const std::string& users_string,
                                   const base::FilePath& path) {
-    DictionaryPrefUpdate shared_extensions(testing_local_state_.Get(),
+    DictionaryPrefUpdateDeprecated shared_extensions(
+        testing_local_state_.Get(),
         ExtensionAssetsManagerChromeOS::kSharedExtensions);
 
     base::DictionaryValue* extension_info_weak = NULL;
diff --git a/chrome/browser/extensions/extension_garbage_collector_unittest.cc b/chrome/browser/extensions/extension_garbage_collector_unittest.cc
index d756199c..d864691 100644
--- a/chrome/browser/extensions/extension_garbage_collector_unittest.cc
+++ b/chrome/browser/extensions/extension_garbage_collector_unittest.cc
@@ -56,7 +56,8 @@
 
   // Simulate that one of them got partially deleted by clearing its pref.
   {
-    DictionaryPrefUpdate update(profile_->GetPrefs(), pref_names::kExtensions);
+    DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                          pref_names::kExtensions);
     base::DictionaryValue* dict = update.Get();
     ASSERT_TRUE(dict != nullptr);
     dict->RemoveKey(kExtensionId);
@@ -91,7 +92,8 @@
 
   // Simulate that one of them got partially deleted by clearing its pref.
   {
-    DictionaryPrefUpdate update(profile_->GetPrefs(), pref_names::kExtensions);
+    DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                          pref_names::kExtensions);
     base::DictionaryValue* dict = update.Get();
     ASSERT_TRUE(dict != nullptr);
     dict->RemoveKey(kExtensionId);
diff --git a/chrome/browser/extensions/extension_override_apitest.cc b/chrome/browser/extensions/extension_override_apitest.cc
index 9a2739a5..8739c84a 100644
--- a/chrome/browser/extensions/extension_override_apitest.cc
+++ b/chrome/browser/extensions/extension_override_apitest.cc
@@ -355,8 +355,9 @@
   }
 
   {
-    DictionaryPrefUpdate update(browser()->profile()->GetPrefs(),
-                                ExtensionWebUI::kExtensionURLOverrides);
+    DictionaryPrefUpdateDeprecated update(
+        browser()->profile()->GetPrefs(),
+        ExtensionWebUI::kExtensionURLOverrides);
     update.Get()->SetKey("history", std::move(list));
   }
 
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 5ecea7a..f9d79d2 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -723,7 +723,8 @@
                const std::string& pref_path,
                std::unique_ptr<base::Value> value,
                const std::string& msg) {
-    DictionaryPrefUpdate update(profile()->GetPrefs(), pref_names::kExtensions);
+    DictionaryPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                          pref_names::kExtensions);
     base::DictionaryValue* dict = update.Get();
     ASSERT_TRUE(dict) << msg;
     base::DictionaryValue* pref = nullptr;
@@ -761,7 +762,8 @@
     std::string msg = " while clearing: ";
     msg += extension_id + " " + pref_path;
 
-    DictionaryPrefUpdate update(profile()->GetPrefs(), pref_names::kExtensions);
+    DictionaryPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                          pref_names::kExtensions);
     base::DictionaryValue* dict = update.Get();
     ASSERT_TRUE(dict) << msg;
     base::DictionaryValue* pref = nullptr;
diff --git a/chrome/browser/extensions/extension_web_ui.cc b/chrome/browser/extensions/extension_web_ui.cc
index f54843f9..893e720 100644
--- a/chrome/browser/extensions/extension_web_ui.cc
+++ b/chrome/browser/extensions/extension_web_ui.cc
@@ -236,7 +236,8 @@
   if (overrides.empty())
     return;
   PrefService* prefs = profile->GetPrefs();
-  DictionaryPrefUpdate update(prefs, ExtensionWebUI::kExtensionURLOverrides);
+  DictionaryPrefUpdateDeprecated update(prefs,
+                                        ExtensionWebUI::kExtensionURLOverrides);
   base::DictionaryValue* all_overrides = update.Get();
   for (const auto& page_override_pair : overrides) {
     base::ListValue* page_overrides = nullptr;
@@ -320,7 +321,8 @@
     Profile* profile,
     base::RepeatingCallback<void(base::ListValue*)> callback) {
   PrefService* prefs = profile->GetPrefs();
-  DictionaryPrefUpdate update(prefs, ExtensionWebUI::kExtensionURLOverrides);
+  DictionaryPrefUpdateDeprecated update(prefs,
+                                        ExtensionWebUI::kExtensionURLOverrides);
   base::DictionaryValue* all_overrides = update.Get();
 
   // DictionaryValue::Iterator cannot be used to modify the list. Generate the
@@ -538,7 +540,7 @@
   if (overrides.empty())
     return;
   PrefService* prefs = profile->GetPrefs();
-  DictionaryPrefUpdate update(prefs, kExtensionURLOverrides);
+  DictionaryPrefUpdateDeprecated update(prefs, kExtensionURLOverrides);
   base::Value* all_overrides = update.Get();
   for (const auto& page_override_pair : overrides) {
     base::Value* page_overrides_weak =
diff --git a/chrome/browser/extensions/extension_web_ui_unittest.cc b/chrome/browser/extensions/extension_web_ui_unittest.cc
index 22fcbaa..fd6e737 100644
--- a/chrome/browser/extensions/extension_web_ui_unittest.cc
+++ b/chrome/browser/extensions/extension_web_ui_unittest.cc
@@ -184,7 +184,8 @@
   PrefService* prefs = profile_->GetPrefs();
   {
     // Add multiple entries for the same extension.
-    DictionaryPrefUpdate update(prefs, ExtensionWebUI::kExtensionURLOverrides);
+    DictionaryPrefUpdateDeprecated update(
+        prefs, ExtensionWebUI::kExtensionURLOverrides);
     base::DictionaryValue* all_overrides = update.Get();
     base::Value newtab_list(base::Value::Type::LIST);
     {
diff --git a/chrome/browser/extensions/webstore_install_helper.cc b/chrome/browser/extensions/webstore_install_helper.cc
index fddd38b8..5c36dcb 100644
--- a/chrome/browser/extensions/webstore_install_helper.cc
+++ b/chrome/browser/extensions/webstore_install_helper.cc
@@ -83,7 +83,6 @@
     icon_fetcher_ =
         std::make_unique<BitmapFetcher>(icon_url_, this, traffic_annotation);
     icon_fetcher_->Init(
-        std::string(),
         net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
         network::mojom::CredentialsMode::kOmit);
     icon_fetcher_->Start(loader_factory);
diff --git a/chrome/browser/external_protocol/external_protocol_handler.cc b/chrome/browser/external_protocol/external_protocol_handler.cc
index 3bf366dd..179dbc9 100644
--- a/chrome/browser/external_protocol/external_protocol_handler.cc
+++ b/chrome/browser/external_protocol/external_protocol_handler.cc
@@ -372,7 +372,7 @@
   if (MayRememberAllowDecisionsForThisOrigin(&initiating_origin)) {
     PrefService* profile_prefs = profile->GetPrefs();
     if (profile_prefs) {  // May be NULL during testing.
-      DictionaryPrefUpdate update_allowed_origin_protocol_pairs(
+      DictionaryPrefUpdateDeprecated update_allowed_origin_protocol_pairs(
           profile_prefs, prefs::kProtocolHandlerPerOriginAllowedProtocols);
 
       const std::string serialized_origin = initiating_origin.Serialize();
diff --git a/chrome/browser/feed/android/java/res/values-night/colors.xml b/chrome/browser/feed/android/java/res/values-night/colors.xml
index 5efbf20..284c726b 100644
--- a/chrome/browser/feed/android/java/res/values-night/colors.xml
+++ b/chrome/browser/feed/android/java/res/values-night/colors.xml
@@ -6,6 +6,9 @@
 <resources>
     <color name="post_follow_illustration_device_outline">@color/modern_grey_600</color>
 
+    <!-- TODO(iwells): Use a Surface-1 color instead. -->
+    <color name="feed_placeholder_color">@color/white_alpha_5</color>
+
     <!-- Footer chip -->
     <color name="menu_footer_chip_background_color">@color/menu_footer_chip_background_color_light</color>
     <color name="menu_footer_chip_background_color_disabled">@color/menu_footer_chip_background_color_disabled_light</color>
diff --git a/chrome/browser/feed/android/java/res/values/colors.xml b/chrome/browser/feed/android/java/res/values/colors.xml
index 2400767..cf7e7fa0 100644
--- a/chrome/browser/feed/android/java/res/values/colors.xml
+++ b/chrome/browser/feed/android/java/res/values/colors.xml
@@ -5,8 +5,9 @@
 
 <resources>
     <color name="post_follow_illustration_device_outline">@color/modern_grey_300</color>
-
-    <color name="feed_placeholder_color">@color/default_bg_color_secondary_light</color>
+    
+    <!-- TODO(iwells): Use a Surface-1 color instead. -->
+    <color name="feed_placeholder_color">@color/black_alpha_5</color>
 
     <!-- Footer chip -->
     <color name="menu_footer_chip_background_color">@color/menu_footer_chip_background_color_dark</color>
diff --git a/chrome/browser/feedback/show_feedback_page.cc b/chrome/browser/feedback/show_feedback_page.cc
index c25a9e3a..aca2bdde 100644
--- a/chrome/browser/feedback/show_feedback_page.cc
+++ b/chrome/browser/feedback/show_feedback_page.cc
@@ -129,7 +129,7 @@
         source == kFeedbackSourceChromeLabs ||
             source == kFeedbackSourceKaleidoscope);
 
-    FeedbackDialog::CreateOrShow(*info);
+    FeedbackDialog::CreateOrShow(profile, *info);
   } else {
     api->RequestFeedbackForFlow(
         description_template, description_placeholder_text, category_tag,
diff --git a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
index 39752c1b..0903ad03 100644
--- a/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
+++ b/chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.cc
@@ -386,8 +386,14 @@
 }
 
 void ChromeInternalLogSource::PopulateSyncLogs(SystemLogsResponse* response) {
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
   // We are only interested in sync logs for the primary user profile.
   Profile* profile = ProfileManager::GetPrimaryUserProfile();
+#else
+  // Get logs for the last used profile since there is no notion of primary
+  // profile.
+  Profile* profile = ProfileManager::GetLastUsedProfile();
+#endif
   if (!profile || !SyncServiceFactory::HasSyncService(profile))
     return;
 
@@ -452,7 +458,7 @@
   data_reduction_proxy::DataReductionProxySettings*
       data_reduction_proxy_settings =
           DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
-              ProfileManager::GetActiveUserProfile());
+              ProfileManager::GetLastUsedProfile());
   bool data_saver_enabled =
       data_reduction_proxy_settings &&
       data_reduction_proxy_settings->IsDataReductionProxyEnabled();
diff --git a/chrome/browser/first_party_sets/first_party_sets_util.cc b/chrome/browser/first_party_sets/first_party_sets_util.cc
index dcebc35..173b487 100644
--- a/chrome/browser/first_party_sets/first_party_sets_util.cc
+++ b/chrome/browser/first_party_sets/first_party_sets_util.cc
@@ -10,6 +10,12 @@
 #include "base/logging.h"
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/first_party_sets/first_party_sets_pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/content_features.h"
+#include "net/base/features.h"
 
 namespace {
 
@@ -43,6 +49,23 @@
   }
 }
 
+bool IsFirstPartySetsEnabledInternal() {
+  if (!base::FeatureList::IsEnabled(features::kFirstPartySets)) {
+    return false;
+  }
+  if (!g_browser_process) {
+    // If browser process doesn't exist (e.g. in minimal mode on android),
+    // default to the feature value which is true since we didn't return above.
+    return true;
+  }
+  PrefService* local_state = g_browser_process->local_state();
+  if (!local_state ||
+      !local_state->FindPreference(first_party_sets::kFirstPartySetsEnabled)) {
+    return true;
+  }
+  return local_state->GetBoolean(first_party_sets::kFirstPartySetsEnabled);
+}
+
 }  // namespace
 
 // static
@@ -51,6 +74,8 @@
   return instance.get();
 }
 
+FirstPartySetsUtil::FirstPartySetsUtil() = default;
+
 void FirstPartySetsUtil::SendAndUpdatePersistedSets(
     const base::FilePath& user_data_dir,
     base::OnceCallback<void(base::OnceCallback<void(const std::string&)>,
@@ -69,6 +94,19 @@
                      persisted_sets_path));
 }
 
+bool FirstPartySetsUtil::IsFirstPartySetsEnabled() {
+  // This method invokes the private IsFirstPartySetsEnabledInternal method
+  // and uses the `enabled_` variable to memoize the result. We can memoize
+  // since the First-Party Sets enterprise policy doesn't support `dynamic
+  // refresh` and the base::Feature doesn't change after start up, the value
+  // of this method will not change in the same in any browser session.
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!enabled_.has_value()) {
+    enabled_ = absl::make_optional(IsFirstPartySetsEnabledInternal());
+  }
+  return enabled_.value();
+}
+
 void FirstPartySetsUtil::OnGetUpdatedSets(const base::FilePath& path,
                                           const std::string& sets) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/chrome/browser/first_party_sets/first_party_sets_util.h b/chrome/browser/first_party_sets/first_party_sets_util.h
index 83fbdeb..b9bc6be 100644
--- a/chrome/browser/first_party_sets/first_party_sets_util.h
+++ b/chrome/browser/first_party_sets/first_party_sets_util.h
@@ -11,6 +11,7 @@
 #include "base/files/file_path.h"
 #include "base/no_destructor.h"
 #include "base/sequence_checker.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class FirstPartySetsUtil {
  public:
@@ -31,10 +32,15 @@
       base::OnceCallback<void(base::OnceCallback<void(const std::string&)>,
                               const std::string&)> send_sets);
 
+  // This method returns whether First-Party Sets are enabled, checking that
+  // both the base::Feature is enabled and the First-Party Sets enterprise
+  // policy is enabled.
+  bool IsFirstPartySetsEnabled();
+
  private:
   friend class base::NoDestructor<FirstPartySetsUtil>;
 
-  FirstPartySetsUtil() = default;
+  FirstPartySetsUtil();
 
   // Called when the instance receives the "current" First-Party Sets.
   // Asynchronously writes those sets to disk.
@@ -49,6 +55,11 @@
       const base::FilePath& path,
       const std::string& sets);
 
+  // This variable is used to memoize the value of IsFirstPartySetsEnabled()
+  // to avoid repeating unnecessary work.
+  absl::optional<bool> enabled_ GUARDED_BY_CONTEXT(sequence_checker_) =
+      absl::nullopt;
+
   SEQUENCE_CHECKER(sequence_checker_);
 };
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index ad3f5de..0566aea 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -125,6 +125,11 @@
     "expiry_milestone": 101
   },
   {
+    "name": "ambient-mode-animation",
+    "owners": [ "esum", "assistive-eng@google.com" ],
+    "expiry_milestone": 103
+  },
+  {
     "name": "ambient-mode-new-url",
     "owners": [ "xiaohuic", "assistive-eng@google.com" ],
     "expiry_milestone": 101
@@ -207,7 +212,7 @@
   {
     "name": "arc-input-overlay",
     "owners": [ "cuicuiruan@google.com", "arc-gaming@google.com" ],
-    "expiry_milestone": 99
+    "expiry_milestone": 110
   },
   {
     "name": "arc-keyboard-shortcut-helper-integration",
@@ -961,7 +966,7 @@
   {
     "name": "conversion-measurement-debug-mode",
     "owners": [ "//content/browser/attribution_reporting/OWNERS" ],
-    "expiry_milestone": 98
+    "expiry_milestone": 105
   },
   {
     "name": "copy-link-to-text",
@@ -1101,6 +1106,11 @@
     "expiry_milestone": 97
   },
   {
+    "name": "default-chrome-apps-migration",
+    "owners": ["vkovalova"],
+    "expiry_milestone": 104
+  },
+  {
     "name": "default-link-capturing-in-browser",
     "owners": [ "tsergeant", "chromeos-apps-foundation-team@google.com"],
     "expiry_milestone": 105
@@ -1145,7 +1155,7 @@
   {
     "name": "destroy-profile-on-browser-close",
     "owners": [ "nicolaso" ],
-    "expiry_milestone": 98
+    "expiry_milestone": 100
   },
   {
     "name": "detect-form-submission-on-form-clear",
@@ -1387,7 +1397,7 @@
   {
     "name": "drive-fs-bidirectional-native-messaging",
     "owners": [ "austinct@chromium.org", "simmonsjosh@google.com" ],
-    "expiry_milestone": 98
+    "expiry_milestone": 104
   },
   {
       "name": "dynamic-color-android",
@@ -1875,17 +1885,17 @@
   {
     "name": "enable-desktop-pwas-tab-strip",
     "owners": [ "desktop-pwas-team@google.com" ],
-    "expiry_milestone": 98
+    "expiry_milestone": 104
   },
   {
     "name": "enable-desktop-pwas-tab-strip-link-capturing",
     "owners": [ "desktop-pwas-team@google.com" ],
-    "expiry_milestone": 98
+    "expiry_milestone": 104
   },
   {
     "name": "enable-desktop-pwas-tab-strip-settings",
     "owners": [ "desktop-pwas-team@google.com" ],
-    "expiry_milestone": 98
+    "expiry_milestone": 104
   },
   {
     "name": "enable-desktop-pwas-url-handling",
@@ -1983,11 +1993,6 @@
     "expiry_milestone": 98
   },
   {
-    "name": "enable-experimental-accessibility-switch-access-setup-guide",
-    "owners": ["anastasi@google.com", "josiahk@google.com", "//ui/accessibility/OWNERS"],
-    "expiry_milestone": 97
-  },
-  {
     "name": "enable-experimental-accessibility-switch-access-text",
     "owners": [ "//ui/accessibility/OWNERS" ],
     "expiry_milestone": 95
@@ -3190,8 +3195,8 @@
   },
   {
     "name": "files-filters-in-recents",
-    "owners": [ "simmonsjosh@google.com", "benhartney@google.com" ],
-    "expiry_milestone": 98
+    "owners": [ "simmonsjosh@google.com", "benhartney@google.com", "wenbojie" ],
+    "expiry_milestone": 103
   },
   {
     "name": "files-single-partition-format",
@@ -3254,11 +3259,6 @@
     "expiry_milestone": 100
   },
   {
-    "name": "force-disable-stacked-tabs",
-    "owners": [ "chrome-desktop-ui-sea@google.com", "tbergquist" ],
-    "expiry_milestone": 98
-  },
-  {
     "name": "force-effective-connection-type",
     "owners": [ "//components/data_reduction_proxy/OWNERS" ],
     // ECT is a baked-in feature of Chrome. This flag is used for frequent
@@ -3704,6 +3704,14 @@
     "expiry_milestone": 130
   },
   {
+    "name": "lacros-profile-migration-force-off",
+    "owners": ["ythjkt", "hidehiko", "lacros-team@google.com"],
+    // Once Lacros is launched, this flag can be removed. Until then, this
+    // absolutely must not expire. We do not yet have a launch milestone.
+    // TODO(https://crbug.com/1148474).
+    "expiry_milestone": 130
+  },
+  {
     "name": "lacros-selection",
     "owners": [ "kimjae", "erikchen", "lacros-team@google.com" ],
     "expiry_milestone": 130
@@ -4001,6 +4009,11 @@
     "expiry_milestone": 99
   },
   {
+    "name": "nearby-sharing-one-page-onboarding",
+    "owners": [ "pushi@google.com", "chromeos-cross-device-eng@google.com" ],
+    "expiry_milestone": 103
+  },
+  {
     "name": "new-canvas-2d-api",
     "owners": [ "aaronhk", "fserb", "juanmihd", "yiyix" ],
     "expiry_milestone": 100
@@ -4222,12 +4235,12 @@
   {
     "name": "omnibox-max-url-matches",
     "owners": [ "orinj", "chrome-omnibox-team@google.com" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 110
   },
   {
     "name": "omnibox-max-zero-suggest-matches",
     "owners": [ "manukh", "chrome-omnibox-team@google.com" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 110
   },
   {
     "name": "omnibox-most-visited-tiles",
@@ -4347,7 +4360,7 @@
   {
     "name": "omnibox-ui-max-autocomplete-matches",
     "owners": [ "jdonnelly", "chrome-omnibox-team@google.com" ],
-    "expiry_milestone": 95
+    "expiry_milestone": 110
   },
   {
     "name": "omnibox-updated-connection-security-indicators",
@@ -4890,7 +4903,7 @@
   {
     "name": "sameparty-cookies-considered-first-party",
     "owners": ["cfredric", "chrome-first-party-sets@chromium.org"],
-    "expiry_milestone": 98
+    "expiry_milestone": 108
   },
   {
     "name": "sanitizer-api",
@@ -5002,11 +5015,6 @@
     "expiry_milestone": 98
   },
   {
-    "name": "send-webui-javascript-error-reports",
-    "owners": ["iby", "cros-telemetry@google.com"],
-    "expiry_milestone": 97
-  },
-  {
     "name": "set-market-url-for-testing",
     "owners": [ "//chrome/android/java/src/org/chromium/chrome/browser/omaha/OWNERS" ],
     // This is required by test teams to verify functionality on devices which
@@ -5019,11 +5027,6 @@
     "expiry_milestone": 96
   },
   {
-    "name": "settings-landing-page-redesign",
-    "owners": [ "dpapad@chromium.org", "johntlee@chromium.org" ],
-    "expiry_milestone": 95
-  },
-  {
     "name": "share-button-in-top-toolbar",
     "owners": [ "jeffreycohen", "chrome-sharing-eng@google.com" ],
     "expiry_milestone": 92
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 7375a988..28404f62 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -90,8 +90,8 @@
 const char kCSSCascadeLayersDescription[] =
     "Enables support for CSS @layer rules and layered @import syntax.";
 
-extern const char kCSSContainerQueriesName[] = "Enable CSS Container Queries";
-extern const char kCSSContainerQueriesDescription[] =
+const char kCSSContainerQueriesName[] = "Enable CSS Container Queries";
+const char kCSSContainerQueriesDescription[] =
     "Enables support for @container, inline-size and block-size values for the "
     "contain property, and the LayoutNG Grid implementation.";
 
@@ -125,6 +125,12 @@
     "is able to work around the history intervention which is not the expected "
     "behavior";
 
+extern const char kDefaultChromeAppsMigrationName[] =
+    "Default Chrome apps policy migration";
+extern const char kDefaultChromeAppsMigrationDescription[] =
+    "Enable replacing policies to force install Chrome apps with policies to "
+    "force install PWAs";
+
 const char kDetectedSourceLanguageOptionName[] =
     "Use Detected Language string on Desktop and Android";
 const char kDetectedSourceLanguageOptionDescription[] =
@@ -167,14 +173,6 @@
     "Enables behaviour for Url blocklist throttle to wait for all policies to "
     "load";
 
-const char kEnableSignedExchangePrefetchCacheForNavigationsName[] =
-    "Enable Signed Exchange prefetch cache for navigations";
-const char kEnableSignedExchangePrefetchCacheForNavigationsDescription[] =
-    "When enabled, the prefetched signed exchanges is stored to a prefetch "
-    "cache attached to the frame. The body of the inner response is stored as "
-    "a blob and the verification process of the signed exchange is skipped for "
-    "the succeeding navigation.";
-
 const char kU2FPermissionPromptName[] =
     "Enable a permission prompt for the U2F Security Key API";
 const char kU2FPermissionPromptDescription[] =
@@ -258,14 +256,14 @@
     "try to use a secure HTTPS connection to look up the addresses of websites "
     "and other web resources.";
 
-extern const char kAssistantConsentSimplifiedTextName[] =
+const char kAssistantConsentSimplifiedTextName[] =
     "AssistantConsentSimplifiedText";
-extern const char kAssistantConsentSimplifiedTextDescription[] =
+const char kAssistantConsentSimplifiedTextDescription[] =
     "Enables simplified consent copy in the Assistant voice search consent "
     "dialog.";
 
-extern const char kAssistantConsentV2Name[] = "AssistanConsentV2";
-extern const char kAssistantConsentV2Description[] =
+const char kAssistantConsentV2Name[] = "AssistanConsentV2";
+const char kAssistantConsentV2Description[] =
     "Enables different strategies for handling backing off from the consent "
     "screen without explicitly clicking yes/no buttons, i.e. when a user taps "
     "outside of the sheet.";
@@ -597,8 +595,8 @@
 const char kDevicePostureDescription[] =
     "Enables Device Posture API (foldable devices)";
 
-extern const char kRestrictedApiOriginsName[] = "Restricted API Origins";
-extern const char kRestrictedApiOriginsDescription[] =
+const char kRestrictedApiOriginsName[] = "Restricted API Origins";
+const char kRestrictedApiOriginsDescription[] =
     "Enables Restricted APIs (Direct Sockets API) for development purposes for "
     "a set of origins, specified as a comma-separated list.";
 
@@ -715,12 +713,6 @@
         "address  will appear at the bottom of InfoBars which has "
         "corresponding account indication footer flags on.";
 
-const char kEnableAutofillPasswordInfoBarAccountIndicationFooterName[] =
-    "Display password InfoBar footers with account indication information";
-const char kEnableAutofillPasswordInfoBarAccountIndicationFooterDescription[] =
-    "When enabled, a footer indicating user's e-mail address will appear at "
-    "the bottom of corresponding password InfoBars.";
-
 const char kEnableAutofillSaveCardInfoBarAccountIndicationFooterName[] =
     "Display SaveCardInfoBar footer with account indication information";
 const char kEnableAutofillSaveCardInfoBarAccountIndicationFooterDescription[] =
@@ -1142,10 +1134,8 @@
     "'Native with thread names' adds the thread name as the first frame of "
     "each native stack. It's also possible to record a pseudo stack using "
     "trace events as identifiers. It's also possible to do a mix of both.";
-const char kMemlogStackModeMixed[] = "Mixed";
 const char kMemlogStackModeNative[] = "Native";
 const char kMemlogStackModeNativeWithThreadNames[] = "Native with thread names";
-const char kMemlogStackModePseudo[] = "Trace events";
 
 const char kEnableAutomaticSnoozeName[] = "Enable Automatic Snooze";
 const char kEnableAutomaticSnoozeDescription[] =
@@ -1372,12 +1362,6 @@
     "Use a redesigned version of the Global Media Controls UI. Requires "
     "#global-media-controls to also be enabled.";
 
-const char kGlobalMediaControlsOverlayControlsName[] =
-    "Enable overlay controls for Global Media Controls";
-const char kGlobalMediaControlsOverlayControlsDescription[] =
-    "Allowing controls to be dragged out from Global Media Controls dialog. "
-    "Requires #global-media-controls to also be enabled.";
-
 const char kOpenscreenCastStreamingSessionName[] =
     "Enable Open Screen Library (libcast) as the Mirroring Service's Cast "
     "Streaming implementation";
@@ -1836,11 +1820,6 @@
     "Configures the maximum number of autocomplete matches displayed in the "
     "Omnibox UI dynamically based on the number of URL matches.";
 
-const char kOmniboxWebUIOmniboxPopupName[] = "WebUI Omnibox Popup";
-const char kOmniboxWebUIOmniboxPopupDescription[] =
-    "If enabled, uses WebUI to render the omnibox suggestions popup, similar "
-    "to how the NTP \"realbox\" is implemented.";
-
 const char kEnableSearchPrefetchName[] = "Search Prefetch";
 const char kEnableSearchPrefetchDescription[] =
     "Allow the default search engine to specify prefetch behavior for "
@@ -2065,12 +2044,6 @@
     "Shows a new subpage in Settings that helps the user to review various "
     "privacy settings.";
 
-const char kSafeBrowsingPerProfileNetworkContextsName[] =
-    "Per-profile Safe Browsing network contexts";
-const char kSafeBrowsingPerProfileNetworkContextsDescription[] =
-    "Keys the Safe Browsing network context by the profile initiating the "
-    "request";
-
 const char kProminentDarkModeActiveTabTitleName[] =
     "Prominent Dark Mode Active Tab Titles";
 const char kProminentDarkModeActiveTabTitleDescription[] =
@@ -2425,11 +2398,6 @@
 const char kTabGroupsCollapseFreezingDescription[] =
     "Experimental tab freezing upon collapsing a tab group.";
 
-const char kTabGroupsFeedbackName[] = "Tab Groups Feedback";
-const char kTabGroupsFeedbackDescription[] =
-    "Enables the feedback app to appear in the tab group editor bubble, if tab "
-    "groups are enabled.";
-
 const char kTabGroupsNewBadgePromoName[] = "Tab Groups 'New' Badge Promo";
 const char kTabGroupsNewBadgePromoDescription[] =
     "Causes a 'New' badge to appear on the entry point for creating a tab "
@@ -2761,16 +2729,10 @@
 const char kSharedHighlightingAmpDescription[] =
     "Enables Shared Highlighting for AMP Viwers.";
 
-const char kPreemptiveLinkToTextGenerationName[] =
-    "Preemptive generation of link to text";
-const char kPreemptiveLinkToTextGenerationDescription[] =
-    "Enables link to text to be generated in advance.";
-
 const char kDraw1PredictedPoint12Ms[] = "1 point 12ms ahead.";
 const char kDraw2PredictedPoints6Ms[] = "2 points, each 6ms ahead.";
 const char kDraw1PredictedPoint6Ms[] = "1 point 6ms ahead.";
 const char kDraw2PredictedPoints3Ms[] = "2 points, each 3ms ahead.";
-const char kDrawPredictedPointsDefault[] = "Disabled";
 const char kDrawPredictedPointsDescription[] =
     "Draw predicted points when using the delegated ink trails API. Requires "
     "experimental web platform features to be enabled.";
@@ -3254,8 +3216,8 @@
 const char kMessagesForAndroidSafetyTipDescription[] =
     "When enabled, safety tip prompt will use the new Messages UI.";
 
-extern const char kMessagesForAndroidSaveCardName[] = "Save Card Messages UI";
-extern const char kMessagesForAndroidSaveCardDescription[] =
+const char kMessagesForAndroidSaveCardName[] = "Save Card Messages UI";
+const char kMessagesForAndroidSaveCardDescription[] =
     "When enabled, save card prompt will use the new Messages UI.";
 
 const char kMessagesForAndroidSyncErrorName[] = "Sync Error Messages UI";
@@ -3291,12 +3253,9 @@
 const char kPageInfoStoreInfoDescription[] =
     "Enable a store info row to the page info menu on eligible pages.";
 
-extern const char kPageInfoV2DesktopName[];
-extern const char kPageInfoV2DesktopDescription[];
-
-extern const char kPasswordProtectionForSignedInUsersName[] =
+const char kPasswordProtectionForSignedInUsersName[] =
     "Password Protection for Signed-In Users";
-extern const char kPasswordProtectionForSignedInUsersDescription[] =
+const char kPasswordProtectionForSignedInUsersDescription[] =
     "Enable signed-in (Google account) password protection for signed-in "
     "users and allows users to change their signed-in password through "
     "password reuse warnings on phishing or low reputation sites.";
@@ -3463,11 +3422,6 @@
     " On tablets with small screens a mobile site will be requested by "
     "default.";
 
-const char kSafeBrowsingClientSideDetectionAndroidName[] =
-    "Safe Browsing Client Side Detection on Android";
-const char kSafeBrowsingClientSideDetectionAndroidDescription[] =
-    "Enable DOM feature collection on Safe Browsing pings on Android";
-
 const char kScrollCaptureName[] = "Scroll Capture";
 const char kScrollCaptureDescription[] =
     "Enables scrolling screenshot capture for web contents.";
@@ -3553,9 +3507,9 @@
 const char kUpdateMenuTypeUpdateAvailable[] = "Update Available";
 const char kUpdateMenuTypeUnsupportedOSVersion[] = "Unsupported OS Version";
 
-extern const char kUseRealColorSpaceForAndroidVideoName[] =
+const char kUseRealColorSpaceForAndroidVideoName[] =
     "Use color space from MediaCodec";
-extern const char kUseRealColorSpaceForAndroidVideoDescription[] =
+const char kUseRealColorSpaceForAndroidVideoDescription[] =
     "When enabled video will use real color space instead of srgb.";
 
 const char kUserMediaScreenCapturingName[] = "Screen Capture API";
@@ -3589,14 +3543,6 @@
     "Enables showing the voice search button in the top toolbar. Enabling "
     "Adaptive Button overrides this.";
 
-const char kWalletRequiresFirstSyncSetupCompleteName[] =
-    "Controls whether Wallet (GPay) integration on Android requires "
-    "first-sync-setup to be complete";
-const char kWalletRequiresFirstSyncSetupCompleteDescription[] =
-    "Controls whether the Wallet (GPay) integration on Android requires "
-    "first-sync-setup to be complete. Only has an effect if "
-    "enable-autofill-account-wallet-storage is also enabled.";
-
 const char kWebBluetoothRequestLargerMtuName[] =
     "Request larger MTU for Web Bluetooth";
 const char kWebBluetoothRequestLargerMtuDescription[] =
@@ -3612,10 +3558,6 @@
     "Allows users to sort their web content in the web feed. "
     "Only works if Web Feed is also enabled.";
 
-const char kWebNotesStylizeName[] = "WebNotes Stylize";
-const char kWebNotesStylizeDescription[] =
-    "Allows users to create and share stylized webnotes.";
-
 const char kXsurfaceMetricsReportingName[] = "Xsurface Metrics Reporting";
 const char kXsurfaceMetricsReportingDescription[] =
     "Allows metrics reporting state to be passed to Xsurface";
@@ -4025,20 +3967,27 @@
 const char kAllowTouchpadHapticClickSettingsDescription[] =
     "Shows settings to adjust click sensitivity for haptic touchpads.";
 
-extern const char kAmbientModeNewUrlName[] =
+const char kAmbientModeAnimationName[] =
+    "Launch the Lottie animated ChromeOS Screensaver";
+const char kAmbientModeAnimationDescription[] =
+    "Launches the animated screensaver (as opposed to the existing photo "
+    "slideshow) when entering ambient mode. Currently, there is only one "
+    "animation theme available (feel the breeze).";
+
+const char kAmbientModeNewUrlName[] =
     "Use new backend URL format for ChromeOS Screensaver";
-extern const char kAmbientModeNewUrlDescription[] =
+const char kAmbientModeNewUrlDescription[] =
     "Use new backend URL format for ChromeOS Screensaver.  This helps with "
     "testing new backend migration.";
 
-extern const char kAppDiscoveryForOobeName[] =
+const char kAppDiscoveryForOobeName[] =
     "OOBE app recommendations with App Discovery Service.";
-extern const char kAppDiscoveryForOobeDescription[] =
+const char kAppDiscoveryForOobeDescription[] =
     "Use the App Discovery Service to request recommended apps for OOBE.";
 
-extern const char kAppDiscoveryRemoteUrlSearchName[] =
+const char kAppDiscoveryRemoteUrlSearchName[] =
     "Remote URL app discovery results";
-extern const char kAppDiscoveryRemoteUrlSearchDescription[] =
+const char kAppDiscoveryRemoteUrlSearchDescription[] =
     "Surface results from a URL in the app discovery service.";
 
 const char kAppServiceExternalProtocolName[] = "App Service External Protocol";
@@ -4324,9 +4273,9 @@
 const char kCrostiniVirtualKeyboardSupportDescription[] =
     "Experimental support for the Virtual Keyboard on Crostini.";
 
-extern const char kCryptAuthV2DedupDeviceLastActivityTimeName[] =
+const char kCryptAuthV2DedupDeviceLastActivityTimeName[] =
     "Dedup devices by last activity time";
-extern const char kCryptAuthV2DedupDeviceLastActivityTimeDescription[] =
+const char kCryptAuthV2DedupDeviceLastActivityTimeDescription[] =
     "Deduplicates phones in multi-device setup drop-down list by last "
     "activity time";
 
@@ -4909,6 +4858,17 @@
     "data migrated from ash. It also has a side effect that lacros will be "
     "disbled until profile migration is completed.";
 
+const char kLacrosProfileMigrationForceOffName[] = "Disable profile migration";
+const char kLacrosProfileMigrationForceOffDescription[] =
+    "Disables lacros profile migration. Lacros profile migration is being "
+    "rolled out to internal users first. Once lacros profile migration becomes "
+    "available to the user, the completion of profile migration becomes a "
+    "requirement to use lacros i.e. if profile migration gets rolled out to "
+    "the user and the migration fails, then lacros becomes unavailable until "
+    "the migration is completed. By enabling this flag, even if profile "
+    "migration is rolled out to the user, the migration will not run and the "
+    "user can continue to use lacros without profile migration.";
+
 const char kLimitShelfItemsToActiveDeskName[] =
     "Limit Shelf items to active desk";
 const char kLimitShelfItemsToActiveDeskDescription[] =
@@ -4984,6 +4944,11 @@
     "persistently scan and present a notification when a nearby device is "
     "attempting to share.";
 
+const char kNearbySharingOnePageOnboardingName[] =
+    "Nearby Sharing one-page Onboarding.";
+const char kNearbySharingOnePageOnboardingDescription[] =
+    "Enable new One-page onboarding workflow for Nearby Share.";
+
 const char kPcieBillboardNotificationName[] = "Pcie billboard notification";
 const char kPcieBillboardNotificationDescription[] =
     "Enable Pcie peripheral billboard notification.";
@@ -5039,8 +5004,8 @@
     "Enables the pre-load app window for "
     "ARC++ app during ARCVM booting stage on full restore process";
 
-extern const char kArcWindowPredictorName[] = "Enable ARC window predictor";
-extern const char kArcWindowPredictorDescription[] =
+const char kArcWindowPredictorName[] = "Enable ARC window predictor";
+const char kArcWindowPredictorDescription[] =
     "Enables the window state and bounds predictor for ARC task windows";
 
 const char kArcInputOverlayName[] = "Enable ARC Input Overlay";
@@ -5053,15 +5018,15 @@
 const char kScanAppMultiPageScanDescription[] =
     "Enables creating a single PDF file from multiple flatbed scans";
 
-extern const char kScanAppSearchablePdfName[] =
+const char kScanAppSearchablePdfName[] =
     "Enable saving scans as a searchable PDF.";
-extern const char kScanAppSearchablePdfDescription[] =
+const char kScanAppSearchablePdfDescription[] =
     "Allow selecting Searchable PDF file type in Scan app"
     " with incorporation of OCR service.";
 
-extern const char kSharesheetCopyToClipboardName[] =
+const char kSharesheetCopyToClipboardName[] =
     "Enable copy to clipboard in the Chrome OS Sharesheet.";
-extern const char kSharesheetCopyToClipboardDescription[] =
+const char kSharesheetCopyToClipboardDescription[] =
     "Enables a share action in the sharesheet that copies the selected data to "
     "the clipboard.";
 
@@ -5312,9 +5277,8 @@
 const char kEnableTtsLacrosSupportDescription[] =
     "Enable or disable lacros support for text to speech.";
 
-extern const char kLinkCapturingUiUpdateName[] =
-    "Enable updated link capturing UI";
-extern const char kLinkCapturingUiUpdateDescription[] =
+const char kLinkCapturingUiUpdateName[] = "Enable updated link capturing UI";
+const char kLinkCapturingUiUpdateDescription[] =
     "Enables updated UI for link capturing flows from the browser to apps, "
     "including the intent picker and an in-app link capturing prompt.";
 #endif  // defined(OS_CHROMEOS)
@@ -5526,15 +5490,6 @@
 
 #endif  // defined(TOOLKIT_VIEWS) || defined(OS_ANDROID)
 
-#if !defined(OS_WIN) && !defined(OS_FUCHSIA)
-const char kSendWebUIJavaScriptErrorReportsName[] =
-    "Send WebUI JavaScript Error Reports";
-const char kSendWebUIJavaScriptErrorReportsDescription[] =
-    "If enabled, and if the user has consented to sending metrics to Google, "
-    "then when the JavaScript has an error on a WebUI page, an error report "
-    "will be sent to Google.";
-#endif
-
 #if defined(OS_WIN) || defined(OS_ANDROID)
 const char kElasticOverscrollName[] = "Elastic Overscroll";
 const char kElasticOverscrollDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 689a3ab..118689d 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_FLAG_DESCRIPTIONS_H_
 
 // Includes needed for macros allowing conditional compilation of some strings.
-#include "base/check_op.h"
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
 #include "build/buildflag.h"
@@ -14,18 +13,11 @@
 #include "chrome/common/buildflags.h"
 #include "components/nacl/common/buildflags.h"
 #include "components/paint_preview/buildflags/buildflags.h"
-#include "components/signin/public/base/signin_buildflags.h"
-#include "components/spellcheck/spellcheck_buildflags.h"
-#include "device/vr/buildflags/buildflags.h"
 #include "media/media_buildflags.h"
 #include "ppapi/buildflags/buildflags.h"
 #include "printing/buildflags/buildflags.h"
 #include "third_party/blink/public/common/buildflags.h"
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
-#include "base/allocator/buildflags.h"
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
-
 // This file declares strings used in chrome://flags. These messages are not
 // translated, because instead of end-users they target Chromium developers and
 // testers. See https://crbug.com/587272 and https://crbug.com/703134 for more
@@ -99,6 +91,9 @@
 extern const char kDebugHistoryInterventionNoUserActivationName[];
 extern const char kDebugHistoryInterventionNoUserActivationDescription[];
 
+extern const char kDefaultChromeAppsMigrationName[];
+extern const char kDefaultChromeAppsMigrationDescription[];
+
 extern const char kForceStartupSigninPromoName[];
 extern const char kForceStartupSigninPromoDescription[];
 
@@ -127,9 +122,6 @@
 extern const char
     kEnablePolicyBlocklistThrottleRequiresPoliciesLoadedDescription[];
 
-extern const char kEnableSignedExchangePrefetchCacheForNavigationsName[];
-extern const char kEnableSignedExchangePrefetchCacheForNavigationsDescription[];
-
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
 extern const char kWebFilterInterstitialRefreshName[];
 extern const char kWebFilterInterstitialRefreshDescription[];
@@ -286,9 +278,6 @@
 extern const char kChromeLabsName[];
 extern const char kChromeLabsDescription[];
 
-extern const char kCompositeAfterPaintName[];
-extern const char kCompositeAfterPaintDescription[];
-
 extern const char kConsolidatedSiteStorageControlsName[];
 extern const char kConsolidatedSiteStorageControlsDescription[];
 
@@ -432,10 +421,6 @@
 extern const char
     kEnableAutofillInfoBarAccountIndicationFooterForSyncUsersDescription[];
 
-extern const char kEnableAutofillPasswordInfoBarAccountIndicationFooterName[];
-extern const char
-    kEnableAutofillPasswordInfoBarAccountIndicationFooterDescription[];
-
 extern const char kEnableAutofillSaveCardInfoBarAccountIndicationFooterName[];
 extern const char
     kEnableAutofillSaveCardInfoBarAccountIndicationFooterDescription[];
@@ -610,10 +595,8 @@
 
 extern const char kMemlogStackModeName[];
 extern const char kMemlogStackModeDescription[];
-extern const char kMemlogStackModeMixed[];
 extern const char kMemlogStackModeNative[];
 extern const char kMemlogStackModeNativeWithThreadNames[];
-extern const char kMemlogStackModePseudo[];
 
 extern const char kQuickActionSearchWidgetAndroidDinoVariantName[];
 extern const char kQuickActionSearchWidgetAndroidDinoVariantDescription[];
@@ -792,9 +775,6 @@
 extern const char kGlobalMediaControlsModernUIName[];
 extern const char kGlobalMediaControlsModernUIDescription[];
 
-extern const char kGlobalMediaControlsOverlayControlsName[];
-extern const char kGlobalMediaControlsOverlayControlsDescription[];
-
 extern const char kOpenscreenCastStreamingSessionName[];
 extern const char kOpenscreenCastStreamingSessionDescription[];
 
@@ -1053,15 +1033,9 @@
 extern const char kOmniboxDynamicMaxAutocompleteName[];
 extern const char kOmniboxDynamicMaxAutocompleteDescription[];
 
-extern const char kOmniboxWebUIOmniboxPopupName[];
-extern const char kOmniboxWebUIOmniboxPopupDescription[];
-
 extern const char kEnableSearchPrefetchName[];
 extern const char kEnableSearchPrefetchDescription[];
 
-extern const char kOopRasterizationName[];
-extern const char kOopRasterizationDescription[];
-
 extern const char kOopRasterizationDDLName[];
 extern const char kOopRasterizationDDLDescription[];
 
@@ -1190,9 +1164,6 @@
 extern const char kPrivacyReviewName[];
 extern const char kPrivacyReviewDescription[];
 
-extern const char kSafeBrowsingPerProfileNetworkContextsName[];
-extern const char kSafeBrowsingPerProfileNetworkContextsDescription[];
-
 extern const char kProminentDarkModeActiveTabTitleName[];
 extern const char kProminentDarkModeActiveTabTitleDescription[];
 
@@ -1407,9 +1378,6 @@
 extern const char kTabGroupsCollapseFreezingName[];
 extern const char kTabGroupsCollapseFreezingDescription[];
 
-extern const char kTabGroupsFeedbackName[];
-extern const char kTabGroupsFeedbackDescription[];
-
 extern const char kTabGroupsNewBadgePromoName[];
 extern const char kTabGroupsNewBadgePromoDescription[];
 
@@ -1595,14 +1563,10 @@
 extern const char kSharedHighlightingAmpName[];
 extern const char kSharedHighlightingAmpDescription[];
 
-extern const char kPreemptiveLinkToTextGenerationName[];
-extern const char kPreemptiveLinkToTextGenerationDescription[];
-
 extern const char kDraw1PredictedPoint12Ms[];
 extern const char kDraw2PredictedPoints6Ms[];
 extern const char kDraw1PredictedPoint6Ms[];
 extern const char kDraw2PredictedPoints3Ms[];
-extern const char kDrawPredictedPointsDefault[];
 extern const char kDrawPredictedPointsDescription[];
 extern const char kDrawPredictedPointsName[];
 
@@ -1991,9 +1955,6 @@
 extern const char kRequestDesktopSiteForTabletsName[];
 extern const char kRequestDesktopSiteForTabletsDescription[];
 
-extern const char kSafeBrowsingClientSideDetectionAndroidName[];
-extern const char kSafeBrowsingClientSideDetectionAndroidDescription[];
-
 extern const char kScrollCaptureName[];
 extern const char kScrollCaptureDescription[];
 
@@ -2065,9 +2026,6 @@
 extern const char kVoiceButtonInTopToolbarName[];
 extern const char kVoiceButtonInTopToolbarDescription[];
 
-extern const char kWalletRequiresFirstSyncSetupCompleteName[];
-extern const char kWalletRequiresFirstSyncSetupCompleteDescription[];
-
 extern const char kWebBluetoothRequestLargerMtuName[];
 extern const char kWebBluetoothRequestLargerMtuDescription[];
 
@@ -2077,9 +2035,6 @@
 extern const char kWebFeedSortName[];
 extern const char kWebFeedSortDescription[];
 
-extern const char kWebNotesStylizeName[];
-extern const char kWebNotesStylizeDescription[];
-
 extern const char kXsurfaceMetricsReportingName[];
 extern const char kXsurfaceMetricsReportingDescription[];
 
@@ -2323,6 +2278,9 @@
 extern const char kAllowTouchpadHapticClickSettingsName[];
 extern const char kAllowTouchpadHapticClickSettingsDescription[];
 
+extern const char kAmbientModeAnimationName[];
+extern const char kAmbientModeAnimationDescription[];
+
 extern const char kAmbientModeNewUrlName[];
 extern const char kAmbientModeNewUrlDescription[];
 
@@ -2788,6 +2746,9 @@
 extern const char kLacrosProfileMigrationForAnyUserName[];
 extern const char kLacrosProfileMigrationForAnyUserDescription[];
 
+extern const char kLacrosProfileMigrationForceOffName[];
+extern const char kLacrosProfileMigrationForceOffDescription[];
+
 extern const char kImeAssistPersonalInfoName[];
 extern const char kImeAssistPersonalInfoDescription[];
 
@@ -2877,6 +2838,9 @@
 extern const char kNearbySharingBackgroundScanningName[];
 extern const char kNearbySharingBackgroundScanningDescription[];
 
+extern const char kNearbySharingOnePageOnboardingName[];
+extern const char kNearbySharingOnePageOnboardingDescription[];
+
 extern const char kPcieBillboardNotificationName[];
 extern const char kPcieBillboardNotificationDescription[];
 
@@ -3222,11 +3186,6 @@
 
 #endif  // defined(TOOLKIT_VIEWS) || defined(OS_ANDROID)
 
-#if !defined(OS_WIN) && !defined(OS_FUCHSIA)
-extern const char kSendWebUIJavaScriptErrorReportsName[];
-extern const char kSendWebUIJavaScriptErrorReportsDescription[];
-#endif
-
 #if defined(OS_WIN) || defined(OS_ANDROID)
 extern const char kElasticOverscrollName[];
 extern const char kElasticOverscrollDescription[];
diff --git a/chrome/browser/google/google_search_domain_mixing_metrics_emitter.cc b/chrome/browser/google/google_search_domain_mixing_metrics_emitter.cc
index e43fdca..74ff143 100644
--- a/chrome/browser/google/google_search_domain_mixing_metrics_emitter.cc
+++ b/chrome/browser/google/google_search_domain_mixing_metrics_emitter.cc
@@ -85,7 +85,7 @@
   ui_thread_task_runner_->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&GoogleSearchDomainMixingMetricsEmitter::EmitMetrics,
-                     base::Unretained(this)),
+                     weak_ptr_factory_.GetWeakPtr()),
       // Delay at least ten seconds to avoid delaying browser startup.
       std::max(base::Seconds(10), last_domain_mixing_metrics_time +
                                       base::Days(1) - clock_->Now()));
@@ -122,7 +122,7 @@
     timer_->Start(FROM_HERE, base::Days(1),
                   base::BindRepeating(
                       &GoogleSearchDomainMixingMetricsEmitter::EmitMetrics,
-                      base::Unretained(this)));
+                      weak_ptr_factory_.GetWeakPtr()));
   }
 }
 
diff --git a/chrome/browser/google/google_search_domain_mixing_metrics_emitter.h b/chrome/browser/google/google_search_domain_mixing_metrics_emitter.h
index cb7443e9..9579174 100644
--- a/chrome/browser/google/google_search_domain_mixing_metrics_emitter.h
+++ b/chrome/browser/google/google_search_domain_mixing_metrics_emitter.h
@@ -9,6 +9,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/time/clock.h"
@@ -79,6 +80,8 @@
       std::make_unique<base::RepeatingTimer>();
   base::CancelableTaskTracker task_tracker_;
   scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner_;
+  base::WeakPtrFactory<GoogleSearchDomainMixingMetricsEmitter>
+      weak_ptr_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_GOOGLE_GOOGLE_SEARCH_DOMAIN_MIXING_METRICS_EMITTER_H_
diff --git a/chrome/browser/history/domain_diversity_reporter_factory.cc b/chrome/browser/history/domain_diversity_reporter_factory.cc
index 9a52edc..7da124c 100644
--- a/chrome/browser/history/domain_diversity_reporter_factory.cc
+++ b/chrome/browser/history/domain_diversity_reporter_factory.cc
@@ -10,6 +10,7 @@
 #include "base/no_destructor.h"
 #include "base/time/default_clock.h"
 #include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
@@ -18,6 +19,10 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/profiles/profile_helper.h"
+#endif
+
 // static
 DomainDiversityReporter* DomainDiversityReporterFactory::GetForProfile(
     Profile* profile) {
@@ -36,6 +41,21 @@
     content::BrowserContext* context) {
   Profile* profile = static_cast<Profile*>(context);
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // ChromeOS creates various profiles (login, lock screen...) that are not
+  // representative and should not have the reporter created for. Note that
+  // IsRegularProfile() returns true for these, so that ChromeOS specific APIs
+  // must be used to test for the type.
+  if (!chromeos::ProfileHelper::IsRegularProfile(profile))
+    return nullptr;
+#endif
+
+  // Incognito profiles share the HistoryService of the original profile, so no
+  // need for an instance for them. Guest and system profiles are not
+  // representative (guest in particular is transient) and not reported.
+  if (!profile->IsRegularProfile())
+    return nullptr;
+
   history::HistoryService* history_service =
       HistoryServiceFactory::GetForProfile(profile,
                                            ServiceAccessType::EXPLICIT_ACCESS);
diff --git a/chrome/browser/lacros/browser_service_lacros.cc b/chrome/browser/lacros/browser_service_lacros.cc
index 43d7e7d..f23ade8 100644
--- a/chrome/browser/lacros/browser_service_lacros.cc
+++ b/chrome/browser/lacros/browser_service_lacros.cc
@@ -146,7 +146,9 @@
       session_restore_available = true;
   }
 
-  if (ProfilePicker::ShouldShowAtLaunch() && !session_restore_available) {
+  if (ProfilePicker::ShouldShowAtLaunch() && !session_restore_available &&
+      !incognito) {
+    // Profile picker does not support passing through the incognito param.
     ProfilePicker::Show(
         ProfilePicker::EntryPoint::kNewSessionOnExistingProcess);
   } else {
diff --git a/chrome/browser/lacros/browser_service_lacros_browsertest.cc b/chrome/browser/lacros/browser_service_lacros_browsertest.cc
index 49ae052..06dec44 100644
--- a/chrome/browser/lacros/browser_service_lacros_browsertest.cc
+++ b/chrome/browser/lacros/browser_service_lacros_browsertest.cc
@@ -2,12 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "chrome/browser/chromeos/app_mode/app_session.h"
 #include "chrome/browser/lacros/app_mode/kiosk_session_service_lacros.h"
 #include "chrome/browser/lacros/browser_service_lacros.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/profile_picker.h"
@@ -128,20 +130,35 @@
                        ProfilePickerOpensOnStartup) {
   // Create an additional profile.
   ProfileManager* profile_manager = g_browser_process->profile_manager();
-  ProfileAttributesStorage& storage =
-      profile_manager->GetProfileAttributesStorage();
   base::FilePath path_profile2 =
       profile_manager->user_data_dir().Append(FILE_PATH_LITERAL("Profile 2"));
 
   base::RunLoop run_loop;
+  Profile* profile2;
   profile_manager->CreateProfileAsync(
       path_profile2, base::BindLambdaForTesting(
                          [&](Profile* profile, Profile::CreateStatus status) {
-                           run_loop.Quit();
+                           if (status == Profile::CREATE_STATUS_INITIALIZED) {
+                             profile2 = profile;
+                             run_loop.Quit();
+                           }
                          }));
   run_loop.Run();
-  ASSERT_EQ(2u, storage.GetNumberOfProfiles());
-  storage.GetProfileAttributesWithPath(path_profile2)->SetActiveTimeToNow();
+  // Open a browser window to make it the last used profile.
+  chrome::NewEmptyWindow(profile2);
+  ui_test_utils::WaitForBrowserToOpen();
+
+  // Profile picker does _not_ open for incognito windows. Instead, the
+  // incognito window for the last used profile is directly opened.
+  base::RunLoop run_loop2;
+  browser_service()->NewWindow(
+      /*incognito=*/true, /*should_trigger_session_restore=*/false,
+      /*callback=*/base::BindLambdaForTesting([&]() { run_loop2.Quit(); }));
+  run_loop2.Run();
+  EXPECT_FALSE(ProfilePicker::IsOpen());
+  Profile* profile = BrowserList::GetInstance()->GetLastActive()->profile();
+  EXPECT_EQ(profile->GetPath(), path_profile2);
+  EXPECT_TRUE(profile->IsOffTheRecord());
 
   browser_service()->NewWindow(
       /*incognito=*/false, /*should_trigger_session_restore=*/false,
diff --git a/chrome/browser/login_detection/login_detection_prefs.cc b/chrome/browser/login_detection/login_detection_prefs.cc
index 49ffe5d..7200a9a 100644
--- a/chrome/browser/login_detection/login_detection_prefs.cc
+++ b/chrome/browser/login_detection/login_detection_prefs.cc
@@ -38,7 +38,7 @@
 }
 
 void SaveSiteToOAuthSignedInList(PrefService* pref_service, const GURL& url) {
-  DictionaryPrefUpdate update(pref_service, kOAuthSignedInSitesPref);
+  DictionaryPrefUpdateDeprecated update(pref_service, kOAuthSignedInSitesPref);
   base::DictionaryValue* dict = update.Get();
   dict->SetKey(GetSiteNameForURL(url), base::TimeToValue(base::Time::Now()));
 
diff --git a/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc b/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc
index 38d78e8f..f52eab1 100644
--- a/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc
+++ b/chrome/browser/media/android/cdm/media_drm_origin_id_manager.cc
@@ -80,7 +80,7 @@
   if (media::MediaDrmBridge::IsPerApplicationProvisioningSupported())
     return;
 
-  DictionaryPrefUpdate update(pref_service, kMediaDrmOriginIds);
+  DictionaryPrefUpdateDeprecated update(pref_service, kMediaDrmOriginIds);
   auto* origin_id_dict = update.Get();
   origin_id_dict->SetKey(
       kExpirableToken, base::TimeToValue(base::Time::Now() + kExpirationDelta));
@@ -152,7 +152,7 @@
 base::UnguessableToken TakeFirstOriginId(PrefService* const pref_service) {
   DVLOG(3) << __func__;
 
-  DictionaryPrefUpdate update(pref_service, kMediaDrmOriginIds);
+  DictionaryPrefUpdateDeprecated update(pref_service, kMediaDrmOriginIds);
   auto* origin_id_dict = update.Get();
   DCHECK(origin_id_dict->is_dict());
 
@@ -383,7 +383,7 @@
 
   // On devices that need to, check that the user has recently requested
   // an origin ID. If not, then skip pre-provisioning on those devices.
-  DictionaryPrefUpdate update(pref_service_, kMediaDrmOriginIds);
+  DictionaryPrefUpdateDeprecated update(pref_service_, kMediaDrmOriginIds);
   if (!CanPreProvision(update.Get())) {
     // Disable any network monitoring, if it exists.
     network_observer_.reset();
@@ -511,7 +511,7 @@
              origin_id);
     pending_provisioned_origin_id_cbs_.pop();
   } else {
-    DictionaryPrefUpdate update(pref_service_, kMediaDrmOriginIds);
+    DictionaryPrefUpdateDeprecated update(pref_service_, kMediaDrmOriginIds);
     AddOriginId(update.Get(), origin_id.value());
 
     // If we already have enough pre-provisioned origin IDs, we're done.
diff --git a/chrome/browser/media/cdm_document_service_impl_test.cc b/chrome/browser/media/cdm_document_service_impl_test.cc
index ae85d16..58965b6 100644
--- a/chrome/browser/media/cdm_document_service_impl_test.cc
+++ b/chrome/browser/media/cdm_document_service_impl_test.cc
@@ -113,7 +113,8 @@
     entry.SetKey(kOriginId, base::UnguessableTokenToValue(
                                 base::UnguessableToken::Create()));
 
-    DictionaryPrefUpdate update(user_prefs, prefs::kMediaCdmOriginData);
+    DictionaryPrefUpdateDeprecated update(user_prefs,
+                                          prefs::kMediaCdmOriginData);
     base::DictionaryValue* dict = update.Get();
     const std::string serialized_origin =
         web_contents()->GetMainFrame()->GetLastCommittedOrigin().Serialize();
diff --git a/chrome/browser/media/cdm_pref_service_helper.cc b/chrome/browser/media/cdm_pref_service_helper.cc
index 00c1e65..8a2b722 100644
--- a/chrome/browser/media/cdm_pref_service_helper.cc
+++ b/chrome/browser/media/cdm_pref_service_helper.cc
@@ -174,7 +174,7 @@
     const base::RepeatingCallback<bool(const GURL&)>& filter) {
   DVLOG(1) << __func__ << " From [" << start << ", " << end << "]";
 
-  DictionaryPrefUpdate update(user_prefs, prefs::kMediaCdmOriginData);
+  DictionaryPrefUpdateDeprecated update(user_prefs, prefs::kMediaCdmOriginData);
 
   std::vector<std::string> origins_to_delete;
   for (auto key_value : update->DictItems()) {
@@ -244,7 +244,8 @@
   // Create an new entry or overwrite the existing one in case we weren't able
   // to get a valid origin ID from `FromDictValue()`.
   if (!cdm_pref_data) {
-    DictionaryPrefUpdate update(user_prefs, prefs::kMediaCdmOriginData);
+    DictionaryPrefUpdateDeprecated update(user_prefs,
+                                          prefs::kMediaCdmOriginData);
     base::DictionaryValue* update_dict = update.Get();
 
     cdm_pref_data = std::make_unique<CdmPrefData>(
@@ -268,7 +269,7 @@
   const std::string serialized_cdm_origin = cdm_origin.Serialize();
   DCHECK(!serialized_cdm_origin.empty());
 
-  DictionaryPrefUpdate update(user_prefs, prefs::kMediaCdmOriginData);
+  DictionaryPrefUpdateDeprecated update(user_prefs, prefs::kMediaCdmOriginData);
   base::DictionaryValue* dict = update.Get();
 
   base::Value* dict_value =
diff --git a/chrome/browser/media/router/BUILD.gn b/chrome/browser/media/router/BUILD.gn
index f1db1df..51f169d8 100644
--- a/chrome/browser/media/router/BUILD.gn
+++ b/chrome/browser/media/router/BUILD.gn
@@ -196,6 +196,8 @@
     deps = [
       "discovery",
       "//chrome/test:test_support",
+      "//components/cast_channel:cast_channel",
+      "//components/cast_channel:test_support",
       "//components/media_router/browser:test_support",
       "//components/media_router/common:test_support",
       "//components/media_router/common/mojom:media_router",
@@ -204,6 +206,8 @@
     ]
     public_deps = [ ":router" ]
     sources = [
+      "discovery/mdns/cast_media_sink_service_test_helpers.cc",
+      "discovery/mdns/cast_media_sink_service_test_helpers.h",
       "providers/test/test_media_route_provider.cc",
       "providers/test/test_media_route_provider.h",
       "test/media_router_mojo_test.cc",
@@ -257,7 +261,7 @@
       "discovery/dial/dial_device_data_unittest.cc",
       "discovery/dial/dial_media_sink_service_impl_unittest.cc",
       "discovery/dial/dial_registry_unittest.cc",
-      "discovery/dial/dial_service_unittest.cc",
+      "discovery/dial/dial_service_impl_unittest.cc",
       "discovery/dial/dial_url_fetcher_unittest.cc",
       "discovery/dial/safe_dial_app_info_parser_unittest.cc",
       "discovery/dial/safe_dial_device_description_parser_unittest.cc",
diff --git a/chrome/browser/media/router/discovery/BUILD.gn b/chrome/browser/media/router/discovery/BUILD.gn
index d62540d..f0aded11 100644
--- a/chrome/browser/media/router/discovery/BUILD.gn
+++ b/chrome/browser/media/router/discovery/BUILD.gn
@@ -58,8 +58,9 @@
     "dial/dial_media_sink_service_impl.h",
     "dial/dial_registry.cc",
     "dial/dial_registry.h",
-    "dial/dial_service.cc",
     "dial/dial_service.h",
+    "dial/dial_service_impl.cc",
+    "dial/dial_service_impl.h",
     "dial/dial_url_fetcher.cc",
     "dial/dial_url_fetcher.h",
     "dial/parsed_dial_app_info.cc",
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.cc b/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.cc
index 0df70fd8..a446e229d 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.cc
@@ -23,7 +23,8 @@
 
 namespace {
 
-uint8_t ConvertDeviceCapabilitiesToInt(DeviceCapabilities proto) {
+uint8_t ConvertDeviceCapabilitiesToInt(
+    chrome_browser_media::proto::DeviceCapabilities proto) {
   // Meaning of capacity value for each bit:
   // NONE: 0,
   // VIDEO_OUT: 1 << 0,
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.h b/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.h
index 5c56b6c..ae155f3 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.h
+++ b/chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.h
@@ -13,7 +13,6 @@
 
 using DiscoveryDevice = chrome_browser_media::proto::DiscoveryDevice;
 using NetworkInfo = chrome_browser_media::proto::NetworkInfo;
-using DeviceCapabilities = chrome_browser_media::proto::DeviceCapabilities;
 
 // Creates a MediaSinkInternal from |discovery_device|. |cast_sink| is only
 // valid if the returned result is |kOk|.
diff --git a/chrome/browser/media/router/discovery/dial/dial_registry.cc b/chrome/browser/media/router/discovery/dial/dial_registry.cc
index 7fa09ac..87623d1 100644
--- a/chrome/browser/media/router/discovery/dial/dial_registry.cc
+++ b/chrome/browser/media/router/discovery/dial/dial_registry.cc
@@ -13,7 +13,7 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/browser/media/router/discovery/dial/dial_device_data.h"
-#include "chrome/browser/media/router/discovery/dial/dial_service.h"
+#include "chrome/browser/media/router/discovery/dial/dial_service_impl.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/network_service_instance.h"
diff --git a/chrome/browser/media/router/discovery/dial/dial_service.cc b/chrome/browser/media/router/discovery/dial/dial_service.cc
deleted file mode 100644
index e029c5d0..0000000
--- a/chrome/browser/media/router/discovery/dial/dial_service.cc
+++ /dev/null
@@ -1,581 +0,0 @@
-// Copyright (c) 2012 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/media/router/discovery/dial/dial_service.h"
-
-#include <stdint.h>
-
-#include <algorithm>
-#include <set>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/check_op.h"
-#include "base/location.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
-#include "chrome/browser/media/router/discovery/dial/dial_device_data.h"
-#include "components/version_info/version_info.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/network_service_instance.h"
-#include "net/base/address_family.h"
-#include "net/base/io_buffer.h"
-#include "net/base/ip_endpoint.h"
-#include "net/base/net_errors.h"
-#include "net/base/network_interfaces.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_util.h"
-#include "net/log/net_log.h"
-#include "net/log/net_log_source.h"
-#include "services/network/public/mojom/network_service.mojom.h"
-#include "url/gurl.h"
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "base/task/task_runner_util.h"
-#include "chromeos/network/network_state.h"
-#include "chromeos/network/network_state_handler.h"
-#include "third_party/cros_system_api/dbus/service_constants.h"
-#endif
-
-using base::Time;
-using content::BrowserThread;
-using net::HttpResponseHeaders;
-using net::HttpUtil;
-using net::IOBufferWithSize;
-using net::IPAddress;
-using net::NetworkInterface;
-using net::NetworkInterfaceList;
-using net::StringIOBuffer;
-using net::UDPSocket;
-
-namespace media_router {
-
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
-void PostSendNetworkList(
-    base::WeakPtr<DialServiceImpl> impl,
-    const absl::optional<net::NetworkInterfaceList>& networks) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  content::GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE, base::BindOnce(&DialServiceImpl::SendNetworkList,
-                                std::move(impl), networks));
-}
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
-
-namespace {
-
-// The total number of requests to make per discovery cycle.
-const int kDialMaxRequests = 4;
-
-// The interval to wait between successive requests.
-const int kDialRequestIntervalMillis = 1000;
-
-// The maximum delay a device may wait before responding (MX).
-const int kDialMaxResponseDelaySecs = 1;
-
-// The maximum time a response is expected after a M-SEARCH request.
-const int kDialResponseTimeoutSecs = 2;
-
-// The multicast IP address for discovery.
-const char kDialRequestAddress[] = "239.255.255.250";
-
-// The UDP port number for discovery.
-const uint16_t kDialRequestPort = 1900;
-
-// The DIAL service type as part of the search request.
-const char kDialSearchType[] = "urn:dial-multiscreen-org:service:dial:1";
-
-// SSDP headers parsed from the response.
-const char kSsdpLocationHeader[] = "LOCATION";
-const char kSsdpCacheControlHeader[] = "CACHE-CONTROL";
-const char kSsdpConfigIdHeader[] = "CONFIGID.UPNP.ORG";
-const char kSsdpUsnHeader[] = "USN";
-constexpr char kSsdpMaxAgeDirective[] = "max-age";
-constexpr int kSsdpMaxMaxAge = 3600;
-constexpr int kSsdpMaxConfigId = (2 << 24) - 1;
-
-// The receive buffer size, in bytes.
-const int kDialRecvBufferSize = 1500;
-
-// Gets a specific header from |headers| and puts it in |value|.
-bool GetHeader(HttpResponseHeaders* headers,
-               const char* name,
-               std::string* value) {
-  return headers->EnumerateHeader(nullptr, std::string(name), value);
-}
-
-// Returns the request string.
-std::string BuildRequest() {
-  // Extra line at the end to make UPnP lib happy.
-  std::string request(base::StringPrintf(
-      "M-SEARCH * HTTP/1.1\r\n"
-      "HOST: %s:%u\r\n"
-      "MAN: \"ssdp:discover\"\r\n"
-      "MX: %d\r\n"
-      "ST: %s\r\n"
-      "USER-AGENT: %s/%s %s\r\n"
-      "\r\n",
-      kDialRequestAddress, kDialRequestPort, kDialMaxResponseDelaySecs,
-      kDialSearchType, version_info::GetProductName().c_str(),
-      version_info::GetVersionNumber().c_str(),
-      version_info::GetOSType().c_str()));
-  // 1500 is a good MTU value for most Ethernet LANs.
-  DCHECK_LE(request.size(), 1500U);
-  return request;
-}
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-// Finds the IP address of the preferred interface of network type |type|
-// to bind the socket and inserts the address into |bind_address_list|. This
-// ChromeOS version can prioritize wifi and ethernet interfaces.
-void InsertBestBindAddressChromeOS(const chromeos::NetworkTypePattern& type,
-                                   net::IPAddressList* bind_address_list) {
-  const chromeos::NetworkState* state = chromeos::NetworkHandler::Get()
-                                            ->network_state_handler()
-                                            ->ConnectedNetworkByType(type);
-  if (!state)
-    return;
-  std::string state_ip_address = state->GetIpAddress();
-  IPAddress bind_ip_address;
-  if (bind_ip_address.AssignFromIPLiteral(state_ip_address) &&
-      bind_ip_address.IsIPv4()) {
-    bind_address_list->push_back(bind_ip_address);
-  }
-}
-
-net::IPAddressList GetBestBindAddressOnUIThread() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  net::IPAddressList bind_address_list;
-  if (chromeos::NetworkHandler::IsInitialized()) {
-    InsertBestBindAddressChromeOS(chromeos::NetworkTypePattern::Ethernet(),
-                                  &bind_address_list);
-    InsertBestBindAddressChromeOS(chromeos::NetworkTypePattern::WiFi(),
-                                  &bind_address_list);
-  }
-  return bind_address_list;
-}
-#else
-// This function and PostSendNetworkList together handle DialServiceImpl's use
-// of the network service, while keeping all of DialServiceImpl running on the
-// IO thread.  DialServiceImpl has a legacy threading model, where it was
-// designed to be called from the UI thread and run on the IO thread.  Although
-// a WeakPtr is desired for safety when posting tasks, they are not
-// thread/sequence-safe.  DialServiceImpl's simple use of the network service,
-// however, doesn't actually require that any of its state be accessed on the UI
-// thread.  Therefore, the UI thread functions can be free functions which just
-// pass-through an IO thread WeakPtr which will be used when passing the network
-// service result back to the IO thread.  This model will change when the
-// network service is fully launched and this code is updated.
-void GetNetworkListOnUIThread(base::WeakPtr<DialServiceImpl> impl) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  content::GetNetworkService()->GetNetworkList(
-      net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES,
-      base::BindOnce(&PostSendNetworkList, std::move(impl)));
-}
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-}  // namespace
-
-DialServiceImpl::DialSocket::DialSocket(DialServiceImpl* dial_service)
-    : is_writing_(false), is_reading_(false), dial_service_(dial_service) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(dial_service_);
-}
-
-DialServiceImpl::DialSocket::~DialSocket() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-}
-
-bool DialServiceImpl::DialSocket::CreateAndBindSocket(
-    const IPAddress& bind_ip_address,
-    net::NetLog* net_log) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(!socket_);
-  DCHECK(bind_ip_address.IsIPv4());
-
-  socket_ = std::make_unique<UDPSocket>(net::DatagramSocket::RANDOM_BIND,
-                                        net_log, net::NetLogSource());
-
-  // 0 means bind a random port
-  net::IPEndPoint address(bind_ip_address, 0);
-
-  if (socket_->Open(address.GetFamily()) != net::OK ||
-      socket_->SetBroadcast(true) != net::OK ||
-      !CheckResult("Bind", socket_->Bind(address))) {
-    socket_.reset();
-    return false;
-  }
-
-  recv_buffer_ = base::MakeRefCounted<IOBufferWithSize>(kDialRecvBufferSize);
-  return ReadSocket();
-}
-
-void DialServiceImpl::DialSocket::SendOneRequest(
-    const net::IPEndPoint& send_address,
-    const scoped_refptr<net::StringIOBuffer>& send_buffer) {
-  if (!socket_) {
-    return;
-  }
-
-  if (is_writing_) {
-    return;
-  }
-
-  is_writing_ = true;
-  int result = socket_->SendTo(
-      send_buffer.get(), send_buffer->size(), send_address,
-      base::BindOnce(&DialServiceImpl::DialSocket::OnSocketWrite,
-                     base::Unretained(this), send_buffer->size()));
-  bool result_ok = CheckResult("SendTo", result);
-  if (result_ok && result > 0) {
-    // Synchronous write.
-    OnSocketWrite(send_buffer->size(), result);
-  }
-}
-
-bool DialServiceImpl::DialSocket::IsClosed() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  return !socket_;
-}
-
-bool DialServiceImpl::DialSocket::CheckResult(const char* operation,
-                                              int result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (result < net::OK && result != net::ERR_IO_PENDING) {
-    Close();
-    std::string error_str(net::ErrorToString(result));
-    dial_service_->NotifyOnError();
-    return false;
-  }
-  return true;
-}
-
-void DialServiceImpl::DialSocket::Close() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  is_reading_ = false;
-  is_writing_ = false;
-  socket_.reset();
-}
-
-void DialServiceImpl::DialSocket::OnSocketWrite(int send_buffer_size,
-                                                int result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  is_writing_ = false;
-  if (!CheckResult("OnSocketWrite", result))
-    return;
-  dial_service_->NotifyOnDiscoveryRequest();
-}
-
-bool DialServiceImpl::DialSocket::ReadSocket() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (!socket_)
-    return false;
-
-  if (is_reading_)
-    return false;
-
-  int result = net::OK;
-  bool result_ok = true;
-  do {
-    is_reading_ = true;
-    result = socket_->RecvFrom(
-        recv_buffer_.get(), kDialRecvBufferSize, &recv_address_,
-        base::BindOnce(&DialServiceImpl::DialSocket::OnSocketRead,
-                       base::Unretained(this)));
-    result_ok = CheckResult("RecvFrom", result);
-    if (result != net::ERR_IO_PENDING)
-      is_reading_ = false;
-    if (result_ok && result > 0) {
-      // Synchronous read.
-      HandleResponse(result);
-    }
-  } while (result_ok && result != net::OK && result != net::ERR_IO_PENDING);
-  return result_ok;
-}
-
-void DialServiceImpl::DialSocket::OnSocketRead(int result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  is_reading_ = false;
-  if (!CheckResult("OnSocketRead", result))
-    return;
-  if (result > 0)
-    HandleResponse(result);
-
-  // Await next response.
-  ReadSocket();
-}
-
-void DialServiceImpl::DialSocket::HandleResponse(int bytes_read) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK_GT(bytes_read, 0);
-  if (bytes_read > kDialRecvBufferSize) {
-    return;
-  }
-
-  std::string response(recv_buffer_->data(), bytes_read);
-  Time response_time = Time::Now();
-
-  // Attempt to parse response, notify client if successful.
-  DialDeviceData parsed_device;
-  if (ParseResponse(response, response_time, &parsed_device))
-    dial_service_->NotifyOnDeviceDiscovered(parsed_device);
-}
-
-bool DialServiceImpl::DialSocket::ParseResponse(const std::string& response,
-                                                const base::Time& response_time,
-                                                DialDeviceData* device) {
-  device->set_ip_address(recv_address_.address());
-  device->set_response_time(response_time);
-
-  size_t headers_end =
-      HttpUtil::LocateEndOfHeaders(response.c_str(), response.size());
-  if (headers_end == 0 || headers_end == std::string::npos) {
-    return false;
-  }
-  std::string raw_headers = HttpUtil::AssembleRawHeaders(
-      base::StringPiece(response.c_str(), headers_end));
-  auto headers = base::MakeRefCounted<HttpResponseHeaders>(raw_headers);
-
-  std::string device_url_str;
-  if (!GetHeader(headers.get(), kSsdpLocationHeader, &device_url_str) ||
-      device_url_str.empty()) {
-    return false;
-  }
-
-  GURL device_url(device_url_str);
-  if (device->IsValidUrl(device_url)) {
-    device->set_device_description_url(device_url);
-  } else {
-    return false;
-  }
-
-  std::string device_id;
-  if (!GetHeader(headers.get(), kSsdpUsnHeader, &device_id) ||
-      device_id.empty()) {
-    return false;
-  }
-  device->set_device_id(device_id);
-
-  std::string cache_control;
-  if (GetHeader(headers.get(), kSsdpCacheControlHeader, &cache_control) &&
-      !cache_control.empty()) {
-    std::vector<std::string> cache_control_directives = base::SplitString(
-        cache_control, "=", base::WhitespaceHandling::TRIM_WHITESPACE,
-        base::SplitResult::SPLIT_WANT_NONEMPTY);
-    if (cache_control_directives.size() == 2 &&
-        base::EqualsCaseInsensitiveASCII(cache_control_directives[0],
-                                         kSsdpMaxAgeDirective)) {
-      int max_age = 0;
-      if (base::StringToInt(cache_control_directives[1], &max_age) &&
-          max_age > 0) {
-        device->set_max_age(std::min(max_age, kSsdpMaxMaxAge));
-      }
-    }
-  }
-
-  std::string config_id;
-  int config_id_int;
-  if (GetHeader(headers.get(), kSsdpConfigIdHeader, &config_id) &&
-      !config_id.empty() && base::StringToInt(config_id, &config_id_int) &&
-      config_id_int > 0 && config_id_int <= kSsdpMaxConfigId) {
-    device->set_config_id(config_id_int);
-  }
-  return true;
-}
-
-DialServiceImpl::DialServiceImpl(DialService::Client& client,
-                                 net::NetLog* net_log)
-    : client_(client),
-      net_log_(net_log),
-      discovery_active_(false),
-      num_requests_sent_(0),
-      max_requests_(kDialMaxRequests),
-      finish_delay_(base::Milliseconds((kDialMaxRequests - 1) *
-                                       kDialRequestIntervalMillis) +
-                    base::Seconds(kDialResponseTimeoutSecs)),
-      request_interval_(base::Milliseconds(kDialRequestIntervalMillis)) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  IPAddress address;
-  bool success = address.AssignFromIPLiteral(kDialRequestAddress);
-  DCHECK(success);
-  send_address_ = net::IPEndPoint(address, kDialRequestPort);
-  send_buffer_ = base::MakeRefCounted<StringIOBuffer>(BuildRequest());
-}
-
-DialServiceImpl::~DialServiceImpl() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-}
-
-bool DialServiceImpl::Discover() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (discovery_active_) {
-    return false;
-  }
-  discovery_active_ = true;
-
-
-  StartDiscovery();
-  return true;
-}
-
-void DialServiceImpl::StartDiscovery() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(discovery_active_);
-  if (HasOpenSockets()) {
-    return;
-  }
-
-  auto ui_task_runner = content::GetUIThreadTaskRunner({});
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  ui_task_runner->PostTaskAndReplyWithResult(
-      FROM_HERE, base::BindOnce(&GetBestBindAddressOnUIThread),
-      base::BindOnce(&DialServiceImpl::DiscoverOnAddresses,
-                     weak_ptr_factory_.GetWeakPtr()));
-#else
-  ui_task_runner->PostTask(FROM_HERE,
-                           base::BindOnce(&GetNetworkListOnUIThread,
-                                          weak_ptr_factory_.GetWeakPtr()));
-#endif
-}
-
-void DialServiceImpl::SendNetworkList(
-    const absl::optional<NetworkInterfaceList>& networks) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  using InterfaceIndexAddressFamily = std::pair<uint32_t, net::AddressFamily>;
-  std::set<InterfaceIndexAddressFamily> interface_index_addr_family_seen;
-  net::IPAddressList ip_addresses;
-
-  if (networks.has_value()) {
-    // Binds a socket to each IPv4 network interface found. Note that
-    // there may be duplicates in |networks|, so address family + interface
-    // index is used to identify unique interfaces.
-    // TODO(mfoltz): Support IPV6 multicast.  http://crbug.com/165286
-    for (const auto& network : *networks) {
-      net::AddressFamily addr_family = net::GetAddressFamily(network.address);
-      if (addr_family == net::ADDRESS_FAMILY_IPV4) {
-        InterfaceIndexAddressFamily interface_index_addr_family =
-            std::make_pair(network.interface_index, addr_family);
-        bool inserted =
-            interface_index_addr_family_seen.insert(interface_index_addr_family)
-                .second;
-        // We have not seen this interface before, so add its IP address to the
-        // discovery list.
-        if (inserted) {
-          ip_addresses.push_back(network.address);
-        }
-      }
-    }
-  } else {
-  }
-
-  DiscoverOnAddresses(ip_addresses);
-}
-
-void DialServiceImpl::DiscoverOnAddresses(
-    const net::IPAddressList& ip_addresses) {
-  if (ip_addresses.empty()) {
-    FinishDiscovery();
-    return;
-  }
-
-  // Schedule a timer to finish the discovery process (and close the sockets).
-  if (finish_delay_ > base::Seconds(0)) {
-    finish_timer_.Start(FROM_HERE, finish_delay_, this,
-                        &DialServiceImpl::FinishDiscovery);
-  }
-
-  for (const auto& address : ip_addresses) {
-    BindAndAddSocket(address);
-  }
-
-  SendOneRequest();
-}
-
-void DialServiceImpl::BindAndAddSocket(const IPAddress& bind_ip_address) {
-  std::unique_ptr<DialServiceImpl::DialSocket> dial_socket(CreateDialSocket());
-  if (dial_socket->CreateAndBindSocket(bind_ip_address, net_log_))
-    dial_sockets_.push_back(std::move(dial_socket));
-}
-
-std::unique_ptr<DialServiceImpl::DialSocket>
-DialServiceImpl::CreateDialSocket() {
-  return std::make_unique<DialServiceImpl::DialSocket>(this);
-}
-
-void DialServiceImpl::SendOneRequest() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (num_requests_sent_ == max_requests_) {
-    request_timer_.Stop();
-    return;
-  }
-  num_requests_sent_++;
-  for (const auto& socket : dial_sockets_) {
-    if (!socket->IsClosed())
-      socket->SendOneRequest(send_address_, send_buffer_);
-  }
-}
-
-void DialServiceImpl::NotifyOnDiscoveryRequest() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  // If discovery is inactive, no reason to notify client.
-  if (!discovery_active_) {
-    return;
-  }
-
-  client_.OnDiscoveryRequest();
-  // If we need to send additional requests, schedule a timer to do so.
-  if (num_requests_sent_ < max_requests_ && num_requests_sent_ == 1) {
-    // TODO(imcheng): Move this to SendOneRequest() once the implications are
-    // understood.
-    request_timer_.Start(FROM_HERE, request_interval_, this,
-                         &DialServiceImpl::SendOneRequest);
-  }
-}
-
-void DialServiceImpl::NotifyOnDeviceDiscovered(
-    const DialDeviceData& device_data) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (!discovery_active_) {
-    return;
-  }
-  client_.OnDeviceDiscovered(device_data);
-}
-
-void DialServiceImpl::NotifyOnError() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  client_.OnError(HasOpenSockets() ? DIAL_SERVICE_SOCKET_ERROR
-                                   : DIAL_SERVICE_NO_INTERFACES);
-}
-
-void DialServiceImpl::FinishDiscovery() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(discovery_active_);
-  // Close all open sockets.
-  dial_sockets_.clear();
-  finish_timer_.Stop();
-  request_timer_.Stop();
-  discovery_active_ = false;
-  num_requests_sent_ = 0;
-  client_.OnDiscoveryFinished();
-}
-
-bool DialServiceImpl::HasOpenSockets() {
-  for (const auto& socket : dial_sockets_) {
-    if (!socket->IsClosed())
-      return true;
-  }
-  return false;
-}
-
-}  // namespace media_router
diff --git a/chrome/browser/media/router/discovery/dial/dial_service.h b/chrome/browser/media/router/discovery/dial/dial_service.h
index cd8a047b..7152e84 100644
--- a/chrome/browser/media/router/discovery/dial/dial_service.h
+++ b/chrome/browser/media/router/discovery/dial/dial_service.h
@@ -5,22 +5,6 @@
 #ifndef CHROME_BROWSER_MEDIA_ROUTER_DISCOVERY_DIAL_DIAL_SERVICE_H_
 #define CHROME_BROWSER_MEDIA_ROUTER_DISCOVERY_DIAL_DIAL_SERVICE_H_
 
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/gtest_prod_util.h"
-#include "base/memory/raw_ptr.h"
-#include "base/timer/timer.h"
-#include "net/base/ip_address.h"
-#include "net/socket/udp_socket.h"
-
-namespace net {
-class IPEndPoint;
-class StringIOBuffer;
-class NetLog;
-}  // namespace net
-
 namespace media_router {
 
 class DialDeviceData;
@@ -83,201 +67,6 @@
   virtual bool Discover() = 0;
 };
 
-// Implements DialService.
-//
-// NOTE(mfoltz): It would make this class cleaner to refactor most of the state
-// associated with a single discovery cycle into its own |DiscoveryOperation|
-// object.  This would also simplify lifetime of the object w.r.t. DialRegistry;
-// the Registry would not need to create/destroy the Service on demand.
-// DialServiceImpl lives on the IO thread.
-class DialServiceImpl : public DialService {
- public:
-  DialServiceImpl(DialService::Client& client, net::NetLog* net_log);
-
-  DialServiceImpl(const DialServiceImpl&) = delete;
-  DialServiceImpl(DialServiceImpl&&) = delete;
-  DialServiceImpl& operator=(const DialServiceImpl&) = delete;
-  DialServiceImpl& operator=(DialServiceImpl&&) = delete;
-
-  ~DialServiceImpl() override;
-
-  // DialService implementation
-  bool Discover() override;
-
- private:
-  friend void PostSendNetworkList(
-      base::WeakPtr<DialServiceImpl> impl,
-      const absl::optional<net::NetworkInterfaceList>& networks);
-
-  // Represents a socket binding to a single network interface.
-  // DialSocket lives on the IO thread.
-  class DialSocket {
-   public:
-    explicit DialSocket(DialServiceImpl* dial_service);
-
-    DialSocket(const DialSocket&) = delete;
-    DialSocket& operator=(const DialSocket&) = delete;
-
-    ~DialSocket();
-
-    // Creates a socket using |net_log| and binds it to |bind_ip_address|.
-    bool CreateAndBindSocket(const net::IPAddress& bind_ip_address,
-                             net::NetLog* net_log);
-
-    // Sends a single discovery request |send_buffer| to |send_address|
-    // over the socket.
-    void SendOneRequest(const net::IPEndPoint& send_address,
-                        const scoped_refptr<net::StringIOBuffer>& send_buffer);
-
-    // Returns true if the socket is closed.
-    bool IsClosed();
-
-   private:
-    FRIEND_TEST_ALL_PREFIXES(DialServiceTest, TestNotifyOnError);
-    FRIEND_TEST_ALL_PREFIXES(DialServiceTest, TestOnDeviceDiscovered);
-    FRIEND_TEST_ALL_PREFIXES(DialServiceTest, TestOnDiscoveryRequest);
-    FRIEND_TEST_ALL_PREFIXES(DialServiceTest, TestResponseParsing);
-
-    // Checks the result of a socket operation.  The name of the socket
-    // operation is given by |operation| and the result of the operation is
-    // given by |result|. If the result is an error, closes the socket,
-    // calls |on_error_cb_|, and returns |false|.  Returns
-    // |true| otherwise. |operation| and |result| are logged.
-    bool CheckResult(const char* operation, int result);
-
-    // Closes the socket.
-    void Close();
-
-    // Callback invoked for socket writes.
-    void OnSocketWrite(int buffer_size, int result);
-
-    // Establishes the callback to read from the socket.  Returns true if
-    // successful.
-    bool ReadSocket();
-
-    // Callback invoked for socket reads.
-    void OnSocketRead(int result);
-
-    // Callback invoked for socket reads.
-    void HandleResponse(int bytes_read);
-
-    // Parses a response into a DialDeviceData object. If the DIAL response is
-    // invalid or does not contain enough information, then the return
-    // value will be false and |device| is not changed.
-    bool ParseResponse(const std::string& response,
-                       const base::Time& response_time,
-                       DialDeviceData* device);
-
-    // The UDP socket.
-    std::unique_ptr<net::UDPSocket> socket_;
-
-    // Buffer for socket reads.
-    scoped_refptr<net::IOBufferWithSize> recv_buffer_;
-
-    // The source of of the last socket read.
-    net::IPEndPoint recv_address_;
-
-    // Marks whether there is an active write callback.
-    bool is_writing_;
-
-    // Marks whether there is an active read callback.
-    bool is_reading_;
-
-    // Pointer to the DialServiceImpl that owns this socket.
-    const raw_ptr<DialServiceImpl> dial_service_;
-  };
-
-  // Starts the control flow for one discovery cycle.
-  void StartDiscovery();
-
-  // For each network interface in |list|, finds all unqiue IPv4 network
-  // interfaces and call |DiscoverOnAddresses()| with their IP addresses.
-  void SendNetworkList(const absl::optional<net::NetworkInterfaceList>& list);
-
-  // Calls |BindAndAddSocket()| for each address in |ip_addresses|, calls
-  // |SendOneRequest()|, and start the timer to finish discovery if needed.
-  // The (Address family, interface index) of each address in |ip_addresses|
-  // must be unique. If |ip_address| is empty, calls |FinishDiscovery()|.
-  void DiscoverOnAddresses(const net::IPAddressList& ip_addresses);
-
-  // Creates a DialSocket, binds it to |bind_ip_address| and if
-  // successful, add the DialSocket to |dial_sockets_|.
-  void BindAndAddSocket(const net::IPAddress& bind_ip_address);
-
-  // Creates a DialSocket with callbacks to this object.
-  std::unique_ptr<DialSocket> CreateDialSocket();
-
-  // Sends a single discovery request to every socket that are currently open.
-  void SendOneRequest();
-
-  // Notify observers that a discovery request was made.
-  void NotifyOnDiscoveryRequest();
-
-  // Notify observers a device has been discovered.
-  void NotifyOnDeviceDiscovered(const DialDeviceData& device_data);
-
-  // Notify observers that there has been an error with one of the DialSockets.
-  void NotifyOnError();
-
-  // Called from finish_timer_ when we are done with the current round of
-  // discovery.
-  void FinishDiscovery();
-
-  // Returns |true| if there are open sockets.
-  bool HasOpenSockets();
-
-  // Unowned reference to the DialService::Client.
-  DialService::Client& client_;
-
-  // DialSockets for each network interface whose ip address was
-  // successfully bound.
-  std::vector<std::unique_ptr<DialSocket>> dial_sockets_;
-
-  // The NetLog for this service.
-  const raw_ptr<net::NetLog> net_log_;
-
-  // The multicast address:port for search requests.
-  net::IPEndPoint send_address_;
-
-  // Buffer for socket writes.
-  scoped_refptr<net::StringIOBuffer> send_buffer_;
-
-  // True when we are currently doing discovery.
-  bool discovery_active_;
-
-  // The number of requests that have been sent in the current discovery.
-  int num_requests_sent_;
-
-  // The maximum number of requests to send per discovery cycle.
-  int max_requests_;
-
-  // Timer for finishing discovery.
-  base::OneShotTimer finish_timer_;
-
-  // The delay for |finish_timer_|; how long to wait for discovery to finish.
-  // Setting this to zero disables the timer.
-  base::TimeDelta finish_delay_;
-
-  // Timer for sending multiple requests at fixed intervals.
-  base::RepeatingTimer request_timer_;
-
-  // The delay for |request_timer_|; how long to wait between successive
-  // requests.
-  base::TimeDelta request_interval_;
-
-  // WeakPtrFactory for WeakPtrs that are invalidated on IO thread.
-  base::WeakPtrFactory<DialServiceImpl> weak_ptr_factory_{this};
-
-  friend class DialServiceTest;
-  FRIEND_TEST_ALL_PREFIXES(DialServiceTest, TestSendMultipleRequests);
-  FRIEND_TEST_ALL_PREFIXES(DialServiceTest, TestMultipleNetworkInterfaces);
-  FRIEND_TEST_ALL_PREFIXES(DialServiceTest, TestNotifyOnError);
-  FRIEND_TEST_ALL_PREFIXES(DialServiceTest, TestOnDeviceDiscovered);
-  FRIEND_TEST_ALL_PREFIXES(DialServiceTest, TestOnDiscoveryFinished);
-  FRIEND_TEST_ALL_PREFIXES(DialServiceTest, TestOnDiscoveryRequest);
-  FRIEND_TEST_ALL_PREFIXES(DialServiceTest, TestResponseParsing);
-};
-
 }  // namespace media_router
 
 #endif  // CHROME_BROWSER_MEDIA_ROUTER_DISCOVERY_DIAL_DIAL_SERVICE_H_
diff --git a/chrome/browser/media/router/discovery/dial/dial_service_impl.cc b/chrome/browser/media/router/discovery/dial/dial_service_impl.cc
new file mode 100644
index 0000000..7ebee76
--- /dev/null
+++ b/chrome/browser/media/router/discovery/dial/dial_service_impl.cc
@@ -0,0 +1,580 @@
+// Copyright (c) 2022 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/media/router/discovery/dial/dial_service_impl.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/check_op.h"
+#include "base/location.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
+#include "chrome/browser/media/router/discovery/dial/dial_device_data.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/network_service_instance.h"
+#include "net/base/address_family.h"
+#include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/base/network_interfaces.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "net/log/net_log.h"
+#include "net/log/net_log_source.h"
+#include "services/network/public/mojom/network_service.mojom.h"
+#include "url/gurl.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "base/task/task_runner_util.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+#endif
+
+using base::Time;
+using content::BrowserThread;
+using net::HttpResponseHeaders;
+using net::HttpUtil;
+using net::IOBufferWithSize;
+using net::IPAddress;
+using net::NetworkInterface;
+using net::NetworkInterfaceList;
+using net::StringIOBuffer;
+using net::UDPSocket;
+
+namespace media_router {
+
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+void PostSendNetworkList(
+    base::WeakPtr<DialServiceImpl> impl,
+    const absl::optional<net::NetworkInterfaceList>& networks) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  content::GetIOThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce(&DialServiceImpl::SendNetworkList,
+                                std::move(impl), networks));
+}
+#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+
+namespace {
+
+// The total number of requests to make per discovery cycle.
+const int kDialMaxRequests = 4;
+
+// The interval to wait between successive requests.
+const int kDialRequestIntervalMillis = 1000;
+
+// The maximum delay a device may wait before responding (MX).
+const int kDialMaxResponseDelaySecs = 1;
+
+// The maximum time a response is expected after a M-SEARCH request.
+const int kDialResponseTimeoutSecs = 2;
+
+// The multicast IP address for discovery.
+const char kDialRequestAddress[] = "239.255.255.250";
+
+// The UDP port number for discovery.
+const uint16_t kDialRequestPort = 1900;
+
+// The DIAL service type as part of the search request.
+const char kDialSearchType[] = "urn:dial-multiscreen-org:service:dial:1";
+
+// SSDP headers parsed from the response.
+const char kSsdpLocationHeader[] = "LOCATION";
+const char kSsdpCacheControlHeader[] = "CACHE-CONTROL";
+const char kSsdpConfigIdHeader[] = "CONFIGID.UPNP.ORG";
+const char kSsdpUsnHeader[] = "USN";
+constexpr char kSsdpMaxAgeDirective[] = "max-age";
+constexpr int kSsdpMaxMaxAge = 3600;
+constexpr int kSsdpMaxConfigId = (2 << 24) - 1;
+
+// The receive buffer size, in bytes.
+const int kDialRecvBufferSize = 1500;
+
+// Gets a specific header from |headers| and puts it in |value|.
+bool GetHeader(HttpResponseHeaders* headers,
+               const char* name,
+               std::string* value) {
+  return headers->EnumerateHeader(nullptr, std::string(name), value);
+}
+
+// Returns the request string.
+std::string BuildRequest() {
+  // Extra line at the end to make UPnP lib happy.
+  std::string request(base::StringPrintf(
+      "M-SEARCH * HTTP/1.1\r\n"
+      "HOST: %s:%u\r\n"
+      "MAN: \"ssdp:discover\"\r\n"
+      "MX: %d\r\n"
+      "ST: %s\r\n"
+      "USER-AGENT: %s/%s %s\r\n"
+      "\r\n",
+      kDialRequestAddress, kDialRequestPort, kDialMaxResponseDelaySecs,
+      kDialSearchType, version_info::GetProductName().c_str(),
+      version_info::GetVersionNumber().c_str(),
+      version_info::GetOSType().c_str()));
+  // 1500 is a good MTU value for most Ethernet LANs.
+  DCHECK_LE(request.size(), 1500U);
+  return request;
+}
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+// Finds the IP address of the preferred interface of network type |type|
+// to bind the socket and inserts the address into |bind_address_list|. This
+// ChromeOS version can prioritize wifi and ethernet interfaces.
+void InsertBestBindAddressChromeOS(const chromeos::NetworkTypePattern& type,
+                                   net::IPAddressList* bind_address_list) {
+  const chromeos::NetworkState* state = chromeos::NetworkHandler::Get()
+                                            ->network_state_handler()
+                                            ->ConnectedNetworkByType(type);
+  if (!state)
+    return;
+  std::string state_ip_address = state->GetIpAddress();
+  IPAddress bind_ip_address;
+  if (bind_ip_address.AssignFromIPLiteral(state_ip_address) &&
+      bind_ip_address.IsIPv4()) {
+    bind_address_list->push_back(bind_ip_address);
+  }
+}
+
+net::IPAddressList GetBestBindAddressOnUIThread() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  net::IPAddressList bind_address_list;
+  if (chromeos::NetworkHandler::IsInitialized()) {
+    InsertBestBindAddressChromeOS(chromeos::NetworkTypePattern::Ethernet(),
+                                  &bind_address_list);
+    InsertBestBindAddressChromeOS(chromeos::NetworkTypePattern::WiFi(),
+                                  &bind_address_list);
+  }
+  return bind_address_list;
+}
+#else
+// This function and PostSendNetworkList together handle DialServiceImpl's use
+// of the network service, while keeping all of DialServiceImpl running on the
+// IO thread.  DialServiceImpl has a legacy threading model, where it was
+// designed to be called from the UI thread and run on the IO thread.  Although
+// a WeakPtr is desired for safety when posting tasks, they are not
+// thread/sequence-safe.  DialServiceImpl's simple use of the network service,
+// however, doesn't actually require that any of its state be accessed on the UI
+// thread.  Therefore, the UI thread functions can be free functions which just
+// pass-through an IO thread WeakPtr which will be used when passing the network
+// service result back to the IO thread.  This model will change when the
+// network service is fully launched and this code is updated.
+void GetNetworkListOnUIThread(base::WeakPtr<DialServiceImpl> impl) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  content::GetNetworkService()->GetNetworkList(
+      net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES,
+      base::BindOnce(&PostSendNetworkList, std::move(impl)));
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+}  // namespace
+
+DialServiceImpl::DialSocket::DialSocket(DialServiceImpl* dial_service)
+    : is_writing_(false), is_reading_(false), dial_service_(dial_service) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(dial_service_);
+}
+
+DialServiceImpl::DialSocket::~DialSocket() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+}
+
+bool DialServiceImpl::DialSocket::CreateAndBindSocket(
+    const IPAddress& bind_ip_address,
+    net::NetLog* net_log) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(!socket_);
+  DCHECK(bind_ip_address.IsIPv4());
+
+  socket_ = std::make_unique<UDPSocket>(net::DatagramSocket::RANDOM_BIND,
+                                        net_log, net::NetLogSource());
+
+  // 0 means bind a random port
+  net::IPEndPoint address(bind_ip_address, 0);
+
+  if (socket_->Open(address.GetFamily()) != net::OK ||
+      socket_->SetBroadcast(true) != net::OK ||
+      !CheckResult("Bind", socket_->Bind(address))) {
+    socket_.reset();
+    return false;
+  }
+
+  recv_buffer_ = base::MakeRefCounted<IOBufferWithSize>(kDialRecvBufferSize);
+  return ReadSocket();
+}
+
+void DialServiceImpl::DialSocket::SendOneRequest(
+    const net::IPEndPoint& send_address,
+    const scoped_refptr<net::StringIOBuffer>& send_buffer) {
+  if (!socket_) {
+    return;
+  }
+
+  if (is_writing_) {
+    return;
+  }
+
+  is_writing_ = true;
+  int result = socket_->SendTo(
+      send_buffer.get(), send_buffer->size(), send_address,
+      base::BindOnce(&DialServiceImpl::DialSocket::OnSocketWrite,
+                     base::Unretained(this), send_buffer->size()));
+  bool result_ok = CheckResult("SendTo", result);
+  if (result_ok && result > 0) {
+    // Synchronous write.
+    OnSocketWrite(send_buffer->size(), result);
+  }
+}
+
+bool DialServiceImpl::DialSocket::IsClosed() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  return !socket_;
+}
+
+bool DialServiceImpl::DialSocket::CheckResult(const char* operation,
+                                              int result) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (result < net::OK && result != net::ERR_IO_PENDING) {
+    Close();
+    std::string error_str(net::ErrorToString(result));
+    dial_service_->NotifyOnError();
+    return false;
+  }
+  return true;
+}
+
+void DialServiceImpl::DialSocket::Close() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  is_reading_ = false;
+  is_writing_ = false;
+  socket_.reset();
+}
+
+void DialServiceImpl::DialSocket::OnSocketWrite(int send_buffer_size,
+                                                int result) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  is_writing_ = false;
+  if (!CheckResult("OnSocketWrite", result))
+    return;
+  dial_service_->NotifyOnDiscoveryRequest();
+}
+
+bool DialServiceImpl::DialSocket::ReadSocket() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (!socket_)
+    return false;
+
+  if (is_reading_)
+    return false;
+
+  int result = net::OK;
+  bool result_ok = true;
+  do {
+    is_reading_ = true;
+    result = socket_->RecvFrom(
+        recv_buffer_.get(), kDialRecvBufferSize, &recv_address_,
+        base::BindOnce(&DialServiceImpl::DialSocket::OnSocketRead,
+                       base::Unretained(this)));
+    result_ok = CheckResult("RecvFrom", result);
+    if (result != net::ERR_IO_PENDING)
+      is_reading_ = false;
+    if (result_ok && result > 0) {
+      // Synchronous read.
+      HandleResponse(result);
+    }
+  } while (result_ok && result != net::OK && result != net::ERR_IO_PENDING);
+  return result_ok;
+}
+
+void DialServiceImpl::DialSocket::OnSocketRead(int result) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  is_reading_ = false;
+  if (!CheckResult("OnSocketRead", result))
+    return;
+  if (result > 0)
+    HandleResponse(result);
+
+  // Await next response.
+  ReadSocket();
+}
+
+void DialServiceImpl::DialSocket::HandleResponse(int bytes_read) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_GT(bytes_read, 0);
+  if (bytes_read > kDialRecvBufferSize) {
+    return;
+  }
+
+  std::string response(recv_buffer_->data(), bytes_read);
+  Time response_time = Time::Now();
+
+  // Attempt to parse response, notify client if successful.
+  DialDeviceData parsed_device;
+  if (ParseResponse(response, response_time, &parsed_device))
+    dial_service_->NotifyOnDeviceDiscovered(parsed_device);
+}
+
+bool DialServiceImpl::DialSocket::ParseResponse(const std::string& response,
+                                                const base::Time& response_time,
+                                                DialDeviceData* device) {
+  device->set_ip_address(recv_address_.address());
+  device->set_response_time(response_time);
+
+  size_t headers_end =
+      HttpUtil::LocateEndOfHeaders(response.c_str(), response.size());
+  if (headers_end == 0 || headers_end == std::string::npos) {
+    return false;
+  }
+  std::string raw_headers = HttpUtil::AssembleRawHeaders(
+      base::StringPiece(response.c_str(), headers_end));
+  auto headers = base::MakeRefCounted<HttpResponseHeaders>(raw_headers);
+
+  std::string device_url_str;
+  if (!GetHeader(headers.get(), kSsdpLocationHeader, &device_url_str) ||
+      device_url_str.empty()) {
+    return false;
+  }
+
+  GURL device_url(device_url_str);
+  if (device->IsValidUrl(device_url)) {
+    device->set_device_description_url(device_url);
+  } else {
+    return false;
+  }
+
+  std::string device_id;
+  if (!GetHeader(headers.get(), kSsdpUsnHeader, &device_id) ||
+      device_id.empty()) {
+    return false;
+  }
+  device->set_device_id(device_id);
+
+  std::string cache_control;
+  if (GetHeader(headers.get(), kSsdpCacheControlHeader, &cache_control) &&
+      !cache_control.empty()) {
+    std::vector<std::string> cache_control_directives = base::SplitString(
+        cache_control, "=", base::WhitespaceHandling::TRIM_WHITESPACE,
+        base::SplitResult::SPLIT_WANT_NONEMPTY);
+    if (cache_control_directives.size() == 2 &&
+        base::EqualsCaseInsensitiveASCII(cache_control_directives[0],
+                                         kSsdpMaxAgeDirective)) {
+      int max_age = 0;
+      if (base::StringToInt(cache_control_directives[1], &max_age) &&
+          max_age > 0) {
+        device->set_max_age(std::min(max_age, kSsdpMaxMaxAge));
+      }
+    }
+  }
+
+  std::string config_id;
+  int config_id_int;
+  if (GetHeader(headers.get(), kSsdpConfigIdHeader, &config_id) &&
+      !config_id.empty() && base::StringToInt(config_id, &config_id_int) &&
+      config_id_int > 0 && config_id_int <= kSsdpMaxConfigId) {
+    device->set_config_id(config_id_int);
+  }
+  return true;
+}
+
+DialServiceImpl::DialServiceImpl(DialService::Client& client,
+                                 net::NetLog* net_log)
+    : client_(client),
+      net_log_(net_log),
+      discovery_active_(false),
+      num_requests_sent_(0),
+      max_requests_(kDialMaxRequests),
+      finish_delay_(base::Milliseconds((kDialMaxRequests - 1) *
+                                       kDialRequestIntervalMillis) +
+                    base::Seconds(kDialResponseTimeoutSecs)),
+      request_interval_(base::Milliseconds(kDialRequestIntervalMillis)) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  IPAddress address;
+  bool success = address.AssignFromIPLiteral(kDialRequestAddress);
+  DCHECK(success);
+  send_address_ = net::IPEndPoint(address, kDialRequestPort);
+  send_buffer_ = base::MakeRefCounted<StringIOBuffer>(BuildRequest());
+}
+
+DialServiceImpl::~DialServiceImpl() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+}
+
+bool DialServiceImpl::Discover() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (discovery_active_) {
+    return false;
+  }
+  discovery_active_ = true;
+
+  StartDiscovery();
+  return true;
+}
+
+void DialServiceImpl::StartDiscovery() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(discovery_active_);
+  if (HasOpenSockets()) {
+    return;
+  }
+
+  auto ui_task_runner = content::GetUIThreadTaskRunner({});
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  ui_task_runner->PostTaskAndReplyWithResult(
+      FROM_HERE, base::BindOnce(&GetBestBindAddressOnUIThread),
+      base::BindOnce(&DialServiceImpl::DiscoverOnAddresses,
+                     weak_ptr_factory_.GetWeakPtr()));
+#else
+  ui_task_runner->PostTask(FROM_HERE,
+                           base::BindOnce(&GetNetworkListOnUIThread,
+                                          weak_ptr_factory_.GetWeakPtr()));
+#endif
+}
+
+void DialServiceImpl::SendNetworkList(
+    const absl::optional<NetworkInterfaceList>& networks) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  using InterfaceIndexAddressFamily = std::pair<uint32_t, net::AddressFamily>;
+  std::set<InterfaceIndexAddressFamily> interface_index_addr_family_seen;
+  net::IPAddressList ip_addresses;
+
+  if (networks.has_value()) {
+    // Binds a socket to each IPv4 network interface found. Note that
+    // there may be duplicates in |networks|, so address family + interface
+    // index is used to identify unique interfaces.
+    // TODO(mfoltz): Support IPV6 multicast.  http://crbug.com/165286
+    for (const auto& network : *networks) {
+      net::AddressFamily addr_family = net::GetAddressFamily(network.address);
+      if (addr_family == net::ADDRESS_FAMILY_IPV4) {
+        InterfaceIndexAddressFamily interface_index_addr_family =
+            std::make_pair(network.interface_index, addr_family);
+        bool inserted =
+            interface_index_addr_family_seen.insert(interface_index_addr_family)
+                .second;
+        // We have not seen this interface before, so add its IP address to the
+        // discovery list.
+        if (inserted) {
+          ip_addresses.push_back(network.address);
+        }
+      }
+    }
+  } else {
+  }
+
+  DiscoverOnAddresses(ip_addresses);
+}
+
+void DialServiceImpl::DiscoverOnAddresses(
+    const net::IPAddressList& ip_addresses) {
+  if (ip_addresses.empty()) {
+    FinishDiscovery();
+    return;
+  }
+
+  // Schedule a timer to finish the discovery process (and close the sockets).
+  if (finish_delay_ > base::Seconds(0)) {
+    finish_timer_.Start(FROM_HERE, finish_delay_, this,
+                        &DialServiceImpl::FinishDiscovery);
+  }
+
+  for (const auto& address : ip_addresses) {
+    BindAndAddSocket(address);
+  }
+
+  SendOneRequest();
+}
+
+void DialServiceImpl::BindAndAddSocket(const IPAddress& bind_ip_address) {
+  std::unique_ptr<DialServiceImpl::DialSocket> dial_socket(CreateDialSocket());
+  if (dial_socket->CreateAndBindSocket(bind_ip_address, net_log_))
+    dial_sockets_.push_back(std::move(dial_socket));
+}
+
+std::unique_ptr<DialServiceImpl::DialSocket>
+DialServiceImpl::CreateDialSocket() {
+  return std::make_unique<DialServiceImpl::DialSocket>(this);
+}
+
+void DialServiceImpl::SendOneRequest() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (num_requests_sent_ == max_requests_) {
+    request_timer_.Stop();
+    return;
+  }
+  num_requests_sent_++;
+  for (const auto& socket : dial_sockets_) {
+    if (!socket->IsClosed())
+      socket->SendOneRequest(send_address_, send_buffer_);
+  }
+}
+
+void DialServiceImpl::NotifyOnDiscoveryRequest() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  // If discovery is inactive, no reason to notify client.
+  if (!discovery_active_) {
+    return;
+  }
+
+  client_.OnDiscoveryRequest();
+  // If we need to send additional requests, schedule a timer to do so.
+  if (num_requests_sent_ < max_requests_ && num_requests_sent_ == 1) {
+    // TODO(imcheng): Move this to SendOneRequest() once the implications are
+    // understood.
+    request_timer_.Start(FROM_HERE, request_interval_, this,
+                         &DialServiceImpl::SendOneRequest);
+  }
+}
+
+void DialServiceImpl::NotifyOnDeviceDiscovered(
+    const DialDeviceData& device_data) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (!discovery_active_) {
+    return;
+  }
+  client_.OnDeviceDiscovered(device_data);
+}
+
+void DialServiceImpl::NotifyOnError() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  client_.OnError(HasOpenSockets() ? DIAL_SERVICE_SOCKET_ERROR
+                                   : DIAL_SERVICE_NO_INTERFACES);
+}
+
+void DialServiceImpl::FinishDiscovery() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(discovery_active_);
+  // Close all open sockets.
+  dial_sockets_.clear();
+  finish_timer_.Stop();
+  request_timer_.Stop();
+  discovery_active_ = false;
+  num_requests_sent_ = 0;
+  client_.OnDiscoveryFinished();
+}
+
+bool DialServiceImpl::HasOpenSockets() {
+  for (const auto& socket : dial_sockets_) {
+    if (!socket->IsClosed())
+      return true;
+  }
+  return false;
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/media/router/discovery/dial/dial_service_impl.h b/chrome/browser/media/router/discovery/dial/dial_service_impl.h
new file mode 100644
index 0000000..4c27ad5
--- /dev/null
+++ b/chrome/browser/media/router/discovery/dial/dial_service_impl.h
@@ -0,0 +1,218 @@
+// Copyright (c) 2022 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_MEDIA_ROUTER_DISCOVERY_DIAL_DIAL_SERVICE_IMPL_H_
+#define CHROME_BROWSER_MEDIA_ROUTER_DISCOVERY_DIAL_DIAL_SERVICE_IMPL_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/raw_ptr.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/media/router/discovery/dial/dial_service.h"
+#include "net/base/ip_address.h"
+#include "net/socket/udp_socket.h"
+
+namespace net {
+class IPEndPoint;
+class StringIOBuffer;
+class NetLog;
+}  // namespace net
+
+namespace media_router {
+
+// Implements DialService using net::UdpSocket.
+class DialServiceImpl : public DialService {
+ public:
+  DialServiceImpl(DialService::Client& client, net::NetLog* net_log);
+
+  DialServiceImpl(const DialServiceImpl&) = delete;
+  DialServiceImpl(DialServiceImpl&&) = delete;
+  DialServiceImpl& operator=(const DialServiceImpl&) = delete;
+  DialServiceImpl& operator=(DialServiceImpl&&) = delete;
+
+  ~DialServiceImpl() override;
+
+  // DialService implementation
+  bool Discover() override;
+
+ private:
+  friend void PostSendNetworkList(
+      base::WeakPtr<DialServiceImpl> impl,
+      const absl::optional<net::NetworkInterfaceList>& networks);
+
+  // Represents a socket binding to a single network interface.
+  // DialSocket lives on the IO thread.
+  class DialSocket {
+   public:
+    explicit DialSocket(DialServiceImpl* dial_service);
+
+    DialSocket(const DialSocket&) = delete;
+    DialSocket& operator=(const DialSocket&) = delete;
+
+    ~DialSocket();
+
+    // Creates a socket using |net_log| and binds it to |bind_ip_address|.
+    bool CreateAndBindSocket(const net::IPAddress& bind_ip_address,
+                             net::NetLog* net_log);
+
+    // Sends a single discovery request |send_buffer| to |send_address|
+    // over the socket.
+    void SendOneRequest(const net::IPEndPoint& send_address,
+                        const scoped_refptr<net::StringIOBuffer>& send_buffer);
+
+    // Returns true if the socket is closed.
+    bool IsClosed();
+
+   private:
+    FRIEND_TEST_ALL_PREFIXES(DialServiceImplTest, TestNotifyOnError);
+    FRIEND_TEST_ALL_PREFIXES(DialServiceImplTest, TestOnDeviceDiscovered);
+    FRIEND_TEST_ALL_PREFIXES(DialServiceImplTest, TestOnDiscoveryRequest);
+    FRIEND_TEST_ALL_PREFIXES(DialServiceImplTest, TestResponseParsing);
+
+    // Checks the result of a socket operation.  The name of the socket
+    // operation is given by |operation| and the result of the operation is
+    // given by |result|. If the result is an error, closes the socket,
+    // calls |on_error_cb_|, and returns |false|.  Returns
+    // |true| otherwise. |operation| and |result| are logged.
+    bool CheckResult(const char* operation, int result);
+
+    // Closes the socket.
+    void Close();
+
+    // Callback invoked for socket writes.
+    void OnSocketWrite(int buffer_size, int result);
+
+    // Establishes the callback to read from the socket.  Returns true if
+    // successful.
+    bool ReadSocket();
+
+    // Callback invoked for socket reads.
+    void OnSocketRead(int result);
+
+    // Callback invoked for socket reads.
+    void HandleResponse(int bytes_read);
+
+    // Parses a response into a DialDeviceData object. If the DIAL response is
+    // invalid or does not contain enough information, then the return
+    // value will be false and |device| is not changed.
+    bool ParseResponse(const std::string& response,
+                       const base::Time& response_time,
+                       DialDeviceData* device);
+
+    // The UDP socket.
+    std::unique_ptr<net::UDPSocket> socket_;
+
+    // Buffer for socket reads.
+    scoped_refptr<net::IOBufferWithSize> recv_buffer_;
+
+    // The source of of the last socket read.
+    net::IPEndPoint recv_address_;
+
+    // Marks whether there is an active write callback.
+    bool is_writing_;
+
+    // Marks whether there is an active read callback.
+    bool is_reading_;
+
+    // Pointer to the DialServiceImpl that owns this socket.
+    const raw_ptr<DialServiceImpl> dial_service_;
+  };
+
+  // Starts the control flow for one discovery cycle.
+  void StartDiscovery();
+
+  // For each network interface in |list|, finds all unique IPv4 network
+  // interfaces and call |DiscoverOnAddresses()| with their IP addresses.
+  void SendNetworkList(const absl::optional<net::NetworkInterfaceList>& list);
+
+  // Calls |BindAndAddSocket()| for each address in |ip_addresses|, calls
+  // |SendOneRequest()|, and start the timer to finish discovery if needed.
+  // The (Address family, interface index) of each address in |ip_addresses|
+  // must be unique. If |ip_address| is empty, calls |FinishDiscovery()|.
+  void DiscoverOnAddresses(const net::IPAddressList& ip_addresses);
+
+  // Creates a DialSocket, binds it to |bind_ip_address| and if
+  // successful, add the DialSocket to |dial_sockets_|.
+  void BindAndAddSocket(const net::IPAddress& bind_ip_address);
+
+  // Creates a DialSocket with callbacks to this object.
+  std::unique_ptr<DialSocket> CreateDialSocket();
+
+  // Sends a single discovery request to every socket that are currently open.
+  void SendOneRequest();
+
+  // Notify observers that a discovery request was made.
+  void NotifyOnDiscoveryRequest();
+
+  // Notify observers a device has been discovered.
+  void NotifyOnDeviceDiscovered(const DialDeviceData& device_data);
+
+  // Notify observers that there has been an error with one of the DialSockets.
+  void NotifyOnError();
+
+  // Called from finish_timer_ when we are done with the current round of
+  // discovery.
+  void FinishDiscovery();
+
+  // Returns |true| if there are open sockets.
+  bool HasOpenSockets();
+
+  // Unowned reference to the DialService::Client.
+  DialService::Client& client_;
+
+  // DialSockets for each network interface whose ip address was
+  // successfully bound.
+  std::vector<std::unique_ptr<DialSocket>> dial_sockets_;
+
+  // The NetLog for this service.
+  const raw_ptr<net::NetLog> net_log_;
+
+  // The multicast address:port for search requests.
+  net::IPEndPoint send_address_;
+
+  // Buffer for socket writes.
+  scoped_refptr<net::StringIOBuffer> send_buffer_;
+
+  // True when we are currently doing discovery.
+  bool discovery_active_;
+
+  // The number of requests that have been sent in the current discovery.
+  int num_requests_sent_;
+
+  // The maximum number of requests to send per discovery cycle.
+  int max_requests_;
+
+  // Timer for finishing discovery.
+  base::OneShotTimer finish_timer_;
+
+  // The delay for |finish_timer_|; how long to wait for discovery to finish.
+  // Setting this to zero disables the timer.
+  base::TimeDelta finish_delay_;
+
+  // Timer for sending multiple requests at fixed intervals.
+  base::RepeatingTimer request_timer_;
+
+  // The delay for |request_timer_|; how long to wait between successive
+  // requests.
+  base::TimeDelta request_interval_;
+
+  // WeakPtrFactory for WeakPtrs that are invalidated on IO thread.
+  base::WeakPtrFactory<DialServiceImpl> weak_ptr_factory_{this};
+
+  friend class DialServiceImplTest;
+  FRIEND_TEST_ALL_PREFIXES(DialServiceImplTest, TestSendMultipleRequests);
+  FRIEND_TEST_ALL_PREFIXES(DialServiceImplTest, TestMultipleNetworkInterfaces);
+  FRIEND_TEST_ALL_PREFIXES(DialServiceImplTest, TestNotifyOnError);
+  FRIEND_TEST_ALL_PREFIXES(DialServiceImplTest, TestOnDeviceDiscovered);
+  FRIEND_TEST_ALL_PREFIXES(DialServiceImplTest, TestOnDiscoveryFinished);
+  FRIEND_TEST_ALL_PREFIXES(DialServiceImplTest, TestOnDiscoveryRequest);
+  FRIEND_TEST_ALL_PREFIXES(DialServiceImplTest, TestResponseParsing);
+};
+
+}  // namespace media_router
+
+#endif  // CHROME_BROWSER_MEDIA_ROUTER_DISCOVERY_DIAL_DIAL_SERVICE_IMPL_H_
diff --git a/chrome/browser/media/router/discovery/dial/dial_service_impl_unittest.cc b/chrome/browser/media/router/discovery/dial/dial_service_impl_unittest.cc
new file mode 100644
index 0000000..dbbb5b1
--- /dev/null
+++ b/chrome/browser/media/router/discovery/dial/dial_service_impl_unittest.cc
@@ -0,0 +1,279 @@
+// Copyright (c) 2012 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/media/router/discovery/dial/dial_service_impl.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/cxx17_backports.h"
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "chrome/browser/media/router/discovery/dial/dial_device_data.h"
+#include "content/public/test/browser_task_environment.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/base/network_interfaces.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::Time;
+using ::testing::A;
+using ::testing::AtLeast;
+using ::testing::Return;
+
+namespace {
+
+const char kValidResponse[] =
+    "HTTP/1.1 OK\r\n"
+    "LOCATION: http://127.0.0.1/dd.xml\r\n"
+    "USN: some_id\r\n"
+    "CACHE-CONTROL: max-age=1800\r\n"
+    "CONFIGID.UPNP.ORG: 1\r\n\r\n";
+
+}  // namespace
+
+namespace media_router {
+
+class MockDialServiceClient : public DialService::Client {
+ public:
+  ~MockDialServiceClient() override = default;
+
+  MOCK_METHOD(void, OnDiscoveryRequest, ());
+  MOCK_METHOD(void, OnDeviceDiscovered, (const DialDeviceData&));
+  MOCK_METHOD(void, OnDiscoveryFinished, ());
+  MOCK_METHOD(void, OnError, (DialService::DialServiceErrorCode));
+};
+
+class DialServiceImplTest : public testing::Test {
+ public:
+  DialServiceImplTest()
+      : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP),
+        mock_ip_(net::IPAddress::IPv4AllZeros()),
+        dial_service_(mock_client_, net::NetLog::Get()) {
+    dial_socket_ = dial_service_.CreateDialSocket();
+  }
+
+ protected:
+  content::BrowserTaskEnvironment task_environment_;
+  net::IPAddress mock_ip_;
+  DialServiceImpl dial_service_;
+  std::unique_ptr<DialServiceImpl::DialSocket> dial_socket_;
+  MockDialServiceClient mock_client_;
+};
+
+TEST_F(DialServiceImplTest, TestSendMultipleRequests) {
+  // Setting the finish delay to zero disables the timer that invokes
+  // FinishDiscovery().
+  dial_service_.finish_delay_ = base::Seconds(0);
+  dial_service_.request_interval_ = base::Seconds(0);
+  dial_service_.max_requests_ = 4;
+  dial_service_.discovery_active_ = true;
+  EXPECT_CALL(mock_client_, OnDiscoveryRequest()).Times(4);
+  EXPECT_CALL(mock_client_, OnDiscoveryFinished()).Times(1);
+  dial_service_.BindAndAddSocket(mock_ip_);
+  EXPECT_EQ(1u, dial_service_.dial_sockets_.size());
+  EXPECT_TRUE(dial_service_.dial_sockets_[0]);
+  EXPECT_FALSE(dial_service_.dial_sockets_[0]->IsClosed());
+  dial_service_.SendOneRequest();
+  base::RunLoop().RunUntilIdle();
+  dial_service_.FinishDiscovery();
+}
+
+TEST_F(DialServiceImplTest, TestMultipleNetworkInterfaces) {
+  // Setting the finish delay to zero disables the timer that invokes
+  // FinishDiscovery().
+  dial_service_.finish_delay_ = base::Seconds(0);
+  dial_service_.request_interval_ = base::Seconds(0);
+  dial_service_.max_requests_ = 4;
+  dial_service_.discovery_active_ = true;
+  net::NetworkInterfaceList interface_list;
+  interface_list.push_back(net::NetworkInterface(
+      "network1", "network1", 0, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
+      mock_ip_, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
+  interface_list.push_back(net::NetworkInterface(
+      "network2", "network2", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
+      mock_ip_, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
+  interface_list.push_back(net::NetworkInterface(
+      "network3", "network3", 2, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
+      mock_ip_, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
+
+  // "network4" is equivalent to "network2" because both the address family
+  // and interface index are the same.
+  interface_list.push_back(net::NetworkInterface(
+      "network4", "network4", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
+      mock_ip_, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
+
+  // 3 sockets * 4 requests per socket = 12 requests
+  EXPECT_CALL(mock_client_, OnDiscoveryRequest()).Times(12);
+  EXPECT_CALL(mock_client_, OnDiscoveryFinished()).Times(1);
+
+  dial_service_.SendNetworkList(interface_list);
+  EXPECT_EQ(3u, dial_service_.dial_sockets_.size());
+
+  base::RunLoop().RunUntilIdle();
+  dial_service_.FinishDiscovery();
+}
+
+TEST_F(DialServiceImplTest, TestOnDiscoveryRequest) {
+  dial_service_.discovery_active_ = true;
+  dial_service_.num_requests_sent_ = 1;
+  dial_service_.max_requests_ = 1;
+  size_t num_bytes = dial_service_.send_buffer_->size();
+  EXPECT_CALL(mock_client_, OnDiscoveryRequest()).Times(1);
+  dial_socket_->OnSocketWrite(num_bytes, num_bytes);
+}
+
+TEST_F(DialServiceImplTest, TestNotifyOnError) {
+  EXPECT_CALL(mock_client_, OnError(DialService::DIAL_SERVICE_NO_INTERFACES));
+  dial_socket_->OnSocketWrite(0, net::ERR_CONNECTION_REFUSED);
+}
+
+TEST_F(DialServiceImplTest, TestOnDeviceDiscovered) {
+  dial_service_.discovery_active_ = true;
+  int response_size = base::size(kValidResponse) - 1;
+  dial_socket_->recv_buffer_ =
+      base::MakeRefCounted<net::IOBufferWithSize>(response_size);
+  strncpy(dial_socket_->recv_buffer_->data(), kValidResponse, response_size);
+  dial_socket_->recv_address_ =
+      net::IPEndPoint(net::IPAddress::IPv4Localhost(), 12345);
+
+  DialDeviceData expected_device;
+  expected_device.set_device_id("some_id");
+
+  EXPECT_CALL(mock_client_, OnDeviceDiscovered(expected_device)).Times(1);
+  dial_socket_->OnSocketRead(response_size);
+}
+
+TEST_F(DialServiceImplTest, TestOnDiscoveryFinished) {
+  dial_service_.discovery_active_ = true;
+
+  EXPECT_CALL(mock_client_, OnDiscoveryFinished()).Times(1);
+  dial_service_.FinishDiscovery();
+  EXPECT_FALSE(dial_service_.discovery_active_);
+}
+
+TEST_F(DialServiceImplTest, TestResponseParsing) {
+  Time now = Time::Now();
+
+  // Force the socket address to match what is expected in the response.
+  dial_socket_->recv_address_ =
+      net::IPEndPoint(net::IPAddress::IPv4Localhost(), 12345);
+
+  // Successful case, all values parsed successfully.
+  DialDeviceData parsed;
+  EXPECT_TRUE(dial_socket_->ParseResponse(kValidResponse, now, &parsed));
+  EXPECT_EQ("some_id", parsed.device_id());
+  EXPECT_EQ("http://127.0.0.1/dd.xml", parsed.device_description_url().spec());
+  EXPECT_EQ(1, parsed.config_id());
+  EXPECT_EQ(now, parsed.response_time());
+  EXPECT_EQ(1800, parsed.max_age());
+
+  // Cases where we do not fail entirely, but we are unable to extract the
+  // CACHE-CONTROL or CONFIG header values.
+
+  // Max-age is too low
+  DialDeviceData parsed_max_age_low;
+  EXPECT_TRUE(
+      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
+                                  "LOCATION: http://127.0.0.1/dd.xml\r\n"
+                                  "USN: some_id\r\n"
+                                  "CACHE-CONTROL: max-age=-100\r\n"
+                                  "CONFIGID.UPNP.ORG: 1\r\n\r\n",
+                                  now, &parsed_max_age_low));
+  EXPECT_EQ(-1, parsed_max_age_low.max_age());
+
+  // max-age is too high
+  DialDeviceData parsed_max_age_high;
+  EXPECT_TRUE(
+      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
+                                  "LOCATION: http://127.0.0.1/dd.xml\r\n"
+                                  "USN: some_id\r\n"
+                                  "CACHE-CONTROL: max-age=5000\r\n"
+                                  "CONFIGID.UPNP.ORG: 1\r\n\r\n",
+                                  now, &parsed_max_age_high));
+  EXPECT_EQ(3600, parsed_max_age_high.max_age());
+
+  // Invalid CACHE-CONTROL directive
+  DialDeviceData parsed_invalid_cache;
+  EXPECT_TRUE(
+      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
+                                  "LOCATION: http://127.0.0.1/dd.xml\r\n"
+                                  "USN: some_id\r\n"
+                                  "CACHE-CONTROL: xyzzy=100,\r\n"
+                                  "CONFIGID.UPNP.ORG: 1\r\n\r\n",
+                                  now, &parsed_invalid_cache));
+  EXPECT_EQ(-1, parsed_invalid_cache.max_age());
+
+  // Extra CACHE-CONTROL directives
+  DialDeviceData parsed_extra_cache;
+  EXPECT_TRUE(
+      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
+                                  "LOCATION: http://127.0.0.1/dd.xml\r\n"
+                                  "USN: some_id\r\n"
+                                  "CACHE-CONTROL: bar=a,max-age=1800,foo=b\r\n"
+                                  "CONFIGID.UPNP.ORG: 1\r\n\r\n",
+                                  now, &parsed_extra_cache));
+  EXPECT_EQ(-1, parsed_extra_cache.max_age());
+
+  // Invalid CONFIGID.UPNP.ORG value
+  DialDeviceData parsed_invalid_config;
+  EXPECT_TRUE(
+      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
+                                  "LOCATION: http://127.0.0.1/dd.xml\r\n"
+                                  "USN: some_id\r\n"
+                                  "CACHE-CONTROL: max-age=1000\r\n"
+                                  "CONFIGID.UPNP.ORG: -100\r\n\r\n",
+                                  now, &parsed_invalid_config));
+  EXPECT_EQ(-1, parsed_invalid_config.config_id());
+
+  // Failure cases
+  DialDeviceData not_parsed;
+
+  // Empty, garbage
+  EXPECT_FALSE(dial_socket_->ParseResponse(std::string(), now, &not_parsed));
+  EXPECT_FALSE(dial_socket_->ParseResponse("\r\n\r\n", now, &not_parsed));
+  EXPECT_FALSE(dial_socket_->ParseResponse("xyzzy", now, &not_parsed));
+
+  // No headers
+  EXPECT_FALSE(
+      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n\r\n", now, &not_parsed));
+
+  // Missing USN
+  EXPECT_FALSE(
+      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
+                                  "LOCATION: http://127.0.0.1/dd.xml\r\n\r\n",
+                                  now, &not_parsed));
+
+  // Empty USN
+  EXPECT_FALSE(
+      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
+                                  "LOCATION: http://127.0.0.1/dd.xml\r\n"
+                                  "USN:\r\n\r\n",
+                                  now, &not_parsed));
+
+  // Missing LOCATION
+  EXPECT_FALSE(
+      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
+                                  "USN: some_id\r\n\r\n",
+                                  now, &not_parsed));
+
+  // Empty LOCATION
+  EXPECT_FALSE(
+      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
+                                  "LOCATION:\r\n"
+                                  "USN: some_id\r\n\r\n",
+                                  now, &not_parsed));
+
+  // Invalid LOCATION
+  EXPECT_FALSE(
+      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
+                                  "LOCATION: http://127.8.8.8/dd.xml\r\n"
+                                  "USN:\r\n\r\n",
+                                  now, &not_parsed));
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/media/router/discovery/dial/dial_service_unittest.cc b/chrome/browser/media/router/discovery/dial/dial_service_unittest.cc
deleted file mode 100644
index e1f26f83..0000000
--- a/chrome/browser/media/router/discovery/dial/dial_service_unittest.cc
+++ /dev/null
@@ -1,279 +0,0 @@
-// Copyright (c) 2012 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/media/router/discovery/dial/dial_service.h"
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/cxx17_backports.h"
-#include "base/memory/ref_counted.h"
-#include "base/run_loop.h"
-#include "chrome/browser/media/router/discovery/dial/dial_device_data.h"
-#include "content/public/test/browser_task_environment.h"
-#include "net/base/ip_address.h"
-#include "net/base/ip_endpoint.h"
-#include "net/base/net_errors.h"
-#include "net/base/network_interfaces.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using base::Time;
-using ::testing::A;
-using ::testing::AtLeast;
-using ::testing::Return;
-
-namespace {
-
-const char kValidResponse[] =
-    "HTTP/1.1 OK\r\n"
-    "LOCATION: http://127.0.0.1/dd.xml\r\n"
-    "USN: some_id\r\n"
-    "CACHE-CONTROL: max-age=1800\r\n"
-    "CONFIGID.UPNP.ORG: 1\r\n\r\n";
-
-}  // namespace
-
-namespace media_router {
-
-class MockDialServiceClient : public DialService::Client {
- public:
-  ~MockDialServiceClient() override = default;
-
-  MOCK_METHOD(void, OnDiscoveryRequest, ());
-  MOCK_METHOD(void, OnDeviceDiscovered, (const DialDeviceData&));
-  MOCK_METHOD(void, OnDiscoveryFinished, ());
-  MOCK_METHOD(void, OnError, (DialService::DialServiceErrorCode));
-};
-
-class DialServiceTest : public testing::Test {
- public:
-  DialServiceTest()
-      : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP),
-        mock_ip_(net::IPAddress::IPv4AllZeros()),
-        dial_service_(mock_client_, net::NetLog::Get()) {
-    dial_socket_ = dial_service_.CreateDialSocket();
-  }
-
- protected:
-  content::BrowserTaskEnvironment task_environment_;
-  net::IPAddress mock_ip_;
-  DialServiceImpl dial_service_;
-  std::unique_ptr<DialServiceImpl::DialSocket> dial_socket_;
-  MockDialServiceClient mock_client_;
-};
-
-TEST_F(DialServiceTest, TestSendMultipleRequests) {
-  // Setting the finish delay to zero disables the timer that invokes
-  // FinishDiscovery().
-  dial_service_.finish_delay_ = base::Seconds(0);
-  dial_service_.request_interval_ = base::Seconds(0);
-  dial_service_.max_requests_ = 4;
-  dial_service_.discovery_active_ = true;
-  EXPECT_CALL(mock_client_, OnDiscoveryRequest()).Times(4);
-  EXPECT_CALL(mock_client_, OnDiscoveryFinished()).Times(1);
-  dial_service_.BindAndAddSocket(mock_ip_);
-  EXPECT_EQ(1u, dial_service_.dial_sockets_.size());
-  EXPECT_TRUE(dial_service_.dial_sockets_[0]);
-  EXPECT_FALSE(dial_service_.dial_sockets_[0]->IsClosed());
-  dial_service_.SendOneRequest();
-  base::RunLoop().RunUntilIdle();
-  dial_service_.FinishDiscovery();
-}
-
-TEST_F(DialServiceTest, TestMultipleNetworkInterfaces) {
-  // Setting the finish delay to zero disables the timer that invokes
-  // FinishDiscovery().
-  dial_service_.finish_delay_ = base::Seconds(0);
-  dial_service_.request_interval_ = base::Seconds(0);
-  dial_service_.max_requests_ = 4;
-  dial_service_.discovery_active_ = true;
-  net::NetworkInterfaceList interface_list;
-  interface_list.push_back(net::NetworkInterface(
-      "network1", "network1", 0, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
-      mock_ip_, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
-  interface_list.push_back(net::NetworkInterface(
-      "network2", "network2", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
-      mock_ip_, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
-  interface_list.push_back(net::NetworkInterface(
-      "network3", "network3", 2, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
-      mock_ip_, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
-
-  // "network4" is equivalent to "network2" because both the address family
-  // and interface index are the same.
-  interface_list.push_back(net::NetworkInterface(
-      "network4", "network4", 1, net::NetworkChangeNotifier::CONNECTION_UNKNOWN,
-      mock_ip_, 0, net::IP_ADDRESS_ATTRIBUTE_NONE));
-
-  // 3 sockets * 4 requests per socket = 12 requests
-  EXPECT_CALL(mock_client_, OnDiscoveryRequest()).Times(12);
-  EXPECT_CALL(mock_client_, OnDiscoveryFinished()).Times(1);
-
-  dial_service_.SendNetworkList(interface_list);
-  EXPECT_EQ(3u, dial_service_.dial_sockets_.size());
-
-  base::RunLoop().RunUntilIdle();
-  dial_service_.FinishDiscovery();
-}
-
-TEST_F(DialServiceTest, TestOnDiscoveryRequest) {
-  dial_service_.discovery_active_ = true;
-  dial_service_.num_requests_sent_ = 1;
-  dial_service_.max_requests_ = 1;
-  size_t num_bytes = dial_service_.send_buffer_->size();
-  EXPECT_CALL(mock_client_, OnDiscoveryRequest()).Times(1);
-  dial_socket_->OnSocketWrite(num_bytes, num_bytes);
-}
-
-TEST_F(DialServiceTest, TestNotifyOnError) {
-  EXPECT_CALL(mock_client_, OnError(DialService::DIAL_SERVICE_NO_INTERFACES));
-  dial_socket_->OnSocketWrite(0, net::ERR_CONNECTION_REFUSED);
-}
-
-TEST_F(DialServiceTest, TestOnDeviceDiscovered) {
-  dial_service_.discovery_active_ = true;
-  int response_size = base::size(kValidResponse) - 1;
-  dial_socket_->recv_buffer_ =
-      base::MakeRefCounted<net::IOBufferWithSize>(response_size);
-  strncpy(dial_socket_->recv_buffer_->data(), kValidResponse, response_size);
-  dial_socket_->recv_address_ =
-      net::IPEndPoint(net::IPAddress::IPv4Localhost(), 12345);
-
-  DialDeviceData expected_device;
-  expected_device.set_device_id("some_id");
-
-  EXPECT_CALL(mock_client_, OnDeviceDiscovered(expected_device)).Times(1);
-  dial_socket_->OnSocketRead(response_size);
-}
-
-TEST_F(DialServiceTest, TestOnDiscoveryFinished) {
-  dial_service_.discovery_active_ = true;
-
-  EXPECT_CALL(mock_client_, OnDiscoveryFinished()).Times(1);
-  dial_service_.FinishDiscovery();
-  EXPECT_FALSE(dial_service_.discovery_active_);
-}
-
-TEST_F(DialServiceTest, TestResponseParsing) {
-  Time now = Time::Now();
-
-  // Force the socket address to match what is expected in the response.
-  dial_socket_->recv_address_ =
-      net::IPEndPoint(net::IPAddress::IPv4Localhost(), 12345);
-
-  // Successful case, all values parsed successfully.
-  DialDeviceData parsed;
-  EXPECT_TRUE(dial_socket_->ParseResponse(kValidResponse, now, &parsed));
-  EXPECT_EQ("some_id", parsed.device_id());
-  EXPECT_EQ("http://127.0.0.1/dd.xml", parsed.device_description_url().spec());
-  EXPECT_EQ(1, parsed.config_id());
-  EXPECT_EQ(now, parsed.response_time());
-  EXPECT_EQ(1800, parsed.max_age());
-
-  // Cases where we do not fail entirely, but we are unable to extract the
-  // CACHE-CONTROL or CONFIG header values.
-
-  // Max-age is too low
-  DialDeviceData parsed_max_age_low;
-  EXPECT_TRUE(
-      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
-                                  "LOCATION: http://127.0.0.1/dd.xml\r\n"
-                                  "USN: some_id\r\n"
-                                  "CACHE-CONTROL: max-age=-100\r\n"
-                                  "CONFIGID.UPNP.ORG: 1\r\n\r\n",
-                                  now, &parsed_max_age_low));
-  EXPECT_EQ(-1, parsed_max_age_low.max_age());
-
-  // max-age is too high
-  DialDeviceData parsed_max_age_high;
-  EXPECT_TRUE(
-      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
-                                  "LOCATION: http://127.0.0.1/dd.xml\r\n"
-                                  "USN: some_id\r\n"
-                                  "CACHE-CONTROL: max-age=5000\r\n"
-                                  "CONFIGID.UPNP.ORG: 1\r\n\r\n",
-                                  now, &parsed_max_age_high));
-  EXPECT_EQ(3600, parsed_max_age_high.max_age());
-
-  // Invalid CACHE-CONTROL directive
-  DialDeviceData parsed_invalid_cache;
-  EXPECT_TRUE(
-      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
-                                  "LOCATION: http://127.0.0.1/dd.xml\r\n"
-                                  "USN: some_id\r\n"
-                                  "CACHE-CONTROL: xyzzy=100,\r\n"
-                                  "CONFIGID.UPNP.ORG: 1\r\n\r\n",
-                                  now, &parsed_invalid_cache));
-  EXPECT_EQ(-1, parsed_invalid_cache.max_age());
-
-  // Extra CACHE-CONTROL directives
-  DialDeviceData parsed_extra_cache;
-  EXPECT_TRUE(
-      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
-                                  "LOCATION: http://127.0.0.1/dd.xml\r\n"
-                                  "USN: some_id\r\n"
-                                  "CACHE-CONTROL: bar=a,max-age=1800,foo=b\r\n"
-                                  "CONFIGID.UPNP.ORG: 1\r\n\r\n",
-                                  now, &parsed_extra_cache));
-  EXPECT_EQ(-1, parsed_extra_cache.max_age());
-
-  // Invalid CONFIGID.UPNP.ORG value
-  DialDeviceData parsed_invalid_config;
-  EXPECT_TRUE(
-      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
-                                  "LOCATION: http://127.0.0.1/dd.xml\r\n"
-                                  "USN: some_id\r\n"
-                                  "CACHE-CONTROL: max-age=1000\r\n"
-                                  "CONFIGID.UPNP.ORG: -100\r\n\r\n",
-                                  now, &parsed_invalid_config));
-  EXPECT_EQ(-1, parsed_invalid_config.config_id());
-
-  // Failure cases
-  DialDeviceData not_parsed;
-
-  // Empty, garbage
-  EXPECT_FALSE(dial_socket_->ParseResponse(std::string(), now, &not_parsed));
-  EXPECT_FALSE(dial_socket_->ParseResponse("\r\n\r\n", now, &not_parsed));
-  EXPECT_FALSE(dial_socket_->ParseResponse("xyzzy", now, &not_parsed));
-
-  // No headers
-  EXPECT_FALSE(
-      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n\r\n", now, &not_parsed));
-
-  // Missing USN
-  EXPECT_FALSE(
-      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
-                                  "LOCATION: http://127.0.0.1/dd.xml\r\n\r\n",
-                                  now, &not_parsed));
-
-  // Empty USN
-  EXPECT_FALSE(
-      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
-                                  "LOCATION: http://127.0.0.1/dd.xml\r\n"
-                                  "USN:\r\n\r\n",
-                                  now, &not_parsed));
-
-  // Missing LOCATION
-  EXPECT_FALSE(
-      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
-                                  "USN: some_id\r\n\r\n",
-                                  now, &not_parsed));
-
-  // Empty LOCATION
-  EXPECT_FALSE(
-      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
-                                  "LOCATION:\r\n"
-                                  "USN: some_id\r\n\r\n",
-                                  now, &not_parsed));
-
-  // Invalid LOCATION
-  EXPECT_FALSE(
-      dial_socket_->ParseResponse("HTTP/1.1 OK\r\n"
-                                  "LOCATION: http://127.8.8.8/dd.xml\r\n"
-                                  "USN:\r\n\r\n",
-                                  now, &not_parsed));
-}
-
-}  // namespace media_router
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc
index a0dbdb67..6b6c4da 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.cc
@@ -719,4 +719,8 @@
   logger_.reset_on_disconnect();
 }
 
+bool CastMediaSinkServiceImpl::HasSink(const MediaSink::Id& sink_id) {
+  return base::Contains(GetSinks(), sink_id);
+}
+
 }  // namespace media_router
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h
index 0e713af..65f895a 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h
@@ -8,7 +8,6 @@
 #include <memory>
 #include <set>
 
-#include "base/containers/small_map.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
@@ -101,6 +100,18 @@
 
   void BindLogger(mojo::PendingRemote<mojom::Logger> pending_remote);
 
+  // Opens cast channel. This method will not open a channel if there is already
+  // a pending request for |ip_endpoint|, or if a channel for |ip_endpoint|
+  // already exists.
+  // |cast_sink|: Cast sink created from mDNS service description or DIAL sink.
+  // |backoff_entry|: backoff entry passed to |OnChannelOpened| callback.
+  void OpenChannel(const MediaSinkInternal& cast_sink,
+                   std::unique_ptr<net::BackoffEntry> backoff_entry,
+                   SinkSource sink_source);
+
+  // Check to see if the given cast sink exists the sinks_.
+  bool HasSink(const MediaSink::Id& sink_id);
+
  private:
   friend class CastMediaSinkServiceImplTest;
   FRIEND_TEST_ALL_PREFIXES(CastMediaSinkServiceImplTest,
@@ -160,6 +171,7 @@
                            TestFailureOnChannelErrorRetry);
   FRIEND_TEST_ALL_PREFIXES(CastMediaSinkServiceImplTest,
                            OpenChannelNewIPSameSink);
+  FRIEND_TEST_ALL_PREFIXES(CastMediaSinkServiceImplTest, TestHasSink);
 
   // Holds parameters controlling Cast channel retry strategy.
   struct RetryParams {
@@ -224,15 +236,6 @@
   cast_channel::CastSocketOpenParams CreateCastSocketOpenParams(
       const MediaSinkInternal& sink);
 
-  // Opens cast channel. This method will not open a channel if there is already
-  // a pending request for |ip_endpoint|, or if a channel for |ip_endpoint|
-  // already exists.
-  // |cast_sink|: Cast sink created from mDNS service description or DIAL sink.
-  // |backoff_entry|: backoff entry passed to |OnChannelOpened| callback.
-  void OpenChannel(const MediaSinkInternal& cast_sink,
-                   std::unique_ptr<net::BackoffEntry> backoff_entry,
-                   SinkSource sink_source);
-
   // Invoked when opening cast channel on IO thread completes.
   // |cast_sink|: Cast sink created from mDNS service description or DIAL sink.
   // |backoff_entry|: backoff entry passed to |OnChannelErrorMayRetry| callback
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc
index 33a46535..e344785 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl_unittest.cc
@@ -1355,6 +1355,16 @@
   media_sink_service_impl_.BindLogger(std::move(pending_remote_3));
 }
 
+TEST_P(CastMediaSinkServiceImplTest, TestHasSink) {
+  MediaSinkInternal cast_sink1 = CreateCastSink(1);
+  MediaSinkInternal cast_sink2 = CreateCastSink(2);
+
+  media_sink_service_impl_.AddOrUpdateSink(cast_sink1);
+
+  EXPECT_TRUE(media_sink_service_impl_.HasSink(cast_sink1.id()));
+  EXPECT_FALSE(media_sink_service_impl_.HasSink(cast_sink2.id()));
+}
+
 INSTANTIATE_TEST_SUITE_P(DialMediaSinkServiceEnabled,
                          CastMediaSinkServiceImplTest,
                          testing::Bool());
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_test_helpers.cc b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_test_helpers.cc
new file mode 100644
index 0000000..9b3e2e06b
--- /dev/null
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_test_helpers.cc
@@ -0,0 +1,59 @@
+// Copyright 2022 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/media/router/discovery/mdns/cast_media_sink_service_test_helpers.h"
+
+#include <string>
+
+#include "base/memory/raw_ptr.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/mock_callback.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/timer/mock_timer.h"
+#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
+#include "chrome/browser/media/router/discovery/mdns/media_sink_util.h"
+#include "chrome/browser/media/router/test/mock_dns_sd_registry.h"
+#include "chrome/browser/media/router/test/provider_test_helpers.h"
+#include "components/cast_channel/cast_socket.h"
+#include "components/cast_channel/cast_socket_service.h"
+#include "components/cast_channel/cast_test_util.h"
+#include "components/media_router/common/test/test_helper.h"
+#include "content/public/browser/network_service_instance.h"
+#include "content/public/test/browser_task_environment.h"
+#include "net/base/ip_address.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media_router {
+
+MockCastMediaSinkServiceImpl::MockCastMediaSinkServiceImpl(
+    const OnSinksDiscoveredCallback& callback,
+    cast_channel::CastSocketService* cast_socket_service,
+    DiscoveryNetworkMonitor* network_monitor,
+    MediaSinkServiceBase* dial_media_sink_service)
+    : CastMediaSinkServiceImpl(callback,
+                               cast_socket_service,
+                               network_monitor,
+                               dial_media_sink_service,
+                               /* allow_all_ips */ false),
+      sinks_discovered_cb_(callback) {}
+
+MockCastMediaSinkServiceImpl::~MockCastMediaSinkServiceImpl() = default;
+
+std::unique_ptr<CastMediaSinkServiceImpl, base::OnTaskRunnerDeleter>
+TestCastMediaSinkService::CreateImpl(
+    const OnSinksDiscoveredCallback& sinks_discovered_cb,
+    MediaSinkServiceBase* dial_media_sink_service) {
+  auto mock_impl =
+      std::unique_ptr<MockCastMediaSinkServiceImpl, base::OnTaskRunnerDeleter>(
+          new NiceMock<MockCastMediaSinkServiceImpl>(
+              sinks_discovered_cb, cast_socket_service_, network_monitor_,
+              dial_media_sink_service),
+          base::OnTaskRunnerDeleter(cast_socket_service_->task_runner()));
+  mock_impl_ = mock_impl.get();
+  return mock_impl;
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_test_helpers.h b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_test_helpers.h
new file mode 100644
index 0000000..1d3678f
--- /dev/null
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_test_helpers.h
@@ -0,0 +1,80 @@
+// Copyright 2022 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_MEDIA_ROUTER_DISCOVERY_MDNS_CAST_MEDIA_SINK_SERVICE_TEST_HELPERS_H_
+#define CHROME_BROWSER_MEDIA_ROUTER_DISCOVERY_MDNS_CAST_MEDIA_SINK_SERVICE_TEST_HELPERS_H_
+
+#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h"
+
+#include <string>
+
+#include "base/memory/raw_ptr.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/mock_callback.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/timer/mock_timer.h"
+#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
+#include "chrome/browser/media/router/discovery/mdns/media_sink_util.h"
+#include "chrome/browser/media/router/test/mock_dns_sd_registry.h"
+#include "chrome/browser/media/router/test/provider_test_helpers.h"
+#include "components/cast_channel/cast_socket.h"
+#include "components/cast_channel/cast_socket_service.h"
+#include "components/cast_channel/cast_test_util.h"
+#include "components/media_router/common/test/test_helper.h"
+#include "content/public/browser/network_service_instance.h"
+#include "content/public/test/browser_task_environment.h"
+#include "net/base/ip_address.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::NiceMock;
+
+namespace media_router {
+
+class MockCastMediaSinkServiceImpl : public CastMediaSinkServiceImpl {
+ public:
+  MockCastMediaSinkServiceImpl(
+      const OnSinksDiscoveredCallback& callback,
+      cast_channel::CastSocketService* cast_socket_service,
+      DiscoveryNetworkMonitor* network_monitor,
+      MediaSinkServiceBase* dial_media_sink_service);
+  ~MockCastMediaSinkServiceImpl() override;
+
+  void Start() override { DoStart(); }
+  MOCK_METHOD0(DoStart, void());
+  MOCK_METHOD2(OpenChannels,
+               void(const std::vector<MediaSinkInternal>& cast_sinks,
+                    CastMediaSinkServiceImpl::SinkSource sink_source));
+
+  OnSinksDiscoveredCallback sinks_discovered_cb() {
+    return sinks_discovered_cb_;
+  }
+
+ private:
+  OnSinksDiscoveredCallback sinks_discovered_cb_;
+};
+
+class TestCastMediaSinkService : public CastMediaSinkService {
+ public:
+  TestCastMediaSinkService(cast_channel::CastSocketService* cast_socket_service,
+                           DiscoveryNetworkMonitor* network_monitor)
+      : cast_socket_service_(cast_socket_service),
+        network_monitor_(network_monitor) {}
+  ~TestCastMediaSinkService() override = default;
+
+  std::unique_ptr<CastMediaSinkServiceImpl, base::OnTaskRunnerDeleter>
+  CreateImpl(const OnSinksDiscoveredCallback& sinks_discovered_cb,
+             MediaSinkServiceBase* dial_media_sink_service) override;
+
+  MockCastMediaSinkServiceImpl* mock_impl() { return mock_impl_; }
+
+ private:
+  const raw_ptr<cast_channel::CastSocketService> cast_socket_service_;
+  const raw_ptr<DiscoveryNetworkMonitor> network_monitor_;
+  raw_ptr<MockCastMediaSinkServiceImpl> mock_impl_ = nullptr;
+};
+
+}  // namespace media_router
+#endif  // CHROME_BROWSER_MEDIA_ROUTER_DISCOVERY_MDNS_CAST_MEDIA_SINK_SERVICE_TEST_HELPERS_H_
diff --git a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_unittest.cc b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_unittest.cc
index 2faaac45..488a78dc 100644
--- a/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_unittest.cc
+++ b/chrome/browser/media/router/discovery/mdns/cast_media_sink_service_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/test/test_simple_task_runner.h"
 #include "base/timer/mock_timer.h"
 #include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
+#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_test_helpers.h"
 #include "chrome/browser/media/router/discovery/mdns/media_sink_util.h"
 #include "chrome/browser/media/router/test/mock_dns_sd_registry.h"
 #include "chrome/browser/media/router/test/provider_test_helpers.h"
@@ -62,64 +63,6 @@
 
 namespace media_router {
 
-class MockCastMediaSinkServiceImpl : public CastMediaSinkServiceImpl {
- public:
-  MockCastMediaSinkServiceImpl(
-      const OnSinksDiscoveredCallback& callback,
-      cast_channel::CastSocketService* cast_socket_service,
-      DiscoveryNetworkMonitor* network_monitor,
-      MediaSinkServiceBase* dial_media_sink_service)
-      : CastMediaSinkServiceImpl(callback,
-                                 cast_socket_service,
-                                 network_monitor,
-                                 dial_media_sink_service,
-                                 /* allow_all_ips */ false),
-        sinks_discovered_cb_(callback) {}
-  ~MockCastMediaSinkServiceImpl() override {}
-
-  void Start() override { DoStart(); }
-  MOCK_METHOD0(DoStart, void());
-  MOCK_METHOD2(OpenChannels,
-               void(const std::vector<MediaSinkInternal>& cast_sinks,
-                    CastMediaSinkServiceImpl::SinkSource sink_source));
-
-  OnSinksDiscoveredCallback sinks_discovered_cb() {
-    return sinks_discovered_cb_;
-  }
-
- private:
-  OnSinksDiscoveredCallback sinks_discovered_cb_;
-};
-
-class TestCastMediaSinkService : public CastMediaSinkService {
- public:
-  TestCastMediaSinkService(cast_channel::CastSocketService* cast_socket_service,
-                           DiscoveryNetworkMonitor* network_monitor)
-      : cast_socket_service_(cast_socket_service),
-        network_monitor_(network_monitor) {}
-  ~TestCastMediaSinkService() override = default;
-
-  std::unique_ptr<CastMediaSinkServiceImpl, base::OnTaskRunnerDeleter>
-  CreateImpl(const OnSinksDiscoveredCallback& sinks_discovered_cb,
-             MediaSinkServiceBase* dial_media_sink_service) override {
-    auto mock_impl = std::unique_ptr<MockCastMediaSinkServiceImpl,
-                                     base::OnTaskRunnerDeleter>(
-        new NiceMock<MockCastMediaSinkServiceImpl>(
-            sinks_discovered_cb, cast_socket_service_, network_monitor_,
-            dial_media_sink_service),
-        base::OnTaskRunnerDeleter(cast_socket_service_->task_runner()));
-    mock_impl_ = mock_impl.get();
-    return mock_impl;
-  }
-
-  MockCastMediaSinkServiceImpl* mock_impl() { return mock_impl_; }
-
- private:
-  const raw_ptr<cast_channel::CastSocketService> cast_socket_service_;
-  const raw_ptr<DiscoveryNetworkMonitor> network_monitor_;
-  raw_ptr<MockCastMediaSinkServiceImpl> mock_impl_ = nullptr;
-};
-
 class CastMediaSinkServiceTest : public ::testing::Test {
  public:
   CastMediaSinkServiceTest()
diff --git a/chrome/browser/media/router/discovery/media_sink_discovery_metrics.h b/chrome/browser/media/router/discovery/media_sink_discovery_metrics.h
index 8479373..1e52d071 100644
--- a/chrome/browser/media/router/discovery/media_sink_discovery_metrics.h
+++ b/chrome/browser/media/router/discovery/media_sink_discovery_metrics.h
@@ -84,8 +84,9 @@
     kMdnsDial = 4,  // Device was first discovered via mDNS, then by DIAL.
     kDialMdns = 5,  // Device was first discovered via DIAL, then by mDNS.
     kConnectionRetryOnError = 6,
+    kAccessCode = 7,
 
-    kTotalCount = 7,
+    kTotalCount = 8,
   };
 
   static const char kHistogramCastKnownDeviceCount[];
diff --git a/chrome/browser/media/router/mojo/media_router_desktop.cc b/chrome/browser/media/router/mojo/media_router_desktop.cc
index 9c0da42f..edb6d92c 100644
--- a/chrome/browser/media/router/mojo/media_router_desktop.cc
+++ b/chrome/browser/media/router/mojo/media_router_desktop.cc
@@ -181,7 +181,7 @@
           new CastMediaRouteProvider(
               cast_provider_remote.InitWithNewPipeAndPassReceiver(),
               std::move(media_router_remote),
-              media_sink_service_->GetCastMediaSinkServiceImpl(),
+              media_sink_service_->GetCastMediaSinkServiceBase(),
               media_sink_service_->cast_app_discovery_service(),
               GetCastMessageHandler(), GetHashToken(), task_runner),
           base::OnTaskRunnerDeleter(task_runner));
diff --git a/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc b/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
index 67a7022..2ecd163 100644
--- a/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
+++ b/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
@@ -66,8 +66,8 @@
 // Set the user preference for |origin| to prefer tab mirroring.
 void EnableTabMirroringForOrigin(PrefService* prefs,
                                  const std::string& origin) {
-  ListPrefUpdate update(prefs,
-                        media_router::prefs::kMediaRouterTabMirroringSources);
+  ListPrefUpdateDeprecated update(
+      prefs, media_router::prefs::kMediaRouterTabMirroringSources);
   if (!base::Contains(update->GetList(), base::Value(origin)))
     update->Append(origin);
 }
@@ -797,8 +797,8 @@
 
   // Remove the user preference for |origin|.
   {
-    ListPrefUpdate update(profile()->GetPrefs(),
-                          prefs::kMediaRouterTabMirroringSources);
+    ListPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                    prefs::kMediaRouterTabMirroringSources);
     update->EraseListValue(base::Value(origin));
   }
 
@@ -854,7 +854,7 @@
 
   // Remove the user preference for |origin| in OffTheRecord.
   {
-    ListPrefUpdate update(
+    ListPrefUpdateDeprecated update(
         profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true)->GetPrefs(),
         prefs::kMediaRouterTabMirroringSources);
     update->EraseListValue(base::Value(origin));
diff --git a/chrome/browser/media/router/providers/cast/cast_session_tracker.cc b/chrome/browser/media/router/providers/cast/cast_session_tracker.cc
index 933fba5e..821ed54c 100644
--- a/chrome/browser/media/router/providers/cast/cast_session_tracker.cc
+++ b/chrome/browser/media/router/providers/cast/cast_session_tracker.cc
@@ -21,7 +21,7 @@
     return instance_for_test_;
 
   static CastSessionTracker* instance = new CastSessionTracker(
-      DualMediaSinkService::GetInstance()->GetCastMediaSinkServiceImpl(),
+      DualMediaSinkService::GetInstance()->GetCastMediaSinkServiceBase(),
       GetCastMessageHandler(),
       cast_channel::CastSocketService::GetInstance()->task_runner());
   return instance;
diff --git a/chrome/browser/media/router/providers/cast/cast_session_tracker.h b/chrome/browser/media/router/providers/cast/cast_session_tracker.h
index a513d3b..abf80a8 100644
--- a/chrome/browser/media/router/providers/cast/cast_session_tracker.h
+++ b/chrome/browser/media/router/providers/cast/cast_session_tracker.h
@@ -65,6 +65,7 @@
   friend class CastActivityManagerTest;
   friend class CastMediaRouteProviderTest;
   friend class CastActivityTestBase;
+  friend class AccessCodeCastHandlerTest;
 
   // Use |GetInstance()| instead.
   CastSessionTracker(
diff --git a/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc b/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc
index e42a6b3d..6bc2874 100644
--- a/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc
+++ b/chrome/browser/media/router/providers/cast/dual_media_sink_service.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/media/router/discovery/dial/dial_media_sink_service.h"
 #include "chrome/browser/media/router/discovery/dial/dial_media_sink_service_impl.h"
 #include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service.h"
+#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
 #include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/media/router/providers/cast/cast_app_discovery_service.h"
 #include "chrome/browser/media/router/providers/cast/chrome_cast_message_handler.h"
@@ -40,7 +41,11 @@
   return dial_media_sink_service_->impl();
 }
 
-MediaSinkServiceBase* DualMediaSinkService::GetCastMediaSinkServiceImpl() {
+MediaSinkServiceBase* DualMediaSinkService::GetCastMediaSinkServiceBase() {
+  return cast_media_sink_service_->impl();
+}
+
+CastMediaSinkServiceImpl* DualMediaSinkService::GetCastMediaSinkServiceImpl() {
   return cast_media_sink_service_->impl();
 }
 
diff --git a/chrome/browser/media/router/providers/cast/dual_media_sink_service.h b/chrome/browser/media/router/providers/cast/dual_media_sink_service.h
index 1026acc..517feb1 100644
--- a/chrome/browser/media/router/providers/cast/dual_media_sink_service.h
+++ b/chrome/browser/media/router/providers/cast/dual_media_sink_service.h
@@ -24,6 +24,7 @@
 
 class CastAppDiscoveryService;
 class CastMediaSinkService;
+class CastMediaSinkServiceImpl;
 class DialMediaSinkService;
 class DialMediaSinkServiceImpl;
 class MediaSinkServiceBase;
@@ -54,7 +55,9 @@
   DialMediaSinkServiceImpl* GetDialMediaSinkServiceImpl();
 
   // Used by CastMediaRouteProvider only.
-  MediaSinkServiceBase* GetCastMediaSinkServiceImpl();
+  MediaSinkServiceBase* GetCastMediaSinkServiceBase();
+
+  CastMediaSinkServiceImpl* GetCastMediaSinkServiceImpl();
 
   CastAppDiscoveryService* cast_app_discovery_service() {
     return cast_app_discovery_service_.get();
@@ -95,10 +98,18 @@
 
  private:
   friend class DualMediaSinkServiceTest;
+  friend class AccessCodeCastHandlerTest;
+
   FRIEND_TEST_ALL_PREFIXES(DualMediaSinkServiceTest,
                            AddSinksDiscoveredCallback);
   FRIEND_TEST_ALL_PREFIXES(DualMediaSinkServiceTest,
                            AddSinksDiscoveredCallbackAfterDiscovery);
+  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest,
+                           DiscoveryDeviceMissingWithOk);
+  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest,
+                           ValidDiscoveryDeviceAndCode);
+  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest, InvalidDiscoveryDevice);
+  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest, NonOKResultCode);
   friend class MediaRouterDesktopTest;
 
   static DualMediaSinkService* instance_for_test_;
diff --git a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
index 4d3bcaa..1dc6294 100644
--- a/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
+++ b/chrome/browser/media/webrtc/desktop_capture_access_handler.cc
@@ -390,25 +390,11 @@
   }
 
   // Resolve DesktopMediaID for the specified device id.
-  content::DesktopMediaID media_id;
-  // TODO(miu): Replace "main RenderFrame" IDs with the request's actual
-  // RenderFrame IDs once the desktop capture extension API implementation is
-  // fixed.  http://crbug.com/304341
-  content::WebContents* const web_contents_for_stream =
-      content::WebContents::FromRenderFrameHost(
-          content::RenderFrameHost::FromID(request.render_process_id,
-                                           request.render_frame_id));
-  content::RenderFrameHost* const main_frame =
-      web_contents_for_stream ? web_contents_for_stream->GetMainFrame()
-                              : nullptr;
-  if (main_frame) {
-    media_id =
-        content::DesktopStreamsRegistry::GetInstance()->RequestMediaForStreamId(
-            request.requested_video_device_id,
-            main_frame->GetProcess()->GetID(), main_frame->GetRoutingID(),
-            url::Origin::Create(request.security_origin), nullptr,
-            content::kRegistryStreamTypeDesktop);
-  }
+  content::DesktopMediaID media_id =
+      content::DesktopStreamsRegistry::GetInstance()->RequestMediaForStreamId(
+          request.requested_video_device_id, request.render_process_id,
+          request.render_frame_id, url::Origin::Create(request.security_origin),
+          nullptr, content::kRegistryStreamTypeDesktop);
 
   // Received invalid device id.
   if (media_id.type == content::DesktopMediaID::TYPE_NONE) {
diff --git a/chrome/browser/media_galleries/media_galleries_preferences.cc b/chrome/browser/media_galleries/media_galleries_preferences.cc
index b0d9921..1b9b91e 100644
--- a/chrome/browser/media_galleries/media_galleries_preferences.cc
+++ b/chrome/browser/media_galleries/media_galleries_preferences.cc
@@ -798,8 +798,9 @@
       return *pref_id_it;
 
     PrefService* prefs = profile_->GetPrefs();
-    std::unique_ptr<ListPrefUpdate> update(
-        new ListPrefUpdate(prefs, prefs::kMediaGalleriesRememberedGalleries));
+    std::unique_ptr<ListPrefUpdateDeprecated> update(
+        new ListPrefUpdateDeprecated(
+            prefs, prefs::kMediaGalleriesRememberedGalleries));
     base::ListValue* list = update->Get();
 
     for (auto& gallery_value : list->GetList()) {
@@ -871,7 +872,8 @@
   gallery_info.default_gallery_type = default_gallery_type;
 
   {
-    ListPrefUpdate update(prefs, prefs::kMediaGalleriesRememberedGalleries);
+    ListPrefUpdateDeprecated update(prefs,
+                                    prefs::kMediaGalleriesRememberedGalleries);
     base::ListValue* list = update.Get();
     list->Append(CreateGalleryPrefInfoDictionary(gallery_info));
   }
@@ -894,8 +896,8 @@
       base::PathService::Get(chrome::DIR_USER_VIDEOS, &videos_path);
 
   PrefService* prefs = profile_->GetPrefs();
-  std::unique_ptr<ListPrefUpdate> update(
-      new ListPrefUpdate(prefs, prefs::kMediaGalleriesRememberedGalleries));
+  std::unique_ptr<ListPrefUpdateDeprecated> update(new ListPrefUpdateDeprecated(
+      prefs, prefs::kMediaGalleriesRememberedGalleries));
   base::ListValue* list = update->Get();
 
   std::vector<MediaGalleryPrefId> pref_ids;
@@ -988,8 +990,8 @@
     bool erase) {
   DCHECK(IsInitialized());
   PrefService* prefs = profile_->GetPrefs();
-  std::unique_ptr<ListPrefUpdate> update(
-      new ListPrefUpdate(prefs, prefs::kMediaGalleriesRememberedGalleries));
+  std::unique_ptr<ListPrefUpdateDeprecated> update(new ListPrefUpdateDeprecated(
+      prefs, prefs::kMediaGalleriesRememberedGalleries));
   base::ListValue* list = update->Get();
 
   if (!base::Contains(known_galleries_, id))
diff --git a/chrome/browser/media_galleries/media_galleries_preferences_unittest.cc b/chrome/browser/media_galleries/media_galleries_preferences_unittest.cc
index 19e2d7b..45cb1a6 100644
--- a/chrome/browser/media_galleries/media_galleries_preferences_unittest.cc
+++ b/chrome/browser/media_galleries/media_galleries_preferences_unittest.cc
@@ -189,8 +189,9 @@
 
   void RemovePersistedDefaultGalleryValues() {
     PrefService* prefs = profile_->GetPrefs();
-    std::unique_ptr<ListPrefUpdate> update(
-        new ListPrefUpdate(prefs, prefs::kMediaGalleriesRememberedGalleries));
+    std::unique_ptr<ListPrefUpdateDeprecated> update(
+        new ListPrefUpdateDeprecated(
+            prefs, prefs::kMediaGalleriesRememberedGalleries));
     base::ListValue* list = update->Get();
 
     for (auto& entry : list->GetList()) {
diff --git a/chrome/browser/metrics/bluetooth_available_utility.cc b/chrome/browser/metrics/bluetooth_available_utility.cc
index a08be4c..bfad3b5 100644
--- a/chrome/browser/metrics/bluetooth_available_utility.cc
+++ b/chrome/browser/metrics/bluetooth_available_utility.cc
@@ -13,9 +13,9 @@
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
 namespace bluetooth_utility {
 
@@ -40,13 +40,13 @@
 
 void ReportBluetoothAvailability() {
   // This is only relevant for desktop platforms.
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   // TODO(kenrb): This is separate from other platforms because we get a
   // little bit of extra information from the Mac-specific code. It might not
   // be worth having the extra code path, and we should consider whether to
   // combine them (https://crbug.com/907279).
   ReportAvailability(bluetooth_utility::GetBluetoothAvailability());
-#elif defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS)
+#elif BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   // GetAdapter must be called on the UI thread, because it creates a
   // WeakPtr, which is checked from that thread on future calls.
   if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
@@ -55,12 +55,12 @@
     return;
   }
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   // This is for tests that have not initialized bluez or dbus thread manager.
   // Outside of tests these are initialized earlier during browser startup.
   if (!bluez::BluezDBusManager::IsInitialized())
     return;
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
   if (!device::BluetoothAdapterFactory::Get()->IsBluetoothSupported()) {
     ReportAvailability(BLUETOOTH_NOT_SUPPORTED);
diff --git a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
index ed5af8a1..157782c 100644
--- a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
+++ b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
@@ -47,39 +47,39 @@
 #include "ui/base/ui_base_switches.h"
 #include "ui/display/screen.h"
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/metrics/first_web_contents_profiler.h"
 #include "chrome/browser/metrics/tab_stats/tab_stats_tracker.h"
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
-#if defined(OS_ANDROID) && defined(__arm__)
+#if BUILDFLAG(IS_ANDROID) && defined(__arm__)
 #include <cpu-features.h>
-#endif  // defined(OS_ANDROID) && defined(__arm__)
+#endif  // BUILDFLAG(IS_ANDROID) && defined(__arm__)
 
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
 #include <gnu/libc-version.h>
 
 #include "base/linux_util.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/version.h"
-#endif  // defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
 
 #if defined(USE_OZONE)
 #include "ui/events/devices/device_data_manager.h"
 #include "ui/events/devices/input_device_event_observer.h"
 #endif  // defined(USE_OZONE)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <windows.h>
 
 #include "base/win/base_win_buildflags.h"
 #include "base/win/scoped_handle.h"
 #include "base/win/windows_version.h"
 #include "chrome/browser/shell_integration_win.h"
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chromeos/crosapi/cpp/crosapi_constants.h"
@@ -186,14 +186,14 @@
                            base::SysInfo::NumberOfProcessors());
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 bool IsApplockerRunning();
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 // Called on a background thread, with low priority to avoid slowing down
 // startup with metrics that aren't trivial to compute.
 void RecordStartupMetrics() {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   const base::win::OSInfo& os_info = *base::win::OSInfo::GetInstance();
   int patch = os_info.version_number().patch;
   int build = os_info.version_number().build;
@@ -218,7 +218,7 @@
   // Determine if Applocker is enabled and running. This does not check if
   // Applocker rules are being enforced.
   base::UmaHistogramBoolean("Windows.ApplockerRunning", IsApplockerRunning());
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
   bluetooth_utility::ReportBluetoothAvailability();
 
@@ -238,7 +238,7 @@
 
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
 void RecordLinuxDistroSpecific(const std::string& version_string,
                                size_t parts,
                                const char* histogram_name) {
@@ -316,12 +316,12 @@
 
   base::UmaHistogramSparse("Linux.Distro2", distro_result);
 }
-#endif  // defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
 
 void RecordLinuxGlibcVersion() {
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
   base::Version version(gnu_get_libc_version());
 
   UMALinuxGlibcVersion glibc_version_result = UMA_LINUX_GLIBC_NOT_PARSEABLE;
@@ -411,7 +411,7 @@
 
 #endif  // defined(USE_OZONE)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 void RecordPinnedToTaskbarProcessError(bool error) {
   base::UmaHistogramBoolean("Windows.IsPinnedToTaskbar.ProcessError", error);
 }
@@ -493,9 +493,9 @@
   return status.dwCurrentState == SERVICE_RUNNING;
 }
 
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 // Returns whether the instance has an enterprise brand code.
 bool HasEnterpriseBrandCode() {
   std::string brand;
@@ -510,7 +510,7 @@
       ->HasManagementAuthority(
           policy::EnterpriseManagementAuthority::DOMAIN_LOCAL);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 void RecordDisplayHDRStatus(const display::Display& display) {
   base::UmaHistogramBoolean("Hardware.Display.SupportsHDR",
@@ -548,7 +548,7 @@
   );
 
   // Records whether or not the Segment heap is in use.
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 
   if (base::win::GetVersion() >= base::win::Version::WIN10_20H1) {
     ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial("WinSegmentHeap",
@@ -573,9 +573,9 @@
 #endif
   );
 
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // No need to filter out on Android, because it doesn't support
   // ChromeVariations policy.
   constexpr bool is_enterprise = false;
@@ -613,7 +613,7 @@
       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN};
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
   base::ThreadPool::PostTask(FROM_HERE, kBestEffortTaskTraits,
                              base::BindOnce(&RecordLinuxDistro));
 #endif
@@ -632,11 +632,11 @@
   RecordTouchEventState();
 #endif  // defined(USE_OZONE)
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   RecordMacMetrics();
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // RecordStartupMetrics calls into shell_integration::GetDefaultBrowser(),
   // which requires a COM thread on Windows.
   base::ThreadPool::CreateCOMSTATaskRunner(kBestEffortTaskTraits)
@@ -644,9 +644,9 @@
 #else
   base::ThreadPool::PostTask(FROM_HERE, kBestEffortTaskTraits,
                              base::BindOnce(&RecordStartupMetrics));
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // TODO(isherman): The delay below is currently needed to avoid (flakily)
   // breaking some tests, including all of the ProcessMemoryMetricsEmitterTest
   // tests. Figure out why there is a dependency and fix the tests.
@@ -661,7 +661,7 @@
         FROM_HERE, base::BindOnce(&RecordIsPinnedToTaskbarHistogram),
         base::Seconds(45));
   }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
   auto* screen = display::Screen::GetScreen();
   display_count_ = screen->GetNumDisplays();
@@ -674,7 +674,7 @@
 
   display_observer_.emplace(this);
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
   metrics::BeginFirstWebContentsProfiling();
   // Only instantiate the tab stats tracker if a local state exists. This is
   // always the case for Chrome but not for the unittests.
@@ -684,8 +684,8 @@
         std::make_unique<metrics::TabStatsTracker>(
             g_browser_process->local_state()));
   }
-#endif  // !defined(OS_ANDROID)
-#if defined(OS_MAC) || defined(OS_WIN)
+#endif  // !BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
   // BatteryLevelProvider is supported on mac and windows only, thus we report
   // power metrics only on those platforms.
   if (performance_monitor::ProcessMonitor::Get()) {
@@ -694,7 +694,7 @@
     power_metrics_reporter_ = std::make_unique<PowerMetricsReporter>(
         usage_scenario_tracker_->data_store(), BatteryLevelProvider::Create());
   }
-#endif  // defined(OS_MAC) || defined (OS_WIN)
+#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
 }
 
 void ChromeBrowserMainExtraPartsMetrics::PreMainMessageLoopRun() {
diff --git a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.h b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.h
index ff32d29ac..179a93a 100644
--- a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.h
+++ b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.h
@@ -45,10 +45,10 @@
   void PreMainMessageLoopRun() override;
 
  private:
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   // Records Mac specific metrics.
   void RecordMacMetrics();
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
 
   // DisplayObserver overrides.
   void OnDisplayAdded(const display::Display& new_display) override;
@@ -68,7 +68,7 @@
   std::unique_ptr<ui::InputDeviceEventObserver> input_device_event_observer_;
 #endif  // defined(USE_OZONE)
 
-#if defined(OS_MAC) || defined(OS_WIN)
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
   // Tracks coarse usage scenarios that affect performance during a given
   // interval of time (e.g. navigating to a new page, watching a video). The
   // data tracked by this is used by other classes (see below) to report metrics
@@ -80,7 +80,7 @@
   // |usage_scenario_tracker_|, used to analyze the correlation between usage
   // scenarios and power consumption.
   std::unique_ptr<PowerMetricsReporter> power_metrics_reporter_;
-#endif  // defined(OS_MAC) || defined (OS_WIN)
+#endif  // BUILDFLAG(IS_MAC) || defined (OS_WIN)
 };
 
 #endif  // CHROME_BROWSER_METRICS_CHROME_BROWSER_MAIN_EXTRA_PARTS_METRICS_H_
diff --git a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics_unittest.cc b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics_unittest.cc
index 89a597505..abd55147 100644
--- a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics_unittest.cc
+++ b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics_unittest.cc
@@ -151,7 +151,7 @@
 
 // Verify a TouchEventsEnabled value is recorded during PostBrowserStart.
 // Flaky on Win only.  http://crbug.com/1026946
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #define MAYBE_VerifyTouchEventsEnabledIsRecordedAfterPostBrowserStart \
   DISABLED_VerifyTouchEventsEnabledIsRecordedAfterPostBrowserStart
 #else
diff --git a/chrome/browser/metrics/chrome_feature_list_creator.cc b/chrome/browser/metrics/chrome_feature_list_creator.cc
index f8cddd5..89db9d4 100644
--- a/chrome/browser/metrics/chrome_feature_list_creator.cc
+++ b/chrome/browser/metrics/chrome_feature_list_creator.cc
@@ -16,6 +16,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "cc/base/switches.h"
 #include "chrome/browser/about_flags.h"
@@ -92,7 +93,7 @@
   return std::move(browser_policy_connector_);
 }
 
-#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
 std::unique_ptr<installer::InitialPreferences>
 ChromeFeatureListCreator::TakeInitialPrefs() {
   return std::move(installer_initial_prefs_);
@@ -105,10 +106,10 @@
       base::PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_file);
   DCHECK(result);
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   base::UmaHistogramBoolean("UMA.Startup.LocalStateFileExistence",
                             base::PathExists(local_state_file));
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
   auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>();
   RegisterLocalState(pref_registry.get());
@@ -130,7 +131,7 @@
 // TODO(asvitkine): This is done here so that the pref is set before
 // VariationsService queries the locale. This should potentially be moved to
 // somewhere better, e.g. as a helper in first_run namespace.
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   if (first_run::IsChromeFirstRun()) {
     // During first run we read the google_update registry key to find what
     // language the user selected when downloading the installer. This
@@ -142,7 +143,7 @@
                               base::WideToASCII(install_lang));
     }
   }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 }
 
 void ChromeFeatureListCreator::ConvertFlagsToSwitches() {
@@ -211,7 +212,7 @@
 void ChromeFeatureListCreator::SetupInitialPrefs() {
 // Android does first run in Java instead of native.
 // Chrome OS has its own out-of-box-experience code.
-#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
   // On first run, we need to process the predictor preferences before the
   // browser's profile_manager object is created, but after ResourceBundle
   // is initialized.
@@ -246,5 +247,5 @@
     local_state_->SetInt64(variations::prefs::kVariationsSeedDate,
                            base::Time::Now().ToInternalValue());
   }
-#endif  // !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
 }
diff --git a/chrome/browser/metrics/chrome_feature_list_creator.h b/chrome/browser/metrics/chrome_feature_list_creator.h
index d43b1d1..0904c96 100644
--- a/chrome/browser/metrics/chrome_feature_list_creator.h
+++ b/chrome/browser/metrics/chrome_feature_list_creator.h
@@ -65,7 +65,7 @@
   std::unique_ptr<policy::ChromeBrowserPolicyConnector>
   TakeChromeBrowserPolicyConnector();
 
-#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
   std::unique_ptr<installer::InitialPreferences> TakeInitialPrefs();
 #endif
 
@@ -119,7 +119,7 @@
 
   std::unique_ptr<ChromeBrowserFieldTrials> browser_field_trials_;
 
-#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
   std::unique_ptr<installer::InitialPreferences> installer_initial_prefs_;
 #endif
 
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index d99e22c..69653a6 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -118,7 +118,7 @@
 #include "printing/buildflags/buildflags.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/metrics/chrome_android_metrics_provider.h"
 #include "chrome/browser/metrics/page_load_metrics_provider.h"
 #include "components/metrics/android_metrics_provider.h"
@@ -126,7 +126,7 @@
 #include "chrome/browser/metrics/browser_activity_watcher.h"
 #endif
 
-#if defined(OS_POSIX)
+#if BUILDFLAG(IS_POSIX)
 #include <signal.h>
 #endif
 
@@ -158,7 +158,7 @@
 #include "components/metrics/structured/structured_metrics_provider.h"  // nogncheck
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <windows.h>
 
 #include "chrome/browser/metrics/antivirus_metrics_provider_win.h"
@@ -168,7 +168,7 @@
 #include "chrome/notification_helper/notification_helper_constants.h"
 #endif
 
-#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
 #include "third_party/crashpad/crashpad/client/crashpad_info.h"
 #endif
 
@@ -178,17 +178,17 @@
 #include "components/signin/core/browser/signin_status_metrics_provider.h"
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/metrics/upgrade_metrics_provider.h"
-#endif  // !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 #include "chrome/browser/metrics/power/power_metrics_provider_mac.h"
 #endif
 
 namespace {
 
-#if defined(OS_ANDROID) || BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS_ASH)
 const int kMaxHistogramStorageKiB = 100 << 10;  // 100 MiB
 #else
 const int kMaxHistogramStorageKiB = 500 << 10;  // 500 MiB
@@ -202,7 +202,7 @@
 // third_party/crashpad/crashpad/handler/handler_main.cc.
 const char kCrashpadHistogramAllocatorName[] = "CrashpadMetrics";
 
-#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
 // The stream type assigned to the minidump stream that holds the serialized
 // system profile proto.
 const uint32_t kSystemProfileMinidumpStreamType = 0x4B6B0003;
@@ -213,7 +213,7 @@
 // not assume it.
 // TODO(manzagop): revisit this if the Crashpad API evolves.
 base::LazyInstance<std::string>::Leaky g_environment_for_crash_reporter;
-#endif  // defined(OS_WIN) || defined(OS_MAC) || defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
 
 void RegisterFileMetricsPreferences(PrefRegistrySimple* registry) {
   metrics::FileMetricsProvider::RegisterSourcePrefs(registry,
@@ -222,7 +222,7 @@
   metrics::FileMetricsProvider::RegisterSourcePrefs(
       registry, kCrashpadHistogramAllocatorName);
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   metrics::FileMetricsProvider::RegisterSourcePrefs(
       registry, installer::kSetupHistogramAllocatorName);
 
@@ -313,7 +313,7 @@
     }
   }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // Read metrics file from setup.exe.
   base::FilePath program_dir;
   base::PathService::Get(base::DIR_EXE, &program_dir);
@@ -360,7 +360,7 @@
   if (g_is_process_running)
     return g_is_process_running(pid);
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid);
   if (process) {
     DWORD ret = WaitForSingleObject(process, 0);
@@ -368,12 +368,12 @@
     if (ret == WAIT_TIMEOUT)
       return true;
   }
-#elif defined(OS_POSIX)
+#elif BUILDFLAG(IS_POSIX)
   // Sending a signal value of 0 will cause error checking to be performed
   // with no signal being sent.
   if (kill(pid, 0) == 0 || errno != ESRCH)
     return true;
-#elif defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_FUCHSIA)
   // TODO(crbug.com/1235293)
   NOTIMPLEMENTED_LOG_ONCE();
 #else
@@ -492,9 +492,9 @@
 
   metrics::RegisterMetricsReportingStatePrefs(registry);
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   ChromeAndroidMetricsProvider::RegisterPrefs(registry);
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(ENABLE_PLUGINS)
   PluginMetricsProvider::RegisterPrefs(registry);
@@ -549,7 +549,7 @@
 }
 
 void ChromeMetricsServiceClient::OnEnvironmentUpdate(std::string* environment) {
-#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
   DCHECK(environment);
 
   // Register the environment with the crash reporter. Note this only registers
@@ -724,29 +724,29 @@
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<safe_browsing::SafeBrowsingMetricsProvider>());
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<metrics::AndroidMetricsProvider>());
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<ChromeAndroidMetricsProvider>(local_state));
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<PageLoadMetricsProvider>());
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<GoogleUpdateMetricsProviderWin>());
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<AntiVirusMetricsProvider>());
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#if defined(OS_WIN) || defined(OS_MAC) || \
-    (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || \
+    (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<DesktopPlatformFeaturesMetricsProvider>());
-#endif  // defined(OS_WIN) || defined(OS_MAC) || (defined(OS_LINUX) ||
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_LINUX) ||
         // BUILDFLAG(IS_CHROMEOS_LACROS))
 
 #if BUILDFLAG(ENABLE_PLUGINS)
@@ -828,20 +828,20 @@
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<CertificateReportingMetricsProvider>());
 
-#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<UpgradeMetricsProvider>());
-#endif  //! defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  //! BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<PowerMetricsProvider>());
 #endif
 
-#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
   metrics_service_->RegisterMetricsProvider(
       metrics::CreateDesktopSessionMetricsProvider());
-#endif  // defined(OS_WIN) || defined(OS_MAC) || (defined(OS_LINUX)
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_LINUX)
 }
 
 void ChromeMetricsServiceClient::RegisterUKMProviders() {
@@ -971,7 +971,7 @@
               &ChromeMetricsServiceClient::OnURLOpenedFromOmnibox,
               base::Unretained(this)));
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
   browser_activity_watcher_ = std::make_unique<BrowserActivityWatcher>(
       base::BindRepeating(&metrics::MetricsService::OnApplicationNotIdle,
                           base::Unretained(metrics_service_.get())));
@@ -1014,8 +1014,8 @@
 #endif
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#if defined(OS_WIN) || defined(OS_MAC) || \
-    (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || \
+    (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
   // This creates the DesktopProfileSessionDurationsServices if it didn't exist
   // already.
   metrics::DesktopProfileSessionDurationsServiceFactory::GetForBrowserContext(
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.h b/chrome/browser/metrics/chrome_metrics_service_client.h
index b22495e..f178333 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.h
+++ b/chrome/browser/metrics/chrome_metrics_service_client.h
@@ -164,11 +164,11 @@
   // Called when a URL is opened from the Omnibox.
   void OnURLOpenedFromOmnibox(OmniboxLog* log);
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // Counts (and removes) the browser crash dump attempt signals left behind by
   // any previous browser processes which generated a crash dump.
   void CountBrowserCrashDumpAttempts();
-#endif  // OS_WIN
+#endif  // BUILDFLAG(IS_WIN)
 
   // Check if an extension is installed via the Web Store.
   static bool IsWebstoreExtension(base::StringPiece id);
@@ -214,7 +214,7 @@
   // omnibox.
   base::CallbackListSubscription omnibox_url_opened_subscription_;
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
   std::unique_ptr<BrowserActivityWatcher> browser_activity_watcher_;
 #endif
 
diff --git a/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc b/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc
index fca8e06f..7dc613a 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client_unittest.cc
@@ -169,16 +169,16 @@
   expected_providers++;  // ExtensionsMetricsProvider.
 #endif                   // defined(ENABLE_EXTENSIONS)
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // AndroidMetricsProvider, ChromeAndroidMetricsProvider, and
   // PageLoadMetricsProvider.
   expected_providers += 3;
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // GoogleUpdateMetricsProviderWin and AntiVirusMetricsProvider.
   expected_providers += 2;
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 #if BUILDFLAG(ENABLE_PLUGINS)
   // PluginMetricsProvider.
@@ -205,25 +205,25 @@
   expected_providers += 2;
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
   expected_providers++;  // UpgradeMetricsProvider
-#endif                   //! defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  //! BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   expected_providers++;  // PowerMetricsProvider
-#endif                   // defined(OS_MAC)
+#endif                   // BUILDFLAG(IS_MAC)
 
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#if defined(OS_WIN) || defined(OS_MAC) || \
-    (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || \
+    (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
   expected_providers++;  // DesktopPlatformFeaturesMetricsProvider
-#endif  // defined(OS_WIN) || defined(OS_MAC) || (defined(OS_LINUX) ||
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_LINUX) ||
         // BUILDFLAG(IS_CHROMEOS_LACROS))
 
-#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
   expected_providers++;  // DesktopSessionMetricsProvider
-#endif  // defined(OS_WIN) || defined(OS_MAC) || (defined(OS_LINUX)
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_LINUX)
 
   std::unique_ptr<ChromeMetricsServiceClient> chrome_metrics_service_client =
       ChromeMetricsServiceClient::Create(metrics_state_manager_.get());
diff --git a/chrome/browser/metrics/chrome_metrics_services_manager_client.cc b/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
index b1779bf..a60a8007 100644
--- a/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
@@ -17,6 +17,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
+#include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/metrics/chrome_metrics_service_client.h"
@@ -39,28 +40,28 @@
 #include "content/public/browser/network_service_instance.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
+#include "base/metrics/histogram_functions.h"
 #include "chrome/browser/android/metrics/uma_session_stats.h"
 #include "chrome/browser/ui/android/tab_model/tab_model.h"
 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
 #else
 #include "chrome/browser/ui/browser_list.h"
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "base/win/registry.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/install_static/install_util.h"
 #include "components/crash/core/app/crash_export_thunks.h"
 #include "components/crash/core/app/crashpad.h"
-#endif  // OS_WIN
+#endif  // BUILDFLAG(IS_WIN)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/settings/stats_reporting_controller.h"
 #include "components/metrics/structured/neutrino_logging.h"       // nogncheck
 #include "components/metrics/structured/neutrino_logging_util.h"  // nogncheck
 #include "components/metrics/structured/recorder.h"               // nogncheck
-
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -134,7 +135,7 @@
 // Returns the name of a key under HKEY_CURRENT_USER that can be used to store
 // backups of metrics data. Unused except on Windows.
 std::wstring GetRegistryBackupKey() {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   return install_static::GetRegistryPath().append(L"\\StabilityMetrics");
 #else
   return std::wstring();
@@ -296,13 +297,14 @@
     base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
 
     metrics::StartupVisibility startup_visibility;
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     startup_visibility = UmaSessionStats::HasVisibleActivity()
                              ? metrics::StartupVisibility::kForeground
                              : metrics::StartupVisibility::kBackground;
+    base::UmaHistogramEnumeration("UMA.StartupVisibility", startup_visibility);
 #else
     startup_visibility = metrics::StartupVisibility::kForeground;
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
     std::string client_id;
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -337,7 +339,7 @@
 }
 
 bool ChromeMetricsServicesManagerClient::IsOffTheRecordSessionActive() {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // This differs from TabModelList::IsOffTheRecordSessionActive in that it
   // does not ignore TabModels that have no open tabs, because it may be checked
   // before tabs get added to the TabModel. This means it may be more
@@ -358,7 +360,7 @@
 #endif
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 void ChromeMetricsServicesManagerClient::UpdateRunningServices(
     bool may_record,
     bool may_upload) {
@@ -372,4 +374,4 @@
   // This isn't a problem though, since they will be consistent.
   SetUploadConsent_ExportThunk(may_record && may_upload);
 }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
diff --git a/chrome/browser/metrics/chrome_metrics_services_manager_client.h b/chrome/browser/metrics/chrome_metrics_services_manager_client.h
index 8a5a6ad..5f3ccf3 100644
--- a/chrome/browser/metrics/chrome_metrics_services_manager_client.h
+++ b/chrome/browser/metrics/chrome_metrics_services_manager_client.h
@@ -94,10 +94,10 @@
   bool IsMetricsReportingEnabled() override;
   bool IsMetricsConsentGiven() override;
   bool IsOffTheRecordSessionActive() override;
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // On Windows, the client controls whether Crashpad can upload crash reports.
   void UpdateRunningServices(bool may_record, bool may_upload) override;
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
   // MetricsStateManager which is passed as a parameter to service constructors.
   std::unique_ptr<metrics::MetricsStateManager> metrics_state_manager_;
diff --git a/chrome/browser/metrics/desktop_session_duration/audible_contents_tracker_browsertest.cc b/chrome/browser/metrics/desktop_session_duration/audible_contents_tracker_browsertest.cc
index 0fe57d9..4c9e1e0 100644
--- a/chrome/browser/metrics/desktop_session_duration/audible_contents_tracker_browsertest.cc
+++ b/chrome/browser/metrics/desktop_session_duration/audible_contents_tracker_browsertest.cc
@@ -74,7 +74,8 @@
 };
 
 // TODO(crbug.com/1124845): Flaky on Win7 32-bit.
-#if defined(OS_WIN) && defined(ARCH_CPU_X86_FAMILY) && defined(ARCH_CPU_32_BITS)
+#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_X86_FAMILY) && \
+    defined(ARCH_CPU_32_BITS)
 #define MAYBE_TestAudioNotifications DISABLED_TestAudioNotifications
 #else
 #define MAYBE_TestAudioNotifications TestAudioNotifications
diff --git a/chrome/browser/metrics/desktop_session_duration/chrome_visibility_observer_interactive_uitest.cc b/chrome/browser/metrics/desktop_session_duration/chrome_visibility_observer_interactive_uitest.cc
index c3e6e28c..8294d2f 100644
--- a/chrome/browser/metrics/desktop_session_duration/chrome_visibility_observer_interactive_uitest.cc
+++ b/chrome/browser/metrics/desktop_session_duration/chrome_visibility_observer_interactive_uitest.cc
@@ -49,7 +49,7 @@
   EXPECT_TRUE(is_active());
 
 // BrowserWindow::Deactivate() not implemented on Mac (https://crbug.com/51364).
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
   // Deactivating and activating the browser should affect the observer
   // accordingly.
   browser()->window()->Deactivate();
@@ -57,7 +57,7 @@
   browser()->window()->Activate();
   ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
   EXPECT_TRUE(is_active());
-#endif  // !defined(OS_MAC)
+#endif  // !BUILDFLAG(IS_MAC)
 
   // Creating and closing new browsers should keep the observer active.
   Browser* new_browser = CreateBrowser(browser()->profile());
diff --git a/chrome/browser/metrics/first_web_contents_profiler_base.cc b/chrome/browser/metrics/first_web_contents_profiler_base.cc
index 3f2d06b..1dab30d1 100644
--- a/chrome/browser/metrics/first_web_contents_profiler_base.cc
+++ b/chrome/browser/metrics/first_web_contents_profiler_base.cc
@@ -30,13 +30,13 @@
   content::WebContents* contents =
       browser->tab_strip_model()->GetActiveWebContents();
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   // TODO(https://crbug.com/1032348): It is incorrect to have a visible
   // browser window with no active WebContents, but reports on Mac show that
   // it happens.
   if (!contents)
     return nullptr;
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
 
   if (contents->GetVisibility() != content::Visibility::VISIBLE)
     return nullptr;
diff --git a/chrome/browser/metrics/metrics_reporting_state.cc b/chrome/browser/metrics/metrics_reporting_state.cc
index 489157a..9066a66d 100644
--- a/chrome/browser/metrics/metrics_reporting_state.cc
+++ b/chrome/browser/metrics/metrics_reporting_state.cc
@@ -9,6 +9,7 @@
 #include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task/task_runner_util.h"
+#include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
@@ -28,9 +29,9 @@
 #include "components/metrics/structured/neutrino_logging.h"  // nogncheck
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include "components/policy/core/common/features.h"
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
 namespace {
 
@@ -105,7 +106,7 @@
 void ChangeMetricsReportingStateWithReply(
     bool enabled,
     OnMetricsReportingCallbackType callback_fn) {
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
   if (IsMetricsReportingPolicyManaged()) {
     if (!callback_fn.is_null()) {
       const bool metrics_enabled =
@@ -157,13 +158,13 @@
 }
 
 void ApplyMetricsReportingPolicy() {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // Android must verify if this policy is feature-enabled.
   if (!base::FeatureList::IsEnabled(
           policy::features::kActivateMetricsReportingEnabledPolicyAndroid)) {
     return;
   }
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
   GoogleUpdateSettings::CollectStatsConsentTaskRunner()->PostTask(
       FROM_HERE,
diff --git a/chrome/browser/metrics/metrics_service_browsertest.cc b/chrome/browser/metrics/metrics_service_browsertest.cc
index d48440c..4f4e368 100644
--- a/chrome/browser/metrics/metrics_service_browsertest.cc
+++ b/chrome/browser/metrics/metrics_service_browsertest.cc
@@ -46,15 +46,15 @@
 #include "ui/base/window_open_disposition.h"
 #include "url/gurl.h"
 
-#if defined(OS_POSIX)
+#if BUILDFLAG(IS_POSIX)
 #include <sys/wait.h>
 #endif
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "sandbox/win/src/sandbox_types.h"
 #endif
 
-#if defined(OS_MAC) || defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 namespace {
 
 // Check CrashExitCodes.Renderer histogram for a single bucket entry and then
@@ -72,7 +72,7 @@
 }
 
 }  // namespace
-#endif  // defined(OS_MAC) || defined(OS_LINUX) || defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
 // This test class verifies that metrics reporting works correctly for various
 // renderer behaviors such as page loads, recording crashed tabs, and browser
@@ -170,7 +170,7 @@
 // Flaky on Linux. See http://crbug.com/131094
 // Child crashes fail the process on ASan (see crbug.com/411251,
 // crbug.com/368525).
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(ADDRESS_SANITIZER)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || defined(ADDRESS_SANITIZER)
 #define MAYBE_CrashRenderers DISABLED_CrashRenderers
 #define MAYBE_CheckCrashRenderers DISABLED_CheckCrashRenderers
 #else
@@ -190,13 +190,13 @@
   EXPECT_EQ(4, prefs->GetInteger(metrics::prefs::kStabilityPageLoadCount));
   EXPECT_EQ(1, prefs->GetInteger(metrics::prefs::kStabilityRendererCrashCount));
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // Consult Stability Team before changing this test as it's recorded to
   // histograms and used for stability measurement.
   histogram_tester.ExpectUniqueSample(
       "CrashExitCodes.Renderer",
       std::abs(static_cast<int32_t>(STATUS_ACCESS_VIOLATION)), 1);
-#elif defined(OS_MAC) || defined(OS_LINUX) || defined(OS_CHROMEOS)
+#elif BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   VerifyRendererExitCodeIsSignal(histogram_tester, SIGSEGV);
 #endif
   histogram_tester.ExpectUniqueSample("Tabs.SadTab.CrashCreated", 1, 1);
@@ -208,7 +208,7 @@
 //
 // This test is disabled on Windows because it flakes.
 // See: https://crbug.com/1277825
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 IN_PROC_BROWSER_TEST_F(MetricsServiceBrowserTest,
                        DISABLED_HeapCorruptionInRenderer) {
   base::HistogramTester histogram_tester;
@@ -243,20 +243,20 @@
   EXPECT_EQ(4, prefs->GetInteger(metrics::prefs::kStabilityPageLoadCount));
   EXPECT_EQ(1, prefs->GetInteger(metrics::prefs::kStabilityRendererCrashCount));
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // Consult Stability Team before changing this test as it's recorded to
   // histograms and used for stability measurement.
   histogram_tester.ExpectUniqueSample(
       "CrashExitCodes.Renderer",
       std::abs(static_cast<int32_t>(STATUS_BREAKPOINT)), 1);
-#elif defined(OS_MAC) || defined(OS_LINUX) || defined(OS_CHROMEOS)
+#elif BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   VerifyRendererExitCodeIsSignal(histogram_tester, SIGTRAP);
 #endif
   histogram_tester.ExpectUniqueSample("Tabs.SadTab.CrashCreated", 1, 1);
 }
 
 // OOM code only works on Windows.
-#if defined(OS_WIN) && !defined(ADDRESS_SANITIZER)
+#if BUILDFLAG(IS_WIN) && !defined(ADDRESS_SANITIZER)
 IN_PROC_BROWSER_TEST_F(MetricsServiceBrowserTest, OOMRenderers) {
   // Disable stack traces during this test since DbgHelp is unreliable in
   // low-memory conditions (see crbug.com/692564).
diff --git a/chrome/browser/metrics/metrics_service_user_demographics_browsertest.cc b/chrome/browser/metrics/metrics_service_user_demographics_browsertest.cc
index 24f552e..855e53d 100644
--- a/chrome/browser/metrics/metrics_service_user_demographics_browsertest.cc
+++ b/chrome/browser/metrics/metrics_service_user_demographics_browsertest.cc
@@ -148,12 +148,12 @@
     histogram.ExpectTotalCount("UMA.UserDemographics.Status", /*count=*/0);
   }
 
-#if !defined(OS_CHROMEOS)
+#if !BUILDFLAG(IS_CHROMEOS)
   // Sign out the user to revoke all refresh tokens. This prevents any posted
   // tasks from successfully fetching an access token during the tear-down
   // phase and crashing on a DCHECK. See crbug/1102746 for more details.
   harness->SignOutPrimaryAccount();
-#endif  // !defined(OS_CHROMEOS)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/metrics/network_quality_estimator_provider_impl.cc b/chrome/browser/metrics/network_quality_estimator_provider_impl.cc
index 0010522..b609ab1 100644
--- a/chrome/browser/metrics/network_quality_estimator_provider_impl.cc
+++ b/chrome/browser/metrics/network_quality_estimator_provider_impl.cc
@@ -41,7 +41,7 @@
     return;
   }
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // TODO(tbansal): https://crbug.com/898304: Tasks posted at BEST_EFFORT
   // may take up to ~20 seconds to execute. Figure out a way to call
   // g_browser_process->network_quality_tracker earlier rather than waiting for
diff --git a/chrome/browser/metrics/oom/out_of_memory_reporter.cc b/chrome/browser/metrics/oom/out_of_memory_reporter.cc
index 124913086..3ce73c9c 100644
--- a/chrome/browser/metrics/oom/out_of_memory_reporter.cc
+++ b/chrome/browser/metrics/oom/out_of_memory_reporter.cc
@@ -8,6 +8,7 @@
 
 #include "base/check.h"
 #include "base/time/default_tick_clock.h"
+#include "build/build_config.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
@@ -29,7 +30,7 @@
     : content::WebContentsObserver(web_contents),
       content::WebContentsUserData<OutOfMemoryReporter>(*web_contents),
       tick_clock_(std::make_unique<base::DefaultTickClock>()) {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // This adds N async observers for N WebContents, which isn't great but
   // probably won't be a big problem on Android, where many multiple tabs are
   // rarer.
@@ -94,20 +95,20 @@
 
 // On Android, we care about OOM protected crashes, which are obtained via
 // crash dump analysis. Otherwise we can use the termination status to
-// deterine OOM.
-#if !defined(OS_ANDROID)
+// determine OOM.
+#if !BUILDFLAG(IS_ANDROID)
   if (status == base::TERMINATION_STATUS_OOM
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
       || status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM
 #endif
   ) {
     OnForegroundOOMDetected(web_contents()->GetLastCommittedURL(),
                             *last_committed_source_id_);
   }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 }
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 // This should always be called *after* the associated RenderProcessGone. This
 // is because the crash dump is processed asynchronously on the IO thread in
 // response to RenderProcessHost::ProcessDied, while RenderProcessGone is called
@@ -129,6 +130,6 @@
                             *last_committed_source_id_);
   }
 }
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(OutOfMemoryReporter);
diff --git a/chrome/browser/metrics/oom/out_of_memory_reporter.h b/chrome/browser/metrics/oom/out_of_memory_reporter.h
index 842f0fe..7ab2187 100644
--- a/chrome/browser/metrics/oom/out_of_memory_reporter.h
+++ b/chrome/browser/metrics/oom/out_of_memory_reporter.h
@@ -18,7 +18,7 @@
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "url/gurl.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include "components/crash/content/browser/crash_metrics_reporter_android.h"
 #endif
 
@@ -30,7 +30,7 @@
 class OutOfMemoryReporter
     : public content::WebContentsObserver,
       public content::WebContentsUserData<OutOfMemoryReporter>
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     ,
       public crash_reporter::CrashMetricsReporter::Observer
 #endif
@@ -65,13 +65,13 @@
   void PrimaryMainFrameRenderProcessGone(
       base::TerminationStatus termination_status) override;
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // crash_reporter::CrashMetricsReporter::Observer:
   void OnCrashDumpProcessed(
       int rph_id,
       const crash_reporter::CrashMetricsReporter::ReportedCrashTypeSet&
           reported_counts) override;
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
   base::ObserverList<Observer>::Unchecked observers_;
 
@@ -80,7 +80,7 @@
   std::unique_ptr<const base::TickClock> tick_clock_;
   int crashed_render_process_id_ = content::ChildProcessHost::kInvalidUniqueID;
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   base::ScopedObservation<crash_reporter::CrashMetricsReporter,
                           crash_reporter::CrashMetricsReporter::Observer>
       scoped_observation_{this};
diff --git a/chrome/browser/metrics/oom/out_of_memory_reporter_browsertest.cc b/chrome/browser/metrics/oom/out_of_memory_reporter_browsertest.cc
index 914a07c3..beaf86f6 100644
--- a/chrome/browser/metrics/oom/out_of_memory_reporter_browsertest.cc
+++ b/chrome/browser/metrics/oom/out_of_memory_reporter_browsertest.cc
@@ -41,7 +41,7 @@
 
 // No current reliable way to determine OOM on Linux/Mac. Sanitizers also
 // interfere with the exit code on OOM, making this detection unreliable.
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || \
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || \
     defined(ADDRESS_SANITIZER)
 #define MAYBE_OutOfMemoryReporterBrowserTest \
   DISABLED_OutOfMemoryReporterBrowserTest
@@ -100,7 +100,7 @@
 
 // No current reliable way to determine OOM on Linux/Mac. Sanitizers also
 // interfere with the exit code on OOM, making this detection unreliable.
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || \
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || \
     defined(ADDRESS_SANITIZER)
 #define MAYBE_PortalOutOfMemoryReporterBrowserTest \
   DISABLED_PortalOutOfMemoryReporterBrowserTest
@@ -210,7 +210,7 @@
 
 // No current reliable way to determine OOM on Linux/Mac. Sanitizers also
 // interfere with the exit code on OOM, making this detection unreliable.
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || \
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || \
     defined(ADDRESS_SANITIZER)
 #define MAYBE_OutOfMemoryReporterPrerenderBrowserTest \
   DISABLED_OutOfMemoryReporterPrerenderBrowserTest
diff --git a/chrome/browser/metrics/oom/out_of_memory_reporter_unittest.cc b/chrome/browser/metrics/oom/out_of_memory_reporter_unittest.cc
index 9ea36ef0..8eff477b 100644
--- a/chrome/browser/metrics/oom/out_of_memory_reporter_unittest.cc
+++ b/chrome/browser/metrics/oom/out_of_memory_reporter_unittest.cc
@@ -42,13 +42,13 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include "chrome/common/chrome_descriptors.h"
 #include "components/crash/content/browser/child_exit_observer_android.h"
 #include "components/crash/content/browser/child_process_crash_observer_android.h"
 #endif
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 // This class listens for notifications that crash dumps have been processed.
 // Notifications will come from all crashes, even if an associated crash dump
 // was not created.
@@ -85,7 +85,7 @@
   base::RunLoop waiter_;
   crash_reporter::CrashMetricsReporter::ReportedCrashTypeSet reported_counts_;
 };
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
 class OutOfMemoryReporterTest : public ChromeRenderViewHostTestHarness,
                                 public OutOfMemoryReporter::Observer {
@@ -99,7 +99,7 @@
 
   // ChromeRenderViewHostTestHarness:
   void SetUp() override {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     crash_reporter::ChildExitObserver::Create();
     crash_reporter::ChildExitObserver::GetInstance()->RegisterClient(
         std::make_unique<crash_reporter::ChildProcessCrashObserver>());
@@ -138,10 +138,10 @@
   void SimulateOOM() {
     test_tick_clock_->Advance(base::Seconds(3));
     SimulateRendererCreated();
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     process()->SimulateRenderProcessExit(base::TERMINATION_STATUS_OOM_PROTECTED,
                                          0);
-#elif defined(OS_CHROMEOS)
+#elif BUILDFLAG(IS_CHROMEOS)
     process()->SimulateRenderProcessExit(
         base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM, 0);
 #else
@@ -153,7 +153,7 @@
   // the OutOfMemoryReporter *should* have received a notification for it.
   void RunCrashClosureAndWait(base::OnceClosure crash_closure,
                               bool oom_expected) {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     CrashDumpWaiter crash_waiter;
     std::move(crash_closure).Run();
     const auto& reported_counts = crash_waiter.Wait();
@@ -215,7 +215,7 @@
   const GURL url("https://example.test/");
   NavigateAndCommit(url);
   SimulateRendererCreated();
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   crash_reporter::ChildExitObserver::GetInstance()->ChildReceivedCrashSignal(
       process()->GetProcess().Handle(), SIGSEGV);
 #endif
diff --git a/chrome/browser/metrics/perf/cpu_identity.cc b/chrome/browser/metrics/perf/cpu_identity.cc
index 8cc21ea..ea23b85 100644
--- a/chrome/browser/metrics/perf/cpu_identity.cc
+++ b/chrome/browser/metrics/perf/cpu_identity.cc
@@ -106,7 +106,7 @@
   result.release =
 #if BUILDFLAG(IS_CHROMEOS_ASH)
       base::SysInfo::KernelVersion();
-#elif defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
       base::SysInfo::OperatingSystemVersion();
 #else
 #error "Unsupported configuration"
diff --git a/chrome/browser/metrics/plugin_metrics_provider.cc b/chrome/browser/metrics/plugin_metrics_provider.cc
index d1eeff2..a4cd575b 100644
--- a/chrome/browser/metrics/plugin_metrics_provider.cc
+++ b/chrome/browser/metrics/plugin_metrics_provider.cc
@@ -204,7 +204,7 @@
 // Saves plugin-related updates from the in-object buffer to Local State
 // for retrieval next time we send a Profile log (generally next launch).
 void PluginMetricsProvider::RecordCurrentState() {
-  ListPrefUpdate update(local_state_, prefs::kStabilityPluginStats);
+  ListPrefUpdateDeprecated update(local_state_, prefs::kStabilityPluginStats);
   base::ListValue* plugins = update.Get();
   DCHECK(plugins);
 
diff --git a/chrome/browser/metrics/plugin_metrics_provider_unittest.cc b/chrome/browser/metrics/plugin_metrics_provider_unittest.cc
index a10dc2e..919fcfc 100644
--- a/chrome/browser/metrics/plugin_metrics_provider_unittest.cc
+++ b/chrome/browser/metrics/plugin_metrics_provider_unittest.cc
@@ -98,7 +98,7 @@
   plugin_dict->SetInteger(prefs::kStabilityPluginInstances, 3);
   plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors, 4);
   {
-    ListPrefUpdate update(prefs(), prefs::kStabilityPluginStats);
+    ListPrefUpdateDeprecated update(prefs(), prefs::kStabilityPluginStats);
     update.Get()->Append(std::move(plugin_dict));
   }
 
diff --git a/chrome/browser/metrics/power/power_details_provider_unittest.cc b/chrome/browser/metrics/power/power_details_provider_unittest.cc
index b35e009..e14069f7 100644
--- a/chrome/browser/metrics/power/power_details_provider_unittest.cc
+++ b/chrome/browser/metrics/power/power_details_provider_unittest.cc
@@ -7,7 +7,7 @@
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 TEST(BatteryLevelProviderTest, Brightness) {
   auto provider = PowerDetailsProvider::Create();
   // There isn't much to test as test bots are usually not using a display.
diff --git a/chrome/browser/metrics/power/power_metrics_reporter.cc b/chrome/browser/metrics/power/power_metrics_reporter.cc
index aa3c341d..9329aed0 100644
--- a/chrome/browser/metrics/power/power_metrics_reporter.cc
+++ b/chrome/browser/metrics/power/power_metrics_reporter.cc
@@ -11,6 +11,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "chrome/browser/lifetime/browser_shutdown.h"
 #include "chrome/browser/metrics/power/power_details_provider.h"
 #include "chrome/browser/performance_monitor/process_metrics_recorder_util.h"
@@ -102,7 +103,7 @@
       base::BindOnce(&PowerMetricsReporter::OnFirstBatteryStateSampled,
                      weak_factory_.GetWeakPtr()));
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   power_details_provider_ = PowerDetailsProvider::Create();
   iopm_power_source_sampling_event_source_.Start(
       base::BindRepeating(&PowerMetricsReporter::OnIOPMPowerSourceSamplingEvent,
@@ -154,7 +155,7 @@
   ReportCPUHistograms(metrics, suffixes);
   ReportBatteryHistograms(interval_duration, discharge_mode,
                           discharge_rate_during_interval, suffixes);
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   RecordCoalitionData(metrics, suffixes);
 #endif
 }
@@ -322,7 +323,7 @@
     builder.SetBatteryDischargeRate(*discharge_rate_during_interval);
   }
   builder.SetCPUTimeMs(metrics.cpu_usage * interval_duration.InMilliseconds());
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   builder.SetIdleWakeUps(metrics.idle_wakeups);
   builder.SetPackageExits(metrics.package_idle_wakeups);
   builder.SetEnergyImpactScore(metrics.energy_impact);
@@ -391,7 +392,7 @@
   static const int64_t kDischargeRateFactor =
       10000 * base::Minutes(1).InSecondsF();
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   // On MacOS, empirical evidence has shown that right after a full charge, the
   // current capacity stays equal to the maximum capacity for several minutes,
   // despite the fact that power was definitely consumed. Reporting a zero
@@ -408,7 +409,7 @@
   return {BatteryDischargeMode::kDischarging, discharge_rate};
 }
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 void PowerMetricsReporter::OnIOPMPowerSourceSamplingEvent() {
   base::TimeTicks now_ticks = base::TimeTicks::Now();
 
@@ -427,4 +428,4 @@
   histogram->AddTime(now_ticks - *last_event_time_ticks_);
   *last_event_time_ticks_ = now_ticks;
 }
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
diff --git a/chrome/browser/metrics/power/power_metrics_reporter.h b/chrome/browser/metrics/power/power_metrics_reporter.h
index ebc5adb2..c2453140 100644
--- a/chrome/browser/metrics/power/power_metrics_reporter.h
+++ b/chrome/browser/metrics/power/power_metrics_reporter.h
@@ -17,9 +17,9 @@
 #include "chrome/browser/performance_monitor/process_monitor.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 #include "components/power_metrics/iopm_power_source_sampling_event_source.h"
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
 
 // Reports metrics related to power (battery discharge, cpu time, etc.) to
 // understand what impacts Chrome's power consumption over an interval of time.
@@ -131,9 +131,9 @@
       const BatteryLevelProvider::BatteryState& new_battery_state,
       base::TimeDelta interval_duration);
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   void OnIOPMPowerSourceSamplingEvent();
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
 
   // The data store used to get the usage scenario data, it needs to outlive
   // this class.
@@ -153,13 +153,13 @@
 
   base::OnceClosure on_battery_sampled_for_testing_;
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   power_metrics::IOPMPowerSourceSamplingEventSource
       iopm_power_source_sampling_event_source_;
 
   // The time ticks from when the last IOPMPowerSource event was received.
   absl::optional<base::TimeTicks> last_event_time_ticks_;
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc b/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc
index 1e7f82d..2a9b4dcf 100644
--- a/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc
+++ b/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc
@@ -216,7 +216,7 @@
 
   performance_monitor::ProcessMonitor::Metrics fake_metrics = {};
   fake_metrics.cpu_usage = ++fake_value * 0.01;
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   fake_metrics.idle_wakeups = ++fake_value;
   fake_metrics.package_idle_wakeups = ++fake_value;
   fake_metrics.energy_impact = ++fake_value;
@@ -244,7 +244,7 @@
       entries[0], UkmEntry::kCPUTimeMsName,
       kExpectedMetricsCollectionInterval.InSeconds() * 1000 *
           fake_metrics.cpu_usage);
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   test_ukm_recorder_.ExpectEntryMetric(entries[0], UkmEntry::kIdleWakeUpsName,
                                        fake_metrics.idle_wakeups);
   test_ukm_recorder_.ExpectEntryMetric(entries[0], UkmEntry::kPackageExitsName,
@@ -330,7 +330,7 @@
 
   performance_monitor::ProcessMonitor::Metrics fake_metrics = {};
   fake_metrics.cpu_usage = 0.5;
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   fake_metrics.idle_wakeups = 42;
   fake_metrics.package_idle_wakeups = 43;
   fake_metrics.energy_impact = 44;
@@ -475,13 +475,13 @@
       PowerMetricsReporterAccess::BatteryDischargeMode::kNoBattery, 1);
 }
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 // Tests that on MacOS, a full |charge_level| while not plugged does not result
 // in a kDischarging value emitted. See https://crbug.com/1249830.
 TEST_F(PowerMetricsReporterUnitTest, UKMsMacFullyCharged) {
   // Set the initial battery level at 100%.
   power_metrics_reporter_->battery_state_for_testing().charge_level = 1.0;
-  
+
   task_environment_.FastForwardBy(kExpectedMetricsCollectionInterval);
   battery_states_.push(BatteryLevelProvider::BatteryState{
       0, 1, 1.0, true, base::TimeTicks::Now()});
@@ -508,7 +508,7 @@
       kBatteryDischargeModeHistogramName,
       PowerMetricsReporterAccess::BatteryDischargeMode::kMacFullyCharged, 1);
 }
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
 
 TEST_F(PowerMetricsReporterUnitTest, UKMsBatteryStateIncrease) {
   // Set the initial battery level at 50%.
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter.cc b/chrome/browser/metrics/process_memory_metrics_emitter.cc
index 7005bc9..8922ba9 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter.cc
@@ -660,14 +660,14 @@
     }
   }
 
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
   // Resident set is not populated on Mac.
   builder->SetResident(pmd.os_dump().resident_set_kb / kKiB);
 #endif
 
   builder->SetPrivateMemoryFootprint(pmd.os_dump().private_footprint_kb / kKiB);
   builder->SetSharedMemoryFootprint(pmd.os_dump().shared_footprint_kb / kKiB);
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
   builder->SetPrivateSwapFootprint(pmd.os_dump().private_footprint_swap_kb /
                                    kKiB);
 #endif
@@ -677,7 +677,7 @@
     return;
 
   const char* process_name = HistogramProcessTypeToString(process_type);
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   // Resident set is not populated on Mac.
   DCHECK_EQ(pmd.os_dump().resident_set_kb, 0U);
 #else
@@ -698,7 +698,7 @@
   MEMORY_METRICS_HISTOGRAM_MB(std::string(kMemoryHistogramPrefix) +
                                   process_name + ".SharedMemoryFootprint",
                               pmd.os_dump().shared_footprint_kb / kKiB);
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
   MEMORY_METRICS_HISTOGRAM_MB(std::string(kMemoryHistogramPrefix) +
                                   process_name + ".PrivateSwapFootprint",
                               pmd.os_dump().private_footprint_swap_kb / kKiB);
@@ -1108,7 +1108,7 @@
     UMA_HISTOGRAM_MEMORY_LARGE_MB(
         "Memory.Experimental.Total2.PrivateMemoryFootprint",
         private_footprint_total_kb / kKiB);
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
     // Resident set is not populated on Mac.
     DCHECK_EQ(resident_set_total_kb, 0U);
 #else
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
index 92c7bd4..a9bc66b 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
@@ -176,7 +176,7 @@
     int count,
     const char* process_type,
     int number_of_processes) {
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
   CheckMemoryMetric(
       std::string("Memory.Experimental.") + process_type + "2.Malloc",
       histogram_tester, count, ValueRestriction::ABOVE_ZERO,
@@ -221,7 +221,7 @@
     int count,
     int number_of_renderer_processes,
     int number_of_extension_processes) {
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
   CheckMemoryMetric("Memory.Experimental.Browser2.Malloc", histogram_tester,
                     count, ValueRestriction::ABOVE_ZERO);
 #endif
@@ -242,13 +242,13 @@
                               int number_of_renderer_processes,
                               int number_of_extension_processes) {
   const int count_for_resident_set =
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
       0;
 #else
       count;
 #endif
   const int count_for_private_swap_footprint =
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
       count;
 #else
       0;
@@ -451,11 +451,11 @@
   }
 
   void CheckUkmRendererEntry(const ukm::mojom::UkmEntry* entry) {
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
     CheckMemoryMetricWithName(entry, UkmEntry::kMallocName,
                               ValueRestriction::ABOVE_ZERO);
 #endif
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
     CheckMemoryMetricWithName(entry, UkmEntry::kResidentName,
                               ValueRestriction::ABOVE_ZERO);
 #endif
@@ -481,11 +481,11 @@
   }
 
   void CheckUkmBrowserEntry(const ukm::mojom::UkmEntry* entry) {
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
     CheckMemoryMetricWithName(entry, UkmEntry::kMallocName,
                               ValueRestriction::ABOVE_ZERO);
 #endif
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
     CheckMemoryMetricWithName(entry, UkmEntry::kResidentName,
                               ValueRestriction::ABOVE_ZERO);
 #endif
@@ -581,7 +581,7 @@
 };
 
 // TODO(crbug.com/732501): Re-enable on Win once not flaky.
-#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || defined(OS_WIN)
+#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || BUILDFLAG(IS_WIN)
 #define MAYBE_FetchAndEmitMetrics DISABLED_FetchAndEmitMetrics
 #else
 #define MAYBE_FetchAndEmitMetrics FetchAndEmitMetrics
@@ -616,9 +616,9 @@
   CheckPageInfoUkmMetrics(url, true);
 }
 
-#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 // TODO(crbug.com/732501): Re-enable on Win once not flaky.
-#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || defined(OS_WIN)
+#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || BUILDFLAG(IS_WIN)
 #define MAYBE_HasZombieProfile DISABLED_HasZombieProfile
 #else
 #define MAYBE_HasZombieProfile HasZombieProfile
@@ -669,7 +669,7 @@
 // TODO(https://crbug.com/990148): Re-enable on Win and Linux once not flaky.
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
-    defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS)
+    BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #define MAYBE_FetchAndEmitMetricsWithExtensions \
   DISABLED_FetchAndEmitMetricsWithExtensions
 #else
@@ -772,7 +772,7 @@
 
 // TODO(crbug.com/989810): Re-enable on Win and Mac once not flaky.
 #if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
-    defined(OS_WIN) || defined(OS_MAC)
+    BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
 #define MAYBE_FetchDuringTrace DISABLED_FetchDuringTrace
 #else
 #define MAYBE_FetchDuringTrace FetchDuringTrace
@@ -866,9 +866,9 @@
 
 // Test is flaky on chromeos and linux. https://crbug.com/938054.
 // Test is flaky on mac and win: https://crbug.com/948674.
-#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) ||      \
-    defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MAC) || \
-    defined(OS_WIN)
+#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) ||            \
+    BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || \
+    BUILDFLAG(IS_WIN)
 #define MAYBE_ForegroundAndBackgroundPages DISABLED_ForegroundAndBackgroundPages
 #else
 #define MAYBE_ForegroundAndBackgroundPages ForegroundAndBackgroundPages
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc b/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
index 6bba6b7..e15ca13 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter_unittest.cc
@@ -40,7 +40,7 @@
 using MetricMap = base::flat_map<const char*, int64_t>;
 
 int GetResidentValue(const MetricMap& metric_map) {
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   // Resident set is not populated on Mac.
   return 0;
 #else
@@ -121,7 +121,7 @@
 OSMemDumpPtr GetFakeOSMemDump(uint32_t resident_set_kb,
                               uint32_t private_footprint_kb,
                               uint32_t shared_footprint_kb
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
                               ,
                               uint32_t private_swap_footprint_kb
 #endif
@@ -132,7 +132,7 @@
       resident_set_kb, resident_set_kb /* peak_resident_set_kb */,
       true /* is_peak_rss_resettable */, private_footprint_kb,
       shared_footprint_kb
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
       ,
       private_swap_footprint_kb
 #endif
@@ -163,7 +163,7 @@
   OSMemDumpPtr os_dump =
       GetFakeOSMemDump(GetResidentValue(metrics_mb) * 1024,
                        metrics_mb["PrivateMemoryFootprint"] * 1024,
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
                        // accessing PrivateSwapFootprint on other OSes will
                        // modify metrics_mb to create the value, which leads to
                        // expectation failures.
@@ -180,13 +180,13 @@
 MetricMap GetExpectedBrowserMetrics() {
   return MetricMap({
     {"ProcessType", static_cast<int64_t>(ProcessType::BROWSER)},
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
         {"Resident", 10},
 #endif
         {"Malloc", 20}, {"PrivateMemoryFootprint", 30},
         {"SharedMemoryFootprint", 35}, {"Uptime", 42},
         {"GpuMemory", kGpuTotalMemory * 1024 * 1024},
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
         {"PrivateSwapFootprint", 50},
 #endif
   });
@@ -327,7 +327,7 @@
   OSMemDumpPtr os_dump =
       GetFakeOSMemDump(GetResidentValue(metrics_mb_or_count) * 1024,
                        metrics_mb_or_count["PrivateMemoryFootprint"] * 1024,
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
                        // accessing PrivateSwapFootprint on other OSes will
                        // modify metrics_mb_or_count to create the value, which
                        // leads to expectation failures.
@@ -349,7 +349,7 @@
 constexpr int kNativeLibraryResidentNotOrderedCodeFootprint = 12345;
 constexpr int kNativeLibraryNotResidentOrderedCodeFootprint = 23456;
 
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
 constexpr int kTestRendererResidentSet = 110;
 #endif
 
@@ -360,7 +360,7 @@
 MetricMap GetExpectedRendererMetrics() {
   return MetricMap({
     {"ProcessType", static_cast<int64_t>(ProcessType::RENDERER)},
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
         {"Resident", kTestRendererResidentSet},
 #endif
         {"Malloc", kTestRendererMalloc},
@@ -388,7 +388,7 @@
         {"V8.Main.Malloc", 2}, {"V8.Workers", 60},
         {"V8.Workers.AllocatedObjects", 40}, {"NumberOfExtensions", 0},
         {"Uptime", 42},
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
         {"PrivateSwapFootprint", 50},
 #endif
         {"NumberOfAdSubframes", 28}, {"NumberOfDetachedScriptStates", 11},
@@ -422,7 +422,7 @@
   OSMemDumpPtr os_dump =
       GetFakeOSMemDump(GetResidentValue(metrics_mb) * 1024,
                        metrics_mb["PrivateMemoryFootprint"] * 1024,
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
                        // accessing PrivateSwapFootprint on other OSes will
                        // modify metrics_mb to create the value, which leads to
                        // expectation failures.
@@ -439,13 +439,13 @@
 MetricMap GetExpectedGpuMetrics() {
   return MetricMap({
     {"ProcessType", static_cast<int64_t>(ProcessType::GPU)},
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
         {"Resident", 210},
 #endif
         {"Malloc", 220}, {"PrivateMemoryFootprint", 230},
         {"SharedMemoryFootprint", 235}, {"CommandBuffer", kGpuCommandBufferMB},
         {"Uptime", 42}, {"GpuMemory", kGpuTotalMemory * 1024 * 1024},
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
         {"PrivateSwapFootprint", 50},
 #endif
   });
@@ -461,7 +461,7 @@
   OSMemDumpPtr os_dump =
       GetFakeOSMemDump(GetResidentValue(metrics_mb) * 1024,
                        metrics_mb["PrivateMemoryFootprint"] * 1024,
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
                        // accessing PrivateSwapFootprint on other OSes will
                        // modify metrics_mb to create the value, which leads to
                        // expectation failures.
@@ -478,12 +478,12 @@
 MetricMap GetExpectedAudioServiceMetrics() {
   return MetricMap({
     {"ProcessType", static_cast<int64_t>(ProcessType::UTILITY)},
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
         {"Resident", 10},
 #endif
         {"Malloc", 20}, {"PrivateMemoryFootprint", 30},
         {"SharedMemoryFootprint", 35}, {"Uptime", 42},
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
         {"PrivateSwapFootprint", 50},
 #endif
   });
@@ -501,7 +501,7 @@
       GetFakeOSMemDump(GetResidentValue(metrics_mb) * 1024,
                        metrics_mb["PrivateMemoryFootprint"] * 1024,
                        metrics_mb["SharedMemoryFootprint"] * 1024
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
                        // accessing PrivateSwapFootprint on other OSes will
                        // modify metrics_mb to create the value, which leads to
                        // expectation failures.
@@ -516,11 +516,11 @@
 MetricMap GetExpectedPaintPreviewCompositorMetrics() {
   return MetricMap({
     {"ProcessType", static_cast<int64_t>(ProcessType::UTILITY)},
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
         {"Resident", 10},
 #endif
         {"PrivateMemoryFootprint", 30}, {"SharedMemoryFootprint", 35},
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
         {"PrivateSwapFootprint", 50},
 #endif
   });
@@ -958,7 +958,7 @@
                                 kTestRendererPrivateMemoryFootprint, 2);
   histograms.ExpectUniqueSample("Memory.Renderer.SharedMemoryFootprint",
                                 kTestRendererSharedMemoryFootprint, 2);
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   histograms.ExpectTotalCount("Memory.Renderer.ResidentSet", 0);
 #else
   histograms.ExpectUniqueSample("Memory.Renderer.ResidentSet",
@@ -975,7 +975,7 @@
                                 2 * kTestRendererMalloc, 1);
   histograms.ExpectUniqueSample("Memory.Total.SharedMemoryFootprint",
                                 2 * kTestRendererSharedMemoryFootprint, 1);
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   histograms.ExpectTotalCount("Memory.Total.ResidentSet", 0);
 #else
   histograms.ExpectUniqueSample("Memory.Total.ResidentSet",
diff --git a/chrome/browser/metrics/shutdown_watcher_helper.cc b/chrome/browser/metrics/shutdown_watcher_helper.cc
index dfc8671..882b59a 100644
--- a/chrome/browser/metrics/shutdown_watcher_helper.cc
+++ b/chrome/browser/metrics/shutdown_watcher_helper.cc
@@ -4,12 +4,13 @@
 
 #include "chrome/browser/metrics/shutdown_watcher_helper.h"
 
+#include "build/build_config.h"
 #include "chrome/browser/metrics/thread_watcher_report_hang.h"
 #include "chrome/common/channel_info.h"
 #include "components/version_info/channel.h"
 
 // ShutdownWatcherHelper is not available on Android.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 
 namespace {
 
@@ -77,4 +78,4 @@
   return actual_duration;
 }
 
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/metrics/shutdown_watcher_helper.h b/chrome/browser/metrics/shutdown_watcher_helper.h
index 28e7eaa..e2c89950 100644
--- a/chrome/browser/metrics/shutdown_watcher_helper.h
+++ b/chrome/browser/metrics/shutdown_watcher_helper.h
@@ -12,7 +12,7 @@
 // ShutdownWatcherHelper is useless on Android because there is no shutdown,
 // Chrome is always killed one way or another (swiped away in the task
 // switcher, OOM-killed, etc.).
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 // This is a wrapper class for detecting hangs during shutdown.
 class ShutdownWatcherHelper {
  public:
@@ -41,6 +41,6 @@
   const base::PlatformThreadId thread_id_;
 };
 
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 #endif  // CHROME_BROWSER_METRICS_SHUTDOWN_WATCHER_HELPER_H_
diff --git a/chrome/browser/metrics/startup_metrics_browsertest.cc b/chrome/browser/metrics/startup_metrics_browsertest.cc
index 1af83f7..7917e1e 100644
--- a/chrome/browser/metrics/startup_metrics_browsertest.cc
+++ b/chrome/browser/metrics/startup_metrics_browsertest.cc
@@ -13,7 +13,7 @@
 #include "components/startup_metric_utils/browser/startup_metric_utils.h"
 #include "content/public/test/browser_test.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include "chrome/test/base/android/android_browser_test.h"
 #else
 #include "chrome/test/base/in_process_browser_test.h"
@@ -29,22 +29,22 @@
 
 // Not Desktop specific but flaky on some Android bots.
 // TODO(crbug.com/1252126): Figure out why.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
     "Startup.LoadTime.ApplicationStartToChromeMain",
     "Startup.LoadTime.ProcessCreateToApplicationStart",
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
 // Desktop specific metrics
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
     "Startup.BrowserWindow.FirstPaint",
     "Startup.BrowserWindowDisplay",
     "Startup.FirstWebContents.MainNavigationFinished",
     "Startup.FirstWebContents.MainNavigationStart",
     "Startup.FirstWebContents.NonEmptyPaint3",
     "Startup.FirstWebContents.RenderProcessHostInit.ToNonEmptyPaint",
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     "Startup.Temperature",
 #endif
 };
diff --git a/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc b/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc
index 2d3dd13..80ecbfd 100644
--- a/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc
+++ b/chrome/browser/metrics/tab_stats/tab_stats_tracker.cc
@@ -18,6 +18,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_observer.h"
@@ -63,7 +64,7 @@
     base::Seconds(30), base::Minutes(1), base::Minutes(10),
     base::Hours(1),    base::Hours(5),   base::Hours(12)};
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 const base::TimeDelta kNativeWindowOcclusionCalculationInterval =
     base::Minutes(10);
 #endif
@@ -193,7 +194,7 @@
   }
 
 // The native window occlusion calculation is specific to Windows.
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   native_window_occlusion_timer_.Start(
       FROM_HERE, kNativeWindowOcclusionCalculationInterval,
       base::BindRepeating(
diff --git a/chrome/browser/metrics/tab_stats/tab_stats_tracker.h b/chrome/browser/metrics/tab_stats/tab_stats_tracker.h
index eff2f96d..ecf065e7 100644
--- a/chrome/browser/metrics/tab_stats/tab_stats_tracker.h
+++ b/chrome/browser/metrics/tab_stats/tab_stats_tracker.h
@@ -75,7 +75,7 @@
  protected:
   FRIEND_TEST_ALL_PREFIXES(TabStatsTrackerBrowserTest,
                            TabDeletionGetsHandledProperly);
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   FRIEND_TEST_ALL_PREFIXES(TabStatsTrackerBrowserTest,
                            TestCalculateAndRecordNativeWindowVisibilities);
 #endif
@@ -167,7 +167,7 @@
   // Functions to call when a WebContents get destroyed.
   void OnWebContentsDestroyed(content::WebContents* web_contents);
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // Function to call aura_extra::ComputeNativeWindowOcclusionStatus() and
   // record the Visibility of all Chrome browser windows on Windows.
   void CalculateAndRecordNativeWindowVisibilities();
@@ -206,7 +206,7 @@
   // triggered.
   base::RepeatingTimer daily_event_timer_;
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // The timer used to periodically calculate the occlusion status of native
   // windows on Windows.
   base::RepeatingTimer native_window_occlusion_timer_;
@@ -286,7 +286,7 @@
       const TabStatsDataStore::TabsStateDuringIntervalMap& interval_map,
       base::TimeDelta interval);
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   void RecordNativeWindowVisibilities(size_t num_occluded,
                                       size_t num_visible,
                                       size_t num_hidden);
diff --git a/chrome/browser/metrics/tab_stats/tab_stats_tracker_browsertest.cc b/chrome/browser/metrics/tab_stats/tab_stats_tracker_browsertest.cc
index cc6993c3..f0c40b2 100644
--- a/chrome/browser/metrics/tab_stats/tab_stats_tracker_browsertest.cc
+++ b/chrome/browser/metrics/tab_stats/tab_stats_tracker_browsertest.cc
@@ -91,7 +91,7 @@
   MockTabStatsTrackerDelegate() = default;
   ~MockTabStatsTrackerDelegate() override = default;
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   OcclusionStatusMap CallComputeNativeWindowOcclusionStatus(
       std::vector<aura::WindowTreeHost*> hosts) override {
     // Checking that the hosts are not nullptr, because of a bug where nullptr
@@ -284,7 +284,7 @@
   EXPECT_EQ(0U, interval_map->size());
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 IN_PROC_BROWSER_TEST_F(TabStatsTrackerBrowserTest,
                        TestCalculateAndRecordNativeWindowVisibilities) {
   std::unique_ptr<MockTabStatsTrackerDelegate> mock_delegate =
@@ -364,7 +364,7 @@
                                       5, 1);
 }
 
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 namespace {
 
@@ -396,7 +396,7 @@
 }  // namespace
 
 // TODO(1183746): Fix the flakiness on MacOS and re-enable the test.
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 #define MAYBE_TabStatsObserverBasics DISABLED_TabStatsObserverBasics
 #else
 #define MAYBE_TabStatsObserverBasics TabStatsObserverBasics
diff --git a/chrome/browser/metrics/tab_stats/tab_stats_tracker_delegate.h b/chrome/browser/metrics/tab_stats/tab_stats_tracker_delegate.h
index 028594e..09787a9 100644
--- a/chrome/browser/metrics/tab_stats/tab_stats_tracker_delegate.h
+++ b/chrome/browser/metrics/tab_stats/tab_stats_tracker_delegate.h
@@ -7,7 +7,7 @@
 
 #include "build/build_config.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #endif
@@ -17,13 +17,13 @@
   TabStatsTrackerDelegate() {}
   virtual ~TabStatsTrackerDelegate() {}
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   using OcclusionStatusMap =
       base::flat_map<aura::WindowTreeHost*, aura::Window::OcclusionState>;
 
   virtual OcclusionStatusMap CallComputeNativeWindowOcclusionStatus(
       std::vector<aura::WindowTreeHost*> hosts);
-#endif  // OS_WIN
+#endif  // BUILDFLAG(IS_WIN)
 };
 
 #endif  // CHROME_BROWSER_METRICS_TAB_STATS_TAB_STATS_TRACKER_DELEGATE_H_
diff --git a/chrome/browser/metrics/thread_watcher_report_hang.cc b/chrome/browser/metrics/thread_watcher_report_hang.cc
index b6e4029..74e3c69 100644
--- a/chrome/browser/metrics/thread_watcher_report_hang.cc
+++ b/chrome/browser/metrics/thread_watcher_report_hang.cc
@@ -8,6 +8,7 @@
 #include "base/debug/debugger.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 
 namespace metrics {
 
@@ -35,7 +36,7 @@
 #endif
 }
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 
 NOINLINE void StartupHang() {
   // TODO(rtenneti): http://crbug.com/440885 enable crashing after fixing false
@@ -51,7 +52,7 @@
   ALLOW_UNUSED_LOCAL(inhibit_comdat);
 }
 
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 NOINLINE void ThreadUnresponsive_UI() {
   ReportThreadHang();
diff --git a/chrome/browser/metrics/thread_watcher_report_hang.h b/chrome/browser/metrics/thread_watcher_report_hang.h
index bce1769..c7929d4 100644
--- a/chrome/browser/metrics/thread_watcher_report_hang.h
+++ b/chrome/browser/metrics/thread_watcher_report_hang.h
@@ -11,7 +11,7 @@
 
 namespace metrics {
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 
 // This function makes it possible to tell from the callstack why startup is
 // taking too long.
@@ -21,7 +21,7 @@
 // taking too long.
 NOINLINE void ShutdownHang();
 
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // This function makes it possible to tell from the callstack alone what thread
 // was unresponsive.
diff --git a/chrome/browser/metrics/ukm_browsertest.cc b/chrome/browser/metrics/ukm_browsertest.cc
index 570b716..c9f5ff8b 100644
--- a/chrome/browser/metrics/ukm_browsertest.cc
+++ b/chrome/browser/metrics/ukm_browsertest.cc
@@ -61,7 +61,7 @@
 #include "third_party/metrics_proto/user_demographics.pb.h"
 #include "url/url_constants.h"
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -74,18 +74,18 @@
 #include "chrome/browser/ui/android/tab_model/tab_model_observer.h"
 #include "chrome/test/base/android/android_browser_test.h"
 #include "content/public/browser/web_contents.h"
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 namespace metrics {
 namespace {
 
 class TestTabModel;
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 typedef Browser* PlatformBrowser;
 #else
 typedef std::unique_ptr<TestTabModel> PlatformBrowser;
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Clears the specified data using BrowsingDataRemover.
 void ClearBrowsingData(Profile* profile) {
@@ -104,7 +104,7 @@
   return g_browser_process->GetMetricsServicesManager()->GetUkmService();
 }
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 
 // ActivityType that doesn't restore tabs on cold start.
 // Any type other than kTabbed is fine.
@@ -152,20 +152,20 @@
   // The WebContents associated with this tab's profile.
   std::unique_ptr<content::WebContents> web_contents_;
 };
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
 }  // namespace
 
 // An observer that returns back to test code after a new profile is
 // initialized.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 void UnblockOnProfileCreation(base::RunLoop* run_loop,
                               Profile* profile,
                               Profile::CreateStatus status) {
   if (status == Profile::CREATE_STATUS_INITIALIZED)
     run_loop->Quit();
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // A helper object for overriding metrics enabled state.
 class MetricsConsentOverride {
@@ -207,7 +207,7 @@
   UkmBrowserTestBase(const UkmBrowserTestBase&) = delete;
   UkmBrowserTestBase& operator=(const UkmBrowserTestBase&) = delete;
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
   ukm::UkmSource* NavigateAndGetSource(const GURL& url,
                                        Browser* browser,
                                        ukm::UkmTestHelper* ukm_test_helper) {
@@ -218,12 +218,12 @@
         observer.navigation_id(), ukm::SourceIdType::NAVIGATION_ID);
     return ukm_test_helper->GetSource(source_id);
   }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
  protected:
   // Creates and returns a platform-appropriate browser for |profile|.
   PlatformBrowser CreatePlatformBrowser(Profile* profile) {
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
     return CreateBrowser(profile);
 #else
     std::unique_ptr<TestTabModel> tab_model =
@@ -232,27 +232,27 @@
     EXPECT_TRUE(content::NavigateToURL(tab_model->GetActiveWebContents(),
                                        GURL("about:blank")));
     return tab_model;
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
   }
 
   // Creates a platform-appropriate incognito browser for |profile|.
   PlatformBrowser CreateIncognitoPlatformBrowser(Profile* profile) {
     EXPECT_TRUE(profile->IsOffTheRecord());
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
     return CreateIncognitoBrowser(profile);
 #else
     return CreatePlatformBrowser(profile);
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
   }
 
   // Closes |browser| in a way that is appropriate for the platform.
   void ClosePlatformBrowser(PlatformBrowser& browser) {
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
     CloseBrowserSynchronously(browser);
 #else
     TabModelList::RemoveTabModel(browser.get());
     browser.reset();
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
   }
 
   std::unique_ptr<SyncServiceImplHarness> EnableSyncForProfile(
@@ -273,7 +273,7 @@
     return harness;
   }
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
   Profile* CreateNonSyncProfile() {
     ProfileManager* profile_manager = g_browser_process->profile_manager();
     base::FilePath new_path =
@@ -286,7 +286,7 @@
     SetupMockGaiaResponsesForProfile(profile);
     return profile;
   }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -299,7 +299,7 @@
   UkmBrowserTest(const UkmBrowserTest&) = delete;
   UkmBrowserTest& operator=(const UkmBrowserTest&) = delete;
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   void PreRunTestOnMainThread() override {
     // At some point during set-up, Android's TabModelList is populated with a
     // TabModel. However, it is desirable to begin the tests with an empty
@@ -313,7 +313,7 @@
     TabModelList::RemoveTabModel(TabModelList::models()[0]);
     EXPECT_EQ(0U, TabModelList::models().size());
   }
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 };
 
 class UkmBrowserTestWithSyncTransport : public UkmBrowserTestBase {
@@ -348,7 +348,7 @@
 // This tests if UKM service is enabled/disabled appropriately based on an
 // input bool param. The bool reflects if metrics reporting state is
 // enabled/disabled via prefs.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 class UkmConsentParamBrowserTest : public UkmBrowserTestBase,
                                    public testing::WithParamInterface<bool> {
  public:
@@ -383,7 +383,7 @@
  private:
   base::FilePath local_state_path_;
 };
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Test the reporting of the synced user's birth year and gender.
 class UkmBrowserTestWithDemographics
@@ -492,7 +492,7 @@
 }
 
 // Make sure that UKM is disabled while a guest profile's window is open.
-#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest, RegularPlusGuestCheck) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   MetricsConsentOverride metrics_consent(true);
@@ -518,11 +518,11 @@
   harness->service()->GetUserSettings()->SetSyncRequested(false);
   CloseBrowserSynchronously(regular_browser);
 }
-#endif  // !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 
-#if defined(OS_MAC) && defined(ARCH_CPU_ARM64)
+#if BUILDFLAG(IS_MAC) && defined(ARCH_CPU_ARM64)
 // https://crbug.com/1223061
 #define MAYBE_OpenNonSyncCheck DISABLED_OpenNonSyncCheck
 #else
@@ -555,7 +555,7 @@
   harness->service()->GetUserSettings()->SetSyncRequested(false);
   CloseBrowserSynchronously(sync_browser);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Make sure that UKM is disabled when metrics consent is revoked.
 // Keep in sync with testMetricsConsent in ios/chrome/browser/metrics/
@@ -591,7 +591,7 @@
   ClosePlatformBrowser(browser);
 }
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest, LogProtoData) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   MetricsConsentOverride metrics_consent(true);
@@ -624,7 +624,7 @@
   harness->service()->GetUserSettings()->SetSyncRequested(false);
   CloseBrowserSynchronously(sync_browser);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // TODO(crbug/1016118): Add the remaining test cases.
 // Keep this test in sync with testUKMDemographicsReportingWithFeatureEnabled
@@ -681,12 +681,12 @@
     histogram.ExpectTotalCount("UKM.UserDemographics.Status", /*count=*/0);
   }
 
-#if !defined(OS_CHROMEOS)
+#if !BUILDFLAG(IS_CHROMEOS)
   // Sign out the user to revoke all refresh tokens. This prevents any posted
   // tasks from successfully fetching an access token during the tear-down
   // phase and crashing on a DCHECK. See crbug/1102746 for more details.
   harness->SignOutPrimaryAccount();
-#endif  // !defined(OS_CHROMEOS)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
   ClosePlatformBrowser(browser);
 }
 
@@ -710,7 +710,7 @@
 
 // Verifies that network provider attaches effective connection type correctly
 // to the UKM report.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest, NetworkProviderPopulatesSystemProfile) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   // Override network quality to 2G. This should cause the
@@ -750,14 +750,14 @@
   harness->service()->GetUserSettings()->SetSyncRequested(false);
   CloseBrowserSynchronously(sync_browser);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Make sure that providing consent doesn't enable UKM when sync is disabled.
 // Keep in sync with testConsentAddedButNoSync in ios/chrome/browser/metrics/
 // ukm_egtest.mm and consentAddedButNoSyncCheck in chrome/android/javatests/src/
 // org/chromium/chrome/browser/sync/UkmTest.java.
 // Flaky on Android crbug.com/1096400
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #define MAYBE_ConsentAddedButNoSyncCheck DISABLED_ConsentAddedButNoSyncCheck
 #else
 #define MAYBE_ConsentAddedButNoSyncCheck ConsentAddedButNoSyncCheck
@@ -784,7 +784,7 @@
 
 // Make sure that extension URLs are disabled when an open sync window
 // disables it.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest, SingleDisableExtensionsSyncCheck) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   MetricsConsentOverride metrics_consent(true);
@@ -814,11 +814,11 @@
   harness->service()->GetUserSettings()->SetSyncRequested(false);
   CloseBrowserSynchronously(sync_browser);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Make sure that extension URLs are disabled when any open sync window
 // disables it.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest, MultiDisableExtensionsSyncCheck) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   MetricsConsentOverride metrics_consent(true);
@@ -854,9 +854,9 @@
   CloseBrowserSynchronously(browser2);
   CloseBrowserSynchronously(browser1);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest, LogsTabId) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   ASSERT_TRUE(embedded_test_server()->Start());
@@ -888,9 +888,9 @@
                            sync_browser, &ukm_test_helper);
   EXPECT_EQ(3, third_source->navigation_data().tab_id);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest, LogsPreviousSourceId) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   ASSERT_TRUE(embedded_test_server()->Start());
@@ -935,9 +935,9 @@
   EXPECT_EQ(new_tab_source->id(),
             subsequent_source->navigation_data().previous_source_id);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest, LogsOpenerSource) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   ASSERT_TRUE(embedded_test_server()->Start());
@@ -979,7 +979,7 @@
   EXPECT_EQ(ukm::kInvalidSourceId,
             subsequent_source->navigation_data().opener_source_id);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // ChromeOS doesn't have the concept of sign-out so this test doesn't make sense
 // there.
@@ -990,7 +990,7 @@
 // Keep in sync with testSingleSyncSignout in ios/chrome/browser/metrics/
 // ukm_egtest.mm and singleSyncSignoutCheck in chrome/android/javatests/src/org/
 // chromium/chrome/browser/sync/UkmTest.java.
-#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest, SingleSyncSignoutCheck) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   MetricsConsentOverride metrics_consent(true);
@@ -1011,14 +1011,13 @@
   harness->service()->GetUserSettings()->SetSyncRequested(false);
   ClosePlatformBrowser(browser);
 }
-#endif  // !defined(OS_CHROMEOS) &&
-        // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
 
 // ChromeOS doesn't have the concept of sign-out so this test doesn't make sense
 // there. Android doesn't have multiple profiles.
-#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
 
-#if defined(OS_MAC) && defined(ARCH_CPU_ARM64)
+#if BUILDFLAG(IS_MAC) && defined(ARCH_CPU_ARM64)
 // https://crbug.com/1223061
 #define MAYBE_MultiSyncSignoutCheck DISABLED_MultiSyncSignoutCheck
 #else
@@ -1054,12 +1053,11 @@
   CloseBrowserSynchronously(browser2);
   CloseBrowserSynchronously(browser1);
 }
-#endif  // !defined(OS_CHROMEOS) &&
-        // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
 
 // Make sure that if history/sync services weren't available when we tried to
 // attach listeners, UKM is not enabled.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest, ServiceListenerInitFailedCheck) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   MetricsConsentOverride metrics_consent(true);
@@ -1075,10 +1073,10 @@
   harness->service()->GetUserSettings()->SetSyncRequested(false);
   CloseBrowserSynchronously(sync_browser);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Make sure that UKM is not affected by MetricsReporting Feature (sampling).
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest, MetricsReportingCheck) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   // Need to set the Metrics Default to OPT_OUT to trigger MetricsReporting.
@@ -1102,7 +1100,7 @@
   harness->service()->GetUserSettings()->SetSyncRequested(false);
   CloseBrowserSynchronously(sync_browser);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Make sure that pending data is deleted when user deletes history.
 //
@@ -1111,7 +1109,7 @@
 // chromium/chrome/browser/metrics/UkmTest.java.
 //
 // Flaky on Android: https://crbug.com/1131541.
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #define MAYBE_HistoryDeleteCheck DISABLED_HistoryDeleteCheck
 #else
 #define MAYBE_HistoryDeleteCheck HistoryDeleteCheck
@@ -1147,7 +1145,7 @@
 
 // On ChromeOS, the test profile starts with a primary account already set, so
 // this test doesn't apply.
-#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTestWithSyncTransport,
                        NotEnabledForSecondaryAccountSync) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
@@ -1181,10 +1179,9 @@
 
   EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
 }
-#endif  // !defined(OS_CHROMEOS) &&
-        // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_P(UkmConsentParamBrowserTest, GroupPolicyConsentCheck) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   // Note we are not using the synthetic MetricsConsentOverride since we are
@@ -1207,19 +1204,19 @@
   harness->service()->GetUserSettings()->SetSyncRequested(false);
   CloseBrowserSynchronously(sync_browser);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 // Verify UKM is enabled/disabled for both potential settings of group policy.
 INSTANTIATE_TEST_SUITE_P(UkmConsentParamBrowserTests,
                          UkmConsentParamBrowserTest,
                          testing::Bool());
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Verify that sources kept alive in-memory will be discarded by UKM service in
 // one reporting cycle after the web contents are destroyed when the tab is
 // closed or when the user navigated away in the same tab.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest, EvictObsoleteSources) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   MetricsConsentOverride metrics_consent(true);
@@ -1338,11 +1335,11 @@
 
   CloseBrowserSynchronously(sync_browser);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Verify that correct sources are marked as obsolete when same-document
 // navigation happens.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest,
                        MarkObsoleteSourcesSameDocumentNavigation) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
@@ -1397,11 +1394,11 @@
   EXPECT_TRUE(ukm_test_helper.IsSourceObsolete(source_id3));
   EXPECT_TRUE(ukm_test_helper.IsSourceObsolete(source_id4));
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Verify that sources are not marked as obsolete by a new navigation that does
 // not commit.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest, NotMarkSourcesIfNavigationNotCommitted) {
   ukm::UkmTestHelper ukm_test_helper(GetUkmService());
   MetricsConsentOverride metrics_consent(true);
@@ -1432,9 +1429,9 @@
   ASSERT_TRUE(ui_test_utils::NavigateToURL(sync_browser, test_url_no_commit));
   EXPECT_FALSE(ukm_test_helper.IsSourceObsolete(source_id));
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(UkmBrowserTest, DebugUiRenders) {
   MetricsConsentOverride metrics_consent(true);
 
@@ -1454,6 +1451,6 @@
   EXPECT_TRUE(ui_test_utils::NavigateToURL(browser, debug_url));
   waiter.WaitForNavigationFinished();
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 }  // namespace metrics
diff --git a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc
index 34a37f4c..38d106c 100644
--- a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc
+++ b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc
@@ -409,7 +409,7 @@
 }
 
 // TODO(1183746): Fix the flakiness on MacOS and re-enable the test.
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 #define MAYBE_FullScreenVideoClosed DISABLED_FullScreenVideoClosed
 #else
 #define MAYBE_FullScreenVideoClosed FullScreenVideoClosed
@@ -466,7 +466,7 @@
 }
 
 // TODO(1183746): Fix the flakiness on MacOS and re-enable the test.
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 #define MAYBE_FullScreenVideoCrash DISABLED_FullScreenVideoCrash
 #else
 #define MAYBE_FullScreenVideoCrash FullScreenVideoCrash
diff --git a/chrome/browser/metrics/usertype_by_devicetype_metrics_provider_browsertest.cc b/chrome/browser/metrics/usertype_by_devicetype_metrics_provider_browsertest.cc
index aec8e4f..46a646a 100644
--- a/chrome/browser/metrics/usertype_by_devicetype_metrics_provider_browsertest.cc
+++ b/chrome/browser/metrics/usertype_by_devicetype_metrics_provider_browsertest.cc
@@ -400,7 +400,7 @@
 };
 
 // Flacky on CrOS (http://crbug.com/1248669).
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
 #define MAYBE_Uma DISABLED_Uma
 #else
 #define MAYBE_Uma Uma
diff --git a/chrome/browser/metrics/variations/chrome_variations_service_client.cc b/chrome/browser/metrics/variations/chrome_variations_service_client.cc
index eb001e58..28df646 100644
--- a/chrome/browser/metrics/variations/chrome_variations_service_client.cc
+++ b/chrome/browser/metrics/variations/chrome_variations_service_client.cc
@@ -15,7 +15,7 @@
 #include "components/version_info/version_info.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
-#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/upgrade_detector/build_state.h"
 #endif
 
@@ -23,7 +23,7 @@
 #include "chrome/browser/ash/settings/cros_settings.h"
 #endif
 
-#if defined(OS_WIN) || defined(OS_MAC)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
 #include "base/enterprise_util.h"
 #elif BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chromeos/tpm/install_attributes.h"
@@ -34,11 +34,11 @@
 ChromeVariationsServiceClient::~ChromeVariationsServiceClient() = default;
 
 base::Version ChromeVariationsServiceClient::GetVersionForSimulation() {
-#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
   const auto* build_state = g_browser_process->GetBuildState();
   if (build_state->installed_version().has_value())
     return *build_state->installed_version();
-#endif  // !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
 
   // TODO(asvitkine): Get the version that will be used on restart instead of
   // the current version on Android, iOS and ChromeOS.
@@ -77,7 +77,7 @@
 }
 
 bool ChromeVariationsServiceClient::IsEnterprise() {
-#if defined(OS_WIN) || defined(OS_MAC)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
   return base::IsMachineExternallyManaged();
 #elif BUILDFLAG(IS_CHROMEOS_ASH)
   return chromeos::InstallAttributes::Get()->IsEnterpriseManaged();
diff --git a/chrome/browser/nearby_sharing/common/nearby_share_features.cc b/chrome/browser/nearby_sharing/common/nearby_share_features.cc
index 13b25a4..d32fc60 100644
--- a/chrome/browser/nearby_sharing/common/nearby_share_features.cc
+++ b/chrome/browser/nearby_sharing/common/nearby_share_features.cc
@@ -28,6 +28,10 @@
 const base::Feature kNearbySharingDeviceContacts{
     "NearbySharingDeviceContacts", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables new one-page onboarding workflow for Nearby Share.
+const base::Feature kNearbySharingOnePageOnboarding{
+    "NearbySharingOnePageOnboarding", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables use of WebRTC in Nearby Share.
 const base::Feature kNearbySharingWebRtc{"NearbySharingWebRtc",
                                          base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/browser/nearby_sharing/common/nearby_share_features.h b/chrome/browser/nearby_sharing/common/nearby_share_features.h
index 14d0648..9abfff1 100644
--- a/chrome/browser/nearby_sharing/common/nearby_share_features.h
+++ b/chrome/browser/nearby_sharing/common/nearby_share_features.h
@@ -13,6 +13,7 @@
 extern const base::Feature kNearbySharingBackgroundScanning;
 extern const base::Feature kNearbySharingChildAccounts;
 extern const base::Feature kNearbySharingDeviceContacts;
+extern const base::Feature kNearbySharingOnePageOnboarding;
 extern const base::Feature kNearbySharingWebRtc;
 
 }  // namespace features
diff --git a/chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler_base.cc b/chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler_base.cc
index ff4ca18..cf37951 100644
--- a/chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler_base.cc
+++ b/chrome/browser/nearby_sharing/scheduling/nearby_share_scheduler_base.cc
@@ -176,20 +176,20 @@
 
 void NearbyShareSchedulerBase::SetLastAttemptTime(
     base::Time last_attempt_time) {
-  DictionaryPrefUpdate(pref_service_, pref_name_)
+  DictionaryPrefUpdateDeprecated(pref_service_, pref_name_)
       .Get()
       ->SetKey(kLastAttemptTimeKeyName, base::TimeToValue(last_attempt_time));
 }
 
 void NearbyShareSchedulerBase::SetLastSuccessTime(
     base::Time last_success_time) {
-  DictionaryPrefUpdate(pref_service_, pref_name_)
+  DictionaryPrefUpdateDeprecated(pref_service_, pref_name_)
       .Get()
       ->SetKey(kLastSuccessTimeKeyName, base::TimeToValue(last_success_time));
 }
 
 void NearbyShareSchedulerBase::SetNumConsecutiveFailures(size_t num_failures) {
-  DictionaryPrefUpdate(pref_service_, pref_name_)
+  DictionaryPrefUpdateDeprecated(pref_service_, pref_name_)
       .Get()
       ->SetStringKey(kNumConsecutiveFailuresKeyName,
                      base::NumberToString(num_failures));
@@ -197,7 +197,7 @@
 
 void NearbyShareSchedulerBase::SetHasPendingImmediateRequest(
     bool has_pending_immediate_request) {
-  DictionaryPrefUpdate(pref_service_, pref_name_)
+  DictionaryPrefUpdateDeprecated(pref_service_, pref_name_)
       .Get()
       ->SetBoolKey(kHasPendingImmediateRequestKeyName,
                    has_pending_immediate_request);
@@ -205,7 +205,7 @@
 
 void NearbyShareSchedulerBase::SetIsWaitingForResult(
     bool is_waiting_for_result) {
-  DictionaryPrefUpdate(pref_service_, pref_name_)
+  DictionaryPrefUpdateDeprecated(pref_service_, pref_name_)
       .Get()
       ->SetBoolKey(kIsWaitingForResultKeyName, is_waiting_for_result);
 }
diff --git a/chrome/browser/nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory.cc b/chrome/browser/nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory.cc
new file mode 100644
index 0000000..3b0ccc2
--- /dev/null
+++ b/chrome/browser/nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory.cc
@@ -0,0 +1,86 @@
+// 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/nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory.h"
+
+#include "ash/services/nearby/public/cpp/tcp_server_socket_port.h"
+#include "base/bind.h"
+#include "net/base/address_list.h"
+#include "net/base/ip_address.h"
+#include "net/base/net_errors.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+
+NearbyConnectionsTcpSocketFactory::NearbyConnectionsTcpSocketFactory(
+    NetworkContextGetter network_context_getter)
+    : network_context_getter_(std::move(network_context_getter)) {}
+
+NearbyConnectionsTcpSocketFactory::~NearbyConnectionsTcpSocketFactory() =
+    default;
+
+void NearbyConnectionsTcpSocketFactory::CreateTCPServerSocket(
+    const net::IPAddress& local_addr,
+    const ash::nearby::TcpServerSocketPort& port,
+    uint32_t backlog,
+    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+    mojo::PendingReceiver<network::mojom::TCPServerSocket> receiver,
+    CreateTCPServerSocketCallback callback) {
+  network::mojom::NetworkContext* network_context =
+      network_context_getter_.Run();
+  if (!network_context) {
+    std::move(callback).Run(net::ERR_FAILED, /*local_addr_out=*/absl::nullopt);
+    return;
+  }
+
+  network_context->CreateTCPServerSocket(
+      net::IPEndPoint(local_addr, port.port()), backlog, traffic_annotation,
+      std::move(receiver),
+      base::BindOnce(
+          &NearbyConnectionsTcpSocketFactory::OnTcpServerSocketCreated,
+          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void NearbyConnectionsTcpSocketFactory::CreateTCPConnectedSocket(
+    const absl::optional<net::IPEndPoint>& local_addr,
+    const net::AddressList& remote_addr_list,
+    network::mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options,
+    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+    mojo::PendingReceiver<network::mojom::TCPConnectedSocket> receiver,
+    mojo::PendingRemote<network::mojom::SocketObserver> observer,
+    CreateTCPConnectedSocketCallback callback) {
+  network::mojom::NetworkContext* network_context =
+      network_context_getter_.Run();
+  if (!network_context) {
+    std::move(callback).Run(
+        net::ERR_FAILED, /*local_addr=*/absl::nullopt,
+        /*peer_addr=*/absl::nullopt,
+        /*receive_stream=*/mojo::ScopedDataPipeConsumerHandle(),
+        /*send_stream=*/mojo::ScopedDataPipeProducerHandle());
+    return;
+  }
+
+  network_context->CreateTCPConnectedSocket(
+      local_addr, remote_addr_list, std::move(tcp_connected_socket_options),
+      traffic_annotation, std::move(receiver), std::move(observer),
+      base::BindOnce(
+          &NearbyConnectionsTcpSocketFactory::OnTcpConnectedSocketCreated,
+          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void NearbyConnectionsTcpSocketFactory::OnTcpServerSocketCreated(
+    CreateTCPServerSocketCallback callback,
+    int32_t result,
+    const absl::optional<net::IPEndPoint>& local_addr) {
+  std::move(callback).Run(result, local_addr);
+}
+
+void NearbyConnectionsTcpSocketFactory::OnTcpConnectedSocketCreated(
+    CreateTCPConnectedSocketCallback callback,
+    int32_t result,
+    const absl::optional<net::IPEndPoint>& local_addr,
+    const absl::optional<net::IPEndPoint>& peer_addr,
+    mojo::ScopedDataPipeConsumerHandle receive_stream,
+    mojo::ScopedDataPipeProducerHandle send_stream) {
+  std::move(callback).Run(result, local_addr, peer_addr,
+                          std::move(receive_stream), std::move(send_stream));
+}
diff --git a/chrome/browser/nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory.h b/chrome/browser/nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory.h
new file mode 100644
index 0000000..7c4e14b
--- /dev/null
+++ b/chrome/browser/nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory.h
@@ -0,0 +1,85 @@
+// 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_NEARBY_SHARING_TCP_SOCKET_NEARBY_CONNECTIONS_TCP_SOCKET_FACTORY_H_
+#define CHROME_BROWSER_NEARBY_SHARING_TCP_SOCKET_NEARBY_CONNECTIONS_TCP_SOCKET_FACTORY_H_
+
+#include "ash/services/nearby/public/mojom/tcp_socket_factory.mojom.h"
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "net/base/ip_endpoint.h"
+#include "services/network/public/mojom/network_context.mojom.h"
+#include "services/network/public/mojom/tcp_socket.mojom.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace ash {
+namespace nearby {
+class TcpServerSocketPort;
+}  // namespace nearby
+}  // namespace ash
+
+namespace net {
+class AddressList;
+struct MutableNetworkTrafficAnnotationTag;
+class IPAddress;
+}  // namespace net
+
+// An implementation of the mojo service used to create TCP sockets for the
+// Nearby Connections WifiLan medium. We guarantee that callbacks will not be
+// invoked after this class is destroyed.
+class NearbyConnectionsTcpSocketFactory
+    : public sharing::mojom::TcpSocketFactory {
+ public:
+  using NetworkContextGetter =
+      base::RepeatingCallback<network::mojom::NetworkContext*()>;
+
+  explicit NearbyConnectionsTcpSocketFactory(
+      NetworkContextGetter network_context_getter);
+  NearbyConnectionsTcpSocketFactory(const NearbyConnectionsTcpSocketFactory&) =
+      delete;
+  NearbyConnectionsTcpSocketFactory& operator=(
+      const NearbyConnectionsTcpSocketFactory&) = delete;
+  ~NearbyConnectionsTcpSocketFactory() override;
+
+  // sharing::mojom:TcpSocketFactory:
+  void CreateTCPServerSocket(
+      const net::IPAddress& local_addr,
+      const ash::nearby::TcpServerSocketPort& port,
+      uint32_t backlog,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+      mojo::PendingReceiver<network::mojom::TCPServerSocket> receiver,
+      CreateTCPServerSocketCallback callback) override;
+  void CreateTCPConnectedSocket(
+      const absl::optional<net::IPEndPoint>& local_addr,
+      const net::AddressList& remote_addr_list,
+      network::mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+      mojo::PendingReceiver<network::mojom::TCPConnectedSocket> receiver,
+      mojo::PendingRemote<network::mojom::SocketObserver> observer,
+      CreateTCPConnectedSocketCallback callback) override;
+
+ private:
+  // Wrapper callbacks that are bound with weak pointers. Used to guarantee that
+  // input callbacks are not invoked after this class is destroyed.
+  void OnTcpServerSocketCreated(
+      CreateTCPServerSocketCallback callback,
+      int32_t result,
+      const absl::optional<net::IPEndPoint>& local_addr);
+  void OnTcpConnectedSocketCreated(
+      CreateTCPConnectedSocketCallback callback,
+      int32_t result,
+      const absl::optional<net::IPEndPoint>& local_addr,
+      const absl::optional<net::IPEndPoint>& peer_addr,
+      mojo::ScopedDataPipeConsumerHandle receive_stream,
+      mojo::ScopedDataPipeProducerHandle send_stream);
+
+  NetworkContextGetter network_context_getter_;
+  base::WeakPtrFactory<NearbyConnectionsTcpSocketFactory> weak_ptr_factory_{
+      this};
+};
+
+#endif  // CHROME_BROWSER_NEARBY_SHARING_TCP_SOCKET_NEARBY_CONNECTIONS_TCP_SOCKET_FACTORY_H_
diff --git a/chrome/browser/nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory_unittest.cc b/chrome/browser/nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory_unittest.cc
new file mode 100644
index 0000000..200eae3
--- /dev/null
+++ b/chrome/browser/nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory_unittest.cc
@@ -0,0 +1,151 @@
+// 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/nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory.h"
+
+#include <memory>
+
+#include "ash/services/nearby/public/cpp/tcp_server_socket_port.h"
+#include "base/bind.h"
+#include "base/test/bind.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/test/test_network_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+uint32_t kBacklog = 10;
+const net::MutableNetworkTrafficAnnotationTag kAnnotation =
+    net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+const net::IPEndPoint kLocalAddress(net::IPAddress(192, 168, 86, 01),
+                                    ash::nearby::TcpServerSocketPort::kMin);
+const net::IPEndPoint kRemoteAddress(net::IPAddress(192, 168, 86, 02),
+                                     ash::nearby::TcpServerSocketPort::kMax);
+
+}  // namespace
+
+class NearbyConnectionsTcpSocketFactoryTest : public ::testing::Test {
+ protected:
+  // Verifies input data and immediately invokes callbacks.
+  class FakeNetworkContext : public network::TestNetworkContext {
+   public:
+    FakeNetworkContext() = default;
+    ~FakeNetworkContext() override = default;
+
+   private:
+    // network::TestNetworkContext:
+    void CreateTCPServerSocket(
+        const net::IPEndPoint& local_addr,
+        uint32_t backlog,
+        const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+        mojo::PendingReceiver<network::mojom::TCPServerSocket> socket,
+        CreateTCPServerSocketCallback callback) override {
+      EXPECT_EQ(kLocalAddress, local_addr);
+      EXPECT_EQ(kBacklog, backlog);
+      EXPECT_EQ(traffic_annotation, net::MutableNetworkTrafficAnnotationTag(
+                                        TRAFFIC_ANNOTATION_FOR_TESTS));
+      std::move(callback).Run(net::OK, local_addr);
+    }
+    void CreateTCPConnectedSocket(
+        const absl::optional<net::IPEndPoint>& local_addr,
+        const net::AddressList& remote_addr_list,
+        network::mojom::TCPConnectedSocketOptionsPtr
+            tcp_connected_socket_options,
+        const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+        mojo::PendingReceiver<network::mojom::TCPConnectedSocket> socket,
+        mojo::PendingRemote<network::mojom::SocketObserver> observer,
+        CreateTCPConnectedSocketCallback callback) override {
+      EXPECT_EQ(kLocalAddress, local_addr);
+      EXPECT_EQ(1, remote_addr_list.size());
+      EXPECT_EQ(kRemoteAddress, remote_addr_list[0]);
+      EXPECT_EQ(kAnnotation, traffic_annotation);
+      std::move(callback).Run(
+          net::OK, local_addr, remote_addr_list[0],
+          /*receive_stream=*/mojo::ScopedDataPipeConsumerHandle(),
+          /*send_stream=*/mojo::ScopedDataPipeProducerHandle());
+    }
+  };
+
+  NearbyConnectionsTcpSocketFactoryTest() = default;
+
+  ~NearbyConnectionsTcpSocketFactoryTest() override = default;
+
+  void SetUp() override {
+    fake_network_context_ = std::make_unique<FakeNetworkContext>();
+    factory_ =
+        std::make_unique<NearbyConnectionsTcpSocketFactory>(base::BindRepeating(
+            &NearbyConnectionsTcpSocketFactoryTest::GetNetworkContext,
+            base::Unretained(this)));
+  }
+
+  network::mojom::NetworkContext* GetNetworkContext() {
+    return fake_network_context_.get();
+  }
+
+  std::unique_ptr<FakeNetworkContext> fake_network_context_;
+  std::unique_ptr<NearbyConnectionsTcpSocketFactory> factory_;
+};
+
+TEST_F(NearbyConnectionsTcpSocketFactoryTest, NetworkContextExists) {
+  factory_->CreateTCPServerSocket(
+      kLocalAddress.address(),
+      *ash::nearby::TcpServerSocketPort::FromUInt16(kLocalAddress.port()),
+      kBacklog, kAnnotation, /*receiver=*/mojo::NullReceiver(),
+      base::BindLambdaForTesting(
+          [](int32_t result,
+             const absl::optional<net::IPEndPoint>& local_addr) {
+            EXPECT_EQ(net::OK, result);
+            EXPECT_EQ(kLocalAddress, local_addr);
+          }));
+  factory_->CreateTCPConnectedSocket(
+      kLocalAddress, net::AddressList(kRemoteAddress),
+      /*tcp_connected_socket_options=*/nullptr, kAnnotation,
+      /*receiver=*/mojo::NullReceiver(),
+      /*observer=*/mojo::NullRemote(),
+      base::BindLambdaForTesting(
+          [](int32_t result, const absl::optional<net::IPEndPoint>& local_addr,
+             const absl::optional<net::IPEndPoint>& peer_addr,
+             mojo::ScopedDataPipeConsumerHandle receive_stream,
+             mojo::ScopedDataPipeProducerHandle send_stream) {
+            EXPECT_EQ(net::OK, result);
+            EXPECT_EQ(kLocalAddress, local_addr);
+            EXPECT_EQ(kRemoteAddress, peer_addr);
+          }));
+}
+
+TEST_F(NearbyConnectionsTcpSocketFactoryTest, NetworkContextDoesNotExist) {
+  fake_network_context_.reset();
+
+  // Expect trivial data in callback when the network context is null.
+  factory_->CreateTCPServerSocket(
+      kLocalAddress.address(),
+      *ash::nearby::TcpServerSocketPort::FromUInt16(kLocalAddress.port()),
+      kBacklog, kAnnotation, /*receiver=*/mojo::NullReceiver(),
+      base::BindLambdaForTesting(
+          [](int32_t result,
+             const absl::optional<net::IPEndPoint>& local_addr) {
+            EXPECT_EQ(net::ERR_FAILED, result);
+            EXPECT_EQ(absl::nullopt, local_addr);
+          }));
+  factory_->CreateTCPConnectedSocket(
+      kLocalAddress, net::AddressList(kRemoteAddress),
+      /*tcp_connected_socket_options=*/nullptr, kAnnotation,
+      /*receiver=*/mojo::NullReceiver(),
+      /*observer=*/mojo::NullRemote(),
+      base::BindLambdaForTesting(
+          [](int32_t result, const absl::optional<net::IPEndPoint>& local_addr,
+             const absl::optional<net::IPEndPoint>& peer_addr,
+             mojo::ScopedDataPipeConsumerHandle receive_stream,
+             mojo::ScopedDataPipeProducerHandle send_stream) {
+            EXPECT_EQ(net::ERR_FAILED, result);
+            EXPECT_EQ(absl::nullopt, local_addr);
+            EXPECT_EQ(absl::nullopt, peer_addr);
+            EXPECT_EQ(mojo::ScopedDataPipeConsumerHandle(), receive_stream);
+            EXPECT_EQ(mojo::ScopedDataPipeProducerHandle(), send_stream);
+          }));
+}
diff --git a/chrome/browser/net/cookie_store_sameparty_browsertest.cc b/chrome/browser/net/cookie_store_sameparty_browsertest.cc
index b90a2c5..992e0d5 100644
--- a/chrome/browser/net/cookie_store_sameparty_browsertest.cc
+++ b/chrome/browser/net/cookie_store_sameparty_browsertest.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_paths.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
@@ -41,7 +42,7 @@
       : https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
         enable_fps_(enable_fps) {
     if (!enable_fps) {
-      feature_list_.InitAndDisableFeature(net::features::kFirstPartySets);
+      feature_list_.InitAndDisableFeature(features::kFirstPartySets);
     }
     // If FPS is to be enabled, that happens in `SetUpCommandLine` when the
     // `kUseFirstPartySet` switch is provided.
diff --git a/chrome/browser/net/system_network_context_manager_browsertest.cc b/chrome/browser/net/system_network_context_manager_browsertest.cc
index 8bb4b78..48d72d2 100644
--- a/chrome/browser/net/system_network_context_manager_browsertest.cc
+++ b/chrome/browser/net/system_network_context_manager_browsertest.cc
@@ -23,6 +23,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/network_service_instance.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/network_service_util.h"
 #include "content/public/common/user_agent.h"
 #include "content/public/test/browser_test.h"
@@ -204,7 +205,7 @@
 
   void SetUpInProcessBrowserTestFixture() override {
     SystemNetworkContextManagerBrowsertest::SetUpInProcessBrowserTestFixture();
-    feature_list_.InitAndEnableFeature(net::features::kFirstPartySets);
+    feature_list_.InitAndEnableFeature(features::kFirstPartySets);
     CHECK(component_dir_.CreateUniqueTempDir());
     base::ScopedAllowBlockingForTesting allow_blocking;
 
diff --git a/chrome/browser/new_tab_page/modules/drive/BUILD.gn b/chrome/browser/new_tab_page/modules/drive/BUILD.gn
index 3256c4bb..f00cb13 100644
--- a/chrome/browser/new_tab_page/modules/drive/BUILD.gn
+++ b/chrome/browser/new_tab_page/modules/drive/BUILD.gn
@@ -6,5 +6,6 @@
 
 mojom("mojo_bindings") {
   sources = [ "drive.mojom" ]
+  webui_module_path = "/"
   public_deps = [ "//url/mojom:url_mojom_gurl" ]
 }
diff --git a/chrome/browser/new_tab_page/modules/photos/BUILD.gn b/chrome/browser/new_tab_page/modules/photos/BUILD.gn
index 62ec147..4479875 100644
--- a/chrome/browser/new_tab_page/modules/photos/BUILD.gn
+++ b/chrome/browser/new_tab_page/modules/photos/BUILD.gn
@@ -6,5 +6,6 @@
 
 mojom("mojo_bindings") {
   sources = [ "photos.mojom" ]
+  webui_module_path = "/"
   public_deps = [ "//url/mojom:url_mojom_gurl" ]
 }
diff --git a/chrome/browser/new_tab_page/modules/task_module/task_module_service.cc b/chrome/browser/new_tab_page/modules/task_module/task_module_service.cc
index 6b071b2..3ea37ca 100644
--- a/chrome/browser/new_tab_page/modules/task_module/task_module_service.cc
+++ b/chrome/browser/new_tab_page/modules/task_module/task_module_service.cc
@@ -256,8 +256,8 @@
 void TaskModuleService::DismissTask(
     task_module::mojom::TaskModuleType task_module_type,
     const std::string& task_name) {
-  ListPrefUpdate update(profile_->GetPrefs(),
-                        GetDismissedTasksPrefName(task_module_type));
+  ListPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                  GetDismissedTasksPrefName(task_module_type));
   base::Value task_name_value(task_name);
   if (!base::Contains(update->GetList(), task_name_value))
     update->Append(std::move(task_name_value));
@@ -266,8 +266,8 @@
 void TaskModuleService::RestoreTask(
     task_module::mojom::TaskModuleType task_module_type,
     const std::string& task_name) {
-  ListPrefUpdate update(profile_->GetPrefs(),
-                        GetDismissedTasksPrefName(task_module_type));
+  ListPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                  GetDismissedTasksPrefName(task_module_type));
   update->EraseListValue(base::Value(task_name));
 }
 
diff --git a/chrome/browser/new_tab_page/promos/promo_service.cc b/chrome/browser/new_tab_page/promos/promo_service.cc
index 0893ff2..39e713b 100644
--- a/chrome/browser/new_tab_page/promos/promo_service.cc
+++ b/chrome/browser/new_tab_page/promos/promo_service.cc
@@ -252,7 +252,8 @@
     return;
   }
 
-  DictionaryPrefUpdate update(profile_->GetPrefs(), prefs::kNtpPromoBlocklist);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kNtpPromoBlocklist);
   double now = base::Time::Now().ToDeltaSinceWindowsEpoch().InSecondsF();
   update->SetDoubleKey(promo_id, now);
 
@@ -304,8 +305,8 @@
   }
 
   if (!expired_ids.empty()) {
-    DictionaryPrefUpdate update(profile_->GetPrefs(),
-                                prefs::kNtpPromoBlocklist);
+    DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                          prefs::kNtpPromoBlocklist);
     for (const std::string& key : expired_ids)
       update->RemoveKey(key);
   }
diff --git a/chrome/browser/new_tab_page/promos/promo_service_unittest.cc b/chrome/browser/new_tab_page/promos/promo_service_unittest.cc
index ef3866e..f48a7d8 100644
--- a/chrome/browser/new_tab_page/promos/promo_service_unittest.cc
+++ b/chrome/browser/new_tab_page/promos/promo_service_unittest.cc
@@ -206,7 +206,7 @@
   feature_list.InitAndEnableFeature(ntp_features::kDismissPromos);
 
   {
-    DictionaryPrefUpdate update(prefs(), prefs::kNtpPromoBlocklist);
+    DictionaryPrefUpdateDeprecated update(prefs(), prefs::kNtpPromoBlocklist);
     base::Time recent = base::Time::Now() - base::Hours(2);
     update->SetDoubleKey("42", recent.ToDeltaSinceWindowsEpoch().InSecondsF());
   }
@@ -264,7 +264,7 @@
   feature_list.InitAndEnableFeature(ntp_features::kDismissPromos);
 
   {
-    DictionaryPrefUpdate update(prefs(), prefs::kNtpPromoBlocklist);
+    DictionaryPrefUpdateDeprecated update(prefs(), prefs::kNtpPromoBlocklist);
     ASSERT_EQ(0u, update->DictSize());
     base::Time past = base::Time::Now() - base::Days(365);
     update->SetDoubleKey("42", past.ToDeltaSinceWindowsEpoch().InSecondsF());
@@ -298,7 +298,7 @@
   feature_list.InitAndEnableFeature(ntp_features::kDismissPromos);
 
   {
-    DictionaryPrefUpdate update(prefs(), prefs::kNtpPromoBlocklist);
+    DictionaryPrefUpdateDeprecated update(prefs(), prefs::kNtpPromoBlocklist);
     ASSERT_EQ(0u, update->DictSize());
     update->SetDoubleKey("42", 5);
     update->SetStringKey("84", "wrong type");
diff --git a/chrome/browser/notifications/notifier_state_tracker.cc b/chrome/browser/notifications/notifier_state_tracker.cc
index 6f9c642..03dbeb51 100644
--- a/chrome/browser/notifications/notifier_state_tracker.cc
+++ b/chrome/browser/notifications/notifier_state_tracker.cc
@@ -129,7 +129,7 @@
   }
   DCHECK(pref_name != NULL);
 
-  ListPrefUpdate update(profile_->GetPrefs(), pref_name);
+  ListPrefUpdateDeprecated update(profile_->GetPrefs(), pref_name);
   if (add_new_item) {
     if (!base::Contains(update->GetList(), id))
       update->Append(std::move(id));
diff --git a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer_unittest.cc
index 03a5d1d..aa01b8a 100644
--- a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer_unittest.cc
@@ -678,15 +678,14 @@
   EXPECT_TRUE(events[0]->HasArg("data"));
   base::Value arg;
   EXPECT_TRUE(events[0]->GetArgAsValue("data", &arg));
-  base::DictionaryValue* arg_dict;
-  EXPECT_TRUE(arg.GetAsDictionary(&arg_dict));
-  int time = arg_dict->FindIntKey("durationInMilliseconds").value_or(0);
+  ASSERT_TRUE(arg.is_dict());
+  int time = arg.FindIntKey("durationInMilliseconds").value_or(0);
   EXPECT_EQ(600, time);
-  int size = arg_dict->FindIntKey("size").value_or(0);
+  int size = arg.FindIntKey("size").value_or(0);
   EXPECT_EQ(1000, size);
-  std::string type;
-  EXPECT_TRUE(arg_dict->GetString("type", &type));
-  EXPECT_EQ("text", type);
+  const std::string* type = arg.FindStringKey("type");
+  ASSERT_TRUE(type);
+  EXPECT_EQ("text", *type);
 }
 
 TEST_F(UkmPageLoadMetricsObserverTest,
diff --git a/chrome/browser/password_check/android/BUILD.gn b/chrome/browser/password_check/android/BUILD.gn
index cd78ef7..a9765bf 100644
--- a/chrome/browser/password_check/android/BUILD.gn
+++ b/chrome/browser/password_check/android/BUILD.gn
@@ -167,8 +167,6 @@
     "java/res/drawable/password_checkup_warning.xml",
     "java/res/layout/password_check_edit_fragment.xml",
     "java/res/menu/password_check_editor_action_bar_menu.xml",
-    "java/res/values-night/colors.xml",
-    "java/res/values/colors.xml",
   ]
   deps = [
     "//chrome/browser/ui/android/strings:ui_strings_grd",
diff --git a/chrome/browser/password_check/android/internal/java/res/drawable-night/password_check_positive.xml b/chrome/browser/password_check/android/internal/java/res/drawable-night/password_check_positive.xml
index 272f8f9..8a996c8 100644
--- a/chrome/browser/password_check/android/internal/java/res/drawable-night/password_check_positive.xml
+++ b/chrome/browser/password_check/android/internal/java/res/drawable-night/password_check_positive.xml
@@ -4,15 +4,11 @@
      found in the LICENSE file. -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     tools:ignore="VectorRaster"
     android:width="360dp"
     android:height="120dp"
     android:viewportWidth="360"
     android:viewportHeight="120">
-    <path android:pathData="M0 0h360v120H0z"
-        android:fillColor="#28282B"
-        android:fillType="evenOdd"/>
     <path android:pathData="M288.585 52.215V47c10.735 0 19.175-3 25.09-8.955 9.29-9.36 9.325-23.015 9.325-23.15l5.25-0.04c0 0.645 0 15.9-10.815 26.82-6.935 6.995-16.645 10.54-28.85 10.54z"
         android:fillColor="#414447"/>
     <path android:pathData="M253.87 83h-5.245a40.45 40.45 0 0 1 5-18c4.605-8.21 14.4-18 34.96-18v5.215c-14.465 0-24.685 5.17-30.385 15.36A35.345 35.345 0 0 0 253.87 83z"
diff --git a/chrome/browser/password_check/android/internal/java/res/drawable/ic_autofill_assistant_white_24dp.xml b/chrome/browser/password_check/android/internal/java/res/drawable/ic_autofill_assistant_white_24dp.xml
index c4e3af5..bbbe506 100644
--- a/chrome/browser/password_check/android/internal/java/res/drawable/ic_autofill_assistant_white_24dp.xml
+++ b/chrome/browser/password_check/android/internal/java/res/drawable/ic_autofill_assistant_white_24dp.xml
@@ -5,8 +5,6 @@
 
 <vector
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="192"
diff --git a/chrome/browser/password_check/android/internal/java/res/drawable/password_check_positive.xml b/chrome/browser/password_check/android/internal/java/res/drawable/password_check_positive.xml
index eb49de5..7488db90 100644
--- a/chrome/browser/password_check/android/internal/java/res/drawable/password_check_positive.xml
+++ b/chrome/browser/password_check/android/internal/java/res/drawable/password_check_positive.xml
@@ -4,15 +4,11 @@
      found in the LICENSE file. -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     tools:ignore="VectorRaster"
     android:width="360dp"
     android:height="120dp"
     android:viewportWidth="360"
     android:viewportHeight="120">
-    <path android:pathData="M0 0h360v120H0z"
-        android:fillColor="#F8F9FA"
-        android:fillType="evenOdd"/>
     <path android:pathData="M288.585 52.215V47c10.735 0 19.175-3 25.09-8.955 9.29-9.36 9.325-23.015 9.325-23.15l5.25-0.04c0 0.645 0 15.9-10.815 26.82-6.935 6.995-16.645 10.54-28.85 10.54z"
         android:fillColor="#E8E9EB"/>
     <path android:pathData="M253.87 83h-5.245a40.45 40.45 0 0 1 5-18c4.605-8.21 14.4-18 34.96-18v5.215c-14.465 0-24.685 5.17-30.385 15.36A35.345 35.345 0 0 0 253.87 83z"
diff --git a/chrome/browser/password_check/android/internal/java/res/layout/password_check_header_item.xml b/chrome/browser/password_check/android/internal/java/res/layout/password_check_header_item.xml
index 79b3907..53be4f8c 100644
--- a/chrome/browser/password_check/android/internal/java/res/layout/password_check_header_item.xml
+++ b/chrome/browser/password_check/android/internal/java/res/layout/password_check_header_item.xml
@@ -11,7 +11,6 @@
   <LinearLayout
       android:layout_height="wrap_content"
       android:layout_width="match_parent"
-      android:background="@color/password_check_neutral_background"
       android:orientation="horizontal">
 
     <ImageView android:id="@+id/check_status_illustration"
@@ -93,4 +92,4 @@
       android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
       android:visibility="gone" />
 
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/chrome/browser/password_check/android/java/res/drawable-night/password_check_neutral.xml b/chrome/browser/password_check/android/java/res/drawable-night/password_check_neutral.xml
index c06c025..95d988b 100644
--- a/chrome/browser/password_check/android/java/res/drawable-night/password_check_neutral.xml
+++ b/chrome/browser/password_check/android/java/res/drawable-night/password_check_neutral.xml
@@ -5,15 +5,11 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     tools:ignore="VectorRaster"
     android:width="360dp"
     android:height="120dp"
     android:viewportWidth="360"
     android:viewportHeight="120">
-    <path android:pathData="M0 0h360v120H0z"
-        android:fillColor="#28282B"
-        android:fillType="evenOdd"/>
     <path android:pathData="M288.585 52.215V47c10.735 0 19.175-3 25.09-8.955 9.29-9.36 9.325-23.015 9.325-23.15l5.25-0.04c0 0.645 0 15.9-10.815 26.82-6.935 6.995-16.645 10.54-28.85 10.54z"
         android:fillColor="#414447"/>
     <path android:pathData="M253.87 83h-5.245a40.45 40.45 0 0 1 5-18c4.605-8.21 14.4-18 34.96-18v5.215c-14.465 0-24.685 5.17-30.385 15.36A35.345 35.345 0 0 0 253.87 83z"
@@ -110,4 +106,3 @@
     <path android:pathData="M180.634 72.83l-5-8.66 1.732-1 5 8.66z"
         android:fillColor="#FFF"/>
 </vector>
-
diff --git a/chrome/browser/password_check/android/java/res/drawable-night/password_checkup_warning.xml b/chrome/browser/password_check/android/java/res/drawable-night/password_checkup_warning.xml
index 0fe58a3..12ad09a 100644
--- a/chrome/browser/password_check/android/java/res/drawable-night/password_checkup_warning.xml
+++ b/chrome/browser/password_check/android/java/res/drawable-night/password_checkup_warning.xml
@@ -5,9 +5,6 @@
     android:width="180dp"
     android:viewportHeight="120"
     android:viewportWidth="360">
-    <path android:fillColor="#28282B"
-        android:fillType="evenOdd"
-        android:pathData="M0 0h360v120H0z"/>
     <path android:fillColor="#3A3D40"
         android:pathData="M289.58 37.22V32c10.74 0 19.18-3 25.1-8.95C323.95 13.68 324 0.03 324-0.11l5.25-0.03c0 0.64 0 15.9-10.81 26.82-6.94 6.99-16.65 10.54-28.86 10.54zM254.87 68h-5.25a40.45 40.45 0 0 1 5-18c4.61-8.21 14.4-18 34.96-18v5.22c-14.46 0-24.68 5.16-30.38 15.36A35.34 35.34 0 0 0 254.87 68z"/>
     <path android:fillColor="#262628"
diff --git a/chrome/browser/password_check/android/java/res/drawable/password_check_neutral.xml b/chrome/browser/password_check/android/java/res/drawable/password_check_neutral.xml
index 81f588f..a78cf4d 100644
--- a/chrome/browser/password_check/android/java/res/drawable/password_check_neutral.xml
+++ b/chrome/browser/password_check/android/java/res/drawable/password_check_neutral.xml
@@ -5,15 +5,11 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     tools:ignore="VectorRaster"
     android:width="360dp"
     android:height="120dp"
     android:viewportWidth="360"
     android:viewportHeight="120">
-    <path android:pathData="M0 0h360v120H0z"
-        android:fillColor="#F8F9FA"
-        android:fillType="evenOdd"/>
     <path android:pathData="M288.585 52.215V47c10.735 0 19.175-3 25.09-8.955 9.29-9.36 9.325-23.015 9.325-23.15l5.25-0.04c0 0.645 0 15.9-10.815 26.82-6.935 6.995-16.645 10.54-28.85 10.54z"
         android:fillColor="#E8E9EB"/>
     <path android:pathData="M253.87 83h-5.245a40.45 40.45 0 0 1 5-18c4.605-8.21 14.4-18 34.96-18v5.215c-14.465 0-24.685 5.17-30.385 15.36A35.345 35.345 0 0 0 253.87 83z"
@@ -108,4 +104,3 @@
     <path android:pathData="M180.634 72.83l-5-8.66 1.732-1 5 8.66z"
         android:fillColor="#FFF"/>
 </vector>
-
diff --git a/chrome/browser/password_check/android/java/res/drawable/password_checkup_warning.xml b/chrome/browser/password_check/android/java/res/drawable/password_checkup_warning.xml
index af06076..b3a18e1 100644
--- a/chrome/browser/password_check/android/java/res/drawable/password_checkup_warning.xml
+++ b/chrome/browser/password_check/android/java/res/drawable/password_checkup_warning.xml
@@ -5,9 +5,6 @@
     android:width="180dp"
     android:viewportHeight="120"
     android:viewportWidth="360">
-    <path android:fillColor="#F8F9FA"
-        android:fillType="evenOdd"
-        android:pathData="M0 0h360v120H0z"/>
     <path android:fillColor="#E8E9EB"
         android:pathData="M289.58 37.22V32c10.74 0 19.18-3 25.1-8.95C323.95 13.68 324 0.03 324-0.11l5.25-0.03c0 0.64 0 15.9-10.81 26.82-6.94 6.99-16.65 10.54-28.86 10.54z"/>
     <path android:fillColor="#BDC0C5"
diff --git a/chrome/browser/password_check/android/java/res/values-night/colors.xml b/chrome/browser/password_check/android/java/res/values-night/colors.xml
deleted file mode 100644
index 79f91f9bb..0000000
--- a/chrome/browser/password_check/android/java/res/values-night/colors.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
-  <color name="password_check_neutral_background">@color/header_background_dark</color>
-</resources>
diff --git a/chrome/browser/password_check/android/java/res/values/colors.xml b/chrome/browser/password_check/android/java/res/values/colors.xml
deleted file mode 100644
index f0be79f8..0000000
--- a/chrome/browser/password_check/android/java/res/values/colors.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
-  <color name="password_check_neutral_background">@color/modern_grey_50</color>
-</resources>
diff --git a/chrome/browser/password_manager/android/account_chooser_dialog_android.cc b/chrome/browser/password_manager/android/account_chooser_dialog_android.cc
index 7ae1826..40c38e04 100644
--- a/chrome/browser/password_manager/android/account_chooser_dialog_android.cc
+++ b/chrome/browser/password_manager/android/account_chooser_dialog_android.cc
@@ -25,7 +25,6 @@
 #include "components/password_manager/core/browser/password_ui_utils.h"
 #include "components/password_manager/core/common/credential_manager_types.h"
 #include "components/password_manager/core/common/password_manager_features.h"
-#include "content/public/browser/storage_partition.h"
 #include "ui/android/window_android.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/android/java_bitmap.h"
@@ -92,13 +91,14 @@
 void FetchAvatar(const base::android::ScopedJavaGlobalRef<jobject>& java_dialog,
                  const password_manager::PasswordForm* password_form,
                  int index,
-                 network::mojom::URLLoaderFactory* loader_factory) {
+                 network::mojom::URLLoaderFactory* loader_factory,
+                 const url::Origin& initiator) {
   if (!password_form->icon_url.is_valid())
     return;
   // Fetcher deletes itself once fetching is finished.
   auto* fetcher =
       new AvatarFetcherAndroid(password_form->icon_url, index, java_dialog);
-  fetcher->Start(loader_factory);
+  fetcher->Start(loader_factory, initiator);
 }
 
 }  // namespace
@@ -161,14 +161,14 @@
       base::android::ConvertUTF16ToJavaString(env, title), 0, 0,
       base::android::ConvertUTF8ToJavaString(env, origin),
       base::android::ConvertUTF16ToJavaString(env, signin_button)));
-  network::mojom::URLLoaderFactory* loader_factory =
-      web_contents_->GetBrowserContext()
-          ->GetDefaultStoragePartition()
-          ->GetURLLoaderFactoryForBrowserProcess()
-          .get();
+  mojo::Remote<network::mojom::URLLoaderFactory> loader_factory =
+      GetURLLoaderForMainFrame(web_contents_);
   int avatar_index = 0;
-  for (const auto& form : local_credentials_forms())
-    FetchAvatar(dialog_jobject_, form.get(), avatar_index++, loader_factory);
+  for (const auto& form : local_credentials_forms()) {
+    FetchAvatar(dialog_jobject_, form.get(), avatar_index++,
+                loader_factory.get(),
+                web_contents_->GetMainFrame()->GetLastCommittedOrigin());
+  }
   return true;
 }
 
diff --git a/chrome/browser/password_manager/credential_manager_browsertest.cc b/chrome/browser/password_manager/credential_manager_browsertest.cc
index e27a1d5..cf98aeba 100644
--- a/chrome/browser/password_manager/credential_manager_browsertest.cc
+++ b/chrome/browser/password_manager/credential_manager_browsertest.cc
@@ -1,9 +1,9 @@
 // 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.
-
 #include "base/command_line.h"
 #include "base/containers/contains.h"
+#include "base/strings/strcat.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
@@ -19,6 +19,8 @@
 #include "components/password_manager/core/browser/password_bubble_experiment.h"
 #include "components/password_manager/core/browser/test_password_store.h"
 #include "content/public/browser/back_forward_cache.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/content_features.h"
@@ -50,10 +52,6 @@
            password_manager::ui::CREDENTIAL_REQUEST_STATE;
   }
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    PasswordManagerBrowserTestBase::SetUpCommandLine(command_line);
-  }
-
   // Similarly to PasswordManagerBrowserTestBase::NavigateToFile this is a
   // wrapper around ui_test_utils::NavigateURL that waits until DidFinishLoad()
   // fires. Different to NavigateToFile this method allows passing a test_server
@@ -1005,4 +1003,128 @@
   WaitForElementValue("password_field", "12345");
 }
 
+class CredentialManagerAvatarTest : public PasswordManagerBrowserTestBase {
+ public:
+  static const char kAvatarOrigin[];
+  static const char kAvatarPath[];
+  static const char kLoginPath[];
+
+  CredentialManagerAvatarTest() {
+    https_test_server().RegisterRequestHandler(base::BindRepeating(
+        &CredentialManagerAvatarTest::HandleRequest, base::Unretained(this)));
+  }
+
+  // Add a Credential Management API password with an icon to the store.
+  void AddPasswordForURL(const GURL& url);
+
+  // A counter for requests made to fetch the avatar.
+  size_t avatar_request_counter() const { return avatar_request_counter_; }
+
+  // Runs the message loop until the avatar counter equals |expected|.
+  void WaitForAvatarCounter(size_t expected);
+
+ private:
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request);
+  void OnIncrementAvatarCounter();
+
+  size_t avatar_request_counter_ = 0;
+
+  // A pointer to the run loop used to wait for the avatar.
+  base::RunLoop* run_loop_ = nullptr;
+};
+
+const char CredentialManagerAvatarTest::kAvatarOrigin[] = "avatarserver.com";
+const char CredentialManagerAvatarTest::kAvatarPath[] = "/image.png";
+const char CredentialManagerAvatarTest::kLoginPath[] = "/login";
+
+void CredentialManagerAvatarTest::AddPasswordForURL(const GURL& url) {
+  password_manager::PasswordForm form;
+  form.url = url;
+  form.signon_realm = form.url.GetWithEmptyPath().spec();
+  form.username_value = u"User";
+  form.password_value = u"12345";
+  form.type = password_manager::PasswordForm::Type::kApi;
+  form.skip_zero_click = true;
+  form.icon_url = https_test_server().GetURL(kAvatarOrigin, kAvatarPath);
+
+  scoped_refptr<password_manager::PasswordStoreInterface> password_store =
+      PasswordStoreFactory::GetForProfile(browser()->profile(),
+                                          ServiceAccessType::EXPLICIT_ACCESS);
+  password_store->AddLogin(form);
+}
+
+void CredentialManagerAvatarTest::WaitForAvatarCounter(size_t expected) {
+  if (avatar_request_counter_ == expected)
+    return;
+  // The logic doesn't support increments by more than one.
+  EXPECT_EQ(expected, avatar_request_counter_ + 1);
+  base::RunLoop loop;
+  run_loop_ = &loop;
+  loop.Run();
+  EXPECT_EQ(expected, avatar_request_counter_);
+}
+
+std::unique_ptr<net::test_server::HttpResponse>
+CredentialManagerAvatarTest::HandleRequest(
+    const net::test_server::HttpRequest& request) {
+  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
+  if (request.relative_url == kAvatarPath) {
+    http_response->set_code(net::HTTP_OK);
+    http_response->set_content_type("image/gif");
+    http_response->AddCustomHeader("Cache-Control", "max-age=100000");
+    content::GetUIThreadTaskRunner({})->PostTask(
+        FROM_HERE,
+        base::BindOnce(&CredentialManagerAvatarTest::OnIncrementAvatarCounter,
+                       base::Unretained(this)));
+  } else if (request.relative_url == kLoginPath) {
+    http_response->set_code(net::HTTP_OK);
+    http_response->set_content_type("text/plain");
+    http_response->set_content("Login now");
+  }
+  return http_response;
+}
+
+void CredentialManagerAvatarTest::OnIncrementAvatarCounter() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  avatar_request_counter_++;
+  if (run_loop_) {
+    run_loop_->Quit();
+    run_loop_ = nullptr;
+  }
+}
+
+// Test that the avatar is requested in the context of the main frame. Thus,
+// it should not be cached by one origin for another origin.
+IN_PROC_BROWSER_TEST_F(CredentialManagerAvatarTest,
+                       AvatarFetchIsolatedPerOrigin) {
+  const GURL a_url = https_test_server().GetURL("a.com", kLoginPath);
+  const GURL b_url = https_test_server().GetURL("b.com", kLoginPath);
+
+  AddPasswordForURL(a_url);
+  AddPasswordForURL(b_url);
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), a_url));
+  ASSERT_TRUE(content::ExecuteScript(
+      WebContents(), "navigator.credentials.get({password: true})"));
+
+  // The account chooser UI requested the avatar.
+  BubbleObserver(WebContents()).WaitForAccountChooser();
+  WaitForAvatarCounter(1u);
+
+  // Navigate to the second site, the icon is requested again.
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), b_url));
+  ASSERT_TRUE(content::ExecuteScript(
+      WebContents(), "navigator.credentials.get({password: true})"));
+  BubbleObserver(WebContents()).WaitForAccountChooser();
+  WaitForAvatarCounter(2u);
+
+  // Navigate back to the first site, the icon is already cached.
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), a_url));
+  ASSERT_TRUE(content::ExecuteScript(
+      WebContents(), "navigator.credentials.get({password: true})"));
+  BubbleObserver(WebContents()).WaitForAccountChooser();
+  EXPECT_EQ(avatar_request_counter(), 2u);
+}
+
 }  // namespace
diff --git a/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc b/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc
index 9b19518b..21452a3 100644
--- a/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc
+++ b/chrome/browser/permissions/chrome_permission_request_manager_unittest.cc
@@ -591,8 +591,8 @@
   WaitForBubbleToBeShown();
   Deny();
 
-  DictionaryPrefUpdate update(profile()->GetPrefs(),
-                              permissions::prefs::kPermissionActions);
+  DictionaryPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                        permissions::prefs::kPermissionActions);
   const auto permissions_actions =
       update->FindListPath("notifications")->GetList();
   PermissionActionsHistoryFactory::GetForProfile(profile())->ClearHistory(
diff --git a/chrome/browser/plugins/plugin_prefs.cc b/chrome/browser/plugins/plugin_prefs.cc
index dbc3f0e..ce965d7f 100644
--- a/chrome/browser/plugins/plugin_prefs.cc
+++ b/chrome/browser/plugins/plugin_prefs.cc
@@ -113,7 +113,7 @@
   }
 
   {  // Scoped update of prefs::kPluginsPluginsList.
-    ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
+    ListPrefUpdateDeprecated update(prefs_, prefs::kPluginsPluginsList);
     base::ListValue* saved_plugins_list = update.Get();
     if (saved_plugins_list) {
       for (auto& plugin_value : saved_plugins_list->GetList()) {
diff --git a/chrome/browser/policy/test/developer_tools_policy_browsertest.cc b/chrome/browser/policy/test/developer_tools_policy_browsertest.cc
index 13ab57e..332470b 100644
--- a/chrome/browser/policy/test/developer_tools_policy_browsertest.cc
+++ b/chrome/browser/policy/test/developer_tools_policy_browsertest.cc
@@ -117,6 +117,21 @@
   EXPECT_FALSE(DevToolsWindow::GetInstanceForInspectedWebContents(contents));
 }
 
+IN_PROC_BROWSER_TEST_F(PolicyTest,
+                       ViewSourceDisabledByDeveloperToolsAvailability) {
+  // Verifies that entry points to ViewSource can be disabled by setting the
+  // DeveloperToolsAvailability policy.
+
+  // Disable devtools via policy.
+  PolicyMap policies;
+  policies.Set(key::kDeveloperToolsAvailability, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+               base::Value(2 /* DeveloperToolsDisallowed */), nullptr);
+  UpdateProviderPolicy(policies);
+  // Verify that it's not possible to ViewSource.
+  EXPECT_FALSE(chrome::ExecuteCommand(browser(), IDC_VIEW_SOURCE));
+}
+
 IN_PROC_BROWSER_TEST_F(PolicyTest, DeveloperToolsDisabledExtensionsDevMode) {
   // Verifies that when DeveloperToolsDisabled policy is set, the "dev mode"
   // in chrome://extensions is actively turned off and the checkbox
diff --git a/chrome/browser/predictors/autocomplete_action_predictor.cc b/chrome/browser/predictors/autocomplete_action_predictor.cc
index 510e6e27..c00a51e 100644
--- a/chrome/browser/predictors/autocomplete_action_predictor.cc
+++ b/chrome/browser/predictors/autocomplete_action_predictor.cc
@@ -185,9 +185,6 @@
     no_state_prefetch_handle_->OnCancel();
     no_state_prefetch_handle_.reset();
   }
-
-  // TODO(https://crbug.com/1166085): Find a proper way to reset
-  // prerender_handle_ here.
 }
 
 void AutocompleteActionPredictor::StartPrerendering(
@@ -333,9 +330,6 @@
         NoStatePrefetchStatus::kNotStarted);
   }
 
-  // TODO(https://crbug.com/1166085): Find a proper way to reset
-  // prerender_handle_ here.
-
   UpdateDatabaseFromTransitionalMatches(opened_url);
 }
 
@@ -692,6 +686,10 @@
     TryDeleteOldEntries(history_service);
 }
 
+void AutocompleteActionPredictor::OnFinishedNavigation() {
+  prerender_handle_.reset();
+}
+
 AutocompleteActionPredictor::TransitionalMatch::TransitionalMatch() {
 }
 
diff --git a/chrome/browser/predictors/autocomplete_action_predictor.h b/chrome/browser/predictors/autocomplete_action_predictor.h
index b96f670..ef6751e6 100644
--- a/chrome/browser/predictors/autocomplete_action_predictor.h
+++ b/chrome/browser/predictors/autocomplete_action_predictor.h
@@ -119,6 +119,9 @@
   // Should be called when a URL is opened from the omnibox.
   void OnOmniboxOpenedUrl(const OmniboxLog& log);
 
+  // Should be called when a navigation finished.
+  void OnFinishedNavigation();
+
  private:
   friend class AutocompleteActionPredictorTest;
   friend class ::PredictorsHandler;
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 652ad81..0586c61 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
@@ -559,6 +559,13 @@
     public static final String MULTI_INSTANCE_CLOSE_WINDOW_SKIP_CONFIRM =
             "Chrome.MultiWindow.CloseWindowSkipConfirm";
 
+    public static final String MULTI_INSTANCE_START_TIME = "Chrome.MultiInstance.StartTime";
+
+    // Start timestamp of 1-day period for measuring the max count of instances used simultaneously.
+    public static final String MULTI_INSTANCE_MAX_COUNT_TIME = "Chrome.MultiInstance.MaxCountTime";
+    // Max count of Chrome instances used in a day.
+    public static final String MULTI_INSTANCE_MAX_INSTANCE_COUNT =
+            "Chrome.MultiInstance.MaxInstanceCount";
     // Information on each instance.
     public static final KeyPrefix MULTI_INSTANCE_INCOGNITO_TAB_COUNT =
             new KeyPrefix("Chrome.MultiInstance.IncognitoTabCount.*");
@@ -1075,7 +1082,10 @@
                 MULTI_INSTANCE_CLOSE_WINDOW_SKIP_CONFIRM,
                 MULTI_INSTANCE_IS_INCOGNITO_SELECTED.pattern(),
                 MULTI_INSTANCE_INCOGNITO_TAB_COUNT.pattern(),
+                MULTI_INSTANCE_MAX_COUNT_TIME,
+                MULTI_INSTANCE_MAX_INSTANCE_COUNT,
                 MULTI_INSTANCE_LAST_ACCESSED_TIME.pattern(),
+                MULTI_INSTANCE_START_TIME,
                 MULTI_INSTANCE_TAB_COUNT.pattern(),
                 MULTI_INSTANCE_TASK_MAP.pattern(),
                 MULTI_INSTANCE_TITLE.pattern(),
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index e64a2fb5..4625d01a 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -69,7 +69,6 @@
 #include "chrome/browser/sharing/sharing_sync_preference.h"
 #include "chrome/browser/sharing_hub/sharing_hub_features.h"
 #include "chrome/browser/ssl/ssl_config_service_manager.h"
-#include "chrome/browser/subresource_redirect/https_image_compression_infobar_decider.h"
 #include "chrome/browser/task_manager/task_manager_interface.h"
 #include "chrome/browser/tracing/chrome_tracing_delegate.h"
 #include "chrome/browser/ui/browser_ui_prefs.h"
@@ -686,6 +685,10 @@
 const char kStabilitySessionEndCompleted[] =
     "user_experience_metrics.stability.session_end_completed";
 
+// Deprecated 01/2022.
+constexpr char kHasSeenLiteModeInfoBar[] =
+    "litemode.https-image-compression.user-has-seen-infobar";
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // Deprecated 12/2021.
 const char kEduCoexistenceSecondaryAccountsInvalidationVersion[] =
@@ -934,6 +937,8 @@
   registry->RegisterBooleanPref(kSearchGeolocationPostDisclosureMetricsRecorded,
                                 false);
 #endif
+
+  registry->RegisterBooleanPref(kHasSeenLiteModeInfoBar, false);
 }
 
 }  // namespace
@@ -1196,7 +1201,6 @@
   federated_learning::FlocId::RegisterPrefs(registry);
   history_clusters::prefs::RegisterProfilePrefs(registry);
   HostContentSettingsMap::RegisterProfilePrefs(registry);
-  HttpsImageCompressionInfoBarDecider::RegisterProfilePrefs(registry);
   image_fetcher::ImageCache::RegisterProfilePrefs(registry);
   site_engagement::ImportantSitesUtil::RegisterProfilePrefs(registry);
   IncognitoModePrefs::RegisterProfilePrefs(registry);
@@ -1819,6 +1823,9 @@
   profile_prefs->ClearPref(kSearchGeolocationPostDisclosureMetricsRecorded);
 #endif  // defined(OS_ANDROID)
 
+  // Added 01/2022.
+  profile_prefs->ClearPref(kHasSeenLiteModeInfoBar);
+
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_PROFILE_PREFS
 }
diff --git a/chrome/browser/prefs/session_startup_pref.cc b/chrome/browser/prefs/session_startup_pref.cc
index 03cc7dc..466c3dc 100644
--- a/chrome/browser/prefs/session_startup_pref.cc
+++ b/chrome/browser/prefs/session_startup_pref.cc
@@ -87,7 +87,7 @@
     // Always save the URLs, that way the UI can remain consistent even if the
     // user changes the startup type pref.
     // Ownership of the ListValue retains with the pref service.
-    ListPrefUpdate update(prefs, prefs::kURLsToRestoreOnStartup);
+    ListPrefUpdateDeprecated update(prefs, prefs::kURLsToRestoreOnStartup);
     base::ListValue* url_pref_list = update.Get();
     DCHECK(url_pref_list);
     url_pref_list->ClearList();
diff --git a/chrome/browser/prefs/tracked/pref_hash_browsertest.cc b/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
index bf2594c..83199f03 100644
--- a/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
+++ b/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
@@ -740,8 +740,8 @@
     profile()->GetPrefs()->SetInteger(prefs::kRestoreOnStartup,
                                       SessionStartupPref::URLS);
 
-    ListPrefUpdate update(profile()->GetPrefs(),
-                          prefs::kURLsToRestoreOnStartup);
+    ListPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                    prefs::kURLsToRestoreOnStartup);
     update->Append("http://example.com");
   }
 
diff --git a/chrome/browser/prerender/omnibox_prerender_browsertest.cc b/chrome/browser/prerender/omnibox_prerender_browsertest.cc
index 0278754..98171a3 100644
--- a/chrome/browser/prerender/omnibox_prerender_browsertest.cc
+++ b/chrome/browser/prerender/omnibox_prerender_browsertest.cc
@@ -88,6 +88,38 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
+// This test class uses the kPrerender2 default value, which is true for Android
+// and false for others. In contrast, OmniboxPrerenderBrowserTest enables
+// kPrerender2 by PrerenderTestHelper.
+class OmniboxPrerenderDefaultPrerender2BrowserTest
+    : public PlatformBrowserTest {
+ public:
+  OmniboxPrerenderDefaultPrerender2BrowserTest() {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kOmniboxTriggerForPrerender2);
+  }
+
+  void SetUp() override { PlatformBrowserTest::SetUp(); }
+
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    embedded_test_server()->ServeFilesFromDirectory(
+        base::PathService::CheckedGet(chrome::DIR_TEST_DATA));
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+
+  void TearDownOnMainThread() override {
+    ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
+  }
+
+  content::WebContents* GetActiveWebContents() {
+    return chrome_test_utils::GetActiveWebContents(this);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
 // Tests that Prerender2 cannot be triggered when preload setting is disabled.
 IN_PROC_BROWSER_TEST_F(OmniboxPrerenderBrowserTest, DisableNetworkPrediction) {
   // Disable network prediction.
@@ -124,4 +156,86 @@
   EXPECT_NE(host_id, content::RenderFrameHost::kNoFrameTreeNodeId);
 }
 
+// Verifies that prerendering functions in document are properly exposed.
+// TODO(crbug.com/1286374): Flakes on Windows, Mac and Linux.
+#if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MAC)
+#define MAYBE_PrerenderFunctionsProperlyExportedWhenInitiatedByOmnibox \
+  DISABLED_PrerenderFunctionsProperlyExportedWhenInitiatedByOmnibox
+#else
+#define MAYBE_PrerenderFunctionsProperlyExportedWhenInitiatedByOmnibox \
+  PrerenderFunctionsProperlyExportedWhenInitiatedByOmnibox
+#endif
+IN_PROC_BROWSER_TEST_F(
+    OmniboxPrerenderBrowserTest,
+    MAYBE_PrerenderFunctionsProperlyExportedWhenInitiatedByOmnibox) {
+  const GURL kInitialUrl = embedded_test_server()->GetURL("/empty.html");
+  ASSERT_TRUE(GetActiveWebContents());
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), kInitialUrl));
+  EXPECT_EQ(true,
+            EvalJs(GetActiveWebContents(), "document.prerendering === false"));
+  EXPECT_EQ(
+      0,
+      EvalJs(GetActiveWebContents(),
+             "performance.getEntriesByType('navigation')[0].activationStart"));
+
+  const GURL kPrerenderingUrl =
+      embedded_test_server()->GetURL("/prerender/onprerendering_check.html");
+
+  GetAutocompleteActionPredictor()->StartPrerendering(
+      kPrerenderingUrl, *GetActiveWebContents(), gfx::Size(50, 50));
+
+  int host_id = prerender_helper().GetHostForUrl(kPrerenderingUrl);
+  content::RenderFrameHost* prerender_frame_host =
+      prerender_helper().GetPrerenderedMainFrameHost(host_id);
+  EXPECT_EQ(true,
+            EvalJs(prerender_frame_host, "document.prerendering === true"));
+
+  // Simulate a browser-initiated navigation.
+  content::test::PrerenderHostObserver prerender_observer(
+      *GetActiveWebContents(), kPrerenderingUrl);
+
+  GetActiveWebContents()->OpenURL(content::OpenURLParams(
+      kPrerenderingUrl, content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
+      ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
+                                ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
+      /*is_renderer_initiated=*/false));
+  prerender_observer.WaitForActivation();
+
+  EXPECT_EQ(true,
+            EvalJs(prerender_frame_host, "onprerenderingchange_observed"));
+  EXPECT_LT(
+      0.0,
+      EvalJs(prerender_frame_host,
+             "performance.getEntriesByType('navigation')[0].activationStart"));
+}
+
+// Verifies that the exportation of prerendering functions in the document is
+// handled properly when Prerender2 is set to be the default value. For android,
+// on which Prerender2 is enabled,  those functions are expected to be exported,
+// while the functions are not supposed to be exported on other platforms.
+IN_PROC_BROWSER_TEST_F(OmniboxPrerenderDefaultPrerender2BrowserTest,
+                       PrerenderFunctionsCheckWithDefaultFlag) {
+  const GURL kInitialUrl = embedded_test_server()->GetURL("/empty.html");
+  ASSERT_TRUE(GetActiveWebContents());
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), kInitialUrl));
+
+#if defined(OS_ANDROID)
+  EXPECT_EQ(true,
+            EvalJs(GetActiveWebContents(), "document.prerendering === false"));
+  EXPECT_EQ(0, EvalJs(GetActiveWebContents(),
+                      "performance.getEntriesByType('navigation')[0]."
+                      "activationStart"));
+  EXPECT_EQ(true, EvalJs(GetActiveWebContents(),
+                         "'onprerenderingchange' in document"));
+#else
+  EXPECT_EQ(true, EvalJs(GetActiveWebContents(),
+                         "document.prerendering === undefined"));
+  EXPECT_EQ(true, EvalJs(GetActiveWebContents(),
+                         "performance.getEntriesByType('navigation')[0]."
+                         "activationStart === undefined"));
+  EXPECT_EQ(true, EvalJs(GetActiveWebContents(),
+                         "document.onprerenderingchange === undefined"));
+#endif
+}
+
 }  // namespace
diff --git a/chrome/browser/prerender/prerender_omnibox_ui_browsertest.cc b/chrome/browser/prerender/prerender_omnibox_ui_browsertest.cc
index 424294bd..e3a283a 100644
--- a/chrome/browser/prerender/prerender_omnibox_ui_browsertest.cc
+++ b/chrome/browser/prerender/prerender_omnibox_ui_browsertest.cc
@@ -229,7 +229,7 @@
       kPrerenderingUrl, *GetActiveWebContents(), gfx::Size(50, 50));
 
   histogram_tester.ExpectUniqueSample(
-      "Prerender.Experimental.PrerenderHostFinalStatus",
+      "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_DirectURLInput",
       /*PrerenderHost::FinalStatus::kTriggerDestroyed*/ 1, 0);
 
   StartOmniboxNavigationAndWaitForActivation(kPrerenderingUrl);
@@ -265,4 +265,30 @@
   EXPECT_EQ(GetActiveWebContents()->GetLastCommittedURL(), kPrerenderingUrl);
 }
 
+// Verifies that same url can be prerendered after activation.
+IN_PROC_BROWSER_TEST_F(PrerenderOmniboxUIBrowserTest,
+                       SameUrlPrerenderingCanBeUsedAgainAfterActivation) {
+  base::HistogramTester histogram_tester;
+  const GURL kInitialUrl = embedded_test_server()->GetURL("/empty.html");
+  ASSERT_TRUE(GetActiveWebContents());
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), kInitialUrl));
+  const GURL kPrerenderingUrl =
+      embedded_test_server()->GetURL("/empty.html?prerendering");
+
+  GetAutocompleteActionPredictor()->StartPrerendering(
+      kPrerenderingUrl, *GetActiveWebContents(), gfx::Size(50, 50));
+  StartOmniboxNavigationAndWaitForActivation(kPrerenderingUrl);
+
+  // Test whether same prerendering url can be started successfully again and be
+  // activated.
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), kInitialUrl));
+  GetAutocompleteActionPredictor()->StartPrerendering(
+      kPrerenderingUrl, *GetActiveWebContents(), gfx::Size(50, 50));
+  StartOmniboxNavigationAndWaitForActivation(kPrerenderingUrl);
+
+  histogram_tester.ExpectUniqueSample(
+      "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_DirectURLInput",
+      /*PrerenderHost::FinalStatus::kActivated*/ 0, 2);
+}
+
 }  // namespace
diff --git a/chrome/browser/privacy_review/android/java/res/drawable/privacy_review_illustration.xml b/chrome/browser/privacy_review/android/java/res/drawable/privacy_review_illustration.xml
index a294367f..539d505 100644
--- a/chrome/browser/privacy_review/android/java/res/drawable/privacy_review_illustration.xml
+++ b/chrome/browser/privacy_review/android/java/res/drawable/privacy_review_illustration.xml
@@ -7,7 +7,6 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:aapt="http://schemas.android.com/aapt"
     xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     tools:ignore="VectorRaster"
     android:width="200dp"
     android:height="120dp"
diff --git a/chrome/browser/profile_resetter/profile_resetter.cc b/chrome/browser/profile_resetter/profile_resetter.cc
index 72e47cfe..003297e 100644
--- a/chrome/browser/profile_resetter/profile_resetter.cc
+++ b/chrome/browser/profile_resetter/profile_resetter.cc
@@ -182,7 +182,7 @@
     if (search_engines) {
       // This Chrome distribution channel provides a custom search engine. We
       // must reset to it.
-      ListPrefUpdate update(prefs, prefs::kSearchProviderOverrides);
+      ListPrefUpdateDeprecated update(prefs, prefs::kSearchProviderOverrides);
       update->Swap(search_engines.get());
     }
 
@@ -301,7 +301,8 @@
   std::unique_ptr<base::ListValue> url_list(
       master_settings_->GetUrlsToRestoreOnStartup());
   if (url_list)
-    ListPrefUpdate(prefs, prefs::kURLsToRestoreOnStartup)->Swap(url_list.get());
+    ListPrefUpdateDeprecated(prefs, prefs::kURLsToRestoreOnStartup)
+        ->Swap(url_list.get());
 
   int restore_on_startup;
   if (master_settings_->GetRestoreOnStartup(&restore_on_startup))
diff --git a/chrome/browser/profiles/profile_attributes_entry.cc b/chrome/browser/profiles/profile_attributes_entry.cc
index 58ad8e7..33796b78 100644
--- a/chrome/browser/profiles/profile_attributes_entry.cc
+++ b/chrome/browser/profiles/profile_attributes_entry.cc
@@ -778,7 +778,7 @@
 
   {
     // Bundle the changes in a single update.
-    DictionaryPrefUpdate update(prefs_, prefs::kProfileAttributes);
+    DictionaryPrefUpdateDeprecated update(prefs_, prefs::kProfileAttributes);
     base::DictionaryValue* attributes_dict = update.Get();
     base::Value* entry = attributes_dict->FindDictKey(storage_key_);
     if (!entry) {
@@ -990,7 +990,7 @@
   if (old_value && *old_value == value)
     return false;
 
-  DictionaryPrefUpdate update(prefs_, prefs::kProfileAttributes);
+  DictionaryPrefUpdateDeprecated update(prefs_, prefs::kProfileAttributes);
   base::DictionaryValue* attributes_dict = update.Get();
   base::Value* entry = attributes_dict->FindDictKey(storage_key_);
   if (!entry) {
@@ -1006,7 +1006,7 @@
   if (!old_value)
     return false;
 
-  DictionaryPrefUpdate update(prefs_, prefs::kProfileAttributes);
+  DictionaryPrefUpdateDeprecated update(prefs_, prefs::kProfileAttributes);
   base::DictionaryValue* attributes_dict = update.Get();
   base::Value* entry = attributes_dict->FindDictKey(storage_key_);
   DCHECK(entry);
diff --git a/chrome/browser/profiles/profile_attributes_storage.cc b/chrome/browser/profiles/profile_attributes_storage.cc
index ba175ad9..880924b 100644
--- a/chrome/browser/profiles/profile_attributes_storage.cc
+++ b/chrome/browser/profiles/profile_attributes_storage.cc
@@ -267,7 +267,7 @@
            base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
       user_data_dir_(user_data_dir) {
   // Populate the attributes storage.
-  DictionaryPrefUpdate update(prefs_, prefs::kProfileAttributes);
+  DictionaryPrefUpdateDeprecated update(prefs_, prefs::kProfileAttributes);
   base::DictionaryValue* attributes = update.Get();
   for (auto kv : attributes->DictItems()) {
     base::Value& info = kv.second;
@@ -343,7 +343,7 @@
 
 void ProfileAttributesStorage::AddProfile(ProfileAttributesInitParams params) {
   std::string key = StorageKeyFromProfilePath(params.profile_path);
-  DictionaryPrefUpdate update(prefs_, prefs::kProfileAttributes);
+  DictionaryPrefUpdateDeprecated update(prefs_, prefs::kProfileAttributes);
   base::DictionaryValue* attributes = update.Get();
 
   base::Value info(base::Value::Type::DICTIONARY);
@@ -426,7 +426,7 @@
   for (auto& observer : observer_list_)
     observer.OnProfileWillBeRemoved(profile_path);
 
-  DictionaryPrefUpdate update(prefs_, prefs::kProfileAttributes);
+  DictionaryPrefUpdateDeprecated update(prefs_, prefs::kProfileAttributes);
   base::DictionaryValue* attributes = update.Get();
   std::string key = StorageKeyFromProfilePath(profile_path);
   attributes->RemoveKey(key);
diff --git a/chrome/browser/profiles/profile_avatar_downloader.cc b/chrome/browser/profiles/profile_avatar_downloader.cc
index 4438845..7e8f220 100644
--- a/chrome/browser/profiles/profile_avatar_downloader.cc
+++ b/chrome/browser/profiles/profile_avatar_downloader.cc
@@ -54,8 +54,7 @@
   fetcher_ = std::make_unique<BitmapFetcher>(url, this, traffic_annotation);
 }
 
-ProfileAvatarDownloader::~ProfileAvatarDownloader() {
-}
+ProfileAvatarDownloader::~ProfileAvatarDownloader() = default;
 
 void ProfileAvatarDownloader::Start() {
   SystemNetworkContextManager* system_network_context_manager =
@@ -67,7 +66,6 @@
       system_network_context_manager->GetURLLoaderFactory();
   if (loader_factory) {
     fetcher_->Init(
-        std::string(),
         net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
         network::mojom::CredentialsMode::kInclude);
     fetcher_->Start(loader_factory);
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index e7d59e6..4339f23 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -283,8 +283,8 @@
   ProfilesToDelete()[path] = ProfileDeletionStage::MARKED;
   // Remember that this profile was deleted and files should have been deleted
   // on shutdown. In case of a crash remaining files are removed on next start.
-  ListPrefUpdate deleted_profiles(g_browser_process->local_state(),
-                                  prefs::kProfilesDeleted);
+  ListPrefUpdateDeprecated deleted_profiles(g_browser_process->local_state(),
+                                            prefs::kProfilesDeleted);
   deleted_profiles->Append(base::FilePathToValue(path));
 }
 
@@ -359,8 +359,8 @@
 
 // Called after a deleted profile was checked and cleaned up.
 void ProfileCleanedUp(base::Value profile_path_value) {
-  ListPrefUpdate deleted_profiles(g_browser_process->local_state(),
-                                  prefs::kProfilesDeleted);
+  ListPrefUpdateDeprecated deleted_profiles(g_browser_process->local_state(),
+                                            prefs::kProfilesDeleted);
   deleted_profiles->EraseListValue(profile_path_value);
 }
 
@@ -427,7 +427,7 @@
 void RemoveFromLastActiveProfilesPrefList(base::FilePath path) {
   PrefService* local_state = g_browser_process->local_state();
   DCHECK(local_state);
-  ListPrefUpdate update(local_state, prefs::kProfilesLastActive);
+  ListPrefUpdateDeprecated update(local_state, prefs::kProfilesLastActive);
   base::ListValue* profile_list = update.Get();
   base::Value entry_value = base::Value(path.BaseName().AsUTF8Unsafe());
   profile_list->EraseListValue(entry_value);
@@ -2253,7 +2253,7 @@
 void ProfileManager::SaveActiveProfiles() {
   PrefService* local_state = g_browser_process->local_state();
   DCHECK(local_state);
-  ListPrefUpdate update(local_state, prefs::kProfilesLastActive);
+  ListPrefUpdateDeprecated update(local_state, prefs::kProfilesLastActive);
   base::ListValue* profile_list = update.Get();
 
   profile_list->ClearList();
diff --git a/chrome/browser/profiles/profile_manager_unittest.cc b/chrome/browser/profiles/profile_manager_unittest.cc
index 7f79fd5..453eabfd 100644
--- a/chrome/browser/profiles/profile_manager_unittest.cc
+++ b/chrome/browser/profiles/profile_manager_unittest.cc
@@ -1544,7 +1544,7 @@
   local_state->SetString(prefs::kProfileLastUsed, profile_name1);
 
   // Set the last used profiles.
-  ListPrefUpdate update(local_state, prefs::kProfilesLastActive);
+  ListPrefUpdateDeprecated update(local_state, prefs::kProfilesLastActive);
   base::ListValue* initial_last_active_profile_list = update.Get();
   initial_last_active_profile_list->Append(
       std::make_unique<base::Value>(path1.BaseName().MaybeAsASCII()));
@@ -1615,7 +1615,7 @@
   local_state->SetString(prefs::kProfileLastUsed, guest_profile_name);
 
   // Set the last used profiles.
-  ListPrefUpdate update(local_state, prefs::kProfilesLastActive);
+  ListPrefUpdateDeprecated update(local_state, prefs::kProfilesLastActive);
   base::ListValue* initial_last_active_profile_list = update.Get();
   initial_last_active_profile_list->Append(
       std::make_unique<base::Value>(guest_path.BaseName().MaybeAsASCII()));
diff --git a/chrome/browser/push_messaging/push_messaging_app_identifier.cc b/chrome/browser/push_messaging/push_messaging_app_identifier.cc
index a09e96f..e50f606 100644
--- a/chrome/browser/push_messaging/push_messaging_app_identifier.cc
+++ b/chrome/browser/push_messaging/push_messaging_app_identifier.cc
@@ -221,8 +221,8 @@
 
 // static
 void PushMessagingAppIdentifier::DeleteAllFromPrefs(Profile* profile) {
-  DictionaryPrefUpdate update(profile->GetPrefs(),
-                              prefs::kPushMessagingAppIdentifierMap);
+  DictionaryPrefUpdateDeprecated update(profile->GetPrefs(),
+                                        prefs::kPushMessagingAppIdentifierMap);
   base::Value* map = update.Get();
   map->DictClear();
 }
@@ -259,8 +259,8 @@
 void PushMessagingAppIdentifier::PersistToPrefs(Profile* profile) const {
   DCheckValid();
 
-  DictionaryPrefUpdate update(profile->GetPrefs(),
-                              prefs::kPushMessagingAppIdentifierMap);
+  DictionaryPrefUpdateDeprecated update(profile->GetPrefs(),
+                                        prefs::kPushMessagingAppIdentifierMap);
   base::Value* map = update.Get();
 
   // Delete any stale entry with the same origin and Service Worker
@@ -278,8 +278,8 @@
 void PushMessagingAppIdentifier::DeleteFromPrefs(Profile* profile) const {
   DCheckValid();
 
-  DictionaryPrefUpdate update(profile->GetPrefs(),
-                              prefs::kPushMessagingAppIdentifierMap);
+  DictionaryPrefUpdateDeprecated update(profile->GetPrefs(),
+                                        prefs::kPushMessagingAppIdentifierMap);
   base::Value* map = update.Get();
   map->RemoveKey(app_id_);
 }
diff --git a/chrome/browser/push_messaging/push_messaging_app_identifier_unittest.cc b/chrome/browser/push_messaging/push_messaging_app_identifier_unittest.cc
index 5b7c649..d59b117 100644
--- a/chrome/browser/push_messaging/push_messaging_app_identifier_unittest.cc
+++ b/chrome/browser/push_messaging/push_messaging_app_identifier_unittest.cc
@@ -142,7 +142,8 @@
 
   // Create a legacy preferences entry (the test happens to use PersistToPrefs
   // since that currently works, but it's ok to change the behavior of
-  // PersistToPrefs; if so, this test can just do a raw DictionaryPrefUpdate).
+  // PersistToPrefs; if so, this test can just do a raw
+  // DictionaryPrefUpdateDeprecated).
   original_.app_id_ = legacy_app_id;
   original_.PersistToPrefs(profile());
 
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 2dba2d3..05dae823 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -2934,6 +2934,10 @@
           source_web_contents_)) {
     return false;
   }
+  // Disallow ViewSource if DevTools are disabled.
+  if (!IsDevCommandEnabled(IDC_CONTENT_CONTEXT_INSPECTELEMENT)) {
+    return false;
+  }
   return (params_.media_type != ContextMenuDataMediaType::kPlugin) &&
          embedder_web_contents_->GetController().CanViewSource();
 }
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn
index 2e507c3..598aeac 100644
--- a/chrome/browser/resources/BUILD.gn
+++ b/chrome/browser/resources/BUILD.gn
@@ -162,9 +162,6 @@
     if (enable_nacl) {
       deps += [ "about_nacl:closure_compile" ]
     }
-    if (enable_pdf) {
-      deps += [ "pdf:closure_compile" ]
-    }
     if (is_android) {
       deps += [
         "explore_sites_internals:closure_compile",
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js
index 81ec2d0..5de65f5 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation.js
@@ -255,6 +255,7 @@
     }
 
     const macro = await this.speechParser_.parse(transcript);
+    MetricsUtils.recordMacroRecognized(macro);
     // Check if the macro can execute.
     // TODO(crbug.com/1264544): Deal with ambiguous results here.
     const checkContextResult = macro.checkContext();
@@ -399,7 +400,7 @@
     }
 
     this.interimText_ = '';
-    this.inputController_.showBubble('');
+    this.inputController_.showBubble();
     if (this.clearUITextTimeoutId_) {
       clearTimeout(this.clearUITextTimeoutId_);
       this.clearUITextTimeoutId_ = null;
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
index 867c700d..163d6cc8b 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/input_controller.js
@@ -134,7 +134,7 @@
 
   /**
    * Shows the bubble UI with the given text.
-   * @param {string} text
+   * @param {string=} text
    */
   showBubble(text) {
     chrome.accessibilityPrivate.updateDictationBubble(/*visible=*/ true, text);
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/metrics_utils.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/metrics_utils.js
index 26509d6..0c5123b 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/metrics_utils.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/metrics_utils.js
@@ -23,6 +23,12 @@
   }
 
   /** @param {!Macro} macro */
+  static recordMacroRecognized(macro) {
+    chrome.metricsPrivate.recordSparseValue(
+        MetricsUtils.MACRO_RECOGNIZED_METRIC, macro.getMacroName());
+  }
+
+  /** @param {!Macro} macro */
   static recordMacroSucceeded(macro) {
     chrome.metricsPrivate.recordSparseValue(
         MetricsUtils.MACRO_SUCCEEDED_METRIC, macro.getMacroName());
@@ -96,6 +102,13 @@
     'Accessibility.CrosDictation.ListeningDuration.NetworkRecognition';
 
 /**
+ * The metric used to record that a macro was recognized.
+ * @const {string}
+ */
+MetricsUtils.MACRO_RECOGNIZED_METRIC =
+    'Accessibility.CrosDictation.MacroRecognized';
+
+/**
  * The metric used to record that a macro succeeded.
  * @const {string}
  */
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/speech_parser.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/speech_parser.js
index 986f9de..d25ac78 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/speech_parser.js
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/speech_parser.js
@@ -52,7 +52,7 @@
    * Parses user text to produce a macro command. Async to allow pumpkin to
    * complete loading if needed.
    * @param {string} text The text to parse.
-   * @return {Promise<Macro>}
+   * @return {!Promise<!Macro>}
    */
   async parse(text) {
     // Try pumpkin parsing first.
@@ -65,11 +65,13 @@
 
     // Fall-back to simple parsing.
     if (this.simpleParseStrategy_) {
-      return await this.simpleParseStrategy_.parse(text);
+      return await /** @type {!Promise<!Macro>} */ (
+          this.simpleParseStrategy_.parse(text));
     }
 
     // Input text as-is as a catch-all.
-    return await this.inputTextStrategy_.parse(text);
+    return await /** @type {!Promise<!Macro>} */ (
+        this.inputTextStrategy_.parse(text));
   }
 }
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
index 47487e1d..587019f 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
@@ -442,7 +442,9 @@
         DesktopAutomationHandler.instance.textEditHandler.injectInferredIntents(
             [{
               command: chrome.automation.IntentCommandType.MOVE_SELECTION,
-              textBoundary: chrome.automation.IntentTextBoundaryType.WORD_END
+              textBoundary: command === 'nativeNextWord' ?
+                  chrome.automation.IntentTextBoundaryType.WORD_END :
+                  chrome.automation.IntentTextBoundaryType.WORD_START
             }]);
       }
       return true;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing_test.js
index d4e25f3..ef52b5e 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing_test.js
@@ -2117,3 +2117,35 @@
         .replay();
   });
 });
+
+TEST_F('ChromeVoxEditingTest', 'NativeCharWordCommands', function() {
+  const mockFeedback = this.createMockFeedback();
+  const site = `
+    <p>start</p>
+    <div role="textbox" contenteditable>This is a test</div>
+  `;
+  this.runWithLoadedTree(site, async function(root) {
+    await this.focusFirstTextField(root);
+
+    const textField = root.find({role: RoleType.TEXT_FIELD});
+    mockFeedback.expectSpeech('Text area')
+        .call(this.press(KeyCode.HOME, {ctrl: true}))
+        .call(this.press(KeyCode.RIGHT))
+        .expectSpeech('h')
+        .call(this.press(KeyCode.RIGHT))
+        .expectSpeech('i')
+        .call(this.press(KeyCode.LEFT))
+        .expectSpeech('h')
+
+        .call(this.press(KeyCode.RIGHT, {ctrl: true}))
+        .expectSpeech('This')
+        .call(this.press(KeyCode.RIGHT, {ctrl: true}))
+        .expectSpeech('is')
+        .call(this.press(KeyCode.LEFT, {ctrl: true}))
+        .expectSpeech('is')
+        .call(this.press(KeyCode.LEFT, {ctrl: true}))
+        .expectSpeech('This')
+
+        .replay();
+  });
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_role_info.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_role_info.js
index da98109..c2a09079 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_role_info.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output/output_role_info.js
@@ -215,7 +215,7 @@
   superscript: {msgId: 'role_superscript', inherits: 'abstractSpan'},
   tab: {msgId: 'role_tab', inherits: 'abstractContainer'},
   tabList: {msgId: 'role_tablist', inherits: 'abstractFormFieldContainer'},
-  tabPanel: {msgId: 'role_tabpanel'},
+  tabPanel: {msgId: 'role_tabpanel', inherits: 'abstractContainer'},
   searchBox: {msgId: 'role_search', earconId: 'EDITABLE_TEXT'},
   textField: {msgId: 'input_type_text', earconId: 'EDITABLE_TEXT'},
   textFieldWithComboBox: {msgId: 'role_combobox', earconId: 'EDITABLE_TEXT'},
diff --git a/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js b/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
index be93b0d..bc10535 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/automation_predicate.js
@@ -442,7 +442,7 @@
     return AutomationPredicate.match({
       anyRole: [
         Role.GENERIC_CONTAINER, Role.DOCUMENT, Role.GROUP, Role.LIST,
-        Role.LIST_ITEM, Role.TAB, Role.TOOLBAR, Role.WINDOW
+        Role.LIST_ITEM, Role.TAB, Role.TAB_PANEL, Role.TOOLBAR, Role.WINDOW
       ],
       anyPredicate: [
         AutomationPredicate.landmark, AutomationPredicate.structuralContainer,
diff --git a/chrome/browser/resources/chromeos/accessibility/strings/accessibility_strings.grd b/chrome/browser/resources/chromeos/accessibility/strings/accessibility_strings.grd
index c500bf655..317a4ab2 100644
--- a/chrome/browser/resources/chromeos/accessibility/strings/accessibility_strings.grd
+++ b/chrome/browser/resources/chromeos/accessibility/strings/accessibility_strings.grd
@@ -3,6 +3,7 @@
 <grit base_dir="." current_release="1" latest_public_release="0"
       output_all_resource_defines="false" enc_check="möl" source_lang_id="en">
   <outputs>
+    <output filename="_locales/af/messages.json.gz" type="chrome_messages_json_gzip" lang="af"/>
     <output filename="_locales/am/messages.json.gz" type="chrome_messages_json_gzip" lang="am"/>
     <output filename="_locales/ar/messages.json.gz" type="chrome_messages_json_gzip" lang="ar"/>
     <output filename="_locales/bg/messages.json.gz" type="chrome_messages_json_gzip" lang="bg"/>
@@ -57,6 +58,7 @@
     <output filename="_locales/vi/messages.json.gz" type="chrome_messages_json_gzip" lang="vi"/>
     <output filename="_locales/zh_CN/messages.json.gz" type="chrome_messages_json_gzip" lang="zh-CN"/>
     <output filename="_locales/zh_TW/messages.json.gz" type="chrome_messages_json_gzip" lang="zh-TW"/>
+    <output filename="_locales/zu/messages.json.gz" type="chrome_messages_json_gzip" lang="zu"/>
   </outputs>
   <translations>
     <file path="accessibility_strings_af.xtb" lang="af" />
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/metrics.js b/chrome/browser/resources/chromeos/accessibility/switch_access/metrics.js
index c993af1..4546f93 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/metrics.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/metrics.js
@@ -12,7 +12,7 @@
   recordMenuAction: (menuAction) => {
     const metricName = 'Accessibility.CrosSwitchAccess.MenuAction.' +
         SwitchAccessMetrics.toUpperCamelCase(menuAction);
-    chrome.metricsPrivate.recordBoolean(metricName, true);
+    chrome.metricsPrivate.recordUserAction(metricName);
   },
 
   /**
diff --git a/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog_container.html b/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog_container.html
index 21be0d4b..c417849 100644
--- a/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog_container.html
+++ b/chrome/browser/resources/chromeos/bluetooth_pairing_dialog/bluetooth_pairing_dialog_container.html
@@ -6,6 +6,25 @@
     <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
     <link rel="stylesheet" href="chrome://resources/chromeos/colors/cros_styles.css">
   </head>
+  <style>
+     /* The :not(body) selector allows the variable definitions below to 'win'
+       against the shared ones. */
+       html:not(body) {
+      /* Override default setting from settings_vars.html. Setting these on a
+         specific element will still override this. */
+      --iron-icon-fill-color: var(--cros-icon-color-primary);
+      --cr-focus-outline-color: var(--cros-focus-ring-color);
+      --cr-focused-item-color: var(--cros-highlight-color-focus);
+      --cr-link-row-start-icon-color: var(--cros-icon-color-primary);
+      --cr-primary-text-color: var(--cros-text-color-primary);
+      --cr-secondary-text-color: var(--cros-text-color-secondary);
+      --cr-tooltip-icon-fill-color: var(--cros-icon-color-primary);
+      background-color: var(--cros-bg-color);
+      /* Remove 300ms delay for 'click' event, when using touch interface. */
+      touch-action: manipulation;
+    }
+
+  </style>
   <body>
     <bluetooth-pairing-dialog></bluetooth-pairing-dialog>
     <script type="module" src="bluetooth_pairing_dialog.js"></script>
diff --git a/chrome/browser/resources/chromeos/login/components/dialogs/oobe_content_dialog.html b/chrome/browser/resources/chromeos/login/components/dialogs/oobe_content_dialog.html
index 7e48a4c6..cb48a559 100644
--- a/chrome/browser/resources/chromeos/login/components/dialogs/oobe_content_dialog.html
+++ b/chrome/browser/resources/chromeos/login/components/dialogs/oobe_content_dialog.html
@@ -105,6 +105,15 @@
         display: none;
       }
 
+      :host([fullscreen]) #scrollContainer {
+        padding-inline-end: 0;
+        padding-inline-start: 0;
+      }
+
+      :host([fullscreen]) #contentContainer {
+        width: 100%;
+      }
+
       .bottom-buttons-container {
         padding-bottom: var(--oobe-adaptive-dialog-buttons-vertical-padding);
         padding-inline-end:
diff --git a/chrome/browser/resources/chromeos/login/components/gaia_dialog.html b/chrome/browser/resources/chromeos/login/components/gaia_dialog.html
index 1d9c8098..d03e441 100644
--- a/chrome/browser/resources/chromeos/login/components/gaia_dialog.html
+++ b/chrome/browser/resources/chromeos/login/components/gaia_dialog.html
@@ -66,14 +66,16 @@
         };
       }
 
-      #saml-close-button {
+      #saml-close-button,
+      #saml-back-button {
         --cr-icon-button-margin-end: 0;
         --cr-icon-button-margin-start: 0;
       }
 
-      #saml-notice-container {
+      #saml-notice-container,
+      #saml-footer-container {
         align-items: center;
-        background: rgb(241, 243, 244); /* #F1F3F4 */
+        background: var(--google-grey-100);
         box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.17);
         display: flex;
         height: 44px;
@@ -86,7 +88,6 @@
       }
 
       #saml-notice-message {
-        color: rgb(106, 106, 106);
         font-size: 13px;
       }
 
@@ -124,13 +125,16 @@
     <link rel="stylesheet" href="oobe_popup_overlay.css">
     <!-- As this dialog have pre-loading logic that require access to elements,
          dialog is marked as no-lazy. -->
-    <oobe-content-dialog role="dialog" id="gaiaDialog"
-        no-buttons$="[[isSamlSsoVisible]]" no-footer-padding no-lazy>
+    <oobe-content-dialog role="dialog" id="gaiaDialog" no-lazy
+        no-buttons$="[[isSamlSsoVisible]]" fullscreen$="[[isSamlSsoVisible]]">
       <div slot="content" id="signin-frame-container"
           hideshadows$="[[isPopUpOverlayVisible_]]"
           class="flex layout vertical">
         <div id="saml-notice-container" class="layout horizontal center"
             hidden$="[[!isSamlSsoVisible]]">
+          <cr-icon-button id="saml-back-button" iron-icon="cr:arrow-back"
+              on-click="close_" hidden="[[isSamlBackButtonHidden_]]">
+          </cr-icon-button>
           <div class="flex layout horizontal center-justified">
             <span id="saml-notice-recording-indicator"
                 hidden$="[[!videoEnabled]]">
@@ -139,9 +143,9 @@
             <span id="saml-notice-message">
               [[getSamlNoticeMessage_(locale, videoEnabled, authDomain_)]]
             </span>
-            </div>
+          </div>
           <cr-icon-button id="saml-close-button" iron-icon="cr:close"
-              on-click="close_">
+              on-click="close_" hidden="[[flagRedirectToDefaultIdPEnabled_]]">
           </cr-icon-button>
         </div>
         <h3 id="sshWarning" hidden>
@@ -149,6 +153,16 @@
         </h3>
         <webview id="signin-frame" class="flex" name="[[webviewName]]">
         </webview>
+        <div hidden="[[!flagRedirectToDefaultIdPEnabled_]]">
+          <div id="saml-footer-container" hidden="[[!isDefaultSsoProvider]]"
+              class="layout horizontal end-justified">
+            <div>[[i18nDynamic(locale, 'samlChangeProviderMessage')]]</div>
+            <oobe-text-button id="change-account"
+                text-key="samlChangeProviderButton"
+                on-click="onChangeSigninProviderClicked_">
+            </oobe-text-button>
+          </div>
+        </div>
       </div>
       <div slot="back-navigation" hidden$="[[navigationHidden]]">
         <oobe-back-button id="signin-back-button"
diff --git a/chrome/browser/resources/chromeos/login/components/gaia_dialog.js b/chrome/browser/resources/chromeos/login/components/gaia_dialog.js
index 6dadb85e..3583e7c 100644
--- a/chrome/browser/resources/chromeos/login/components/gaia_dialog.js
+++ b/chrome/browser/resources/chromeos/login/components/gaia_dialog.js
@@ -50,6 +50,14 @@
       },
 
       /**
+       * Whether the dialog can be closed.
+       */
+      isClosable: {
+        type: Boolean,
+        value: false,
+      },
+
+      /**
        * Whether SAML IdP page is shown
        */
       isSamlSsoVisible: {
@@ -58,6 +66,14 @@
       },
 
       /**
+       * Whether default SAML IdP is shown.
+       */
+      isDefaultSsoProvider: {
+        type: Boolean,
+        value: false,
+      },
+
+      /**
        * Whether to hide back button if form can't go back.
        */
       hideBackButtonIfCantGoBack: {
@@ -153,6 +169,22 @@
         type: Boolean,
         computed: 'showOverlay_(navigationEnabled, isSamlSsoVisible)'
       },
+
+      /**
+       * Whether the redirect to default IdP without interstitial step is
+       * enabled.
+       * @private {boolean}
+       */
+      flagRedirectToDefaultIdPEnabled_: {
+        type: Boolean,
+        value: loadTimeData.getBoolean('isRedirectToDefaultIdPEnabled'),
+      },
+
+      isSamlBackButtonHidden_: {
+        type: Boolean,
+        computed: 'isSamlBackButtonHidden(isDefaultSsoProvider, isClosable,' +
+            'flagRedirectToDefaultIdPEnabled_)',
+      }
     };
   }
 
@@ -346,6 +378,12 @@
   }
 
   /* @private */
+  onChangeSigninProviderClicked_() {
+    this.dispatchEvent(new CustomEvent(
+        'changesigninprovider', {bubbles: true, composed: true}));
+  }
+
+  /* @private */
   onBackButtonClicked_() {
     if (this.canGoBack) {
       this.getFrame().back();
@@ -393,6 +431,20 @@
   }
 
   /**
+   * Whether the back button on SAML screen is hidden.
+   * @param {boolean} isDefaultSsoProvider - whether it is default SAML page.
+   * @param {boolean} isClosable - whether the form can be closed.
+   * @param {boolean} flagRedirectToDefaultIdPEnabled - whether redirect to
+   *     default IdP is enabled.
+   * @private
+   */
+  isSamlBackButtonHidden(
+      isDefaultSsoProvider, isClosable, flagRedirectToDefaultIdPEnabled) {
+    return !flagRedirectToDefaultIdPEnabled ||
+        isDefaultSsoProvider && !isClosable;
+  }
+
+  /**
    * Whether popup overlay should be open.
    * @param {boolean} navigationEnabled
    * @param {boolean} isSamlSsoVisible
@@ -403,4 +455,4 @@
   }
 }
 
-customElements.define(GaiaDialog.is, GaiaDialog);
\ No newline at end of file
+customElements.define(GaiaDialog.is, GaiaDialog);
diff --git a/chrome/browser/resources/chromeos/login/screens/common/gaia_signin.html b/chrome/browser/resources/chromeos/login/screens/common/gaia_signin.html
index 5fee73d..b58fac22 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/gaia_signin.html
+++ b/chrome/browser/resources/chromeos/login/screens/common/gaia_signin.html
@@ -55,6 +55,8 @@
         transparent$="[[isLoadingUiShown_]]" class="flex"
         for-step="online-gaia, gaia-loading"
         is-saml-sso-visible="[[isSamlSsoVisible_]]"
+        is-default-sso-provider="[[isDefaultSsoProvider_]]"
+        is-closable="[[isClosable_]]"
         video-enabled="{{videoEnabled_}}"
         auth-flow="{{authFlow}}"
         navigation-enabled="{{navigationEnabled_}}"
@@ -70,38 +72,42 @@
         on-showview="onShowView_"
         on-startenrollment="onStartEnrollment_"
         on-exit="onExitMessage_"
-        on-removeuserbyemail="onRemoveUserByEmailMessage_">
+        on-removeuserbyemail="onRemoveUserByEmailMessage_"
+        on-changesigninprovider="onSamlPageChangeAccount_">
     </gaia-dialog>
     <security-token-pin id="pinDialog" parameters="[[pinDialogParameters_]]"
         for-step="pin" on-cancel="onPinDialogCanceled_"
         on-completed="onPinDialogCompleted_">
     </security-token-pin>
-    <oobe-adaptive-dialog id="saml-interstitial" role="dialog" has-buttons
-        for-step="saml-interstitial">
-      <iron-icon slot="icon" icon="oobe-32:googleg"></iron-icon>
-      <h1 slot="title">
-        [[i18nDynamic(locale, 'loginWelcomeMessage')]]
-      </h1>
-      <div slot="subtitle" hidden="[[isEmpty_(samlInterstitialDomainManager_)]]">
-        <iron-icon class="icon20" icon="cr20:domain"></iron-icon>
-        [[i18nDynamic(locale, 'samlInterstitialMessage',
-                      samlInterstitialDomainManager_)]]
-        <p>
-          <a class="oobe-local-link" on-click="onSamlPageChangeAccount_"
-              id="interstitial-change-account" is="action-link">
-            [[i18nDynamic(locale, 'samlInterstitialChangeAccountLink')]]
-          </a>
-        </p>
-      </div>
-      <div slot="back-navigation">
-        <oobe-back-button id="interstitial-back"
-            on-click="onInterstitialBackButtonClicked_"></oobe-back-button>
-      </div>
-      <div slot="bottom-buttons">
-        <oobe-next-button on-click="onSamlInterstitialNext_"
-            id="interstitial-next" class="focus-on-show"></oobe-next-button>
-      </div>
-    </oobe-adaptive-dialog>
+    <div hidden="[[flagRedirectToDefaultIdPEnabled_]]">
+      <oobe-adaptive-dialog id="saml-interstitial" role="dialog" has-buttons
+          for-step="saml-interstitial">
+        <iron-icon slot="icon" icon="oobe-32:googleg"></iron-icon>
+        <h1 slot="title">
+          [[i18nDynamic(locale, 'loginWelcomeMessage')]]
+        </h1>
+        <div slot="subtitle"
+            hidden="[[isEmpty_(samlInterstitialDomainManager_)]]">
+          <iron-icon class="icon20" icon="cr20:domain"></iron-icon>
+          [[i18nDynamic(locale, 'samlInterstitialMessage',
+                        samlInterstitialDomainManager_)]]
+          <p>
+            <a class="oobe-local-link" on-click="onSamlPageChangeAccount_"
+                id="interstitial-change-account" is="action-link">
+              [[i18nDynamic(locale, 'samlInterstitialChangeAccountLink')]]
+            </a>
+          </p>
+        </div>
+        <div slot="back-navigation">
+          <oobe-back-button id="interstitial-back" hidden="[[!isClosable_]]"
+              on-click="onInterstitialBackButtonClicked_"></oobe-back-button>
+        </div>
+        <div slot="bottom-buttons">
+          <oobe-next-button on-click="onSamlInterstitialNext_"
+              id="interstitial-next" class="focus-on-show"></oobe-next-button>
+        </div>
+      </oobe-adaptive-dialog>
+    </div>
     <div id="gaia-loading" class="layout center center-justified vertical fit"
         for-step="loading, gaia-loading">
       <throbber-notice text-key="gaiaLoading"></throbber-notice>
diff --git a/chrome/browser/resources/chromeos/login/screens/common/gaia_signin.js b/chrome/browser/resources/chromeos/login/screens/common/gaia_signin.js
index 6c9471d..e354a08 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/gaia_signin.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/gaia_signin.js
@@ -225,6 +225,42 @@
         type: Boolean,
         value: false,
       },
+
+      /**
+       * Whether the default SAML 3rd-party page is configured for the device.
+       * @private
+       */
+      isDefaultSsoProviderConfigured_: {
+        type: Boolean,
+        value: false,
+      },
+
+      /**
+       * Whether the default SAML 3rd-party page is visible.
+       * @private
+       */
+      isDefaultSsoProvider_: {
+        type: Boolean,
+        value: false,
+      },
+
+      /**
+       * Whether the screen can be hidden.
+       */
+      isClosable_: {
+        type: Boolean,
+        value: false,
+      },
+
+      /**
+       * Whether the redirect to default IdP without interstitial step is
+       * enabled.
+       * @private {boolean}
+       */
+      flagRedirectToDefaultIdPEnabled_: {
+        type: Boolean,
+        value: loadTimeData.getBoolean('isRedirectToDefaultIdPEnabled'),
+      },
     };
   }
 
@@ -355,26 +391,14 @@
   }
 
   /**
-   * Whether the dialog could be closed.
-   * This is being checked in cancel() when user clicks on signin-back-button
-   * (normal gaia flow) or the "Back" button in other authentication frames.
-   * @return {boolean}
-   * @private
-   */
-  isClosable_() {
-    return Oobe.getInstance().hasUserPods;
-  }
-
-  /**
    * Updates whether the Guest and Apps button is allowed to be shown. (Note
    * that the C++ side contains additional logic that decides whether the
    * Guest button should be shown.)
    * @private
    */
   isFirstSigninStep(uiStep, canGaiaGoBack, isSaml) {
-    return !this.isClosable_() &&
-        POSSIBLE_FIRST_SIGNIN_STEPS.includes(uiStep) && !canGaiaGoBack &&
-        !isSaml;
+    return !this.isClosable_ && POSSIBLE_FIRST_SIGNIN_STEPS.includes(uiStep) &&
+        !canGaiaGoBack && !(isSaml && !this.isDefaultSsoProvider_);
   }
 
   onIsFirstSigninStepChanged(isFirstSigninStep) {
@@ -412,6 +436,7 @@
    */
   loadAuthenticator_(doSamlRedirect) {
     this.loadingFrameContents_ = true;
+    this.isDefaultSsoProvider_ = doSamlRedirect;
     this.startLoadingTimer_();
 
     this.authenticatorParams_.doSamlRedirect = doSamlRedirect;
@@ -499,8 +524,11 @@
 
   /**
    * Event handler that is invoked just before the frame is shown.
+   * @param {Object} data Screen init payload
    */
-  onBeforeShow() {
+  onBeforeShow(data) {
+    // Show spinner early.
+    // this.loadingFrameContents_ = true;
     chrome.send('loginUIStateChanged', ['gaia-signin', true]);
 
     // Ensure that GAIA signin (or loading UI) is actually visible.
@@ -513,6 +541,9 @@
 
     this.isShown_ = true;
 
+    if (data && 'hasUserPods' in data)
+      this.isClosable_ = data.hasUserPods;
+
     cr.ui.login.invokePolymerMethod(this.$.pinDialog, 'onBeforeShow');
   }
 
@@ -526,6 +557,8 @@
 
   /** @private */
   getActiveFrame_() {
+    if (this.flagRedirectToDefaultIdPEnabled_)
+      return this.getSigninFrame_();
     switch (this.screenMode_) {
       case ScreenAuthMode.DEFAULT:
         return this.getSigninFrame_();
@@ -569,7 +602,11 @@
 
     this.authenticator_.setWebviewPartition(data.webviewPartitionName);
 
-    this.screenMode_ = data.screenMode;
+    // `screenMode_` is not used when `flagRedirectToDefaultIdPEnabled_` is true
+    // and will be removed after RedirectToDefaultIdP will be enabled by
+    // default.
+    if (!this.flagRedirectToDefaultIdPEnabled_)
+      this.screenMode_ = data.screenMode;
     this.authCompleted_ = false;
     this.navigationButtonsHidden_ = false;
 
@@ -588,9 +625,9 @@
       }
     });
 
-
-    params.doSamlRedirect =
-        (this.screenMode_ == ScreenAuthMode.SAML_INTERSTITIAL);
+    this.isDefaultSsoProviderConfigured_ =
+        data.screenMode == ScreenAuthMode.SAML_INTERSTITIAL;
+    params.doSamlRedirect = data.screenMode == ScreenAuthMode.SAML_INTERSTITIAL;
     params.menuEnterpriseEnrollment =
         !(data.enterpriseManagedDevice || data.hasDeviceOwner);
     params.isFirstUser = !(data.enterpriseManagedDevice || data.hasDeviceOwner);
@@ -599,14 +636,18 @@
 
     this.authenticatorParams_ = params;
 
-    switch (this.screenMode_) {
-      case ScreenAuthMode.DEFAULT:
-        this.loadAuthenticator_(false /* doSamlRedirect */);
-        break;
-      case ScreenAuthMode.SAML_INTERSTITIAL:
-        this.samlInterstitialDomainManager_ = data.enterpriseDomainManager;
-        this.loadingFrameContents_ = false;
-        break;
+    if (this.flagRedirectToDefaultIdPEnabled_) {
+      this.loadAuthenticator_(params.doSamlRedirect);
+    } else {
+      switch (this.screenMode_) {
+        case ScreenAuthMode.DEFAULT:
+          this.loadAuthenticator_(false /* doSamlRedirect */);
+          break;
+        case ScreenAuthMode.SAML_INTERSTITIAL:
+          this.samlInterstitialDomainManager_ = data.enterpriseDomainManager;
+          this.loadingFrameContents_ = false;
+          break;
+      }
     }
     chrome.send('authExtensionLoaded');
   }
@@ -960,6 +1001,11 @@
 
   /**
    * Called when user canceled signin.
+   *
+   * If `flagRedirectToDefaultIdPEnabled_`:
+   * If it is default SAML page we try to close the page. If SAML page was
+   * derived from GAIA we return to configured default page (GAIA or default
+   * SAML page).
    */
   cancel(isBackClicked = false) {
     this.clearVideoTimer_();
@@ -970,6 +1016,15 @@
       return;
     }
 
+    if (this.flagRedirectToDefaultIdPEnabled_) {
+      // If user goes back from the derived SAML page or GAIA page that is shown
+      // to change SSO provider we need to reload default authenticator.
+      if ((this.isSamlSsoVisible_ || this.isDefaultSsoProviderConfigured_) &&
+          !this.isDefaultSsoProvider_) {
+        this.userActed('reloadDefault');
+        return;
+      }
+    }
     this.userActed(isBackClicked ? 'back' : 'cancel');
   }
 
@@ -1173,6 +1228,9 @@
 
   /**
    * Invoked when "Change account" link is pressed on SAML Interstitial screen.
+   *
+   * If flagRedirectToDefaultIdPEnabled_:
+   * Invoked when "Enter Google Account info" button is pressed on SAML screen.
    * @param {!CustomEvent} e
    * @private
    */
@@ -1211,4 +1269,4 @@
   }
 }
 
-customElements.define(GaiaSigninElement.is, GaiaSigninElement);
\ No newline at end of file
+customElements.define(GaiaSigninElement.is, GaiaSigninElement);
diff --git a/chrome/browser/resources/new_tab_page/BUILD.gn b/chrome/browser/resources/new_tab_page/BUILD.gn
index 65c273c6..823c5a1 100644
--- a/chrome/browser/resources/new_tab_page/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/BUILD.gn
@@ -19,6 +19,15 @@
                     "js_module_root=" + rebase_path(
                             "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/new_tab_page/foo",
                             root_build_dir),
+                    "js_module_root=" + rebase_path(
+                            "$root_gen_dir/mojom-webui/chrome/browser/cart",
+                            root_build_dir),
+                    "js_module_root=" + rebase_path(
+                            "$root_gen_dir/mojom-webui/chrome/browser/new_tab_page/modules/photos",
+                            root_build_dir),
+                    "js_module_root=" + rebase_path(
+                            "$root_gen_dir/mojom-webui/chrome/browser/new_tab_page/modules/drive",
+                            root_build_dir),
                   ]
 
   deps = [
@@ -327,21 +336,20 @@
 generate_grd("build_drive_mojo_grdp") {
   grd_prefix = grd_prefix
   out_grd = "$target_gen_dir/drive_mojo_resources.grdp"
-  input_files = [ "drive.mojom-lite.js" ]
+  input_files = [ "drive.mojom-webui.js" ]
   input_files_base_dir =
-      rebase_path("$root_gen_dir/chrome/browser/new_tab_page/modules/drive",
-                  root_build_dir)
-  resource_path_prefix = "modules/drive"
+      rebase_path(
+          "$root_gen_dir/mojom-webui/chrome/browser/new_tab_page/modules/drive",
+          root_build_dir)
 }
 
 generate_grd("build_photos_mojo_grdp") {
   grd_prefix = grd_prefix
   out_grd = "$target_gen_dir/photos_mojo_resources.grdp"
-  input_files = [ "photos.mojom-lite.js" ]
-  input_files_base_dir =
-      rebase_path("$root_gen_dir/chrome/browser/new_tab_page/modules/photos",
-                  root_build_dir)
-  resource_path_prefix = "modules/photos"
+  input_files = [ "photos.mojom-webui.js" ]
+  input_files_base_dir = rebase_path(
+          "$root_gen_dir/mojom-webui/chrome/browser/new_tab_page/modules/photos",
+          root_build_dir)
 }
 
 generate_grd("build_task_module_mojo_grdp") {
@@ -358,10 +366,10 @@
 generate_grd("build_chrome_cart_mojo_grdp") {
   grd_prefix = grd_prefix
   out_grd = "$target_gen_dir/chrome_cart_mojo_resources.grdp"
-  input_files = [ "chrome_cart.mojom-lite.js" ]
+  input_files = [ "chrome_cart.mojom-webui.js" ]
   input_files_base_dir =
-      rebase_path("$root_gen_dir/chrome/browser/cart", root_build_dir)
-  resource_path_prefix = "modules/cart"
+      rebase_path("$root_gen_dir/mojom-webui/chrome/browser/cart",
+                  root_build_dir)
 }
 
 generate_grd("build_grd") {
@@ -512,11 +520,11 @@
       "chrome://resources/js/browser_command/browser_command.mojom-webui.js",
       "new_tab_page.mojom-lite.js",
       "realbox/realbox.mojom-lite.js",
-      "modules/drive/drive.mojom-lite.js",
-      "modules/photos/photos.mojom-lite.js",
+      "drive.mojom-webui.js",
+      "photos.mojom-webui.js",
       "modules/task_module/task_module.mojom-lite.js",
       "foo.mojom-webui.js",
-      "modules/cart/chrome_cart.mojom-lite.js",
+      "chrome_cart.mojom-webui.js",
     ]
   }
 }
diff --git a/chrome/browser/resources/new_tab_page/modules/cart/BUILD.gn b/chrome/browser/resources/new_tab_page/modules/cart/BUILD.gn
index 98607ce..d85dfe99 100644
--- a/chrome/browser/resources/new_tab_page/modules/cart/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/modules/cart/BUILD.gn
@@ -10,6 +10,7 @@
     ":chrome_cart_proxy",
     "..:module_descriptor",
     "../..:i18n_setup",
+    "//chrome/browser/cart:mojo_bindings_webui_js",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu",
     "//ui/webui/resources/cr_elements/cr_auto_img",
@@ -19,7 +20,7 @@
 }
 
 js_library("chrome_cart_proxy") {
-  deps = [ "//chrome/browser/cart:mojo_bindings_js_library_for_compile" ]
+  deps = [ "//chrome/browser/cart:mojo_bindings_webui_js" ]
 }
 
 html_to_js("web_components") {
diff --git a/chrome/browser/resources/new_tab_page/modules/cart/chrome_cart_proxy.js b/chrome/browser/resources/new_tab_page/modules/cart/chrome_cart_proxy.js
index 940ecaf..066cbbc 100644
--- a/chrome/browser/resources/new_tab_page/modules/cart/chrome_cart_proxy.js
+++ b/chrome/browser/resources/new_tab_page/modules/cart/chrome_cart_proxy.js
@@ -2,10 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// TODO(crbug.com/1179821): Migrate to JS module Mojo bindings.
-import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
-import 'chrome://resources/mojo/url/mojom/url.mojom-lite.js';
-import './chrome_cart.mojom-lite.js';
+import {CartHandler, CartHandlerRemote} from '../../chrome_cart.mojom-webui.js';
 
 /**
  * @fileoverview This file provides a class that exposes the Mojo handler
@@ -13,16 +10,16 @@
  * browser and receiving the browser response.
  */
 
-/** @type {?chromeCart.mojom.CartHandlerRemote} */
+/** @type {?CartHandlerRemote} */
 let handler = null;
 
 export class ChromeCartProxy {
-  /** @return {!chromeCart.mojom.CartHandlerRemote} */
+  /** @return {!CartHandlerRemote} */
   static getHandler() {
-    return handler || (handler = chromeCart.mojom.CartHandler.getRemote());
+    return handler || (handler = CartHandler.getRemote());
   }
 
-  /** @param {!chromeCart.mojom.CartHandlerRemote} newHandler */
+  /** @param {!CartHandlerRemote} newHandler */
   static setHandler(newHandler) {
     handler = newHandler;
   }
diff --git a/chrome/browser/resources/new_tab_page/modules/cart/module.js b/chrome/browser/resources/new_tab_page/modules/cart/module.js
index 9a7e0cf..c1f7f96 100644
--- a/chrome/browser/resources/new_tab_page/modules/cart/module.js
+++ b/chrome/browser/resources/new_tab_page/modules/cart/module.js
@@ -12,6 +12,7 @@
 
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {MerchantCart} from '../../chrome_cart.mojom-webui.js';
 import {I18nBehavior, loadTimeData} from '../../i18n_setup.js';
 import {recordOccurence} from '../../metrics_utils.js';
 import {$$} from '../../utils.js';
@@ -37,7 +38,7 @@
 
   static get properties() {
     return {
-      /** @type {!Array<!chromeCart.mojom.MerchantCart>} */
+      /** @type {!Array<!MerchantCart>} */
       cartItems: Array,
 
       /** @type {string} */
diff --git a/chrome/browser/resources/new_tab_page/modules/cart_v2/BUILD.gn b/chrome/browser/resources/new_tab_page/modules/cart_v2/BUILD.gn
index 7b82595..e53c405 100644
--- a/chrome/browser/resources/new_tab_page/modules/cart_v2/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/modules/cart_v2/BUILD.gn
@@ -10,6 +10,7 @@
     "..:module_descriptor",
     "..:module_header",
     "../..:i18n_setup",
+    "//chrome/browser/cart:mojo_bindings_webui_js",
     "//chrome/browser/resources/new_tab_page/modules/cart:chrome_cart_proxy",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu",
diff --git a/chrome/browser/resources/new_tab_page/modules/cart_v2/module.js b/chrome/browser/resources/new_tab_page/modules/cart_v2/module.js
index 219a7b18..da2fd66 100644
--- a/chrome/browser/resources/new_tab_page/modules/cart_v2/module.js
+++ b/chrome/browser/resources/new_tab_page/modules/cart_v2/module.js
@@ -12,6 +12,7 @@
 
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {MerchantCart} from '../../chrome_cart.mojom-webui.js';
 import {I18nBehavior, loadTimeData} from '../../i18n_setup.js';
 import {$$} from '../../utils.js';
 import {ChromeCartProxy} from '../cart/chrome_cart_proxy.js';
@@ -35,7 +36,7 @@
 
   static get properties() {
     return {
-      /** @type {!Array<!chromeCart.mojom.MerchantCart>} */
+      /** @type {!Array<!MerchantCart>} */
       cartItems: Array,
 
       /** @type {string} */
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 4bbd971..bf3f19d 100644
--- a/chrome/browser/resources/new_tab_page/modules/drive/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/modules/drive/BUILD.gn
@@ -11,6 +11,7 @@
     "..:info_dialog",
     "..:module_descriptor",
     "../..:i18n_setup",
+    "//chrome/browser/new_tab_page/modules/drive:mojo_bindings_webui_js",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_auto_img",
     "//ui/webui/resources/cr_elements/cr_lazy_render:cr_lazy_render.m",
@@ -18,7 +19,8 @@
 }
 
 js_library("drive_module_proxy") {
-  deps = [ "//chrome/browser/new_tab_page/modules/drive:mojo_bindings_js_library_for_compile" ]
+  deps =
+      [ "//chrome/browser/new_tab_page/modules/drive:mojo_bindings_webui_js" ]
 }
 
 html_to_js("web_components") {
diff --git a/chrome/browser/resources/new_tab_page/modules/drive/drive_module_proxy.js b/chrome/browser/resources/new_tab_page/modules/drive/drive_module_proxy.js
index 1bb281c..59013c52 100644
--- a/chrome/browser/resources/new_tab_page/modules/drive/drive_module_proxy.js
+++ b/chrome/browser/resources/new_tab_page/modules/drive/drive_module_proxy.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import './drive.mojom-lite.js';
+import {DriveHandler, DriveHandlerRemote} from '../../drive.mojom-webui.js';
 
 /**
  * @fileoverview This file provides a class that exposes the Mojo handler
@@ -10,16 +10,16 @@
  * and receiving the browser response.
  */
 
-/** @type {?drive.mojom.DriveHandlerRemote} */
+/** @type {?DriveHandlerRemote} */
 let handler = null;
 
 export class DriveProxy {
-  /** @return {!drive.mojom.DriveHandlerRemote} */
+  /** @return {!DriveHandlerRemote} */
   static getHandler() {
-    return handler || (handler = drive.mojom.DriveHandler.getRemote());
+    return handler || (handler = DriveHandler.getRemote());
   }
 
-  /** @param {!drive.mojom.DriveHandlerRemote} newHandler */
+  /** @param {!DriveHandlerRemote} newHandler */
   static setHandler(newHandler) {
     handler = newHandler;
   }
diff --git a/chrome/browser/resources/new_tab_page/modules/drive/module.js b/chrome/browser/resources/new_tab_page/modules/drive/module.js
index 8be13bd..828b5f09 100644
--- a/chrome/browser/resources/new_tab_page/modules/drive/module.js
+++ b/chrome/browser/resources/new_tab_page/modules/drive/module.js
@@ -8,6 +8,7 @@
 
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {File} from '../../drive.mojom-webui.js';
 import {I18nBehavior, loadTimeData} from '../../i18n_setup.js';
 import {InfoDialogElement} from '../info_dialog.js';
 import {ModuleDescriptor} from '../module_descriptor.js';
@@ -32,7 +33,7 @@
 
   static get properties() {
     return {
-      /** @type {!Array<!drive.mojom.File>} */
+      /** @type {!Array<!File>} */
       files: Array,
     };
   }
@@ -72,7 +73,7 @@
   }
 
   /**
-   * @param {drive.mojom.File} file
+   * @param {File} file
    * @return {string}
    * @private
    */
diff --git a/chrome/browser/resources/new_tab_page/modules/drive_v2/BUILD.gn b/chrome/browser/resources/new_tab_page/modules/drive_v2/BUILD.gn
index 9bc88100..bebc8942 100644
--- a/chrome/browser/resources/new_tab_page/modules/drive_v2/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/modules/drive_v2/BUILD.gn
@@ -11,6 +11,7 @@
     "..:module_descriptor",
     "..:module_header",
     "../..:i18n_setup",
+    "//chrome/browser/new_tab_page/modules/drive:mojo_bindings_webui_js",
     "//chrome/browser/resources/new_tab_page/modules/drive:drive_module_proxy",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_lazy_render:cr_lazy_render.m",
diff --git a/chrome/browser/resources/new_tab_page/modules/drive_v2/module.js b/chrome/browser/resources/new_tab_page/modules/drive_v2/module.js
index d8fb302..3ffca0a 100644
--- a/chrome/browser/resources/new_tab_page/modules/drive_v2/module.js
+++ b/chrome/browser/resources/new_tab_page/modules/drive_v2/module.js
@@ -7,6 +7,7 @@
 
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {File} from '../../drive.mojom-webui.js';
 import {I18nBehavior, loadTimeData} from '../../i18n_setup.js';
 import {DriveProxy} from '../drive/drive_module_proxy.js';
 import {InfoDialogElement} from '../info_dialog.js';
@@ -30,13 +31,13 @@
 
   static get properties() {
     return {
-      /** @type {!Array<!drive.mojom.File>} */
+      /** @type {!Array<!File>} */
       files: Array,
     };
   }
 
   /**
-   * @param {drive.mojom.File} file
+   * @param {File} file
    * @return {string}
    * @private
    */
diff --git a/chrome/browser/resources/new_tab_page/modules/photos/BUILD.gn b/chrome/browser/resources/new_tab_page/modules/photos/BUILD.gn
index 0180665..52135d719 100644
--- a/chrome/browser/resources/new_tab_page/modules/photos/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/modules/photos/BUILD.gn
@@ -10,6 +10,7 @@
     ":photos_module_proxy",
     "..:module_descriptor",
     "../..:i18n_setup",
+    "//chrome/browser/new_tab_page/modules/photos:mojo_bindings_webui_js",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_auto_img",
     "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
@@ -19,7 +20,7 @@
 
 js_library("photos_module_proxy") {
   deps = [
-    "//chrome/browser/new_tab_page/modules/photos:mojo_bindings_js_library_for_compile",
+    "//chrome/browser/new_tab_page/modules/photos:mojo_bindings_webui_js",
     "//ui/webui/resources/js:cr.m",
   ]
 }
diff --git a/chrome/browser/resources/new_tab_page/modules/photos/module.js b/chrome/browser/resources/new_tab_page/modules/photos/module.js
index e53a092..42890a8 100644
--- a/chrome/browser/resources/new_tab_page/modules/photos/module.js
+++ b/chrome/browser/resources/new_tab_page/modules/photos/module.js
@@ -10,6 +10,7 @@
 
 import {I18nBehavior, loadTimeData} from '../../i18n_setup.js';
 import {recordOccurence} from '../../metrics_utils.js';
+import {Memory} from '../../photos.mojom-webui.js';
 import {InfoDialogElement} from '../info_dialog.js';
 import {ModuleDescriptor} from '../module_descriptor.js';
 
@@ -32,7 +33,7 @@
 
   static get properties() {
     return {
-      /** @type {Array<!photos.mojom.Memory>} */
+      /** @type {Array<!Memory>} */
       memories: Array,
 
       /** @type {boolean} */
diff --git a/chrome/browser/resources/new_tab_page/modules/photos/photos_module_proxy.js b/chrome/browser/resources/new_tab_page/modules/photos/photos_module_proxy.js
index 6ee90e4..3105369 100644
--- a/chrome/browser/resources/new_tab_page/modules/photos/photos_module_proxy.js
+++ b/chrome/browser/resources/new_tab_page/modules/photos/photos_module_proxy.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import './photos.mojom-lite.js';
+import {PhotosHandler, PhotosHandlerRemote} from '../../photos.mojom-webui.js';
 
 /**
  * @fileoverview This file provides a class that exposes the Mojo handler
@@ -10,16 +10,16 @@
  * and receiving the browser response.
  */
 
-/** @type {?photos.mojom.PhotosHandlerRemote} */
+/** @type {?PhotosHandlerRemote} */
 let handler = null;
 
 export class PhotosProxy {
-  /** @return {!photos.mojom.PhotosHandlerRemote} */
+  /** @return {!PhotosHandlerRemote} */
   static getHandler() {
-    return handler || (handler = photos.mojom.PhotosHandler.getRemote());
+    return handler || (handler = PhotosHandler.getRemote());
   }
 
-  /** @param {!photos.mojom.PhotosHandlerRemote} newHandler */
+  /** @param {!PhotosHandlerRemote} newHandler */
   static setHandler(newHandler) {
     handler = newHandler;
   }
diff --git a/chrome/browser/resources/new_tab_page/modules/task_module/module.html b/chrome/browser/resources/new_tab_page/modules/task_module/module.html
index 52ba67b..126d87d 100644
--- a/chrome/browser/resources/new_tab_page/modules/task_module/module.html
+++ b/chrome/browser/resources/new_tab_page/modules/task_module/module.html
@@ -202,7 +202,7 @@
     show-info-button on-info-button-click="onInfoButtonClick_"
     show-dismiss-button on-dismiss-button-click="onDismissButtonClick_"
     on-disable-button-click="onDisableButtonClick_">
-  [[task.title]]
+  [[title_]]
 </ntp-module-header>
 <div id="moduleContent">
   <div id="taskItems">
diff --git a/chrome/browser/resources/new_tab_page/modules/task_module/module.js b/chrome/browser/resources/new_tab_page/modules/task_module/module.js
index 038327e..cb515878 100644
--- a/chrome/browser/resources/new_tab_page/modules/task_module/module.js
+++ b/chrome/browser/resources/new_tab_page/modules/task_module/module.js
@@ -44,6 +44,12 @@
       task: Object,
 
       /** @private {string} */
+      title_: {
+        type: String,
+        computed: 'computeTitle_(taskModuleType, task)',
+      },
+
+      /** @private {string} */
       dismissName_: {
         type: String,
         computed: 'computeDismissName_(taskModuleType, task)',
@@ -67,6 +73,21 @@
    * @return {string}
    * @private
    */
+  computeTitle_() {
+    switch (this.taskModuleType) {
+      case taskModule.mojom.TaskModuleType.kRecipe:
+        return loadTimeData.getString('modulesRecipeTasksSentence');
+      case taskModule.mojom.TaskModuleType.kShopping:
+        return this.task.title;
+      default:
+        return '';
+    }
+  }
+
+  /**
+   * @return {string}
+   * @private
+   */
   computeDismissName_() {
     switch (this.taskModuleType) {
       case taskModule.mojom.TaskModuleType.kRecipe:
diff --git a/chrome/browser/resources/new_tab_page/modules/task_module/task_module_handler_proxy.js b/chrome/browser/resources/new_tab_page/modules/task_module/task_module_handler_proxy.js
index 02f8fd3..1a5ba0f 100644
--- a/chrome/browser/resources/new_tab_page/modules/task_module/task_module_handler_proxy.js
+++ b/chrome/browser/resources/new_tab_page/modules/task_module/task_module_handler_proxy.js
@@ -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/1179821): Migrate to JS module Mojo bindings.
+import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
+import 'chrome://resources/mojo/url/mojom/url.mojom-lite.js';
 import './task_module.mojom-lite.js';
 
 /**
diff --git a/chrome/browser/resources/new_tab_page/new_tab_page.js b/chrome/browser/resources/new_tab_page/new_tab_page.js
index 8c1ccbf..d138d2f6 100644
--- a/chrome/browser/resources/new_tab_page/new_tab_page.js
+++ b/chrome/browser/resources/new_tab_page/new_tab_page.js
@@ -14,9 +14,6 @@
 export {NtpElement} from './app.js';
 export {BackgroundManager} from './background_manager.js';
 export {CustomizeDialogPage} from './customize_dialog_types.js';
-// <if expr="not is_official_build">
-export {FooHandlerRemote} from './foo.mojom-webui.js';
-// </if>
 export {recordDuration, recordLoadDuration, recordOccurence, recordPerdecage} from './metrics_utils.js';
 export {ChromeCartProxy} from './modules/cart/chrome_cart_proxy.js';
 export {chromeCartDescriptor} from './modules/cart/module.js';
diff --git a/chrome/browser/resources/pdf/BUILD.gn b/chrome/browser/resources/pdf/BUILD.gn
index 656f2239..ed28e65 100644
--- a/chrome/browser/resources/pdf/BUILD.gn
+++ b/chrome/browser/resources/pdf/BUILD.gn
@@ -4,62 +4,62 @@
 
 import("//chrome/common/features.gni")
 import("//pdf/features.gni")
-import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/grit/grit_rule.gni")
 import("//tools/grit/preprocess_if_expr.gni")
 import("//tools/polymer/html_to_js.gni")
+import("//tools/typescript/ts_library.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
 import("../tools/optimize_webui.gni")
 import("./pdf.gni")
 
-preprocess_folder = "preprocessed"
-preprocess_manifest = "preprocessed_manifest.json"
-preprocess_gen_manifest = "preprocessed_gen_manifest.json"
-
 assert(enable_pdf, "enable_pdf check failed")
 
+preprocess_folder = "preprocessed"
+tsc_folder = "tsc"
+
 preprocess_if_expr("preprocess") {
   in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
-  out_manifest = "$target_gen_dir/$preprocess_manifest"
   defines = [ "enable_ink=$enable_ink" ]
-  in_files = pdf_non_webcomponents_files + shared_non_webcomponents_files
+  in_files = pdf_non_webcomponents_files + shared_non_webcomponents_files +
+             print_preview_non_webcomponents_files
 }
 
 preprocess_if_expr("preprocess_generated") {
   deps = [ ":web_components" ]
   in_folder = target_gen_dir
   out_folder = "$target_gen_dir/$preprocess_folder"
-  out_manifest = "$target_gen_dir/$preprocess_gen_manifest"
   defines = [ "enable_ink=$enable_ink" ]
-  in_files = pdf_webcomponents_files + shared_webcomponents_files
+  in_files = pdf_webcomponents_files + shared_webcomponents_files +
+             print_preview_webcomponents_files
 }
 
-# Extra files that are needed by Print Preview.
-preprocess_if_expr("preprocess_print_preview") {
+# Preprocess and build a manifest file for the Print Preview HTML/CSS files,
+# which aren't passed to ts_library().
+print_preview_html_css_manifest = "print_preview_html_css_manifest.json"
+preprocess_if_expr("preprocess_print_preview_html_css") {
   in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = print_preview_non_webcomponents_files
+  out_manifest = "$target_gen_dir/$print_preview_html_css_manifest"
+  in_files = [
+    "index_pp.html",
+    "index.css",
+  ]
 }
 
-preprocess_if_expr("preprocess_print_preview_generated") {
-  deps = [ ":web_components" ]
-  in_folder = target_gen_dir
-  out_folder = "$target_gen_dir/$preprocess_folder"
-  in_files = print_preview_webcomponents_files
-}
-
+# Build the grdp for Print Preview.
 generate_grd("build_print_preview_grdp") {
+  # Get all the shared files out of the tsc folder.
   input_files =
-      print_preview_non_webcomponents_files + shared_non_webcomponents_files +
-      print_preview_webcomponents_files + shared_webcomponents_files
+      shared_non_webcomponents_files + shared_webcomponents_files +
+      print_preview_non_webcomponents_files + print_preview_webcomponents_files
   input_files_base_dir =
-      rebase_path("$target_gen_dir/$preprocess_folder", root_build_dir)
+      rebase_path("$target_gen_dir/$tsc_folder", root_build_dir)
+
+  manifest_files = [ "$target_gen_dir/$print_preview_html_css_manifest" ]
   deps = [
-    ":preprocess",
-    ":preprocess_generated",
-    ":preprocess_print_preview",
-    ":preprocess_print_preview_generated",
+    ":build_ts",
+    ":preprocess_print_preview_html_css",
   ]
   resource_path_rewrites = [
     "index_pp.html|index.html",
@@ -75,31 +75,30 @@
   out_grd = "$target_gen_dir/${grd_prefix}_resources.grdp"
 }
 
-# Just copy to $preprocess_folder.
-copy("preprocess_internal_plugin") {
-  sources = [ "pdf_internal_plugin_wrapper.js" ]
-  outputs = [ "$target_gen_dir/$preprocess_folder/{{source_file_part}}" ]
-}
-
 if (optimize_webui) {
   build_manifest = "build_manifest.json"
 
   optimize_webui("build") {
     host = "chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai"
-    input = rebase_path("$target_gen_dir/$preprocess_folder", root_build_dir)
+    input = rebase_path("$target_gen_dir/$tsc_folder", root_build_dir)
     js_out_files = [ "pdf_viewer_wrapper.rollup.js" ]
     js_module_in_files = [ "pdf_viewer_wrapper.js" ]
     out_manifest = "$target_gen_dir/$build_manifest"
 
     deps = [
-      ":preprocess",
-      ":preprocess_generated",
+      ":build_ts",
       "../../../../ui/webui/resources:preprocess",
     ]
     excludes = [
       "browser_api.js",
       "chrome://resources/js/cr.m.js",
     ]
+    if (enable_ink) {
+      excludes += [
+        "ink/drawing_canvas_externs.js",
+        "ink/ink_api.js",
+      ]
+    }
   }
 }
 
@@ -108,14 +107,13 @@
 build_internal_plugin_manifest = "build_internal_plugin_manifest.json"
 optimize_webui("build_internal_plugin") {
   host = "about:blank"
-  input = rebase_path("$target_gen_dir/$preprocess_folder", root_build_dir)
+  input = rebase_path("$target_gen_dir/$tsc_folder", root_build_dir)
   js_out_files = [ "pdf_internal_plugin_wrapper.rollup.js" ]
   js_module_in_files = [ "pdf_internal_plugin_wrapper.js" ]
   out_manifest = "$target_gen_dir/$build_internal_plugin_manifest"
 
   deps = [
-    ":preprocess",
-    ":preprocess_internal_plugin",
+    ":build_ts",
     "../../../../ui/webui/resources:preprocess",
   ]
   excludes = [
@@ -126,33 +124,29 @@
 
 generate_grd("build_grd") {
   input_files = [
-    "browser_api.js",
     "index.css",
     "index.html",
-    "main.js",
   ]
   if (enable_ink) {
-    input_files += [
-      "ink/index.html",
-      "ink/ink_api.js",
-    ]
+    input_files += [ "ink/index.html" ]
   }
   input_files_base_dir = rebase_path(".", "//")
 
   if (optimize_webui) {
+    input_files += [
+      "browser_api.js",
+      "main.js",
+    ]
+    if (enable_ink) {
+      input_files += [ "ink/ink_api.js" ]
+    }
     deps = [ ":build" ]
     resource_path_rewrites =
         [ "pdf_viewer_wrapper.rollup.js|pdf_viewer_wrapper.js" ]
     manifest_files = [ "$target_gen_dir/$build_manifest" ]
   } else {
-    deps = [
-      ":preprocess",
-      ":preprocess_generated",
-    ]
-    manifest_files = [
-      "$target_gen_dir/$preprocess_manifest",
-      "$target_gen_dir/$preprocess_gen_manifest",
-    ]
+    deps = [ ":build_ts" ]
+    manifest_files = [ "$target_gen_dir/tsconfig.manifest" ]
   }
 
   deps += [ ":build_internal_plugin" ]
@@ -186,238 +180,26 @@
              shared_webcomponents_files
 }
 
-group("closure_compile") {
+ts_library("build_ts") {
+  root_dir = "$target_gen_dir/$preprocess_folder"
+  out_dir = "$target_gen_dir/$tsc_folder"
+  tsconfig_base = "tsconfig_base.json"
+  composite = true
+
+  in_files =
+      pdf_non_webcomponents_files + pdf_webcomponents_files +
+      shared_non_webcomponents_files + shared_webcomponents_files +
+      print_preview_non_webcomponents_files + print_preview_webcomponents_files
+  manifest_excludes =
+      [ "pdf_internal_plugin_wrapper.js" ] +
+      print_preview_non_webcomponents_files + print_preview_webcomponents_files
+  definitions = [ "//tools/typescript/definitions/metrics_private.d.ts" ]
   deps = [
-    ":closure_compile_local",
-    "elements:closure_compile",
+    "//third_party/polymer/v3_0:library",
+    "//ui/webui/resources:library",
   ]
-  if (enable_ink) {
-    deps += [ "ink:closure_compile" ]
-  }
-}
-
-js_library("annotation_tool") {
-}
-
-js_library("bookmark_type") {
-}
-
-js_library("browser_api") {
-  deps = [ "//ui/webui/resources/js:assert.m" ]
-  externs_list = [
-    "$externs_path/chrome_extensions.js",
-    "$externs_path/mime_handler_private.js",
-  ]
-}
-
-js_library("constants") {
-}
-
-js_library("gesture_detector") {
-  deps = [
-    ":constants",
-    "//ui/webui/resources/js/cr:event_target.m",
-  ]
-}
-
-js_library("open_pdf_params_parser") {
-  deps = [ ":constants" ]
-  externs_list = [ "$externs_path/pending.js" ]
-}
-
-js_library("pdf_scripting_api") {
-}
-
-js_library("viewport_scroller") {
-}
-
-js_library("viewport") {
-  deps = [
-    ":constants",
-    ":gesture_detector",
-    ":internal_plugin",
-    ":zoom_manager",
-    "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:event_tracker.m",
-    "//ui/webui/resources/js:util.m",
-  ]
-  externs_list = [ "$externs_path/pending.js" ]
-}
-
-js_library("zoom_manager") {
-  deps = [
-    ":browser_api",
-    "//ui/webui/resources/js/cr:event_target.m",
-  ]
-}
-
-js_library("metrics") {
-  deps = [ ":constants" ]
-  externs_list = [ "$externs_path/metrics_private.js" ]
-}
-
-js_library("navigator") {
-  deps = [
-    ":browser_api",
-    ":open_pdf_params_parser",
-    ":viewport",
-  ]
-}
-
-js_library("toolbar_manager") {
-  deps = [
-    "elements:viewer-zoom-toolbar",
-    "//ui/webui/resources/js:util.m",
-  ]
-}
-
-js_library("ink_controller") {
-  deps = [
-    ":annotation_tool",
-    ":controller",
-    ":viewport",
-    "//ui/webui/resources/js/cr:event_target.m",
-  ]
-}
-
-js_library("local_storage_proxy") {
-}
-
-js_library("controller") {
-  deps = [
-    ":gesture_detector",
-    ":internal_plugin",
-    ":viewport",
-    "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:load_time_data.m",
-    "//ui/webui/resources/js:promise_resolver.m",
-    "//ui/webui/resources/js/cr:event_target.m",
-  ]
-}
-
-js_library("internal_plugin") {
-}
-
-js_library("pdf_internal_plugin_wrapper") {
-  deps = [
-    ":constants",
-    ":gesture_detector",
-    ":viewport_scroller",
-  ]
-}
-
-js_library("pdf_viewer_base") {
-  deps = [
-    ":browser_api",
-    ":constants",
-    ":controller",
-    ":metrics",
-    ":pdf_scripting_api",
-    ":pdf_viewer_utils",
-    ":viewport",
-    ":viewport_scroller",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:event_tracker.m",
-    "//ui/webui/resources/js:load_time_data.m",
-    "//ui/webui/resources/js:promise_resolver.m",
-    "//ui/webui/resources/js:util.m",
-  ]
-  externs_list = [ "$externs_path/resources_private.js" ]
-}
-
-js_library("pdf_viewer") {
-  deps = [
-    ":bookmark_type",
-    ":browser_api",
-    ":constants",
-    ":controller",
-    ":ink_controller",
-    ":local_storage_proxy",
-    ":metrics",
-    ":navigator",
-    ":pdf_scripting_api",
-    ":pdf_viewer_base",
-    ":pdf_viewer_utils",
-    "elements:viewer-bookmark",
-    "elements:viewer-error-dialog",
-    "elements:viewer-password-dialog",
-    "elements:viewer-pdf-sidenav",
-    "elements:viewer-properties-dialog",
-    "elements:viewer-toolbar",
-    "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:event_tracker.m",
-    "//ui/webui/resources/js:load_time_data.m",
-    "//ui/webui/resources/js:promise_resolver.m",
-    "//ui/webui/resources/js:util.m",
-  ]
-  externs_list = [ "$externs_path/resources_private.js" ]
-}
-
-js_library("pdf_viewer_wrapper") {
-  deps = [ ":pdf_viewer" ]
-}
-
-js_library("pdf_viewer_utils") {
-  deps = [
-    ":controller",
-    ":viewport",
-    "//ui/webui/resources/js:util.m",
-  ]
-}
-
-js_library("pdf_viewer_pp") {
-  deps = [
-    ":constants",
-    ":controller",
-    ":pdf_scripting_api",
-    ":pdf_viewer_base",
-    ":pdf_viewer_utils",
-    ":toolbar_manager",
-    ":viewport",
-    "elements:viewer-error-dialog",
-    "elements:viewer-page-indicator",
-    "elements:viewer-zoom-toolbar",
-    "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:event_tracker.m",
-    "//ui/webui/resources/js:load_time_data.m",
-    "//ui/webui/resources/js:promise_resolver.m",
-    "//ui/webui/resources/js:util.m",
-  ]
-  externs_list = [ "$externs_path/resources_private.js" ]
-}
-
-js_library("main") {
-  deps = [
-    ":browser_api",
-    ":pdf_viewer",
-  ]
-}
-
-js_type_check("closure_compile_local") {
-  is_polymer3 = true
-  deps = [
-    ":annotation_tool",
-    ":browser_api",
-    ":constants",
-    ":controller",
-    ":gesture_detector",
-    ":ink_controller",
-    ":local_storage_proxy",
-    ":main",
-    ":metrics",
-    ":navigator",
-    ":open_pdf_params_parser",
-    ":pdf_internal_plugin_wrapper",
-    ":pdf_scripting_api",
-    ":pdf_viewer",
-    ":pdf_viewer_base",
-    ":pdf_viewer_pp",
-    ":pdf_viewer_utils",
-    ":pdf_viewer_wrapper",
-    ":toolbar_manager",
-    ":viewport",
-    ":viewport_scroller",
-    ":zoom_manager",
+  extra_deps = [
+    ":preprocess",
+    ":preprocess_generated",
   ]
 }
diff --git a/chrome/browser/resources/pdf/annotation_tool.js b/chrome/browser/resources/pdf/annotation_tool.js
index e8fa6e7..d6ca888 100644
--- a/chrome/browser/resources/pdf/annotation_tool.js
+++ b/chrome/browser/resources/pdf/annotation_tool.js
@@ -2,9 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// @fileoverview This file is not included in the build. Only used for type
-// checking purposes.
-
 /**
  * @typedef {{
  *   tool: string,
diff --git a/chrome/browser/resources/pdf/elements/BUILD.gn b/chrome/browser/resources/pdf/elements/BUILD.gn
deleted file mode 100644
index 66fb3d3..0000000
--- a/chrome/browser/resources/pdf/elements/BUILD.gn
+++ /dev/null
@@ -1,174 +0,0 @@
-# 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.
-
-import("//pdf/features.gni")
-import("//third_party/closure_compiler/compile_js.gni")
-
-js_type_check("closure_compile") {
-  is_polymer3 = true
-  deps = [
-    ":viewer-bookmark",
-    ":viewer-document-outline",
-    ":viewer-download-controls",
-    ":viewer-error-dialog",
-    ":viewer-page-indicator",
-    ":viewer-page-selector",
-    ":viewer-password-dialog",
-    ":viewer-pdf-sidenav",
-    ":viewer-properties-dialog",
-    ":viewer-thumbnail",
-    ":viewer-thumbnail-bar",
-    ":viewer-toolbar",
-    ":viewer-zoom-button",
-    ":viewer-zoom-toolbar",
-  ]
-  if (enable_ink) {
-    deps += [
-      ":viewer-annotations-bar",
-      ":viewer-annotations-mode-dialog",
-      ":viewer-ink-host",
-      ":viewer-pen-options",
-      ":viewer-toolbar-dropdown",
-    ]
-  }
-}
-
-js_library("viewer-bookmark") {
-  deps = [ "..:bookmark_type" ]
-}
-
-js_library("viewer-document-outline") {
-  deps = [
-    ":viewer-bookmark",
-    "..:bookmark_type",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-  ]
-}
-
-js_library("viewer-download-controls") {
-  deps = [
-    "..:constants",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu",
-  ]
-}
-
-js_library("viewer-error-dialog") {
-  deps = [ "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m" ]
-}
-
-js_library("viewer-annotations-bar") {
-  deps = [
-    ":viewer-pen-options",
-    ":viewer-toolbar-dropdown",
-    "..:annotation_tool",
-    "..:ink_controller",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:event_tracker.m",
-  ]
-}
-
-js_library("viewer-annotations-mode-dialog") {
-  deps = [
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
-  ]
-}
-
-if (enable_ink) {
-  js_library("viewer-ink-host") {
-    deps = [
-      "..:metrics",
-      "..:viewport",
-      "../ink:ink_api",
-    ]
-  }
-}
-
-js_library("viewer-page-indicator") {
-  deps = [
-    "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:util.m",
-  ]
-}
-
-js_library("viewer-page-selector") {
-}
-
-js_library("viewer-password-dialog") {
-  deps = [
-    "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
-    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
-    "//ui/webui/resources/cr_elements/cr_input:cr_input.m",
-  ]
-}
-
-js_library("viewer-pdf-sidenav") {
-  deps = [
-    ":viewer-document-outline",
-    ":viewer-thumbnail-bar",
-    "..:metrics",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button.m",
-  ]
-}
-
-js_library("viewer-pen-options") {
-}
-
-js_library("viewer-properties-dialog") {
-  deps = [
-    "..:constants",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
-  ]
-}
-
-js_library("viewer-thumbnail") {
-  deps = [
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/js:assert.m",
-  ]
-}
-
-js_library("viewer-thumbnail-bar") {
-  deps = [
-    ":viewer-thumbnail",
-    "..:controller",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:event_tracker.m",
-    "//ui/webui/resources/js:load_time_data.m",
-    "//ui/webui/resources/js/cr/ui:focus_outline_manager.m",
-  ]
-}
-
-js_library("viewer-toolbar") {
-  deps = [
-    ":viewer-annotations-bar",
-    ":viewer-annotations-mode-dialog",
-    ":viewer-download-controls",
-    ":viewer-page-selector",
-    "..:metrics",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-  ]
-}
-
-js_library("viewer-toolbar-dropdown") {
-  deps = []
-}
-
-js_library("viewer-zoom-toolbar") {
-  deps = [
-    ":viewer-zoom-button",
-    "..:constants",
-    "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:util.m",
-  ]
-}
-
-js_library("viewer-zoom-button") {
-  deps = []
-}
diff --git a/chrome/browser/resources/pdf/elements/viewer-page-indicator.js b/chrome/browser/resources/pdf/elements/viewer-page-indicator.js
index 403df0a..95ae8aa 100644
--- a/chrome/browser/resources/pdf/elements/viewer-page-indicator.js
+++ b/chrome/browser/resources/pdf/elements/viewer-page-indicator.js
@@ -6,6 +6,8 @@
 import {isRTL} from 'chrome://resources/js/util.m.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {Viewport} from '../viewport.js';
+
 export class ViewerPageIndicatorElement extends PolymerElement {
   static get is() {
     return 'viewer-page-indicator';
@@ -27,8 +29,12 @@
 
   constructor() {
     super();
+
     /** @type {number|undefined} */
     this.timerId = undefined;
+
+    /** @private @type {?Viewport} */
+    this.viewport_ = null;
   }
 
   /** @override */
@@ -40,27 +46,32 @@
     });
   }
 
+  /** @param {!Viewport} viewport */
+  setViewport(viewport) {
+    this.viewport_ = viewport;
+  }
+
   /** @private */
   fadeIn_() {
-    const percent = window.scrollY /
-        (document.scrollingElement.scrollHeight -
-         document.documentElement.clientHeight);
+    // Vertically position relative to scroll position.
+    let percent = 0;
+    if (this.viewport_) {
+      percent = this.viewport_.position.y /
+          (this.viewport_.contentSize.height - this.viewport_.size.height);
+    }
     this.style.top =
         percent * (document.documentElement.clientHeight - this.offsetHeight) +
         'px';
-    // <if expr="is_macosx">
-    // If overlay scrollbars are enabled, prevent them from overlapping the
-    // triangle. TODO(dbeam): various platforms can enable overlay scrolling,
-    // not just Mac. The scrollbars seem to have different widths/appearances on
-    // those platforms, though.
+
+    // Horizontally position to compensate for overlay scrollbars.
     assert(document.documentElement.dir);
-    const endEdge = isRTL() ? 'left' : 'right';
-    if (window.innerWidth === document.scrollingElement.scrollWidth) {
-      this.style[endEdge] = '16px';
-    } else {
-      this.style[endEdge] = '0px';
+    let overlayScrollbarWidth = 0;
+    if (this.viewport_ && this.viewport_.documentHasScrollbars().vertical) {
+      overlayScrollbarWidth = this.viewport_.overlayScrollbarWidth;
     }
-    // </if>
+    this.style[isRTL() ? 'left' : 'right'] = `${overlayScrollbarWidth}px`;
+
+    // Animate opacity.
     this.style.opacity = 1;
     clearTimeout(this.timerId);
 
diff --git a/chrome/browser/resources/pdf/elements/viewer-toolbar.js b/chrome/browser/resources/pdf/elements/viewer-toolbar.js
index 0faea81..5df07be 100644
--- a/chrome/browser/resources/pdf/elements/viewer-toolbar.js
+++ b/chrome/browser/resources/pdf/elements/viewer-toolbar.js
@@ -8,13 +8,14 @@
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import 'chrome://resources/polymer/v3_0/paper-progress/paper-progress.js';
 import './icons.js';
-// <if expr="enable_ink">
-import './viewer-annotations-bar.js';
-// </if>
 import './viewer-download-controls.js';
 import './viewer-page-selector.js';
 import './shared-css.js';
 import './shared-vars.js';
+// <if expr="enable_ink">
+import './viewer-annotations-bar.js';
+import './viewer-annotations-mode-dialog.js';
+// </if>
 
 import {AnchorAlignment, CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/browser/resources/pdf/pdf.gni b/chrome/browser/resources/pdf/pdf.gni
index 997158b..329b90d 100644
--- a/chrome/browser/resources/pdf/pdf.gni
+++ b/chrome/browser/resources/pdf/pdf.gni
@@ -11,10 +11,16 @@
   "local_storage_proxy.js",
   "navigator.js",
   "pdf_viewer_wrapper.js",
+  "pdf_internal_plugin_wrapper.js",
 ]
 
 if (enable_ink) {
-  pdf_non_webcomponents_files += [ "ink_controller.js" ]
+  pdf_non_webcomponents_files += [
+    "annotation_tool.js",
+    "ink_controller.js",
+    "ink/drawing_canvas_externs.js",
+    "ink/ink_api.js",
+  ]
 }
 
 # Files that need to be passed to html_to_js() that are used only in PDF Viewer.
@@ -44,15 +50,8 @@
 }
 
 # Files to pass directly to preprocess_if_expr() that are used only in Print
-# Preview. Note: browser_api.js, main.js and index.css are also used by the PDF
-# viewer, but the current PDF viewer build does not preprocess these files.
-print_preview_non_webcomponents_files = [
-  "browser_api.js",
-  "index.css",
-  "index_pp.html",
-  "main.js",
-  "toolbar_manager.js",
-]
+# Preview.
+print_preview_non_webcomponents_files = [ "toolbar_manager.js" ]
 
 # Files that need to be passed to html_to_js() that are used only in Print
 # Preview.
@@ -66,10 +65,12 @@
 # Files to pass directly to preprocess_if_expr() that are shared by PDF Viewer
 # and Print Preview.
 shared_non_webcomponents_files = [
+  "browser_api.js",
   "constants.js",
   "controller.js",
   "gesture_detector.js",
   "internal_plugin.js",
+  "main.js",
   "metrics.js",
   "open_pdf_params_parser.js",
   "pdf_scripting_api.js",
diff --git a/chrome/browser/resources/pdf/pdf_viewer_pp.js b/chrome/browser/resources/pdf/pdf_viewer_pp.js
index eb4edb5a..405b411 100644
--- a/chrome/browser/resources/pdf/pdf_viewer_pp.js
+++ b/chrome/browser/resources/pdf/pdf_viewer_pp.js
@@ -5,6 +5,7 @@
 import './elements/viewer-error-dialog.js';
 import './elements/viewer-page-indicator.js';
 import './elements/shared-vars.js';
+import './elements/viewer-zoom-toolbar.js';
 import './pdf_viewer_shared_style.js';
 
 import {assertNotReached} from 'chrome://resources/js/assert.m.js';
@@ -15,6 +16,7 @@
 import {BrowserApi} from './browser_api.js';
 import {FittingType} from './constants.js';
 import {MessageData, PluginController, PrintPreviewParams} from './controller.js';
+import {ViewerPageIndicatorElement} from './elements/viewer-page-indicator.js';
 import {ViewerZoomToolbarElement} from './elements/viewer-zoom-toolbar.js';
 import {DeserializeKeyEvent, LoadState, SerializeKeyEvent} from './pdf_scripting_api.js';
 import {PDFViewerBaseElement} from './pdf_viewer_base.js';
@@ -65,6 +67,15 @@
   }
 
   /**
+   * @return {!ViewerPageIndicatorElement}
+   * @private
+   */
+  getPageIndicator_() {
+    return /** @type {!ViewerPageIndicatorElement} */ (
+        this.$$('#page-indicator'));
+  }
+
+  /**
    * @return {!ViewerZoomToolbarElement}
    * @private
    */
@@ -82,6 +93,7 @@
     /** @private {?PluginController} */
     this.pluginController_ = PluginController.getInstance();
 
+    this.getPageIndicator_().setViewport(this.viewport);
     this.toolbarManager_ = new ToolbarManager(window, this.getZoomToolbar_());
   }
 
@@ -165,7 +177,7 @@
 
     // Update the page indicator.
     const visiblePage = this.viewport.getMostVisiblePage();
-    const pageIndicator = this.$$('#page-indicator');
+    const pageIndicator = this.getPageIndicator_();
     const lastIndex = pageIndicator.index;
     pageIndicator.index = visiblePage;
     if (this.documentDimensions.pageDimensions.length > 1 &&
@@ -235,7 +247,7 @@
         // Stash the scroll location so that it can be restored when the new
         // document is loaded.
         this.lastViewportPosition = this.viewport.position;
-        this.$$('#page-indicator').pageLabels = messageData.pageNumbers;
+        this.getPageIndicator_().pageLabels = messageData.pageNumbers;
 
         this.pluginController_.resetPrintPreviewMode(messageData);
         return true;
diff --git a/chrome/browser/resources/pdf/tsconfig_base.json b/chrome/browser/resources/pdf/tsconfig_base.json
new file mode 100644
index 0000000..3e71f763
--- /dev/null
+++ b/chrome/browser/resources/pdf/tsconfig_base.json
@@ -0,0 +1,9 @@
+{
+  "extends": "../../../../tools/typescript/tsconfig_base.json",
+  "compilerOptions": {
+    "allowJs": true,
+    "noUncheckedIndexedAccess": false,
+    "noUnusedLocals": false,
+    "strictPropertyInitialization": false
+  }
+}
diff --git a/chrome/browser/resources/pdf/viewport.js b/chrome/browser/resources/pdf/viewport.js
index 2a180d23..082b272 100644
--- a/chrome/browser/resources/pdf/viewport.js
+++ b/chrome/browser/resources/pdf/viewport.js
@@ -82,12 +82,9 @@
     /** @private {!HTMLElement} */
     this.window_ = container;
 
-    /** @private {number} */
-    this.scrollbarWidth_ = scrollbarWidth;
-
     /** @private {!ScrollContent} */
     this.scrollContent_ =
-        new ScrollContent(this.window_, sizer, content, this.scrollbarWidth_);
+        new ScrollContent(this.window_, sizer, content, scrollbarWidth);
 
     /** @private {number} */
     this.defaultZoom_ = defaultZoom;
@@ -504,11 +501,11 @@
   }
 
   /**
-   * Exposes the current content size for testing.
+   * Gets the content size.
    * @return {!Size}
    */
-  get contentSizeForTesting() {
-    return this.scrollContent_.sizeForTesting;
+  get contentSize() {
+    return this.scrollContent_.size;
   }
 
   /** @return {number} The current zoom. */
@@ -680,9 +677,21 @@
     });
   }
 
-  /** @return {number} The width of scrollbars in the viewport in pixels. */
+  /**
+   * Gets the width of scrollbars in the viewport in pixels.
+   * @return {number}
+   */
   get scrollbarWidth() {
-    return this.scrollbarWidth_;
+    return this.scrollContent_.scrollbarWidth;
+  }
+
+  /**
+   * Gets the width of overlay scrollbars in the viewport in pixels, or 0 if not
+   * using overlay scrollbars.
+   * @return {number}
+   */
+  get overlayScrollbarWidth() {
+    return this.scrollContent_.overlayScrollbarWidth;
   }
 
   /** @return {FittingType} The fitting type the viewport is currently in. */
@@ -868,7 +877,7 @@
     const zoomedDimensions = this.getZoomedDocumentDimensions_(zoom);
 
     // Check if adding a scrollbar will result in needing the other scrollbar.
-    const scrollbarWidth = this.scrollbarWidth_;
+    const scrollbarWidth = this.scrollContent_.scrollbarWidth;
     if (needsScrollbars.horizontal &&
         zoomedDimensions.height > this.window_.offsetHeight - scrollbarWidth) {
       needsScrollbars.vertical = true;
@@ -1751,11 +1760,33 @@
     this.dispatchScroll_();
   }
 
+  /** @return {number} */
+  get scrollbarWidth() {
+    return this.scrollbarWidth_;
+  }
+
+  /** @return {number} */
+  get overlayScrollbarWidth() {
+    let overlayScrollbarWidth = 0;
+
+    // TODO(crbug.com/1286009): Support overlay scrollbars on all platforms.
+    // <if expr="is_macosx">
+    overlayScrollbarWidth = 16;
+    // </if>
+    // <if expr="not is_macosx">
+    if (this.unseasonedPlugin_) {
+      overlayScrollbarWidth = this.scrollbarWidth_;
+    }
+    // </if>
+
+    return overlayScrollbarWidth;
+  }
+
   /**
-   * Exposes the current content size for testing.
+   * Gets the content size.
    * @return {!Size}
    */
-  get sizeForTesting() {
+  get size() {
     return {
       width: this.width_,
       height: this.height_,
diff --git a/chrome/browser/resources/print_preview/print_preview.html b/chrome/browser/resources/print_preview/print_preview.html
index 7872e53..da49aa1 100644
--- a/chrome/browser/resources/print_preview/print_preview.html
+++ b/chrome/browser/resources/print_preview/print_preview.html
@@ -30,7 +30,7 @@
     }
 
     .loading body::before {
-      background: rgb(218, 220, 224);  /* --google-grey-refresh-300 */
+      background: rgb(218, 220, 224);  /* --google-grey-300 */
       border-bottom-width: 0;
       border-color: rgb(232, 234, 237);
       border-inline-end-width: 1px;
@@ -45,7 +45,7 @@
 
     @media (prefers-color-scheme: dark) {
       .loading body::before {
-        background: rgb(95, 99, 104);  /* --google-grey-refresh-700 */
+        background: rgb(95, 99, 104);  /* --google-grey-700 */
         border-color: rgba(40, 41, 44, .9);
       }
     }
diff --git a/chrome/browser/resources/print_preview/print_preview_utils.ts b/chrome/browser/resources/print_preview/print_preview_utils.ts
index a68a8c5f..a1e3380 100644
--- a/chrome/browser/resources/print_preview/print_preview_utils.ts
+++ b/chrome/browser/resources/print_preview/print_preview_utils.ts
@@ -79,7 +79,7 @@
   const iconElement = iconset.createIcon(iconName, isRTL()) as HTMLElement;
   const dark = inDarkMode();
   const fillColor = getComputedStyle(el).getPropertyValue(
-      dark ? '--google-grey-refresh-500' : '--google-grey-600');
+      dark ? '--google-grey-500' : '--google-grey-600');
   iconElement.style.fill = fillColor;
   const serializedIcon = serializer.serializeToString(iconElement);
   const uri = encodeURIComponent(serializedIcon);
diff --git a/chrome/browser/resources/print_preview/ui/destination_dropdown_cros.html b/chrome/browser/resources/print_preview/ui/destination_dropdown_cros.html
index 958a7c9..ff32e72 100644
--- a/chrome/browser/resources/print_preview/ui/destination_dropdown_cros.html
+++ b/chrome/browser/resources/print_preview/ui/destination_dropdown_cros.html
@@ -67,7 +67,7 @@
   }
 
   .list-item.highlighted {
-    background-color: var(--google-blue-refresh-100);
+    background-color: var(--google-blue-100);
   }
 
   #destination-icon-box,
@@ -77,7 +77,7 @@
 
   #destination-display-container {
     align-items: center;
-    background-color: var(--google-grey-refresh-100);
+    background-color: var(--google-grey-100);
     border-radius: 4px;
     cursor: pointer;
     display: flex;
diff --git a/chrome/browser/resources/print_preview/ui/destination_list_item.html b/chrome/browser/resources/print_preview/ui/destination_list_item.html
index 12a14d3..705046c1 100644
--- a/chrome/browser/resources/print_preview/ui/destination_list_item.html
+++ b/chrome/browser/resources/print_preview/ui/destination_list_item.html
@@ -38,7 +38,7 @@
 
   @media (prefers-color-scheme: dark) {
     iron-icon {
-      fill: var(--google-grey-refresh-500);
+      fill: var(--google-grey-500);
     }
   }
 
diff --git a/chrome/browser/resources/print_preview/ui/margin_control.html b/chrome/browser/resources/print_preview/ui/margin_control.html
index 7350968..605f467 100644
--- a/chrome/browser/resources/print_preview/ui/margin_control.html
+++ b/chrome/browser/resources/print_preview/ui/margin_control.html
@@ -34,7 +34,7 @@
 
   @media (prefers-color-scheme: dark) {
     #line {
-      border-color: var(--google-blue-refresh-300);
+      border-color: var(--google-blue-300);
     }
   }
 
diff --git a/chrome/browser/resources/print_preview/ui/print_preview_vars_css.html b/chrome/browser/resources/print_preview/ui/print_preview_vars_css.html
index 01acb0a..f720871a 100644
--- a/chrome/browser/resources/print_preview/ui/print_preview_vars_css.html
+++ b/chrome/browser/resources/print_preview/ui/print_preview_vars_css.html
@@ -15,8 +15,8 @@
     --cr-form-field-label-height: initial;
     --cr-form-field-label-line-height: .75rem;
     --destination-item-height: 32px;
-    --preview-area-background-color: var(--google-grey-refresh-300);
-    --iron-icon-fill-color: var(--google-grey-refresh-700);
+    --preview-area-background-color: var(--google-grey-300);
+    --iron-icon-fill-color: var(--google-grey-700);
     --iron-icon-height: var(--cr-icon-size);
     --iron-icon-width: var(--cr-icon-size);
     --search-icon-size: 32px;
@@ -25,9 +25,9 @@
 
   @media (prefers-color-scheme: dark) {
     html {
-      --preview-area-background-color: var(--google-grey-refresh-700);
+      --preview-area-background-color: var(--google-grey-700);
       --print-preview-settings-border: var(--cr-separator-line);
-      --iron-icon-fill-color: var(--google-grey-refresh-500);
+      --iron-icon-fill-color: var(--google-grey-500);
     }
   }
 </style>
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.html b/chrome/browser/resources/settings/autofill_page/passwords_section.html
index 9c0e971..2d1cd6d 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.html
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.html
@@ -218,6 +218,7 @@
                 $i18n{devicePasswordsLinkLabel}
             </div>
             <cr-icon-button iron-icon="cr:arrow-right"
+                aria-labelledby="devicePasswordsLinkLabel"
                 aria-roledescription="$i18n{subpageArrowRoleDescription}">
             </cr-icon-button>
           </div>
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index dc75f495..1d95464 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -495,12 +495,12 @@
     "chromeos/os_apps_page/app_management_page/chrome_app_detail_view.js",
     "chromeos/os_apps_page/app_management_page/dom_switch.js",
     "chromeos/os_apps_page/app_management_page/main_view.js",
-    "chromeos/os_apps_page/app_management_page/more_permissions_item.js",
     "chromeos/os_apps_page/app_management_page/pin_to_shelf_item.js",
     "chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js",
     "chromeos/os_apps_page/app_management_page/pwa_detail_view.js",
     "chromeos/os_apps_page/app_management_page/resize_lock_item.js",
     "chromeos/os_apps_page/app_management_page/shared_style.js",
+    "chromeos/os_apps_page/app_management_page/shared_vars.js",
     "chromeos/os_apps_page/app_management_page/supported_links_overlapping_apps_dialog.js",
     "chromeos/os_apps_page/app_management_page/supported_links_dialog.js",
     "chromeos/os_apps_page/app_management_page/supported_links_item.js",
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.html b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.html
index 6a4113c..6fe6c940d 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.html
@@ -26,10 +26,11 @@
   .illustration {
     background-position: center center;
     background-repeat: no-repeat;
-    background-size: 215px;
-    height: 205px;
+    background-size: 183px;
+    height: 173px;
     margin-top: 15px;
-    width: 215px;
+    padding: 16px;
+    width: 183px;
   }
 
   #intro .illustration {
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn
index 3524b85..3db23a6 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn
@@ -21,12 +21,12 @@
     ":dom_switch",
     ":fake_page_handler",
     ":main_view",
-    ":more_permissions_item",
     ":pin_to_shelf_item",
     ":pwa_detail_view",
     ":reducers",
     ":resize_lock_item",
     ":shared_style",
+    ":shared_vars",
     ":store",
     ":store_client",
     ":supported_links_dialog",
@@ -95,7 +95,6 @@
 js_library("arc_detail_view") {
   deps = [
     ":fake_page_handler",
-    ":more_permissions_item",
     ":pin_to_shelf_item",
     ":store_client",
     ":supported_links_item",
@@ -114,7 +113,6 @@
   deps = [
     ":browser_proxy",
     ":fake_page_handler",
-    ":more_permissions_item",
     ":pin_to_shelf_item",
     ":store_client",
     ":util",
@@ -151,10 +149,6 @@
   ]
 }
 
-js_library("more_permissions_item") {
-  deps = [ "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button.m" ]
-}
-
 js_library("pin_to_shelf_item") {
   deps = [
     ":browser_proxy",
@@ -168,7 +162,6 @@
 js_library("pwa_detail_view") {
   deps = [
     ":fake_page_handler",
-    ":more_permissions_item",
     ":pin_to_shelf_item",
     ":store_client",
     ":supported_links_item",
@@ -195,6 +188,10 @@
   deps = []
 }
 
+js_library("shared_vars") {
+  deps = []
+}
+
 js_library("store") {
   deps = [
     "//ui/webui/resources/js:cr.m",
@@ -273,11 +270,11 @@
     "chrome_app_detail_view.js",
     "dom_switch.js",
     "main_view.js",
-    "more_permissions_item.js",
     "pin_to_shelf_item.js",
     "pwa_detail_view.js",
     "resize_lock_item.js",
     "shared_style.js",
+    "shared_vars.js",
     "supported_links_overlapping_apps_dialog.js",
     "supported_links_dialog.js",
     "supported_links_item.js",
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 ce8ef03..d813f65f 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
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 import './shared_style.js';
-import '//resources/cr_components/app_management/shared_vars.js';
+import './shared_vars.js';
 import '//resources/cr_elements/cr_icons_css.m.js';
 
 import {AppManagementEntryPoint, AppManagementEntryPointsHistogramName, AppType} from '//resources/cr_components/app_management/constants.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.html
index 38ac7e5..e915b95 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.html
@@ -66,7 +66,8 @@
       app="[[app_]]">
   </app-management-resize-lock-item>
   <app-management-more-permissions-item id="more-settings"
-      class="permission-card-row separated-row" app="[[app_]]">
+      class="permission-card-row separated-row" app="[[app_]]"
+      more-permissions-label="$i18n{appManagementMorePermissionsLabel}">
   </app-management-more-permissions-item>
   <app-management-supported-links-item
       id="supportedLinksSetting"
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.js
index 4221e23f..90952425 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.js
@@ -1,12 +1,12 @@
 // Copyright 2018 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 './more_permissions_item.js';
 import './pin_to_shelf_item.js';
 import './resize_lock_item.js';
 import './supported_links_item.js';
 import './shared_style.js';
 import '//resources/cr_components/app_management/icons.js';
+import '//resources/cr_components/app_management/more_permissions_item.js';
 import '//resources/cr_components/app_management/permission_item.js';
 import '//resources/cr_elements/icons.m.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.html
index 1f50ea8..b5216efd 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.html
@@ -46,6 +46,7 @@
 
     <app-management-more-permissions-item id="more-settings"
         class="permission-card-row separated-row" app="[[app_]]"
-        hidden$="[[app_.hideMoreSettings]]">
+        hidden$="[[app_.hideMoreSettings]]"
+        more-permissions-label="$i18n{appManagementMorePermissionsLabel}">
     </app-management-more-permissions-item>
 </div>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.js
index 8a4b91c..f84b348 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.js
@@ -1,7 +1,7 @@
 // Copyright 2018 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 './more_permissions_item.js';
+import '//resources/cr_components/app_management/more_permissions_item.js';
 import './pin_to_shelf_item.js';
 import './shared_style.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js
index 93d8d78e..1f41966 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js
@@ -5,7 +5,7 @@
 import {AppType, OptionalBool} from '//resources/cr_components/app_management/constants.js';
 import {PermissionType, PermissionValue, TriState} from '//resources/cr_components/app_management/permission_constants.js';
 import {createBoolPermission, createTriStatePermission, getTriStatePermissionValue} from '//resources/cr_components/app_management/permission_util.js';
-import {assert} from 'chrome://resources/js/assert.m.js';
+import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
 
 import {AppManagementStore} from './store.js';
@@ -290,6 +290,14 @@
 
   /**
    * @param {string} appId
+   * @param {apps.mojom.WindowMode} windowMode
+   */
+  setWindowMode(appId, windowMode) {
+    assertNotReached();
+  }
+
+  /**
+   * @param {string} appId
    * @return {!Promise<{ appIds: !Array<!string> }>}
    */
   async getOverlappingPreferredApps(appId) {
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.html
deleted file mode 100644
index 9cc72d8..0000000
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<style include="app-management-cros-shared-css">
-  :host {
-    align-items: center;
-    cursor: pointer;
-    display: flex;
-    flex: 1;
-    justify-content: space-between;
-  }
-</style>
-<div id="label" aria-hidden="true">
-  $i18n{appManagementMorePermissionsLabel}
-</div>
-<div class="permission-row-controls">
-  <cr-icon-button class="native-settings-icon icon-external" role="link"
-      tabindex="0" aria-labelledby="label">
-  </cr-icon-button>
-</div>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.js
deleted file mode 100644
index 62c533a..0000000
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.js
+++ /dev/null
@@ -1,32 +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.
-
-import './shared_style.js';
-import '//resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
-
-import {AppManagementUserAction} from '//resources/cr_components/app_management/constants.js';
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {recordAppManagementUserAction} from 'chrome://resources/cr_components/app_management/util.js';
-
-import {BrowserProxy} from './browser_proxy.js';
-
-Polymer({
-  _template: html`{__html_template__}`,
-  is: 'app-management-more-permissions-item',
-
-  properties: {
-    /** @type {!App} */
-    app: Object,
-  },
-
-  listeners: {
-    click: 'onClick_',
-  },
-
-  onClick_() {
-    BrowserProxy.getInstance().handler.openNativeSettings(this.app.id);
-    recordAppManagementUserAction(
-        this.app.type, AppManagementUserAction.NativeSettingsOpened);
-  },
-});
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.html
index f8fcfb930..caeb2b7 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.html
@@ -38,7 +38,9 @@
     </div>
   </div>
   <app-management-more-permissions-item id="more-settings"
-      class="permission-card-row separated-row" app="[[app_]]">
+      class="permission-card-row separated-row"
+      app="[[app_]]"
+      more-permissions-label="$i18n{appManagementMorePermissionsLabel}">
   </app-management-more-permissions-item>
   <app-management-supported-links-item
       id="supportedLinksSetting"
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.js
index dd7fd09..62c43977 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.js
@@ -1,11 +1,11 @@
 // Copyright 2018 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 './more_permissions_item.js';
 import './pin_to_shelf_item.js';
 import './supported_links_item.js';
 import './shared_style.js';
 import '//resources/cr_components/app_management/icons.js';
+import '//resources/cr_components/app_management/more_permissions_item.js';
 import '//resources/cr_components/app_management/permission_item.js';
 import '//resources/cr_elements/icons.m.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_vars.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_vars.html
new file mode 100644
index 0000000..b3c1b224
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_vars.html
@@ -0,0 +1,10 @@
+<custom-style>
+<style>
+  html {
+    --card-separator: 1px solid var(--cros-separator-color);
+    --header-text-color: var(--cros-text-color-secondary);
+    --primary-text-color: var(--cros-text-color-primary);
+    --secondary-text-color: var(--cros-text-color-secondary);
+  }
+</style>
+</custom-style>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_vars.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_vars.js
new file mode 100644
index 0000000..fe8e31f
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_vars.js
@@ -0,0 +1,12 @@
+// Copyright 2018 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 '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import '//resources/cr_elements/shared_vars_css.m.js';
+import '//resources/cr_components/app_management/shared_vars.js';
+
+const $_documentContainer = document.createElement('template');
+$_documentContainer.innerHTML = `{__html_template__}`;
+document.head.appendChild($_documentContainer.content);
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_devices_subpage.js b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_devices_subpage.js
index 66e1d992..be600b3c 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_devices_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_devices_subpage.js
@@ -16,6 +16,7 @@
 import {html, mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {getBluetoothConfig} from 'chrome://resources/cr_components/chromeos/bluetooth/cros_bluetooth_config.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {IronA11yAnnouncer} from 'chrome://resources/polymer/v3_0/iron-a11y-announcer/iron-a11y-announcer.js';
 
 import {Route, Router} from '../../router.js';
 import {routes} from '../os_route.m.js';
@@ -110,6 +111,12 @@
     this.lastSelectedDeviceId_ = null;
   }
 
+  /** @override */
+  ready() {
+    super.ready();
+    IronA11yAnnouncer.requestAvailability();
+  }
+
   /**
    * RouteObserverBehaviorInterface override
    * @param {!Route} route
@@ -205,6 +212,7 @@
       return;
     }
     getBluetoothConfig().setBluetoothEnabledState(this.isBluetoothToggleOn_);
+    this.annouceBluetoothStateChange_();
   }
 
   /**
@@ -246,6 +254,19 @@
   shouldShowNoDevicesFound_() {
     return !this.connectedDevices_.length && !this.unconnectedDevices_.length;
   }
+
+  /** @private */
+  annouceBluetoothStateChange_() {
+    this.dispatchEvent(new CustomEvent('iron-announce', {
+      bubbles: true,
+      composed: true,
+      detail: {
+        text: this.isBluetoothToggleOn_ ?
+            this.i18n('bluetoothEnabledA11YLabel') :
+            this.i18n('bluetoothDisabledA11YLabel')
+      }
+    }));
+  }
 }
 
 customElements.define(
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_summary.js b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_summary.js
index 5b40759..061539e 100644
--- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_summary.js
+++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_summary.js
@@ -16,6 +16,7 @@
 import {html, mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {getDeviceName} from 'chrome://resources/cr_components/chromeos/bluetooth/bluetooth_utils.js';
 import {getBluetoothConfig} from 'chrome://resources/cr_components/chromeos/bluetooth/cros_bluetooth_config.js';
+import {IronA11yAnnouncer} from 'chrome://resources/polymer/v3_0/iron-a11y-announcer/iron-a11y-announcer.js';
 
 import {loadTimeData} from '../../i18n_setup.js';
 import {Router} from '../../router.js';
@@ -121,6 +122,7 @@
     super.ready();
 
     this.addFocusConfig(routes.BLUETOOTH_DEVICES, '.subpage-arrow');
+    IronA11yAnnouncer.requestAvailability();
   }
 
   /** @private */
@@ -146,6 +148,7 @@
       return;
     }
     getBluetoothConfig().setBluetoothEnabledState(this.isBluetoothToggleOn_);
+    this.annouceBluetoothStateChange_();
   }
 
   /**
@@ -287,6 +290,19 @@
       composed: true,
     }));
   }
+
+  /** @private */
+  annouceBluetoothStateChange_() {
+    this.dispatchEvent(new CustomEvent('iron-announce', {
+      bubbles: true,
+      composed: true,
+      detail: {
+        text: this.isBluetoothToggleOn_ ?
+            this.i18n('bluetoothEnabledA11YLabel') :
+            this.i18n('bluetoothDisabledA11YLabel')
+      }
+    }));
+  }
 }
 
 customElements.define(
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.js b/chrome/browser/resources/settings/chromeos/os_settings.js
index 22a81698..b44142a 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings.js
@@ -60,7 +60,7 @@
 import './os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js';
 import './os_apps_page/app_management_page/pwa_detail_view.js';
 import './os_apps_page/app_management_page/shared_style.js';
-import '//resources/cr_components/app_management/shared_vars.js';
+import './os_apps_page/app_management_page/shared_vars.js';
 import './os_apps_page/app_management_page/supported_links_overlapping_apps_dialog.js';
 import './os_apps_page/app_management_page/supported_links_dialog.js';
 import './os_apps_page/app_management_page/supported_links_item.js';
diff --git a/chrome/browser/resources/settings/icons.html b/chrome/browser/resources/settings/icons.html
index 643692a..9c271a5 100644
--- a/chrome/browser/resources/settings/icons.html
+++ b/chrome/browser/resources/settings/icons.html
@@ -158,7 +158,8 @@
 <if expr="not chromeos">
       <g id="web"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-5 14H4v-4h11v4zm0-5H4V9h11v4zm5 5h-4V9h4v9z"></path></g>
 </if>
-      <g id="window-placement"><path d="M21 1H8c-1.1 0-2 .9-2 2v6H3c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h13c1.1 0 2-.9 2-2v-6h3c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm-5 20H3v-8h13v8zm5-8h-3v-2c0-1.1-.9-2-2-2H8V5h13v8z"></path></g>
+      <g id="window-placement"><path d="M18 15v5q0 .825-.5875 1.4125Q16.825 22 16 22H4q-.825 0-1.4125-.5875Q2 20.825 2 20v-9q0-.825.5875-1.4125Q3.175 9 4 9h2V4q0-.825.5875-1.4125Q7.175 2 8 2h12q.825 0 1.4125.5875Q22 3.175 22 4v9q0 .825-.5875 1.4125Q20.825 15 20 15ZM4 13v7h12v-7Zm14 0h2V6H8v3h8q.825 0 1.4125.5875Q18 10.175 18 11Z"></path></g>
+      <g id="window-placement-off"><path d="m20.475 23.3-2.6-2.6q-.2.575-.7125.9375Q16.65 22 16 22H4q-.825 0-1.4125-.5875Q2 20.825 2 20v-9q0-.825.5875-1.4125Q3.175 9 4 9h2v-.175L.675 3.5 2.1 2.075l19.8 19.8ZM4 20h12v-1.175L10.175 13H4v7Zm14-4.875L15.875 13l-4-4H16q.825 0 1.4125.5875Q18 10.175 18 11v2h2V6H8.875L6.15 3.275q.2-.575.7-.925Q7.35 2 8 2h12q.825 0 1.4125.5875Q22 3.175 22 4v9q0 .825-.5875 1.4125Q20.825 15 20 15h-2Z"></path></g>
       <g id="zoom-in"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path></g>
       <g id="font-access"><path d="M0 0h24v24H0V0z" fill="none"></path><path d="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 18H4V4h16v16zM10.69 6h2.6l4.51 12h-2.5l-1.01-2.87H9.7L8.7 18H6.2l4.49-12zm2.87 7.06l-1.06-3.02-.43-1.44h-.13l-.44 1.44-1.07 3.02h3.13z"></path></g>
       <g id="font-access-off"><path d="M4.83,2H20c1.1,0,2,0.9,2,2v15.17l-2-2V4H6.83L4.83,2z M10.92,6l-0.57,1.52l1.36,1.36l0.23-0.66h0.1l0.54,1.52l3.04,3.04 L13.07,6H10.92z M20.49,23.31L19.17,22H4c-1.1,0-2-0.9-2-2V4.83L0.69,3.51L2.1,2.1l19.8,19.8L20.49,23.31z M17.17,20l-5.07-5.07 H9.58L8.49,18H6.41l2.39-6.37L4,6.83V20H17.17z"></path></g>
diff --git a/chrome/browser/resources/settings/lazy_load.ts b/chrome/browser/resources/settings/lazy_load.ts
index 07287aa..27347b7 100644
--- a/chrome/browser/resources/settings/lazy_load.ts
+++ b/chrome/browser/resources/settings/lazy_load.ts
@@ -16,6 +16,9 @@
 import './privacy_page/privacy_review/privacy_review_msbb_fragment.js';
 import './privacy_page/privacy_review/privacy_review_page.js';
 import './privacy_page/security_keys_subpage.js';
+import './privacy_page/security_keys_phones_subpage.js';
+import './privacy_page/security_keys_phones_list.js';
+import './privacy_page/security_keys_phones_dialog.js';
 import './privacy_page/security_page.js';
 import './site_settings/all_sites.js';
 import './site_settings/site_data_details_subpage.js';
@@ -137,8 +140,9 @@
 export {SettingsSecureDnsElement} from './privacy_page/secure_dns.js';
 export {SecureDnsInputElement} from './privacy_page/secure_dns_input.js';
 export {BioEnrollDialogPage, SettingsSecurityKeysBioEnrollDialogElement} from './privacy_page/security_keys_bio_enroll_dialog.js';
-export {Ctap2Status, SampleStatus, SecurityKeysBioEnrollProxy, SecurityKeysBioEnrollProxyImpl, SecurityKeysCredentialBrowserProxy, SecurityKeysCredentialBrowserProxyImpl, SecurityKeysPINBrowserProxy, SecurityKeysPINBrowserProxyImpl, SecurityKeysResetBrowserProxy, SecurityKeysResetBrowserProxyImpl} from './privacy_page/security_keys_browser_proxy.js';
+export {Ctap2Status, SampleStatus, SecurityKeysBioEnrollProxy, SecurityKeysBioEnrollProxyImpl, SecurityKeysCredentialBrowserProxy, SecurityKeysCredentialBrowserProxyImpl, SecurityKeysPhone, SecurityKeysPhonesBrowserProxy, SecurityKeysPhonesBrowserProxyImpl, SecurityKeysPhonesList, SecurityKeysPINBrowserProxy, SecurityKeysPINBrowserProxyImpl, SecurityKeysResetBrowserProxy, SecurityKeysResetBrowserProxyImpl} from './privacy_page/security_keys_browser_proxy.js';
 export {CredentialManagementDialogPage, SettingsSecurityKeysCredentialManagementDialogElement} from './privacy_page/security_keys_credential_management_dialog.js';
+export {SecurityKeysPhonesSubpageElement} from './privacy_page/security_keys_phones_subpage.js';
 export {ResetDialogPage, SettingsSecurityKeysResetDialogElement} from './privacy_page/security_keys_reset_dialog.js';
 export {SetPINDialogPage, SettingsSecurityKeysSetPinDialogElement} from './privacy_page/security_keys_set_pin_dialog.js';
 export {SafeBrowsingSetting, SettingsSecurityPageElement} from './privacy_page/security_page.js';
diff --git a/chrome/browser/resources/settings/people_page/sync_page.ts b/chrome/browser/resources/settings/people_page/sync_page.ts
index bbbf4e35..17a26c33 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.ts
+++ b/chrome/browser/resources/settings/people_page/sync_page.ts
@@ -589,16 +589,27 @@
     this.browserProxy_.setDecryptionPassphrase(this.existingPassphrase_)
         .then(
             sucessfullySet => this.handlePageStatusChanged_(
-                sucessfullySet ? PageStatus.CONFIGURE :
-                                 PageStatus.PASSPHRASE_FAILED));
+                this.computePageStatusAfterPassphraseChange_(sucessfullySet)));
 
     this.existingPassphrase_ = '';
   }
 
   private onPassphraseChanged_(e: CustomEvent<{didChange: boolean}>) {
     this.handlePageStatusChanged_(
-        e.detail.didChange ? PageStatus.CONFIGURE :
-                             PageStatus.PASSPHRASE_FAILED);
+        this.computePageStatusAfterPassphraseChange_(e.detail.didChange));
+  }
+
+  private computePageStatusAfterPassphraseChange_(successfullyChanged: boolean):
+      PageStatus {
+    if (!successfullyChanged) {
+      return PageStatus.PASSPHRASE_FAILED;
+    }
+
+    // Stay on the setup page if the user hasn't approved sync settings yet.
+    // Otherwise, close sync setup.
+    return this.syncStatus && this.syncStatus.firstSetupInProgress ?
+        PageStatus.CONFIGURE :
+        PageStatus.DONE;
   }
 
   /**
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html
index a8beaa3..4b5a6cd0 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -112,6 +112,13 @@
             <security-keys-subpage></security-keys-subpage>
           </settings-subpage>
         </template>
+
+        <template is="dom-if" route-path="/securityKeys/phones">
+          <settings-subpage associated-control="[[$$('#securityLinkRow')]]"
+              page-title="$i18n{securityKeysPhonesManage}">
+            <security-keys-phones-subpage></security-keys-phones-subpage>
+          </settings-subpage>
+        </template>
       </template>
 
       <template is="dom-if" route-path="/content">
@@ -932,16 +939,20 @@
         <settings-subpage page-title="$i18n{siteSettingsWindowPlacement}"
             search-label="$i18n{siteSettingsAllSitesSearch}"
             search-term="{{searchFilter_}}">
-          <category-default-setting
-              toggle-off-label="$i18n{siteSettingsWindowPlacementBlock}"
-              toggle-on-label=
-                  "$i18n{siteSettingsWindowPlacementAskRecommended}"
-              category="[[contentSettingsTypesEnum_.WINDOW_PLACEMENT]]">
-          </category-default-setting>
+          <div class="content-settings-header secondary">
+            $i18n{siteSettingsWindowPlacementDescription}
+          </div>
+          <settings-category-default-radio-group
+              category="[[contentSettingsTypesEnum_.WINDOW_PLACEMENT]]"
+              allow-option-label="$i18n{siteSettingsWindowPlacementAsk}"
+              allow-option-icon="settings:window-placement"
+              block-option-label="$i18n{siteSettingsWindowPlacementBlocked}"
+              block-option-icon="settings:window-placement-off">
+          </settings-category-default-radio-group>
           <category-setting-exceptions
               category="[[contentSettingsTypesEnum_.WINDOW_PLACEMENT]]"
-              block-header="$i18n{siteSettingsBlock}"
-              allow-header="$i18n{siteSettingsAllow}"
+              allow-header="$i18n{siteSettingsWindowPlacementAskExceptions}"
+              block-header="$i18n{siteSettingsWindowPlacementBlockedExceptions}"
               search-filter="[[searchFilter_]]">
           </category-setting-exceptions>
         </settings-subpage>
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_browser_proxy.ts b/chrome/browser/resources/settings/privacy_page/security_keys_browser_proxy.ts
index 8e5c71a..539d379 100644
--- a/chrome/browser/resources/settings/privacy_page/security_keys_browser_proxy.ts
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_browser_proxy.ts
@@ -294,6 +294,46 @@
   close(): void;
 }
 
+/**
+ * An object that represents a known phone. The name will not contain any
+ * new-line characters and is suitable for showing in UI. The publicKey is a
+ * base64-encoded X9.62 P-256 point, but should be treated as an opaque string.
+ * It can be passed to functions in |SecurityKeysPhonesBrowserProxy| to identify
+ * a specific phone.
+ */
+export type SecurityKeysPhone = {
+  name: string,
+  publicKey: string,
+};
+
+/**
+ * A pair of lists of |SecurityKeysPhone|s. The first is a list of phones known
+ * because they are syncing to the same account. The second are phones that have
+ * been linked by scanning a QR code. Only elements from the latter can be
+ * passed to |delete| or |rename| in |SecurityKeysPhonesBrowserProxy|.
+ */
+export type SecurityKeysPhonesList =
+    [Array<SecurityKeysPhone>, Array<SecurityKeysPhone>];
+
+export interface SecurityKeysPhonesBrowserProxy {
+  /**
+   * Enumerates known phones.
+   */
+  enumerate(): Promise<SecurityKeysPhonesList>;
+
+  /**
+   * Deletes a linked phone by public key.
+   */
+  delete(publicKey: string): Promise<SecurityKeysPhonesList>;
+
+  /**
+   * Rename a linked phone.
+   *
+   * Rename the phone the given public key so that it is now known as |newName|.
+   */
+  rename(publicKey: string, newName: string): Promise<void>;
+}
+
 export class SecurityKeysPINBrowserProxyImpl implements
     SecurityKeysPINBrowserProxy {
   startSetPIN() {
@@ -439,3 +479,29 @@
 }
 
 let bioEnrollProxyInstance: SecurityKeysBioEnrollProxy|null = null;
+
+export class SecurityKeysPhonesBrowserProxyImpl implements
+    SecurityKeysPhonesBrowserProxy {
+  enumerate() {
+    return sendWithPromise('securityKeyPhonesEnumerate');
+  }
+
+  delete(name: string) {
+    return sendWithPromise('securityKeyPhonesDelete', name);
+  }
+
+  rename(publicKey: string, newName: string) {
+    return sendWithPromise('securityKeyPhonesRename', publicKey, newName);
+  }
+
+  static getInstance(): SecurityKeysPhonesBrowserProxy {
+    return phonesProxyInstance ||
+        (phonesProxyInstance = new SecurityKeysPhonesBrowserProxyImpl());
+  }
+
+  static setInstance(obj: SecurityKeysPhonesBrowserProxy) {
+    phonesProxyInstance = obj;
+  }
+}
+
+let phonesProxyInstance: SecurityKeysPhonesBrowserProxy|null = null;
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_phones_dialog.html b/chrome/browser/resources/settings/privacy_page/security_keys_phones_dialog.html
new file mode 100644
index 0000000..b17a13e
--- /dev/null
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_phones_dialog.html
@@ -0,0 +1,20 @@
+<style include="settings-shared"></style>
+<cr-dialog id="dialog" close-text="$i18n{close}" show-on-attach>
+  <div slot="title">$i18n{securityKeysPhoneEditDialogTitle}</div>
+  <div slot="body">
+    <cr-input id="name"
+              spellcheck="false"
+              label="$i18n{securityKeysCredentialDisplayNameLabel}"
+              value="[[name]]"
+              on-input="validate_"
+              autofocus>
+    </cr-input>
+  </div>
+  <div slot="button-container">
+    <cr-button class="cancel-button" on-click="onCancelClick_" id="cancel">
+        $i18n{cancel}</cr-button>
+    <cr-button id="actionButton" class="action-button" on-click="onSaveClick_">
+        $i18n{save}
+    </cr-button>
+  </div>
+</cr-dialog>
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_phones_dialog.ts b/chrome/browser/resources/settings/privacy_page/security_keys_phones_dialog.ts
new file mode 100644
index 0000000..afb65e6
--- /dev/null
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_phones_dialog.ts
@@ -0,0 +1,76 @@
+// 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.
+
+/**
+ * @fileoverview A dialog for editing the name of a phone that has been linked
+    for use as a security key.
+ */
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
+
+import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {SecurityKeysPhonesBrowserProxy, SecurityKeysPhonesBrowserProxyImpl} from './security_keys_browser_proxy.js';
+
+export interface SecurityKeysPhonesDialog {
+  $: {
+    name: CrInputElement,
+    dialog: CrDialogElement,
+    actionButton: CrButtonElement,
+  };
+}
+
+export class SecurityKeysPhonesDialog extends PolymerElement {
+  static get is() {
+    return 'security-keys-phones-dialog';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      name: String,
+      publicKey: String,
+    };
+  }
+
+  name: string;
+  publicKey: string;
+  private browserProxy_: SecurityKeysPhonesBrowserProxy =
+      SecurityKeysPhonesBrowserProxyImpl.getInstance();
+
+  private onCancelClick_() {
+    this.$.dialog.cancel();
+  }
+
+  private onSaveClick_() {
+    const newName = this.$.name.value;
+    if (newName === this.name) {
+      this.$.dialog.close();
+      return;
+    }
+
+    this.browserProxy_.rename(this.publicKey, newName)
+        .then(() => this.$.dialog.close());
+  }
+
+  private validate_(event: Event) {
+    const input = event.target as CrInputElement;
+    input.invalid = input.value === '';
+    this.$.actionButton.disabled = input.invalid;
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'security-keys-phones-dialog': SecurityKeysPhonesDialog;
+  }
+}
+
+customElements.define(SecurityKeysPhonesDialog.is, SecurityKeysPhonesDialog);
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_phones_list.html b/chrome/browser/resources/settings/privacy_page/security_keys_phones_list.html
new file mode 100644
index 0000000..360df535
--- /dev/null
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_phones_list.html
@@ -0,0 +1,36 @@
+<style include="settings-shared">
+  .list-item {
+    justify-content: space-between;
+  }
+
+  #table .cr-row:first-child {
+    border-top: none;
+  }
+</style>
+<div id="outer" class="list-frame" role="table">
+  <div role="rowgroup" id="table">
+    <template is="dom-repeat" items="[[phones]]">
+      <div class="list-item cr-row" role="row">
+        <span role="cell" class="name-column">[[item.name]]</span>
+        <template is="dom-if" if="[[!immutable]]">
+          <span role="cell">
+            <cr-icon-button class="icon-more-vert"
+                            on-click="onDotsClick_"
+                            data-phone-public-key$="[[item.publicKey]]"
+                            title="$i18n{moreActions}">
+            </cr-icon-button>
+          </span>
+        </template>
+      </div>
+    </template>
+
+    <cr-action-menu id="menu" role-description="$i18n{menu}">
+      <button class="dropdown-item" on-click="onEditClick_" id="edit">
+        $i18n{edit}
+      </button>
+      <button class="dropdown-item" on-click="onDeleteClick_" id="delete">
+        $i18n{delete}
+      </button>
+    </cr-action-menu>
+  </div>
+</div>
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_phones_list.ts b/chrome/browser/resources/settings/privacy_page/security_keys_phones_list.ts
new file mode 100644
index 0000000..4bb44057
--- /dev/null
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_phones_list.ts
@@ -0,0 +1,76 @@
+// 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.
+
+/**
+ * @fileoverview An element that lists phones usable as security keys,
+    optionally with a drop-down menu for editing or deleting them.
+ */
+import '../settings_shared_css.js';
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+
+import {AnchorAlignment} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {SecurityKeysPhone} from './security_keys_browser_proxy.js';
+
+class SecurityKeysPhonesListElement extends PolymerElement {
+  static get is() {
+    return 'security-keys-phones-list';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      immutable: {type: Boolean, value: false},
+      phones: {type: Array, value: []},
+    };
+  }
+
+  immutable: boolean;
+  phones: Array<SecurityKeysPhone>;
+  // Contains the public key of the phone that the action menu was opened for.
+  private publicKeyForActionMenu_: string|null;
+
+  private onDotsClick_(e: Event) {
+    this.publicKeyForActionMenu_ =
+        (e.target as HTMLElement).dataset['phonePublicKey']!;
+
+    this.shadowRoot!.querySelector('cr-action-menu')!.showAt(
+        e.target as HTMLElement, {
+          anchorAlignmentY: AnchorAlignment.AFTER_END,
+        });
+  }
+
+  private onEditClick_(e: Event) {
+    this.handleClick_(e, 'edit-security-key-phone');
+  }
+
+  private onDeleteClick_(e: Event) {
+    this.handleClick_(e, 'delete-security-key-phone');
+  }
+
+  private handleClick_(
+      e: Event,
+      eventName: 'edit-security-key-phone'|'delete-security-key-phone') {
+    e.stopPropagation();
+    this.closePopupMenu_();
+
+    this.dispatchEvent(new CustomEvent(eventName, {
+      bubbles: true,
+      composed: true,
+      detail: this.publicKeyForActionMenu_,
+    }));
+  }
+
+  private closePopupMenu_() {
+    this.shadowRoot!.querySelector('cr-action-menu')!.close();
+  }
+}
+
+customElements.define(
+    SecurityKeysPhonesListElement.is, SecurityKeysPhonesListElement);
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_phones_subpage.html b/chrome/browser/resources/settings/privacy_page/security_keys_phones_subpage.html
new file mode 100644
index 0000000..4fdafd0
--- /dev/null
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_phones_subpage.html
@@ -0,0 +1,37 @@
+<style include="cr-shared-style settings-shared"></style>
+
+<style>
+  /* An H2 is the first element on the page and the top padding is excessive
+     in that context. */
+  h2:first-of-type {
+    padding-top: 0;
+  }
+</style>
+
+<div class="cr-row first">
+  <div class="flex cr-padded-text">
+    <h2>$i18n{securityKeysPhonesYourDevices}</h2>
+    <div class="secondary">$i18n{securityKeysPhonesSyncedDesc}</div>
+  </div>
+</div>
+
+<security-keys-phones-list id="syncedPhonesList" immutable
+                           phones="[[syncedPhones_]]">
+</security-keys-phones-list>
+
+<div class="cr-row first">
+  <div class="flex cr-padded-text">
+    <h2>$i18n{securityKeysPhonesLinkedDevices}</h2>
+    <div class="secondary">$i18n{securityKeysPhonesLinkedDesc}</div>
+  </div>
+</div>
+
+<security-keys-phones-list id="linkedPhonesList" phones="[[linkedPhones_]]">
+</security-keys-phones-list>
+
+<template is="dom-if" if="[[showDialog_]]" restamp>
+  <security-keys-phones-dialog name="[[dialogName_]]"
+                               public-key="[[dialogPublicKey_]]"
+                               on-close="onDialogClose_">
+  </security-keys-phones-dialog>
+</template>
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_phones_subpage.ts b/chrome/browser/resources/settings/privacy_page/security_keys_phones_subpage.ts
new file mode 100644
index 0000000..5459569
--- /dev/null
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_phones_subpage.ts
@@ -0,0 +1,105 @@
+// 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.
+
+/**
+ * @fileoverview A settings subpage that allows the user to see and manage the
+    set of phones that are usable as security keys.
+ */
+import '../settings_shared_css.js';
+
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {SecurityKeysPhone, SecurityKeysPhonesBrowserProxy, SecurityKeysPhonesBrowserProxyImpl, SecurityKeysPhonesList} from './security_keys_browser_proxy.js';
+
+declare global {
+  interface HTMLElementEventMap {
+    'edit-security-key-phone': CustomEvent<string>;
+    'delete-security-key-phone': CustomEvent<string>;
+  }
+}
+
+export class SecurityKeysPhonesSubpageElement extends PolymerElement {
+  static get is() {
+    return 'security-keys-phones-subpage';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  private syncedPhones_: Array<SecurityKeysPhone>;
+  private linkedPhones_: Array<SecurityKeysPhone>;
+  private showDialog_: boolean;
+  private dialogName_: string;
+  private dialogPublicKey_: string;
+  private browserProxy_: SecurityKeysPhonesBrowserProxy =
+      SecurityKeysPhonesBrowserProxyImpl.getInstance();
+
+  ready() {
+    super.ready();
+
+    this.addEventListener(
+        'edit-security-key-phone', this.editPhone_.bind(this));
+    this.addEventListener(
+        'delete-security-key-phone', this.deletePhone_.bind(this));
+
+    this.browserProxy_.enumerate().then(this.onEnumerateComplete_.bind(this));
+  }
+
+  /**
+   * Called when the browser has a new set of phone details.
+   */
+  private onEnumerateComplete_([syncedPhones, linkedPhones]:
+                                   SecurityKeysPhonesList) {
+    this.syncedPhones_ = syncedPhones;
+    this.linkedPhones_ = linkedPhones;
+  }
+
+  /**
+   * Called when the user clicks "Edit" in a drop-down menu to open the dialog.
+   */
+  private editPhone_(e: CustomEvent<string>) {
+    this.dialogPublicKey_ = e.detail;
+    this.dialogName_ = this.nameFromPublicKey_(e.detail);
+    this.showDialog_ = true;
+  }
+
+  /**
+   * Called when an edit dialog as closed (whether successful or not).
+   */
+  private onDialogClose_() {
+    this.showDialog_ = false;
+    // The dialog may have renamed a phone so refresh the lists.
+    this.browserProxy_.enumerate().then(this.onEnumerateComplete_.bind(this));
+  }
+
+  /**
+   * Called when the user clicks "Delete" in a drop-down menu to delete a linked
+   * phone.
+   */
+  private deletePhone_(e: CustomEvent<string>) {
+    this.browserProxy_.delete(e.detail).then(
+        this.onEnumerateComplete_.bind(this));
+  }
+
+  /**
+   * Returns the name of a linked phone given its public key.
+   */
+  private nameFromPublicKey_(publicKey: string): string {
+    const matchingPhones =
+        this.linkedPhones_.filter(phone => phone.publicKey === publicKey);
+    assert(matchingPhones.length !== 0);
+    return matchingPhones[0].name;
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'security-keys-phones-subpage': SecurityKeysPhonesSubpageElement;
+  }
+}
+
+customElements.define(
+    SecurityKeysPhonesSubpageElement.is, SecurityKeysPhonesSubpageElement);
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_subpage.html b/chrome/browser/resources/settings/privacy_page/security_keys_subpage.html
index a0b0c86..1d7437c 100644
--- a/chrome/browser/resources/settings/privacy_page/security_keys_subpage.html
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_subpage.html
@@ -1,52 +1,60 @@
-    <style include="settings-shared"></style>
+<style include="settings-shared"></style>
 
-    <cr-link-row
-      id="setPINButton"
-      label="$i18n{securityKeysSetPIN}"
-      sub-label="$i18n{securityKeysSetPINDesc}"
-      on-click="onSetPIN_"></cr-link-row>
-    <cr-link-row
-      id="credentialManagementButton"
-      class="hr"
-      label="$i18n{securityKeysCredentialManagementLabel}"
-      sub-label="$i18n{securityKeysCredentialManagementDesc}"
-      on-click="onCredentialManagement_"></cr-link-row>
-    <template is="dom-if" if="[[enableBioEnrollment_]]">
-      <cr-link-row
-        id="bioEnrollButton"
-        class="hr"
-        label="$i18n{securityKeysBioEnrollmentSubpageLabel}"
-        sub-label="$i18n{securityKeysBioEnrollmentSubpageDescription}"
-        on-click="onBioEnroll_"></cr-link-row>
-    </template>
-    <cr-link-row
-      id="resetButton"
-      class="hr"
-      label="$i18n{securityKeysReset}"
-      sub-label="$i18n{securityKeysResetDesc}"
-      on-click="onReset_"></cr-link-row>
+<template is="dom-if" if="[[enableSecurityKeysPhonesSubpage_]]">
+  <cr-link-row
+    id="managePhonesButton"
+    label="$i18n{securityKeysPhonesManage}"
+    sub-label="$i18n{securityKeysPhonesManageDesc}"
+    on-click="onManagePhonesClick_"></cr-link-row>
+</template>
+<cr-link-row
+  id="setPINButton"
+  class$="[[hrIfPhonesSubpageEnabled_]]"
+  label="$i18n{securityKeysSetPIN}"
+  sub-label="$i18n{securityKeysSetPINDesc}"
+  on-click="onSetPIN_"></cr-link-row>
+<cr-link-row
+  id="credentialManagementButton"
+  class="hr"
+  label="$i18n{securityKeysCredentialManagementLabel}"
+  sub-label="$i18n{securityKeysCredentialManagementDesc}"
+  on-click="onCredentialManagement_"></cr-link-row>
+<template is="dom-if" if="[[enableBioEnrollment_]]">
+  <cr-link-row
+    id="bioEnrollButton"
+    class="hr"
+    label="$i18n{securityKeysBioEnrollmentSubpageLabel}"
+    sub-label="$i18n{securityKeysBioEnrollmentSubpageDescription}"
+    on-click="onBioEnroll_"></cr-link-row>
+</template>
+<cr-link-row
+  id="resetButton"
+  class="hr"
+  label="$i18n{securityKeysReset}"
+  sub-label="$i18n{securityKeysResetDesc}"
+  on-click="onReset_"></cr-link-row>
 
-    <template is="dom-if" if="[[showSetPINDialog_]]" restamp>
-      <settings-security-keys-set-pin-dialog on-close="onSetPINDialogClosed_">
-      </settings-security-keys-set-pin-dialog>
-    </template>
+<template is="dom-if" if="[[showSetPINDialog_]]" restamp>
+  <settings-security-keys-set-pin-dialog on-close="onSetPINDialogClosed_">
+  </settings-security-keys-set-pin-dialog>
+</template>
 
-    <template is="dom-if" if="[[showCredentialManagementDialog_]]" restamp>
-      <settings-security-keys-credential-management-dialog
-        on-credential-management-set-pin="onSetPIN_"
-        on-close="onCredentialManagementDialogClosed_">
-      </settings-security-keys-credential-management-dialog>
-    </template>
+<template is="dom-if" if="[[showCredentialManagementDialog_]]" restamp>
+  <settings-security-keys-credential-management-dialog
+    on-credential-management-set-pin="onSetPIN_"
+    on-close="onCredentialManagementDialogClosed_">
+  </settings-security-keys-credential-management-dialog>
+</template>
 
-    <template is="dom-if" if="[[showResetDialog_]]" restamp>
-      <settings-security-keys-reset-dialog on-close="onResetDialogClosed_">
-      </settings-security-keys-reset-dialog>
-    </template>
+<template is="dom-if" if="[[showResetDialog_]]" restamp>
+  <settings-security-keys-reset-dialog on-close="onResetDialogClosed_">
+  </settings-security-keys-reset-dialog>
+</template>
 
-    <template is="dom-if" if="[[showBioEnrollDialog_]]" restamp>
-      <settings-security-keys-bio-enroll-dialog
-          on-bio-enroll-set-pin="onSetPIN_"
-          on-close="onBioEnrollDialogClosed_">
-      </settings-security-keys-bio-enroll-dialog>
-    </template>
+<template is="dom-if" if="[[showBioEnrollDialog_]]" restamp>
+  <settings-security-keys-bio-enroll-dialog
+      on-bio-enroll-set-pin="onSetPIN_"
+      on-close="onBioEnrollDialogClosed_">
+  </settings-security-keys-bio-enroll-dialog>
+</template>
 
diff --git a/chrome/browser/resources/settings/privacy_page/security_keys_subpage.ts b/chrome/browser/resources/settings/privacy_page/security_keys_subpage.ts
index 3ecb0c7..57ce366 100644
--- a/chrome/browser/resources/settings/privacy_page/security_keys_subpage.ts
+++ b/chrome/browser/resources/settings/privacy_page/security_keys_subpage.ts
@@ -18,6 +18,8 @@
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {loadTimeData} from '../i18n_setup.js';
+import {routes} from '../route.js';
+import {Router} from '../router.js';
 
 interface SecurityKeysSubpageElement {
   $: {
@@ -45,6 +47,24 @@
         }
       },
 
+      enableSecurityKeysPhonesSubpage_: {
+        type: Boolean,
+        readOnly: true,
+        value() {
+          return loadTimeData.getBoolean('enableSecurityKeysPhonesSubpage');
+        }
+      },
+
+      hrIfPhonesSubpageEnabled_: {
+        type: String,
+        readOnly: true,
+        value() {
+          return loadTimeData.getBoolean('enableSecurityKeysPhonesSubpage') ?
+              'hr' :
+              '';
+        }
+      },
+
       showSetPINDialog_: {
         type: Boolean,
         value: false,
@@ -68,11 +88,17 @@
   }
 
   private enableBioEnrollment_: boolean;
+  private enableSecurityKeysPhonesSubpage_: boolean;
+  private hrIfPhonesSubpageEnabled_: string;
   private showSetPINDialog_: boolean;
   private showCredentialManagementDialog_: boolean;
   private showResetDialog_: boolean;
   private showBioEnrollDialog_: boolean;
 
+  private onManagePhonesClick_() {
+    Router.getInstance().navigateTo(routes.SECURITY_KEYS_PHONES);
+  }
+
   private onSetPIN_() {
     this.showSetPINDialog_ = true;
   }
diff --git a/chrome/browser/resources/settings/route.ts b/chrome/browser/resources/settings/route.ts
index 486e717..55c6ead 100644
--- a/chrome/browser/resources/settings/route.ts
+++ b/chrome/browser/resources/settings/route.ts
@@ -31,6 +31,8 @@
 
   if (loadTimeData.getBoolean('enableSecurityKeysSubpage')) {
     r.SECURITY_KEYS = r.SECURITY.createChild('/securityKeys');
+    r.SECURITY_KEYS_PHONES =
+        r.SECURITY_KEYS.createChild('/securityKeys/phones');
   }
 
   r.SITE_SETTINGS_ALL = r.SITE_SETTINGS.createChild('all');
diff --git a/chrome/browser/resources/settings/settings.gni b/chrome/browser/resources/settings/settings.gni
index 729ed432..e33e412 100644
--- a/chrome/browser/resources/settings/settings.gni
+++ b/chrome/browser/resources/settings/settings.gni
@@ -94,6 +94,9 @@
   "privacy_page/security_keys_reset_dialog.ts",
   "privacy_page/security_keys_set_pin_dialog.ts",
   "privacy_page/security_keys_subpage.ts",
+  "privacy_page/security_keys_phones_subpage.ts",
+  "privacy_page/security_keys_phones_list.ts",
+  "privacy_page/security_keys_phones_dialog.ts",
   "privacy_page/security_page.ts",
   "privacy_page/privacy_review/privacy_review_clear_on_exit_fragment.ts",
   "privacy_page/privacy_review/privacy_review_completion_fragment.ts",
diff --git a/chrome/browser/resources/settings/settings_routes.ts b/chrome/browser/resources/settings/settings_routes.ts
index 6e83e64..ffffa797 100644
--- a/chrome/browser/resources/settings/settings_routes.ts
+++ b/chrome/browser/resources/settings/settings_routes.ts
@@ -44,6 +44,7 @@
   SEARCH_ENGINES: Route,
   SECURITY: Route,
   SECURITY_KEYS: Route,
+  SECURITY_KEYS_PHONES: Route,
   SIGN_OUT: Route,
   SITE_SETTINGS: Route,
   SITE_SETTINGS_ADS: Route,
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts b/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts
index f06ea2b..5bbe6ec 100644
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts
@@ -295,7 +295,7 @@
       label: 'siteSettingsWindowPlacement',
       icon: 'settings:window-placement',
       enabledLabel: 'siteSettingsWindowPlacementAsk',
-      disabledLabel: 'siteSettingsWindowPlacementBlock',
+      disabledLabel: 'siteSettingsWindowPlacementBlocked',
     },
     {
       route: routes.SITE_SETTINGS_ZOOM_LEVELS,
diff --git a/chrome/browser/resources/signin/BUILD.gn b/chrome/browser/resources/signin/BUILD.gn
index b188527..9ea3b298 100644
--- a/chrome/browser/resources/signin/BUILD.gn
+++ b/chrome/browser/resources/signin/BUILD.gn
@@ -186,6 +186,7 @@
   deps = [
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources:library",
+    "//ui/webui/resources/cr_components/customize_themes:build_ts",
   ]
   extra_deps = [
     ":preprocess",
diff --git a/chrome/browser/resources/tab_strip/BUILD.gn b/chrome/browser/resources/tab_strip/BUILD.gn
index fbb70e2..a89327b 100644
--- a/chrome/browser/resources/tab_strip/BUILD.gn
+++ b/chrome/browser/resources/tab_strip/BUILD.gn
@@ -121,11 +121,7 @@
     "//third_party/polymer/v3_0:library",
     "//ui/webui/resources:library",
   ]
-  definitions = [
-    "//tools/typescript/definitions/chrome_send.d.ts",
-    "//tools/typescript/definitions/metrics_private.d.ts",
-    "//tools/typescript/definitions/tabs.d.ts",
-  ]
+  definitions = [ "//tools/typescript/definitions/metrics_private.d.ts" ]
   extra_deps = [
     ":preprocess",
     ":preprocess_generated",
diff --git a/chrome/browser/resources/tab_strip/tab_list.ts b/chrome/browser/resources/tab_strip/tab_list.ts
index 1e39ea41..f1626ef 100644
--- a/chrome/browser/resources/tab_strip/tab_list.ts
+++ b/chrome/browser/resources/tab_strip/tab_list.ts
@@ -231,14 +231,6 @@
 
     this.scrollListener_ = (e) => this.onScroll_(e);
 
-    this.addWebUIListener_('theme-changed', () => {
-      // Refetch theme colors, group color and tab favicons on theme change.
-      this.fetchAndUpdateColors_();
-      this.fetchAndUpdateGroupData_();
-      this.fetchAndUpdateTabs_();
-    });
-    this.tabsApi_.observeThemeChanges();
-
     const callbackRouter = this.tabsApi_.getCallbackRouter();
     callbackRouter.layoutChanged.addListener(
         this.applyCSSDictionary_.bind(this));
@@ -254,6 +246,13 @@
     callbackRouter.receivedKeyboardFocus.addListener(
         () => this.onReceivedKeyboardFocus_());
 
+    callbackRouter.themeChanged.addListener(() => {
+      // Refetch theme colors, group color and tab favicons on theme change.
+      this.fetchAndUpdateColors_();
+      this.fetchAndUpdateGroupData_();
+      this.fetchAndUpdateTabs_();
+    });
+
     this.eventTracker_.add(
         document, 'contextmenu', e => this.onContextMenu_(e));
     this.eventTracker_.add(
diff --git a/chrome/browser/resources/tab_strip/tabs_api_proxy.ts b/chrome/browser/resources/tab_strip/tabs_api_proxy.ts
index aead1f8..f2d48bb 100644
--- a/chrome/browser/resources/tab_strip/tabs_api_proxy.ts
+++ b/chrome/browser/resources/tab_strip/tabs_api_proxy.ts
@@ -14,7 +14,7 @@
 }
 
 export interface TabsApiProxy {
-  activateTab(tabId: number): Promise<chrome.tabs.Tab>;
+  activateTab(tabId: number): void;
 
   /**
    * @return Object of group IDs as strings mapped to their visual data.
@@ -47,8 +47,6 @@
    */
   getLayout(): Promise<{layout: {[key: string]: string}}>;
 
-  observeThemeChanges(): void;
-
   showEditDialogForGroup(
       groupId: string, locationX: number, locationY: number, width: number,
       height: number): void;
@@ -89,9 +87,7 @@
   }
 
   activateTab(tabId: number) {
-    return new Promise<chrome.tabs.Tab>(resolve => {
-      chrome.tabs.update(tabId, {active: true}, tab => resolve(tab!));
-    });
+    this.handler.activateTab(tabId);
   }
 
   getGroupVisualData() {
@@ -144,11 +140,6 @@
     return this.handler.getLayout();
   }
 
-  observeThemeChanges() {
-    // TODO(crbug.com/1234500): Migrate to mojo as well.
-    chrome.send('observeThemeChanges');
-  }
-
   showEditDialogForGroup(
       groupId: string, locationX: number, locationY: number, width: number,
       height: number) {
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index ea42841..ecec024 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -476,7 +476,7 @@
     ReusedPasswordAccountType password_type,
     const std::string& verdict_token) {
   if (!IsIncognito()) {
-    DictionaryPrefUpdate update(
+    DictionaryPrefUpdateDeprecated update(
         profile_->GetPrefs(), prefs::kSafeBrowsingUnhandledGaiaPasswordReuses);
     // Since base::Value doesn't support int64_t type, we convert the navigation
     // ID to string format and store it in the preference dictionary.
@@ -882,7 +882,7 @@
 void ChromePasswordProtectionService::OnGaiaPasswordChanged(
     const std::string& username,
     bool is_other_gaia_password) {
-  DictionaryPrefUpdate unhandled_gaia_password_reuses(
+  DictionaryPrefUpdateDeprecated unhandled_gaia_password_reuses(
       profile_->GetPrefs(), prefs::kSafeBrowsingUnhandledGaiaPasswordReuses);
   unhandled_gaia_password_reuses->DictClear();
   if (!is_other_gaia_password)
@@ -1071,7 +1071,7 @@
         ReusedPasswordAccountType::NON_GAIA_ENTERPRISE) {
       web_contents_with_unhandled_enterprise_reuses_.erase(web_contents);
     } else {
-      DictionaryPrefUpdate update(
+      DictionaryPrefUpdateDeprecated update(
           profile_->GetPrefs(),
           prefs::kSafeBrowsingUnhandledGaiaPasswordReuses);
       update->RemoveKey(origin.Serialize());
@@ -1580,7 +1580,7 @@
         const history::URLRows& deleted_rows) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  DictionaryPrefUpdate unhandled_sync_password_reuses(
+  DictionaryPrefUpdateDeprecated unhandled_sync_password_reuses(
       profile_->GetPrefs(), prefs::kSafeBrowsingUnhandledGaiaPasswordReuses);
   if (all_history) {
     unhandled_sync_password_reuses->DictClear();
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
index 6c868744..1d02de7 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
@@ -397,7 +397,7 @@
   }
 
   int GetSizeofUnhandledSyncPasswordReuses() {
-    DictionaryPrefUpdate unhandled_sync_password_reuses(
+    DictionaryPrefUpdateDeprecated unhandled_sync_password_reuses(
         profile()->GetPrefs(), prefs::kSafeBrowsingUnhandledGaiaPasswordReuses);
     return unhandled_sync_password_reuses->DictSize();
   }
@@ -1156,8 +1156,8 @@
   GURL url_b("https://www.phishingb.com");
   GURL url_c("https://www.phishingc.com");
 
-  DictionaryPrefUpdate update(profile()->GetPrefs(),
-                              prefs::kSafeBrowsingUnhandledGaiaPasswordReuses);
+  DictionaryPrefUpdateDeprecated update(
+      profile()->GetPrefs(), prefs::kSafeBrowsingUnhandledGaiaPasswordReuses);
   update->SetKey(Origin::Create(url_a).Serialize(),
                  base::Value("navigation_id_a"));
   update->SetKey(Origin::Create(url_b).Serialize(),
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
index 625b96e..5e40df8 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
@@ -1097,8 +1097,8 @@
 
 TEST_F(ClientSideDetectionHostTest, TestPreClassificationAllowlistedByPolicy) {
   // Configures enterprise allowlist.
-  ListPrefUpdate update(profile()->GetPrefs(),
-                        prefs::kSafeBrowsingAllowlistDomains);
+  ListPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                  prefs::kSafeBrowsingAllowlistDomains);
   update->Append("example.com");
 
   EXPECT_CALL(*csd_service_, GetModelStr())
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_test_utils.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_test_utils.cc
index 99eaa9e..1e6660d 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_test_utils.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_test_utils.cc
@@ -565,7 +565,7 @@
                           enterprise_connectors::AnalysisConnector connector,
                           const std::string& pref_value,
                           bool machine_scope) {
-  ListPrefUpdate settings_list(prefs, ConnectorPref(connector));
+  ListPrefUpdateDeprecated settings_list(prefs, ConnectorPref(connector));
   DCHECK(settings_list.Get());
   if (!settings_list->GetList().empty())
     settings_list->ClearList();
@@ -581,8 +581,8 @@
     bool enabled,
     const std::set<std::string>& enabled_event_names,
     bool machine_scope) {
-  ListPrefUpdate settings_list(prefs,
-                               enterprise_connectors::kOnSecurityEventPref);
+  ListPrefUpdateDeprecated settings_list(
+      prefs, enterprise_connectors::kOnSecurityEventPref);
   DCHECK(settings_list.Get());
   if (enabled) {
     if (settings_list->GetList().empty()) {
@@ -612,7 +612,7 @@
 void ClearAnalysisConnector(
     PrefService* prefs,
     enterprise_connectors::AnalysisConnector connector) {
-  ListPrefUpdate settings_list(prefs, ConnectorPref(connector));
+  ListPrefUpdateDeprecated settings_list(prefs, ConnectorPref(connector));
   DCHECK(settings_list.Get());
   settings_list->ClearList();
   prefs->ClearPref(ConnectorScopePref(connector));
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc
index 29567fc..1ae9b73 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc
@@ -196,7 +196,8 @@
     const std::string& trigger,
     DeepScanAccessPoint access_point,
     const int64_t content_size,
-    const enterprise_connectors::ContentAnalysisResponse& response) {
+    const enterprise_connectors::ContentAnalysisResponse& response,
+    absl::optional<std::u16string> user_justification) {
   DCHECK(std::all_of(download_digest_sha256.begin(),
                      download_digest_sha256.end(), [](const char& c) {
                        return (c >= '0' && c <= '9') ||
@@ -214,7 +215,8 @@
 
     router->OnAnalysisConnectorWarningBypassed(
         url, file_name, download_digest_sha256, mime_type, trigger,
-        response.request_token(), access_point, result, content_size);
+        response.request_token(), access_point, result, content_size,
+        user_justification);
   }
 }
 
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h
index 5f2e434..2ce41d8 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h
@@ -104,7 +104,8 @@
     const std::string& trigger,
     DeepScanAccessPoint access_point,
     const int64_t content_size,
-    const enterprise_connectors::ContentAnalysisResponse& response);
+    const enterprise_connectors::ContentAnalysisResponse& response,
+    absl::optional<std::u16string> user_justification = absl::nullopt);
 
 // Helper functions to record DeepScanning UMA metrics for the duration of the
 // request split by its result and bytes/sec for successful requests.
diff --git a/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc b/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc
index f225f3f..e4482eb 100644
--- a/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc
@@ -257,7 +257,8 @@
   }
 
   void AddUrlToProfilePrefList(const char* pref_name, const GURL& url) {
-    ListPrefUpdate(profile_->GetPrefs(), pref_name)->Append(url.host());
+    ListPrefUpdateDeprecated(profile_->GetPrefs(), pref_name)
+        ->Append(url.host());
   }
 
   void SetFeatures(const std::vector<base::Feature>& enabled,
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
index 58e21a2c..d8b5c9e 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
@@ -601,8 +601,8 @@
   }
 
   void AddDomainToEnterpriseAllowlist(const std::string& domain) {
-    ListPrefUpdate update(profile()->GetPrefs(),
-                          prefs::kSafeBrowsingAllowlistDomains);
+    ListPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                    prefs::kSafeBrowsingAllowlistDomains);
     update.Get()->Append(domain);
   }
 
diff --git a/chrome/browser/safe_browsing/incident_reporting/state_store.cc b/chrome/browser/safe_browsing/incident_reporting/state_store.cc
index 8357c63..76cdb2b 100644
--- a/chrome/browser/safe_browsing/incident_reporting/state_store.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/state_store.cc
@@ -94,7 +94,7 @@
 
 base::DictionaryValue* StateStore::Transaction::GetPrefDict() {
   if (!pref_update_) {
-    pref_update_ = std::make_unique<DictionaryPrefUpdate>(
+    pref_update_ = std::make_unique<DictionaryPrefUpdateDeprecated>(
         store_->profile_->GetPrefs(), prefs::kSafeBrowsingIncidentsSent);
     // Getting the dict will cause it to be created if it doesn't exist.
     // Unconditionally refresh the store's read-only view on the preference so
diff --git a/chrome/browser/safe_browsing/incident_reporting/state_store.h b/chrome/browser/safe_browsing/incident_reporting/state_store.h
index 7270513c..239dd92 100644
--- a/chrome/browser/safe_browsing/incident_reporting/state_store.h
+++ b/chrome/browser/safe_browsing/incident_reporting/state_store.h
@@ -81,7 +81,7 @@
 
     // A ScopedUserPrefUpdate through which changes to the incidents_sent
     // preference are made.
-    std::unique_ptr<DictionaryPrefUpdate> pref_update_;
+    std::unique_ptr<DictionaryPrefUpdateDeprecated> pref_update_;
   };
 
   explicit StateStore(Profile* profile);
diff --git a/chrome/browser/search_engines/android/java/res/drawable/search_sogou.xml b/chrome/browser/search_engines/android/java/res/drawable/search_sogou.xml
index b13114e..f434ff0 100644
--- a/chrome/browser/search_engines/android/java/res/drawable/search_sogou.xml
+++ b/chrome/browser/search_engines/android/java/res/drawable/search_sogou.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="140dp"
     android:height="140dp"
     android:viewportWidth="140"
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index b2e17dc..fcf4349 100644
--- a/chrome/browser/sessions/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -864,7 +864,7 @@
           /*user_gesture=*/false);
     }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if defined(OS_CHROMEOS)
     params.restore_id = restore_id;
 #endif
 
diff --git a/chrome/browser/share/android/java/res/drawable/camera_img.xml b/chrome/browser/share/android/java/res/drawable/camera_img.xml
index 48ded87..cedf0ed 100644
--- a/chrome/browser/share/android/java/res/drawable/camera_img.xml
+++ b/chrome/browser/share/android/java/res/drawable/camera_img.xml
@@ -3,8 +3,6 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="104dp"
     android:height="104dp"
     android:viewportWidth="104"
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
index 94d00b310..b106007 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/link_to_text/LinkToTextCoordinator.java
@@ -40,8 +40,7 @@
 
     private static final String SHARE_TEXT_TEMPLATE = "\"%s\"\n";
     private static final String INVALID_SELECTOR = "";
-    private static final long TIMEOUT_MS = 100;
-    private static final long AMP_TIMEOUT_MS = 200;
+    private static final int TIMEOUT_MS = 100;
     private static final Set<String> AMP_VIEWER_DOMAINS =
             new HashSet<>(Arrays.asList("google.com/amp/", "bing.com/amp"));
     private static final int LENGTH_AMP_DOMAIN = 15;
@@ -135,24 +134,19 @@
         }
 
         if (mTab.getWebContents().getMainFrame() != mTab.getWebContents().getFocusedFrame()) {
-            if (ChromeFeatureList.isEnabled(ChromeFeatureList.SHARED_HIGHLIGHTING_AMP)
-                    && isAmpUrl(mShareUrl)) {
-                PostTask.postDelayedTask(
-                        UiThreadTaskTraits.DEFAULT, () -> timeout(), AMP_TIMEOUT_MS);
-                requestSelectorForCanonicalUrl();
+            if (!ChromeFeatureList.isEnabled(ChromeFeatureList.SHARED_HIGHLIGHTING_AMP)
+                    || !isAmpUrl(mShareUrl)) {
+                completeRequestWithFailure(LinkGenerationError.I_FRAME);
                 return;
             }
-
-            completeRequestWithFailure(LinkGenerationError.I_FRAME);
-            return;
         }
 
-        PostTask.postDelayedTask(UiThreadTaskTraits.DEFAULT, () -> timeout(), TIMEOUT_MS);
+        PostTask.postDelayedTask(UiThreadTaskTraits.DEFAULT, () -> timeout(), getTimeout());
         requestSelectorForCanonicalUrl();
     }
 
     private void reshareHighlightedText() {
-        PostTask.postDelayedTask(UiThreadTaskTraits.DEFAULT, () -> timeout(), TIMEOUT_MS);
+        PostTask.postDelayedTask(UiThreadTaskTraits.DEFAULT, () -> timeout(), getTimeout());
         setTextFragmentReceiver();
 
         if (mProducer == null) {
@@ -301,4 +295,10 @@
         LinkToTextBridge.logSuccessMetrics();
         onSelectorReady(selector);
     }
+
+    private int getTimeout() {
+        return ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
+                ChromeFeatureList.PREEMPTIVE_LINK_TO_TEXT_GENERATION, "TimeoutLengthMs",
+                TIMEOUT_MS);
+    }
 }
diff --git a/chrome/browser/sharing/sharing_sync_preference.cc b/chrome/browser/sharing/sharing_sync_preference.cc
index 6f0f0e27..67529a8 100644
--- a/chrome/browser/sharing/sharing_sync_preference.cc
+++ b/chrome/browser/sharing/sharing_sync_preference.cc
@@ -135,7 +135,7 @@
   base::Base64Encode(std::string(vapid_key.begin(), vapid_key.end()),
                      &base64_vapid_key);
 
-  DictionaryPrefUpdate update(prefs_, prefs::kSharingVapidKey);
+  DictionaryPrefUpdateDeprecated update(prefs_, prefs::kSharingVapidKey);
   update->SetStringKey(kVapidECPrivateKey, base64_vapid_key);
   update->SetKey(kVapidCreationTimestamp,
                  base::TimeToValue(creation_timestamp));
@@ -175,7 +175,7 @@
 }
 
 void SharingSyncPreference::SetFCMRegistration(FCMRegistration registration) {
-  DictionaryPrefUpdate update(prefs_, prefs::kSharingFCMRegistration);
+  DictionaryPrefUpdateDeprecated update(prefs_, prefs::kSharingFCMRegistration);
   if (registration.authorized_entity) {
     update->SetStringKey(kRegistrationAuthorizedEntity,
                          std::move(*registration.authorized_entity));
@@ -211,7 +211,7 @@
     list_value.Append(feature);
   }
 
-  DictionaryPrefUpdate local_sharing_info_update(
+  DictionaryPrefUpdateDeprecated local_sharing_info_update(
       prefs_, prefs::kSharingLocalSharingInfo);
   local_sharing_info_update->SetKey(kSharingInfoVapidTargetInfo,
                                     std::move(vapid_target_info));
diff --git a/chrome/browser/sharing/sharing_sync_preference_unittest.cc b/chrome/browser/sharing/sharing_sync_preference_unittest.cc
index 717b903..cb14cbc 100644
--- a/chrome/browser/sharing/sharing_sync_preference_unittest.cc
+++ b/chrome/browser/sharing/sharing_sync_preference_unittest.cc
@@ -64,7 +64,7 @@
 
     enabled_features.Append(feature);
 
-    DictionaryPrefUpdate local_sharing_info_update(
+    DictionaryPrefUpdateDeprecated local_sharing_info_update(
         &prefs_, prefs::kSharingLocalSharingInfo);
     local_sharing_info_update->SetKey(kSharingInfoEnabledFeatures,
                                       std::move(enabled_features));
diff --git a/chrome/browser/shell_integration_linux.cc b/chrome/browser/shell_integration_linux.cc
index 2d53c36..fead1b1 100644
--- a/chrome/browser/shell_integration_linux.cc
+++ b/chrome/browser/shell_integration_linux.cc
@@ -54,6 +54,7 @@
 #include "chrome/grit/chrome_unscaled_resources.h"
 #include "components/version_info/version_info.h"
 #include "third_party/libxml/chromium/xml_writer.h"
+#include "third_party/re2/src/re2/re2.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image_family.h"
 #include "ui/ozone/public/ozone_platform.h"
@@ -280,9 +281,12 @@
     g_key_file_set_string(key_file, section_title.c_str(), "Name",
                           info.name.c_str());
 
+    std::string launch_url_str = info.exec_launch_url.spec();
+    // Escape % as %%.
+    RE2::GlobalReplace(&launch_url_str, "%", "%%");
     base::CommandLine current_cmd(command_line);
     current_cmd.AppendSwitchASCII(switches::kAppLaunchUrlForShortcutsMenuItem,
-                                  info.exec_launch_url.spec());
+                                  launch_url_str);
 
     g_key_file_set_string(
         key_file, section_title.c_str(), "Exec",
diff --git a/chrome/browser/shell_integration_linux_unittest.cc b/chrome/browser/shell_integration_linux_unittest.cc
index 03b0322..4afc3391 100644
--- a/chrome/browser/shell_integration_linux_unittest.cc
+++ b/chrome/browser/shell_integration_linux_unittest.cc
@@ -496,6 +496,8 @@
                                       GURL("https://example.com/action3")),
            web_app::DesktopActionInfo("action4", "Action 4",
                                       GURL("https://example.com/action4")),
+           web_app::DesktopActionInfo("action5", "Action 5",
+                                      GURL("https://example.com/action%205")),
        },
 
        "#!/usr/bin/env xdg-open\n"
@@ -507,7 +509,7 @@
        "Exec=/opt/google/chrome/google-chrome --app-id=TestAppId\n"
        "Icon=IconName\n"
        "StartupWMClass=example.app\n"
-       "Actions=action1;action2;action3;action4\n\n"
+       "Actions=action1;action2;action3;action4;action5\n\n"
        "[Desktop Action action1]\n"
        "Name=Action 1\n"
        "Exec=/opt/google/chrome/google-chrome --app-id=TestAppId "
@@ -527,7 +529,12 @@
        "Name=Action 4\n"
        "Exec=/opt/google/chrome/google-chrome --app-id=TestAppId "
        "--app-launch-url-for-shortcuts-menu-item=https://example.com/"
-       "action4\n"},
+       "action4\n\n"
+       "[Desktop Action action5]\n"
+       "Name=Action 5\n"
+       "Exec=/opt/google/chrome/google-chrome --app-id=TestAppId "
+       "--app-launch-url-for-shortcuts-menu-item=https://example.com/"
+       "action%%205\n"},
   };
 
   for (size_t i = 0; i < base::size(test_cases); i++) {
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.cc b/chrome/browser/signin/dice_web_signin_interceptor.cc
index dc2955c..3cddc79 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor.cc
@@ -783,8 +783,8 @@
 
 void DiceWebSigninInterceptor::RecordProfileCreationDeclined(
     const std::string& email) {
-  DictionaryPrefUpdate update(profile_->GetPrefs(),
-                              kProfileCreationInterceptionDeclinedPref);
+  DictionaryPrefUpdateDeprecated update(
+      profile_->GetPrefs(), kProfileCreationInterceptionDeclinedPref);
   std::string key = GetPersistentEmailHash(email);
   absl::optional<int> declined_count = update->FindIntKey(key);
   update->SetIntKey(key, declined_count.value_or(0) + 1);
diff --git a/chrome/browser/signin/services/android/java/res/drawable/logo_avatar_anonymous.xml b/chrome/browser/signin/services/android/java/res/drawable/logo_avatar_anonymous.xml
index a2ff9d3..43fa9a9 100644
--- a/chrome/browser/signin/services/android/java/res/drawable/logo_avatar_anonymous.xml
+++ b/chrome/browser/signin/services/android/java/res/drawable/logo_avatar_anonymous.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="40dp"
     android:height="40dp"
     android:viewportWidth="192"
diff --git a/chrome/browser/speech/fake_speech_recognition_service.cc b/chrome/browser/speech/fake_speech_recognition_service.cc
index 1751943f..9a4cbed4 100644
--- a/chrome/browser/speech/fake_speech_recognition_service.cc
+++ b/chrome/browser/speech/fake_speech_recognition_service.cc
@@ -62,6 +62,7 @@
   capturing_audio_ = false;
   device_id_ = "";
   audio_parameters_ = absl::nullopt;
+  MarkDone();
 }
 
 void FakeSpeechRecognitionService::SendAudioToSpeechRecognitionService(
@@ -69,6 +70,10 @@
   has_received_audio_ = true;
 }
 
+void FakeSpeechRecognitionService::MarkDone() {
+  recognizer_client_remote_->OnSpeechRecognitionStopped();
+}
+
 void FakeSpeechRecognitionService::SendSpeechRecognitionResult(
     const media::SpeechRecognitionResult& result) {
   ASSERT_TRUE(recognizer_client_remote_.is_bound());
diff --git a/chrome/browser/speech/fake_speech_recognition_service.h b/chrome/browser/speech/fake_speech_recognition_service.h
index 75ac0ec..f2a38f4 100644
--- a/chrome/browser/speech/fake_speech_recognition_service.h
+++ b/chrome/browser/speech/fake_speech_recognition_service.h
@@ -58,6 +58,7 @@
   void SendAudioToSpeechRecognitionService(
       media::mojom::AudioDataS16Ptr buffer) override;
   void OnLanguageChanged(const std::string& language) override {}
+  void MarkDone() override;
 
   // Methods for testing plumbing to SpeechRecognitionRecognizerClient.
   void SendSpeechRecognitionResult(
diff --git a/chrome/browser/speech/network_speech_recognizer_browsertest.cc b/chrome/browser/speech/network_speech_recognizer_browsertest.cc
index 7abbacd2..4941fc7d 100644
--- a/chrome/browser/speech/network_speech_recognizer_browsertest.cc
+++ b/chrome/browser/speech/network_speech_recognizer_browsertest.cc
@@ -46,6 +46,7 @@
            const absl::optional<media::SpeechRecognitionResult>& timing));
   MOCK_METHOD1(OnSpeechSoundLevelChanged, void(int16_t));
   MOCK_METHOD1(OnSpeechRecognitionStateChanged, void(SpeechRecognizerStatus));
+  MOCK_METHOD0(OnSpeechRecognitionStopped, void());
 
  private:
   base::WeakPtrFactory<MockSpeechRecognizerDelegate> weak_factory_{this};
diff --git a/chrome/browser/speech/on_device_speech_recognizer.cc b/chrome/browser/speech/on_device_speech_recognizer.cc
index 1bec3db..13467bc 100644
--- a/chrome/browser/speech/on_device_speech_recognizer.cc
+++ b/chrome/browser/speech/on_device_speech_recognizer.cc
@@ -123,7 +123,7 @@
 
 void OnDeviceSpeechRecognizer::Stop() {
   audio_source_fetcher_->Stop();
-  UpdateStatus(SpeechRecognizerStatus::SPEECH_RECOGNIZER_READY);
+  UpdateStatus(SpeechRecognizerStatus::SPEECH_RECOGNITION_STOPPING);
 }
 
 void OnDeviceSpeechRecognizer::OnSpeechRecognitionRecognitionEvent(
@@ -150,6 +150,11 @@
   // Do nothing.
 }
 
+void OnDeviceSpeechRecognizer::OnSpeechRecognitionStopped() {
+  UpdateStatus(SpeechRecognizerStatus::SPEECH_RECOGNIZER_READY);
+  delegate()->OnSpeechRecognitionStopped();
+}
+
 void OnDeviceSpeechRecognizer::OnRecognizerBound(
     bool is_multichannel_supported) {
   is_multichannel_supported_ = is_multichannel_supported;
diff --git a/chrome/browser/speech/on_device_speech_recognizer.h b/chrome/browser/speech/on_device_speech_recognizer.h
index 090f0fd..24b56a24 100644
--- a/chrome/browser/speech/on_device_speech_recognizer.h
+++ b/chrome/browser/speech/on_device_speech_recognizer.h
@@ -60,6 +60,7 @@
   void OnSpeechRecognitionError() override;
   void OnLanguageIdentificationEvent(
       media::mojom::LanguageIdentificationEventPtr event) override;
+  void OnSpeechRecognitionStopped() override;
 
  private:
   friend class OnDeviceSpeechRecognizerTest;
diff --git a/chrome/browser/speech/on_device_speech_recognizer_browsertest.cc b/chrome/browser/speech/on_device_speech_recognizer_browsertest.cc
index 1df57e9..70f6188 100644
--- a/chrome/browser/speech/on_device_speech_recognizer_browsertest.cc
+++ b/chrome/browser/speech/on_device_speech_recognizer_browsertest.cc
@@ -46,6 +46,7 @@
            const absl::optional<media::SpeechRecognitionResult>& timing));
   MOCK_METHOD1(OnSpeechSoundLevelChanged, void(int16_t));
   MOCK_METHOD1(OnSpeechRecognitionStateChanged, void(SpeechRecognizerStatus));
+  MOCK_METHOD0(OnSpeechRecognitionStopped, void());
 
  private:
   base::WeakPtrFactory<MockSpeechRecognizerDelegate> weak_factory_{this};
@@ -187,9 +188,19 @@
     EXPECT_TRUE(fake_service_->is_capturing_audio());
 
     EXPECT_CALL(*mock_speech_delegate_,
+                OnSpeechRecognitionStateChanged(SPEECH_RECOGNITION_STOPPING))
+        .Times(1)
+        .RetiresOnSaturation();
+
+    EXPECT_CALL(*mock_speech_delegate_,
                 OnSpeechRecognitionStateChanged(SPEECH_RECOGNIZER_READY))
         .Times(1)
         .RetiresOnSaturation();
+
+    EXPECT_CALL(*mock_speech_delegate_, OnSpeechRecognitionStopped())
+        .Times(1)
+        .RetiresOnSaturation();
+
     recognizer_->Stop();
     base::RunLoop().RunUntilIdle();
     EXPECT_FALSE(fake_service_->is_capturing_audio());
diff --git a/chrome/browser/speech/speech_recognizer_delegate.h b/chrome/browser/speech/speech_recognizer_delegate.h
index f8fb236..e5a6bdb 100644
--- a/chrome/browser/speech/speech_recognizer_delegate.h
+++ b/chrome/browser/speech/speech_recognizer_delegate.h
@@ -23,6 +23,8 @@
   SPEECH_RECOGNIZER_IN_SPEECH,
   // There was an error.
   SPEECH_RECOGNIZER_ERROR,
+  // Stopping speech recognition.
+  SPEECH_RECOGNITION_STOPPING
 };
 
 // Delegate for speech recognizer. All methods are called from the thread on
@@ -46,6 +48,9 @@
   virtual void OnSpeechRecognitionStateChanged(
       SpeechRecognizerStatus new_state) = 0;
 
+  // Invoked when the speech recognition has stopped.
+  virtual void OnSpeechRecognitionStopped() = 0;
+
  protected:
   virtual ~SpeechRecognizerDelegate() {}
 };
diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc
index 087ea1ee..d9c838f 100644
--- a/chrome/browser/spellchecker/spellcheck_service.cc
+++ b/chrome/browser/spellchecker/spellcheck_service.cc
@@ -120,7 +120,8 @@
     // preferences for non-Hunspell languages so that there is no attempt to
     // load a non-existent Hunspell dictionary, and so that Hunspell
     // spellchecking isn't broken because of the failed load.
-    ListPrefUpdate update(prefs, spellcheck::prefs::kSpellCheckDictionaries);
+    ListPrefUpdateDeprecated update(prefs,
+                                    spellcheck::prefs::kSpellCheckDictionaries);
     update->EraseListValueIf([](const base::Value& entry) {
       return spellcheck::GetCorrespondingSpellCheckLanguage(entry.GetString())
           .empty();
@@ -653,7 +654,8 @@
   DCHECK(prefs);
   // When following object goes out of scope, preference change observers will
   // be notified (even if there is no preference change).
-  ListPrefUpdate update(prefs, spellcheck::prefs::kSpellCheckDictionaries);
+  ListPrefUpdateDeprecated update(prefs,
+                                  spellcheck::prefs::kSpellCheckDictionaries);
   update->EraseListValueIf([this](const base::Value& entry) {
     const std::string dictionary_name = entry.GetString();
     return (!UsesWindowsDictionary(dictionary_name) &&
@@ -661,7 +663,7 @@
                 .empty());
   });
 
-  // No need to call LoadDictionaries() as when the ListPrefUpdate
+  // No need to call LoadDictionaries() as when the ListPrefUpdateDeprecated
   // object goes out of scope, the preference change handler will do this.
 }
 
diff --git a/chrome/browser/subresource_redirect/DEPS b/chrome/browser/subresource_redirect/DEPS
deleted file mode 100644
index 8665069..0000000
--- a/chrome/browser/subresource_redirect/DEPS
+++ /dev/null
@@ -1,3 +0,0 @@
-include_rules = [
-  "+components/subresource_redirect/common",
-]
diff --git a/chrome/browser/subresource_redirect/DIR_METADATA b/chrome/browser/subresource_redirect/DIR_METADATA
deleted file mode 100644
index ce14958..0000000
--- a/chrome/browser/subresource_redirect/DIR_METADATA
+++ /dev/null
@@ -1,3 +0,0 @@
-monorail: {
-  component: "Internals>Network>DataProxy"
-}
diff --git a/chrome/browser/subresource_redirect/OWNERS b/chrome/browser/subresource_redirect/OWNERS
deleted file mode 100644
index 2783dea..0000000
--- a/chrome/browser/subresource_redirect/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://components/data_reduction_proxy/OWNERS
diff --git a/chrome/browser/subresource_redirect/android/previews_android_bridge.cc b/chrome/browser/subresource_redirect/android/previews_android_bridge.cc
deleted file mode 100644
index bb7d2627..0000000
--- a/chrome/browser/subresource_redirect/android/previews_android_bridge.cc
+++ /dev/null
@@ -1,51 +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/subresource_redirect/android/previews_android_bridge.h"
-
-#include <memory>
-
-#include "base/android/jni_android.h"
-#include "chrome/android/chrome_jni_headers/PreviewsAndroidBridge_jni.h"
-#include "chrome/browser/android/tab_android.h"
-#include "chrome/browser/subresource_redirect/subresource_redirect_observer.h"
-#include "content/public/browser/web_contents.h"
-
-static jlong JNI_PreviewsAndroidBridge_Init(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& obj) {
-  return reinterpret_cast<intptr_t>(new PreviewsAndroidBridge(env, obj));
-}
-
-// static
-bool PreviewsAndroidBridge::CreateHttpsImageCompressionInfoBar(
-    content::WebContents* web_contents) {
-  TabAndroid* tab_android = TabAndroid::FromWebContents(web_contents);
-  DCHECK(tab_android);
-
-  base::android::ScopedJavaLocalRef<jobject> j_tab_android =
-      tab_android->GetJavaObject();
-  DCHECK(!j_tab_android.is_null());
-
-  return Java_PreviewsAndroidBridge_createHttpsImageCompressionInfoBar(
-      base::android::AttachCurrentThread(), j_tab_android);
-}
-
-PreviewsAndroidBridge::PreviewsAndroidBridge(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& obj) {}
-
-PreviewsAndroidBridge::~PreviewsAndroidBridge() {}
-
-jboolean PreviewsAndroidBridge::IsHttpsImageCompressionApplied(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& obj,
-    const base::android::JavaParamRef<jobject>& j_web_contents) {
-  content::WebContents* web_contents =
-      content::WebContents::FromJavaWebContents(j_web_contents);
-  if (!web_contents)
-    return false;
-  return subresource_redirect::SubresourceRedirectObserver::
-      IsHttpsImageCompressionApplied(web_contents);
-}
diff --git a/chrome/browser/subresource_redirect/android/previews_android_bridge.h b/chrome/browser/subresource_redirect/android/previews_android_bridge.h
deleted file mode 100644
index 17a73c94..0000000
--- a/chrome/browser/subresource_redirect/android/previews_android_bridge.h
+++ /dev/null
@@ -1,42 +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_SUBRESOURCE_REDIRECT_ANDROID_PREVIEWS_ANDROID_BRIDGE_H_
-#define CHROME_BROWSER_SUBRESOURCE_REDIRECT_ANDROID_PREVIEWS_ANDROID_BRIDGE_H_
-
-#include "base/android/jni_android.h"
-#include "base/android/jni_string.h"
-#include "base/android/jni_weak_ref.h"
-#include "base/android/scoped_java_ref.h"
-#include "base/memory/weak_ptr.h"
-
-namespace content {
-class WebContents;
-}
-
-class PreviewsAndroidBridge {
- public:
-  // Creates InfoBar that shows https images are optimized in the
-  // |web_contents|, and returns whether InfoBar was displayed successfully.
-  static bool CreateHttpsImageCompressionInfoBar(
-      content::WebContents* web_contents);
-
-  PreviewsAndroidBridge(JNIEnv* env,
-                        const base::android::JavaParamRef<jobject>& obj);
-
-  PreviewsAndroidBridge(const PreviewsAndroidBridge&) = delete;
-  PreviewsAndroidBridge& operator=(const PreviewsAndroidBridge&) = delete;
-
-  virtual ~PreviewsAndroidBridge();
-
-  jboolean IsHttpsImageCompressionApplied(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj,
-      const base::android::JavaParamRef<jobject>& j_web_contents);
-
- private:
-  base::WeakPtrFactory<PreviewsAndroidBridge> weak_factory_{this};
-};
-
-#endif  // CHROME_BROWSER_SUBRESOURCE_REDIRECT_ANDROID_PREVIEWS_ANDROID_BRIDGE_H_
diff --git a/chrome/browser/subresource_redirect/https_image_compression_infobar_decider.cc b/chrome/browser/subresource_redirect/https_image_compression_infobar_decider.cc
deleted file mode 100644
index bc875d4..0000000
--- a/chrome/browser/subresource_redirect/https_image_compression_infobar_decider.cc
+++ /dev/null
@@ -1,102 +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.
-
-#include "chrome/browser/subresource_redirect/https_image_compression_infobar_decider.h"
-
-#include "base/command_line.h"
-#include "build/build_config.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/prefs/pref_service.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/web_contents.h"
-#include "third_party/blink/public/common/features.h"
-
-#if defined(OS_ANDROID)
-#include "chrome/browser/android/tab_android.h"
-#include "chrome/browser/android/tab_web_contents_delegate_android.h"
-#endif
-
-namespace {
-// Pref key that stores whether the user has already seen the infobar. The pref
-// is initialized as false, and updated to true when LiteMode is enabled and
-// infobar has been shown to user.
-constexpr char kHasSeenInfoBar[] =
-    "litemode.https-image-compression.user-has-seen-infobar";
-
-// The time used to compare and identify recent LiteMode users. Users who
-// enabled LiteMode before this time are treated as non-recent and the one-time
-// https image compression InfoBar is shown for them. Set approximate as M88
-// release date, which is the target for https image compression V2 feature.
-constexpr char kRecentLiteModeUserEnableTime[] = "2021-01-19T00:00:01Z";
-
-}  // namespace
-
-HttpsImageCompressionInfoBarDecider::HttpsImageCompressionInfoBarDecider(
-    PrefService* pref_service,
-    data_reduction_proxy::DataReductionProxySettings* drp_settings)
-    : pref_service_(pref_service) {
-  if (!pref_service_ || !drp_settings)
-    return;
-  // The infobar only needs to be shown if the user has never seen it before,
-  // is an existing LiteMode user, and did not recently enable LiteMode.
-  need_to_show_infobar_ =
-      base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect) &&
-      drp_settings->IsDataReductionProxyEnabled() &&
-      !pref_service_->GetBoolean(kHasSeenInfoBar);
-  if (need_to_show_infobar_) {
-    const auto last_enabled_time = drp_settings->GetLastEnabledTime();
-    if (!last_enabled_time.is_null()) {
-      base::Time recent_lite_mode_user_enable_time;
-      bool success = base::Time::FromUTCString(
-          kRecentLiteModeUserEnableTime, &recent_lite_mode_user_enable_time);
-      DCHECK(success);
-      need_to_show_infobar_ =
-          last_enabled_time < recent_lite_mode_user_enable_time;
-    }
-  }
-}
-
-// static
-void HttpsImageCompressionInfoBarDecider::RegisterProfilePrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterBooleanPref(kHasSeenInfoBar, false);
-}
-
-bool HttpsImageCompressionInfoBarDecider::NeedToShowInfoBar() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect));
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          data_reduction_proxy::switches::
-              kOverrideHttpsImageCompressionInfobar)) {
-    return false;
-  }
-  return need_to_show_infobar_;
-}
-
-bool HttpsImageCompressionInfoBarDecider::CanShowInfoBar(
-    content::NavigationHandle* navigation_handle) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect));
-  if (!navigation_handle->GetURL().SchemeIs(url::kHttpsScheme))
-    return false;
-#if defined(OS_ANDROID)
-  auto* tab_android =
-      TabAndroid::FromWebContents(navigation_handle->GetWebContents());
-  if (!tab_android || tab_android->IsCustomTab())
-    return false;
-#endif
-  return true;
-}
-
-void HttpsImageCompressionInfoBarDecider::SetUserHasSeenInfoBar() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(pref_service_);
-  need_to_show_infobar_ = false;
-  pref_service_->SetBoolean(kHasSeenInfoBar, true);
-}
diff --git a/chrome/browser/subresource_redirect/https_image_compression_infobar_decider.h b/chrome/browser/subresource_redirect/https_image_compression_infobar_decider.h
deleted file mode 100644
index 0e4fac6..0000000
--- a/chrome/browser/subresource_redirect/https_image_compression_infobar_decider.h
+++ /dev/null
@@ -1,62 +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 CHROME_BROWSER_SUBRESOURCE_REDIRECT_HTTPS_IMAGE_COMPRESSION_INFOBAR_DECIDER_H_
-#define CHROME_BROWSER_SUBRESOURCE_REDIRECT_HTTPS_IMAGE_COMPRESSION_INFOBAR_DECIDER_H_
-
-#include "base/memory/raw_ptr.h"
-#include "base/sequence_checker.h"
-
-class PrefService;
-
-namespace content {
-class NavigationHandle;
-}
-
-namespace data_reduction_proxy {
-class DataReductionProxySettings;
-}
-
-namespace user_prefs {
-class PrefRegistrySyncable;
-}
-
-// This specifies an interface for deciding to show the InfoBar that notifies
-// the user that LiteMode now also optimizes images in HTTPS pages.
-class HttpsImageCompressionInfoBarDecider {
- public:
-  HttpsImageCompressionInfoBarDecider(
-      PrefService* pref_service,
-      data_reduction_proxy::DataReductionProxySettings* drp_settings);
-
-  HttpsImageCompressionInfoBarDecider(
-      const HttpsImageCompressionInfoBarDecider&) = delete;
-  HttpsImageCompressionInfoBarDecider& operator=(
-      const HttpsImageCompressionInfoBarDecider&) = delete;
-
-  // Registers the prefs used in this class.
-  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
-
-  // Returns true if the infobar needs to be shown to the user before this https
-  // image compression can be applied.
-  bool NeedToShowInfoBar();
-
-  // Returns whether the infobar can be shown for the |navigation_handle|.
-  // Infobar should not be shown for non-https, CCT pages, etc.
-  bool CanShowInfoBar(content::NavigationHandle* navigation_handle);
-
-  // Sets that the user has seen the infobar.
-  void SetUserHasSeenInfoBar();
-
- private:
-  // A reference to the profile's |PrefService|.
-  raw_ptr<PrefService> pref_service_ = nullptr;
-
-  // Whether the infobar infobar needs to be shown to the user.
-  bool need_to_show_infobar_ = false;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-};
-
-#endif  // CHROME_BROWSER_SUBRESOURCE_REDIRECT_HTTPS_IMAGE_COMPRESSION_INFOBAR_DECIDER_H_
diff --git a/chrome/browser/subresource_redirect/https_image_compression_infobar_decider_unittest.cc b/chrome/browser/subresource_redirect/https_image_compression_infobar_decider_unittest.cc
deleted file mode 100644
index 86c5e2c..0000000
--- a/chrome/browser/subresource_redirect/https_image_compression_infobar_decider_unittest.cc
+++ /dev/null
@@ -1,151 +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.
-
-#include "chrome/browser/subresource_redirect/https_image_compression_infobar_decider.h"
-
-#include <memory>
-
-#include "base/command_line.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/task_environment.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
-#include "chrome/browser/data_use_measurement/chrome_data_use_measurement.h"
-#include "chrome/test/base/chrome_render_view_host_test_harness.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
-#include "components/data_reduction_proxy/core/browser/data_store_impl.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
-#include "components/prefs/pref_service.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
-#include "content/public/test/web_contents_tester.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/features.h"
-
-namespace {
-const char kTestUrl[] = "http://www.test.com/";
-}
-
-class HttpsImageCompressionInfoBarDeciderPrefTest
-    : public ChromeRenderViewHostTestHarness {
- protected:
-
-  HttpsImageCompressionInfoBarDecider* GetDeciderWithDRPEnabled(bool enabled) {
-    data_reduction_proxy::DataReductionProxySettings::
-        SetDataSaverEnabledForTesting(profile()->GetPrefs(), enabled);
-
-    if (!data_use_measurement::ChromeDataUseMeasurement::GetInstance()) {
-      data_use_measurement::ChromeDataUseMeasurement::CreateInstance(
-          g_browser_process->local_state());
-    }
-
-    auto* drp_settings =
-        DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
-            profile());
-    drp_settings->InitDataReductionProxySettings(
-        profile(),
-        std::make_unique<data_reduction_proxy::DataStoreImpl>(
-            profile()->GetPath()),
-        task_environment()->GetMainThreadTaskRunner());
-
-    decider_ = std::make_unique<HttpsImageCompressionInfoBarDecider>(
-        profile()->GetPrefs(), drp_settings);
-
-    return decider_.get();
-  }
-
-  // Sets the last enabled time of LiteMode in prefs.
-  void SetLiteModeLastEnableDate(const char* enabled_time) {
-    base::Time time;
-    EXPECT_TRUE(base::Time::FromUTCString(enabled_time, &time));
-    profile()->GetPrefs()->SetInt64(
-        data_reduction_proxy::prefs::kDataReductionProxyLastEnabledTime,
-        time.ToInternalValue());
-  }
-
- private:
-  std::unique_ptr<HttpsImageCompressionInfoBarDecider> decider_;
-  base::test::ScopedFeatureList scoped_feature_list_{
-      blink::features::kSubresourceRedirect};
-};
-
-TEST_F(HttpsImageCompressionInfoBarDeciderPrefTest, TestDRPDisabled) {
-  HttpsImageCompressionInfoBarDecider* decider =
-      GetDeciderWithDRPEnabled(false);
-  EXPECT_FALSE(decider->NeedToShowInfoBar());
-
-  content::WebContentsTester::For(web_contents())
-      ->NavigateAndCommit(GURL(kTestUrl));
-
-  // Should still be false after a navigation
-  EXPECT_FALSE(decider->NeedToShowInfoBar());
-}
-
-TEST_F(HttpsImageCompressionInfoBarDeciderPrefTest, TestDRPEnabled) {
-  HttpsImageCompressionInfoBarDecider* decider = GetDeciderWithDRPEnabled(true);
-  EXPECT_TRUE(decider->NeedToShowInfoBar());
-
-  content::WebContentsTester::For(web_contents())
-      ->NavigateAndCommit(GURL(kTestUrl));
-
-  // Should still be true after a navigation
-  EXPECT_TRUE(decider->NeedToShowInfoBar());
-}
-
-TEST_F(HttpsImageCompressionInfoBarDeciderPrefTest,
-       TestDRPEnabledCmdLineIgnored) {
-  HttpsImageCompressionInfoBarDecider* decider = GetDeciderWithDRPEnabled(true);
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      data_reduction_proxy::switches::kOverrideHttpsImageCompressionInfobar);
-  EXPECT_FALSE(decider->NeedToShowInfoBar());
-
-  content::WebContentsTester::For(web_contents())
-      ->NavigateAndCommit(GURL(kTestUrl));
-
-  // Should still be false after a navigation.
-  EXPECT_FALSE(decider->NeedToShowInfoBar());
-}
-
-TEST_F(HttpsImageCompressionInfoBarDeciderPrefTest, TestDRPEnabledThenNotify) {
-  HttpsImageCompressionInfoBarDecider* decider = GetDeciderWithDRPEnabled(true);
-  EXPECT_TRUE(decider->NeedToShowInfoBar());
-
-  // Simulate the callback being run.
-  decider->SetUserHasSeenInfoBar();
-
-  content::WebContentsTester::For(web_contents())
-      ->NavigateAndCommit(GURL(kTestUrl));
-
-  EXPECT_FALSE(decider->NeedToShowInfoBar());
-}
-
-TEST_F(HttpsImageCompressionInfoBarDeciderPrefTest, TestRecentLiteModeUser) {
-  SetLiteModeLastEnableDate("2021-12-01T00:00:01Z");
-  HttpsImageCompressionInfoBarDecider* decider = GetDeciderWithDRPEnabled(true);
-  EXPECT_FALSE(decider->NeedToShowInfoBar());
-
-  content::WebContentsTester::For(web_contents())
-      ->NavigateAndCommit(GURL(kTestUrl));
-
-  // Should still be false after a navigation.
-  EXPECT_FALSE(decider->NeedToShowInfoBar());
-}
-
-TEST_F(HttpsImageCompressionInfoBarDeciderPrefTest, TestNonRecentLiteModeUser) {
-  HttpsImageCompressionInfoBarDecider* decider = GetDeciderWithDRPEnabled(true);
-  SetLiteModeLastEnableDate("2021-01-01T00:00:01Z");
-  EXPECT_TRUE(decider->NeedToShowInfoBar());
-  decider->SetUserHasSeenInfoBar();
-  EXPECT_FALSE(decider->NeedToShowInfoBar());
-
-  content::WebContentsTester::For(web_contents())
-      ->NavigateAndCommit(GURL(kTestUrl));
-
-  EXPECT_FALSE(decider->NeedToShowInfoBar());
-}
diff --git a/chrome/browser/subresource_redirect/litepages_service_bypass_decider.cc b/chrome/browser/subresource_redirect/litepages_service_bypass_decider.cc
deleted file mode 100644
index 7fed565..0000000
--- a/chrome/browser/subresource_redirect/litepages_service_bypass_decider.cc
+++ /dev/null
@@ -1,60 +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.
-
-#include "chrome/browser/subresource_redirect/litepages_service_bypass_decider.h"
-
-#include "base/metrics/histogram_functions.h"
-#include "chrome/browser/subresource_redirect/subresource_redirect_util.h"
-#include "components/subresource_redirect/common/subresource_redirect_features.h"
-#include "net/http/http_status_code.h"
-#include "third_party/blink/public/common/features.h"
-
-LitePagesServiceBypassDecider::LitePagesServiceBypassDecider() = default;
-
-LitePagesServiceBypassDecider::~LitePagesServiceBypassDecider() = default;
-
-bool LitePagesServiceBypassDecider::ShouldAllowNow() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(subresource_redirect::ShouldEnablePublicImageHintsBasedCompression() ||
-         subresource_redirect::ShouldEnableRobotsRulesFetching());
-  bool should_allow =
-      !bypassed_until_time_ || base::TimeTicks::Now() > bypassed_until_time_;
-  base::UmaHistogramBoolean("SubresourceRedirect.LitePagesService.BypassResult",
-                            !should_allow);
-  return should_allow;
-}
-
-void LitePagesServiceBypassDecider::NotifyFetchFailureWithResponseCode(
-    int response_code,
-    base::TimeDelta retry_after) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(subresource_redirect::ShouldEnablePublicImageHintsBasedCompression() ||
-         subresource_redirect::ShouldEnableRobotsRulesFetching());
-  if (response_code == net::HTTP_SERVICE_UNAVAILABLE ||
-      response_code == net::HTTP_FORBIDDEN) {
-    NotifyFetchFailure(retry_after);
-  }
-}
-
-void LitePagesServiceBypassDecider::NotifyFetchFailure(
-    base::TimeDelta retry_after) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(subresource_redirect::ShouldEnablePublicImageHintsBasedCompression() ||
-         subresource_redirect::ShouldEnableRobotsRulesFetching());
-  if (!retry_after.is_zero()) {
-    // Choose the time mentioned in retry_after, but cap it to a max value.
-    retry_after = std::min(
-        retry_after, subresource_redirect::GetLitePagesBypassMaxDuration());
-  } else {
-    // Bypass for a random duration.
-    retry_after = subresource_redirect::GetLitePagesBypassRandomDuration();
-  }
-  // Take the maximum possible bypass duration.
-  bypassed_until_time_ = bypassed_until_time_
-                             ? std::max(*bypassed_until_time_,
-                                        base::TimeTicks::Now() + retry_after)
-                             : base::TimeTicks::Now() + retry_after;
-  base::UmaHistogramLongTimes("SubresourceRedirect.BypassDuration",
-                              retry_after);
-}
diff --git a/chrome/browser/subresource_redirect/litepages_service_bypass_decider.h b/chrome/browser/subresource_redirect/litepages_service_bypass_decider.h
deleted file mode 100644
index cd7e4df6..0000000
--- a/chrome/browser/subresource_redirect/litepages_service_bypass_decider.h
+++ /dev/null
@@ -1,52 +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 CHROME_BROWSER_SUBRESOURCE_REDIRECT_LITEPAGES_SERVICE_BYPASS_DECIDER_H_
-#define CHROME_BROWSER_SUBRESOURCE_REDIRECT_LITEPAGES_SERVICE_BYPASS_DECIDER_H_
-
-#include "base/memory/weak_ptr.h"
-#include "base/sequence_checker.h"
-#include "base/time/time.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-// Interface to decide whether LitePages service should be bypassed, which is
-// used for fetching compressed image and fetching robots.txt. Whenever an
-// LitePages server fetch fails, subsequent fetches to LitePages is turned off
-// for a random 1-5 minute duration or until the time mentioned in Retry-After
-// response header from the server or up to a maximum duration specified by
-// experimental params.
-class LitePagesServiceBypassDecider
-    : public base::SupportsWeakPtr<LitePagesServiceBypassDecider> {
- public:
-  LitePagesServiceBypassDecider();
-  ~LitePagesServiceBypassDecider();
-
-  // Returns whether a fetch to LitePages service should be allowed.
-  bool ShouldAllowNow();
-
-  // Notifies the decider that a LitePages fetch had failed, with the
-  // |response_code. This will start bypassing subsequent LitePage fetches.
-  void NotifyFetchFailureWithResponseCode(int response_code,
-                                          base::TimeDelta retry_after);
-
-  // Notifies the decider that a LitePages fetch had failed, which will
-  // start bypassing subsequent LitePage fetches.
-  void NotifyFetchFailure(base::TimeDelta retry_after);
-
-  absl::optional<base::TimeTicks> GetBypassUntilTimeForTesting() const {
-    return bypassed_until_time_;
-  }
-  void SetBypassUntilTimeForTesting(base::TimeTicks bypass_until) {
-    bypassed_until_time_ = bypass_until;
-  }
-
- private:
-  // The time until which image compression should be bypassed. Null time
-  // indicates no bypass.
-  absl::optional<base::TimeTicks> bypassed_until_time_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-};
-
-#endif  // CHROME_BROWSER_SUBRESOURCE_REDIRECT_LITEPAGES_SERVICE_BYPASS_DECIDER_H_
diff --git a/chrome/browser/subresource_redirect/litepages_service_bypass_decider_unittest.cc b/chrome/browser/subresource_redirect/litepages_service_bypass_decider_unittest.cc
deleted file mode 100644
index f48680e9..0000000
--- a/chrome/browser/subresource_redirect/litepages_service_bypass_decider_unittest.cc
+++ /dev/null
@@ -1,84 +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.
-
-#include "chrome/browser/subresource_redirect/litepages_service_bypass_decider.h"
-
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/task_environment.h"
-#include "base/time/time.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/features.h"
-
-class LitePagesServiceBypassDeciderTest : public testing::Test {
- public:
-  void SetUp() override {
-    scoped_feature_list_.InitAndEnableFeature(
-        blink::features::kSubresourceRedirect);
-  }
-
- protected:
-  base::test::SingleThreadTaskEnvironment task_environment_{
-      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
-  base::test::ScopedFeatureList scoped_feature_list_;
-  base::HistogramTester histogram_tester_;
-  LitePagesServiceBypassDecider litepages_service_bypass_decider_;
-};
-
-TEST_F(LitePagesServiceBypassDeciderTest, TestNoBypassOnInit) {
-  EXPECT_TRUE(litepages_service_bypass_decider_.ShouldAllowNow());
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LitePagesService.BypassResult", false, 1);
-}
-
-// When a LitePages fetch fails, it should be bypassed for a random duration.
-TEST_F(LitePagesServiceBypassDeciderTest, TestRandomBypass) {
-  litepages_service_bypass_decider_.NotifyFetchFailure(base::TimeDelta());
-  histogram_tester_.ExpectTotalCount("SubresourceRedirect.BypassDuration", 1);
-  EXPECT_FALSE(litepages_service_bypass_decider_.ShouldAllowNow());
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LitePagesService.BypassResult", true, 1);
-
-  // Subsequent fetches are bypassed until a minimum of one minute.
-  task_environment_.FastForwardBy(base::Seconds(59));
-  EXPECT_FALSE(litepages_service_bypass_decider_.ShouldAllowNow());
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LitePagesService.BypassResult", true, 2);
-
-  // After another 5 minutes, bypass should get disabled.
-  task_environment_.FastForwardBy(base::Minutes(5));
-  EXPECT_TRUE(litepages_service_bypass_decider_.ShouldAllowNow());
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LitePagesService.BypassResult", false, 1);
-}
-
-TEST_F(LitePagesServiceBypassDeciderTest, TestExactBypass) {
-  // Bypass for 30 seconds
-  litepages_service_bypass_decider_.NotifyFetchFailure(base::Seconds(30));
-  histogram_tester_.ExpectUniqueSample("SubresourceRedirect.BypassDuration",
-                                       30000, 1);
-  EXPECT_FALSE(litepages_service_bypass_decider_.ShouldAllowNow());
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LitePagesService.BypassResult", true, 1);
-
-  task_environment_.FastForwardBy(base::Seconds(31));
-  EXPECT_TRUE(litepages_service_bypass_decider_.ShouldAllowNow());
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LitePagesService.BypassResult", false, 1);
-}
-
-TEST_F(LitePagesServiceBypassDeciderTest, TestInvalidBypassDuration) {
-  // Bypass for too long duration will limit the bypass to only 5 minutes.
-  litepages_service_bypass_decider_.NotifyFetchFailure(base::Minutes(6));
-  histogram_tester_.ExpectUniqueSample("SubresourceRedirect.BypassDuration",
-                                       5 * 60 * 1000, 1);
-  EXPECT_FALSE(litepages_service_bypass_decider_.ShouldAllowNow());
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LitePagesService.BypassResult", true, 1);
-
-  task_environment_.FastForwardBy(base::Minutes(6));
-  EXPECT_TRUE(litepages_service_bypass_decider_.ShouldAllowNow());
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LitePagesService.BypassResult", false, 1);
-}
diff --git a/chrome/browser/subresource_redirect/origin_robots_rules.cc b/chrome/browser/subresource_redirect/origin_robots_rules.cc
deleted file mode 100644
index b1273f0..0000000
--- a/chrome/browser/subresource_redirect/origin_robots_rules.cc
+++ /dev/null
@@ -1,150 +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.
-
-#include "chrome/browser/subresource_redirect/origin_robots_rules.h"
-
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/time/time.h"
-#include "chrome/browser/subresource_redirect/subresource_redirect_util.h"
-#include "components/variations/net/variations_http_headers.h"
-#include "net/http/http_request_headers.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_status_code.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-#include "services/network/public/mojom/url_response_head.mojom.h"
-
-namespace subresource_redirect {
-
-OriginRobotsRules::FetcherInfo::FetcherInfo(
-    std::unique_ptr<network::SimpleURLLoader> url_loader,
-    OriginRobotsRules::NotifyResponseErrorCallback response_error_callback)
-    : url_loader(std::move(url_loader)),
-      response_error_callback(std::move(response_error_callback)) {}
-
-OriginRobotsRules::FetcherInfo::~FetcherInfo() = default;
-
-OriginRobotsRules::OriginRobotsRules(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const url::Origin& origin,
-    NotifyResponseErrorCallback response_error_callback) {
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("litepages_robots_rules",
-                                          R"(
-        semantics {
-          sender: "LitePages"
-          description:
-            "Requests robots.txt rules from the LitePages robots.txt Service "
-            "to use in providing data saving optimizations for Chrome."
-          trigger:
-            "Requested for each unique origin for the images contained in the "
-            "page, and cached for certain period."
-          data: "A list of allowed and disallowed robots.txt path patterns"
-          destination: GOOGLE_OWNED_SERVICE
-        }
-        policy {
-          cookies_allowed: NO
-          setting:
-            "Users can control Lite mode on Android via 'Lite mode' setting."
-          chrome_policy {
-            DataCompressionProxyEnabled {
-              DataCompressionProxyEnabled: false
-            }
-          }
-        })");
-
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  resource_request->url = GetRobotsServerURL(origin);
-  resource_request->method = "GET";
-  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
-
-  auto url_loader = variations::CreateSimpleURLLoaderWithVariationsHeader(
-      std::move(resource_request), variations::InIncognito::kNo,
-      variations::SignedIn::kNo, traffic_annotation);
-
-  // url_loader should retry on network changes, but no retries on other
-  // failures such as 5xx errors.
-  url_loader->SetRetryOptions(
-      1 /* max_retries */, network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
-
-  url_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-      url_loader_factory.get(),
-      base::BindOnce(&OriginRobotsRules::OnURLLoadComplete,
-                     base::Unretained(this)));
-
-  fetcher_info_ = std::make_unique<FetcherInfo>(
-      std::move(url_loader), std::move(response_error_callback));
-}
-
-OriginRobotsRules::~OriginRobotsRules() {
-  if (fetcher_info_) {
-    for (auto& callback : fetcher_info_->pending_callbacks)
-      std::move(callback).Run(absl::nullopt);
-  }
-}
-
-void OriginRobotsRules::GetRobotsRules(RobotsRulesReceivedCallback callback) {
-  if (fetcher_info_) {
-    // Robots rules fetch is still in progress.
-    fetcher_info_->pending_callbacks.push_back(std::move(callback));
-    return;
-  }
-  std::move(callback).Run(robots_rules_);
-}
-
-void OriginRobotsRules::OnURLLoadComplete(
-    std::unique_ptr<std::string> response_body) {
-  const auto response_headers =
-      fetcher_info_->url_loader->ResponseInfo()
-          ? fetcher_info_->url_loader->ResponseInfo()->headers
-          : nullptr;
-  int response_code = response_headers ? response_headers->response_code() : -1;
-
-  int net_error = fetcher_info_->url_loader->NetError();
-
-  UMA_HISTOGRAM_BOOLEAN(
-      "SubresourceRedirect.RobotsRulesFetcher.CacheHit",
-      fetcher_info_->url_loader->ResponseInfo()
-          ? fetcher_info_->url_loader->ResponseInfo()->was_fetched_via_cache
-          : false);
-  base::UmaHistogramSparse(
-      "SubresourceRedirect.RobotsRulesFetcher.NetErrorCode", -net_error);
-  if (response_code != -1) {
-    UMA_HISTOGRAM_ENUMERATION(
-        "SubresourceRedirect.RobotsRulesFetcher.ResponseCode",
-        static_cast<net::HttpStatusCode>(response_code),
-        net::HTTP_VERSION_NOT_SUPPORTED);
-  }
-
-  // Treat 4xx, 5xx as failures
-  if (response_code >= 400 && response_code <= 599) {
-    std::string retry_after_string;
-    base::TimeDelta retry_after;
-    if (response_headers &&
-        response_headers->EnumerateHeader(nullptr, "Retry-After",
-                                          &retry_after_string) &&
-        net::HttpUtil::ParseRetryAfterHeader(retry_after_string,
-                                             base::Time::Now(), &retry_after)) {
-      std::move(fetcher_info_->response_error_callback)
-          .Run(response_code, retry_after);
-    } else {
-      std::move(fetcher_info_->response_error_callback)
-          .Run(response_code, base::TimeDelta());
-    }
-  }
-
-  if (response_body && net_error == net::OK &&
-      (response_code == net::HTTP_OK ||
-       response_code == net::HTTP_NOT_MODIFIED)) {
-    robots_rules_ = *response_body;
-  }
-  for (auto& callback : fetcher_info_->pending_callbacks)
-    std::move(callback).Run(robots_rules_);
-  fetcher_info_.reset();
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/browser/subresource_redirect/origin_robots_rules.h b/chrome/browser/subresource_redirect/origin_robots_rules.h
deleted file mode 100644
index 6083de4f..0000000
--- a/chrome/browser/subresource_redirect/origin_robots_rules.h
+++ /dev/null
@@ -1,82 +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 CHROME_BROWSER_SUBRESOURCE_REDIRECT_ORIGIN_ROBOTS_RULES_H_
-#define CHROME_BROWSER_SUBRESOURCE_REDIRECT_ORIGIN_ROBOTS_RULES_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/memory/scoped_refptr.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "url/origin.h"
-
-namespace base {
-class TimeDelta;
-}
-
-namespace network {
-class SharedURLLoaderFactory;
-class SimpleURLLoader;
-}  // namespace network
-
-namespace subresource_redirect {
-
-// Holds the robots rules for one origin. Fetches the robots rules on creation.
-class OriginRobotsRules {
- public:
-  // The callback to send the received robots rules. absl::nullopt will be sent
-  // when rule fetch fails.
-  using RobotsRulesReceivedCallback =
-      base::OnceCallback<void(absl::optional<std::string>)>;
-
-  // The callback to notify 4xx, 5xx response codes. Sends the response code and
-  // retry-after response header.
-  using NotifyResponseErrorCallback =
-      base::OnceCallback<void(int, base::TimeDelta)>;
-
-  OriginRobotsRules(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const url::Origin& origin,
-      NotifyResponseErrorCallback response_error_callback);
-  ~OriginRobotsRules();
-
-  // Get the robots rules for this origin. The callback is called immediately if
-  // rules have been fetched. When rules fetch is in progress, the callback will
-  // happen after it is complete.
-  void GetRobotsRules(RobotsRulesReceivedCallback callback);
-
- private:
-  // Holds the info pertaining to when robots rules are fetched.
-  struct FetcherInfo {
-    FetcherInfo(std::unique_ptr<network::SimpleURLLoader> url_loader,
-                NotifyResponseErrorCallback response_error_callback);
-    ~FetcherInfo();
-
-    // Holds the URLLoader when robots rules are fetched.
-    std::unique_ptr<network::SimpleURLLoader> url_loader;
-
-    // Contains the requests that are pending for robots rules to be received.
-    std::vector<RobotsRulesReceivedCallback> pending_callbacks;
-
-    // Callback to notify response errors.
-    NotifyResponseErrorCallback response_error_callback;
-  };
-
-  // URL loader completion callback.
-  void OnURLLoadComplete(std::unique_ptr<std::string> response_body);
-
-  // The received robots rules. Set when rules fetch completes successfully.
-  absl::optional<std::string> robots_rules_;
-
-  // Holds the robots rules fetcher state. Exists only when fetch is in
-  // progress.
-  std::unique_ptr<FetcherInfo> fetcher_info_;
-};
-
-}  // namespace subresource_redirect
-
-#endif  // CHROME_BROWSER_SUBRESOURCE_REDIRECT_ORIGIN_ROBOTS_RULES_H_
diff --git a/chrome/browser/subresource_redirect/origin_robots_rules_cache.cc b/chrome/browser/subresource_redirect/origin_robots_rules_cache.cc
deleted file mode 100644
index a99b28e0..0000000
--- a/chrome/browser/subresource_redirect/origin_robots_rules_cache.cc
+++ /dev/null
@@ -1,51 +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.
-
-#include "chrome/browser/subresource_redirect/origin_robots_rules_cache.h"
-
-#include "base/metrics/histogram_functions.h"
-#include "chrome/browser/subresource_redirect/litepages_service_bypass_decider.h"
-#include "chrome/browser/subresource_redirect/origin_robots_rules.h"
-#include "chrome/browser/subresource_redirect/subresource_redirect_util.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-
-namespace subresource_redirect {
-
-OriginRobotsRulesCache::OriginRobotsRulesCache(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    base::WeakPtr<LitePagesServiceBypassDecider>
-        litepages_service_bypass_decider)
-    : rules_cache_(MaxOriginRobotsRulesCacheSize()),
-      url_loader_factory_(std::move(url_loader_factory)),
-      litepages_service_bypass_decider_(
-          std::move(litepages_service_bypass_decider)) {}
-
-OriginRobotsRulesCache::~OriginRobotsRulesCache() = default;
-
-void OriginRobotsRulesCache::GetRobotsRules(
-    const url::Origin& origin,
-    OriginRobotsRules::RobotsRulesReceivedCallback callback) {
-  DCHECK(!origin.opaque());
-  if (!litepages_service_bypass_decider_ ||
-      !litepages_service_bypass_decider_->ShouldAllowNow()) {
-    std::move(callback).Run(absl::nullopt);
-    return;
-  }
-  auto rules = rules_cache_.Get(origin);
-  base::UmaHistogramBoolean(
-      "SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit",
-      rules != rules_cache_.end());
-  if (rules == rules_cache_.end()) {
-    // Create new rules fetcher
-    rules = rules_cache_.Put(
-        origin, std::make_unique<OriginRobotsRules>(
-                    url_loader_factory_, origin,
-                    base::BindOnce(&LitePagesServiceBypassDecider::
-                                       NotifyFetchFailureWithResponseCode,
-                                   litepages_service_bypass_decider_)));
-  }
-  rules->second->GetRobotsRules(std::move(callback));
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/browser/subresource_redirect/origin_robots_rules_cache.h b/chrome/browser/subresource_redirect/origin_robots_rules_cache.h
deleted file mode 100644
index 1277aa7..0000000
--- a/chrome/browser/subresource_redirect/origin_robots_rules_cache.h
+++ /dev/null
@@ -1,52 +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 CHROME_BROWSER_SUBRESOURCE_REDIRECT_ORIGIN_ROBOTS_RULES_CACHE_H_
-#define CHROME_BROWSER_SUBRESOURCE_REDIRECT_ORIGIN_ROBOTS_RULES_CACHE_H_
-
-#include "base/containers/lru_cache.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/subresource_redirect/origin_robots_rules.h"
-#include "url/origin.h"
-
-class LitePagesServiceBypassDecider;
-
-namespace network {
-class SharedURLLoaderFactory;
-}  // namespace network
-
-namespace subresource_redirect {
-
-// Cache that maintains robots rules for multiple origins.
-class OriginRobotsRulesCache {
- public:
-  OriginRobotsRulesCache(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      base::WeakPtr<LitePagesServiceBypassDecider>
-          litepages_service_bypass_decider);
-  ~OriginRobotsRulesCache();
-
-  OriginRobotsRulesCache(const OriginRobotsRulesCache&) = delete;
-  OriginRobotsRulesCache& operator=(const OriginRobotsRulesCache&) = delete;
-
-  // Gets the robots rules for the origin and invokes the callback with the
-  // result. When the rules are missing in the cache, it will be fetched from
-  // the LitePages service.
-  void GetRobotsRules(const url::Origin& origin,
-                      OriginRobotsRules::RobotsRulesReceivedCallback callback);
-
- private:
-  // Cache keyed by origin
-  base::LRUCache<url::Origin, std::unique_ptr<OriginRobotsRules>> rules_cache_;
-
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
-  base::WeakPtr<LitePagesServiceBypassDecider>
-      litepages_service_bypass_decider_;
-};
-
-}  // namespace subresource_redirect
-
-#endif  // CHROME_BROWSER_SUBRESOURCE_REDIRECT_ORIGIN_ROBOTS_RULES_CACHE_H_
diff --git a/chrome/browser/subresource_redirect/origin_robots_rules_unittest.cc b/chrome/browser/subresource_redirect/origin_robots_rules_unittest.cc
deleted file mode 100644
index a9d62cf..0000000
--- a/chrome/browser/subresource_redirect/origin_robots_rules_unittest.cc
+++ /dev/null
@@ -1,270 +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.
-
-#include "chrome/browser/subresource_redirect/origin_robots_rules.h"
-#include "base/callback.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/run_loop.h"
-#include "base/strings/strcat.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/task_environment.h"
-#include "base/time/time.h"
-#include "components/variations/scoped_variations_ids_provider.h"
-#include "net/base/escape.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
-#include "services/network/public/mojom/url_response_head.mojom.h"
-#include "services/network/test/test_url_loader_factory.h"
-#include "services/network/test/test_utils.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/common/features.h"
-
-namespace subresource_redirect {
-
-constexpr char kLitePagesURL[] = "https://litepages.googlezip.net/robots?u=";
-constexpr char kFooOrigin[] = "https://foo.com/";
-constexpr char kTestResponse[] = "TEST RESPONSE";
-
-class RobotsRulesFetcherState {
- public:
-  OriginRobotsRules::RobotsRulesReceivedCallback
-  GetRobotsRulesReceivedCallback() {
-    return base::BindOnce(&RobotsRulesFetcherState::OnRobotsRulesReceived,
-                          weak_ptr_factory_.GetWeakPtr());
-  }
-
-  OriginRobotsRules::NotifyResponseErrorCallback GetResponseErrorCallback() {
-    return base::BindOnce(&RobotsRulesFetcherState::OnResponseErrorReceived,
-                          weak_ptr_factory_.GetWeakPtr());
-  }
-
- private:
-  friend class SubresourceRedirectOriginRobotsRulesTest;
-
-  void OnResponseErrorReceived(int response_code, base::TimeDelta retry_after) {
-    EXPECT_FALSE(response_error_received_.has_value());
-    response_error_received_ =
-        std::pair<int, base::TimeDelta>(response_code, retry_after);
-  }
-
-  void OnRobotsRulesReceived(absl::optional<std::string> rules) {
-    robots_rules_received_.push_back(rules);
-  }
-
-  absl::optional<std::pair<int, base::TimeDelta>> response_error_received_;
-  std::vector<absl::optional<std::string>> robots_rules_received_;
-  base::WeakPtrFactory<RobotsRulesFetcherState> weak_ptr_factory_{this};
-};
-
-class SubresourceRedirectOriginRobotsRulesTest : public testing::Test {
- public:
-  SubresourceRedirectOriginRobotsRulesTest()
-      : task_environment_(base::test::TaskEnvironment::MainThreadType::UI),
-        shared_url_loader_factory_(
-            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-                &test_url_loader_factory_)) {
-    scoped_feature_list_.InitWithFeaturesAndParameters(
-        {{blink::features::kSubresourceRedirect,
-          {{"enable_login_robots_based_compression", "true"},
-           {"enable_public_image_hints_based_compression", "false"},
-           {"enable_login_robots_for_low_memory", "true"}}}},
-        {});
-  }
-
-  void CreateRobotsRulesFetcher(const std::string& origin) {
-    rules_fetcher_state_ = std::make_unique<RobotsRulesFetcherState>();
-    origin_robots_rules_ = std::make_unique<OriginRobotsRules>(
-        shared_url_loader_factory_, url::Origin::Create(GURL(origin)),
-        rules_fetcher_state_->GetResponseErrorCallback());
-  }
-
-  void GetRobotsRules() {
-    origin_robots_rules_->GetRobotsRules(
-        rules_fetcher_state_->GetRobotsRulesReceivedCallback());
-  }
-
-  bool SimulateResponse(const std::string& lite_pages_url,
-                        std::string robots_origin,
-                        const std::string& content,
-                        int net_error = net::OK,
-                        net::HttpStatusCode http_status = net::HTTP_OK,
-                        bool is_cache_hit = false,
-                        const std::string& retry_after = "") {
-    GURL url(lite_pages_url +
-             net::EscapeQueryParamValue(robots_origin + "robots.txt", true));
-    network::mojom::URLResponseHeadPtr head =
-        network::CreateURLResponseHead(http_status);
-    head->was_fetched_via_cache = is_cache_hit;
-    head->mime_type = "text/html";
-    head->headers->SetHeader("Retry-After", retry_after);
-    network::URLLoaderCompletionStatus status;
-    status.error_code = net_error;
-    status.decoded_body_length = content.size();
-    return test_url_loader_factory_.SimulateResponseForPendingRequest(
-        url, status, std::move(head), content);
-  }
-
-  absl::optional<std::pair<int, base::TimeDelta>> GetResponseErrorReceived() {
-    return rules_fetcher_state_->response_error_received_;
-  }
-
-  std::vector<absl::optional<std::string>> GetRobotsRulesReceived() {
-    return rules_fetcher_state_->robots_rules_received_;
-  }
-
- protected:
-  void RunUntilIdle() {
-    task_environment_.RunUntilIdle();
-    base::RunLoop().RunUntilIdle();
-  }
-
-  base::test::ScopedFeatureList scoped_feature_list_;
-  base::test::TaskEnvironment task_environment_;
-
-  base::HistogramTester histogram_tester_;
-
-  variations::ScopedVariationsIdsProvider scoped_variations_ids_provider_{
-      variations::VariationsIdsProvider::Mode::kUseSignedInState};
-
-  std::unique_ptr<OriginRobotsRules> origin_robots_rules_;
-  std::unique_ptr<RobotsRulesFetcherState> rules_fetcher_state_;
-
-  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
-  network::TestURLLoaderFactory test_url_loader_factory_;
-};
-
-TEST_F(SubresourceRedirectOriginRobotsRulesTest, TestSuccessfulResponse) {
-  CreateRobotsRulesFetcher(kFooOrigin);
-  GetRobotsRules();
-  // No robots rules received yet.
-  EXPECT_EQ(0ULL, GetRobotsRulesReceived().size());
-
-  EXPECT_TRUE(SimulateResponse(kLitePagesURL, kFooOrigin, kTestResponse));
-  EXPECT_EQ(absl::nullopt, GetResponseErrorReceived());
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRulesFetcher.CacheHit", false, 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRulesFetcher.NetErrorCode", 0, 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRulesFetcher.ResponseCode", net::HTTP_OK, 1);
-  EXPECT_THAT(GetRobotsRulesReceived(),
-              testing::ElementsAre(absl::optional<std::string>(kTestResponse)));
-
-  // Subsequent calls will return the response immediately.
-  GetRobotsRules();
-  GetRobotsRules();
-  EXPECT_THAT(GetRobotsRulesReceived(),
-              testing::ElementsAre(absl::optional<std::string>(kTestResponse),
-                                   absl::optional<std::string>(kTestResponse),
-                                   absl::optional<std::string>(kTestResponse)));
-  EXPECT_EQ(absl::nullopt, GetResponseErrorReceived());
-}
-
-TEST_F(SubresourceRedirectOriginRobotsRulesTest, TestSuccessfulCachedResponse) {
-  CreateRobotsRulesFetcher(kFooOrigin);
-  GetRobotsRules();
-  EXPECT_TRUE(SimulateResponse(kLitePagesURL, kFooOrigin, kTestResponse,
-                               net::OK, net::HTTP_OK, true /*is_cache_hit*/));
-  RunUntilIdle();
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRulesFetcher.CacheHit", true, 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRulesFetcher.NetErrorCode", 0, 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRulesFetcher.ResponseCode", net::HTTP_OK, 1);
-  EXPECT_EQ(absl::nullopt, GetResponseErrorReceived());
-  EXPECT_THAT(GetRobotsRulesReceived(),
-              testing::ElementsAre(absl::optional<std::string>(kTestResponse)));
-
-  GetRobotsRules();
-  GetRobotsRules();
-  EXPECT_THAT(GetRobotsRulesReceived(),
-              testing::ElementsAre(absl::optional<std::string>(kTestResponse),
-                                   absl::optional<std::string>(kTestResponse),
-                                   absl::optional<std::string>(kTestResponse)));
-  EXPECT_EQ(absl::nullopt, GetResponseErrorReceived());
-}
-
-TEST_F(SubresourceRedirectOriginRobotsRulesTest, TestFailedResponse) {
-  CreateRobotsRulesFetcher(kFooOrigin);
-  GetRobotsRules();
-  EXPECT_TRUE(SimulateResponse(kLitePagesURL, kFooOrigin, kTestResponse,
-                               net::OK, net::HTTP_INTERNAL_SERVER_ERROR));
-  RunUntilIdle();
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRulesFetcher.CacheHit", false, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.RobotsRulesFetcher.NetErrorCode", 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRulesFetcher.ResponseCode",
-      net::HTTP_INTERNAL_SERVER_ERROR, 1);
-  EXPECT_THAT(
-      *GetResponseErrorReceived(),
-      testing::Pair(net::HTTP_INTERNAL_SERVER_ERROR, base::TimeDelta()));
-  EXPECT_THAT(GetRobotsRulesReceived(), testing::ElementsAre(absl::nullopt));
-
-  // Subsequent calls will return the response immediately.
-  GetRobotsRules();
-  GetRobotsRules();
-  EXPECT_THAT(
-      GetRobotsRulesReceived(),
-      testing::ElementsAre(absl::nullopt, absl::nullopt, absl::nullopt));
-}
-
-TEST_F(SubresourceRedirectOriginRobotsRulesTest,
-       TestFailedResponseWithRetryAfter) {
-  CreateRobotsRulesFetcher(kFooOrigin);
-  GetRobotsRules();
-  EXPECT_TRUE(SimulateResponse(kLitePagesURL, kFooOrigin, kTestResponse,
-                               net::OK, net::HTTP_INTERNAL_SERVER_ERROR,
-                               false /*is_cache_hit*/, "120"));
-  RunUntilIdle();
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRulesFetcher.CacheHit", false, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.RobotsRulesFetcher.NetErrorCode", 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRulesFetcher.ResponseCode",
-      net::HTTP_INTERNAL_SERVER_ERROR, 1);
-  EXPECT_THAT(
-      *GetResponseErrorReceived(),
-      testing::Pair(net::HTTP_INTERNAL_SERVER_ERROR, base::Seconds(120)));
-  EXPECT_THAT(GetRobotsRulesReceived(), testing::ElementsAre(absl::nullopt));
-
-  // Subsequent calls will return the response immediately.
-  GetRobotsRules();
-  GetRobotsRules();
-  EXPECT_THAT(
-      GetRobotsRulesReceived(),
-      testing::ElementsAre(absl::nullopt, absl::nullopt, absl::nullopt));
-}
-
-TEST_F(SubresourceRedirectOriginRobotsRulesTest, TestNetErrorFailedResponse) {
-  CreateRobotsRulesFetcher(kFooOrigin);
-  GetRobotsRules();
-  EXPECT_TRUE(SimulateResponse(kLitePagesURL, kFooOrigin, kTestResponse,
-                               net::ERR_ADDRESS_UNREACHABLE));
-  RunUntilIdle();
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRulesFetcher.CacheHit", false, 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.RobotsRulesFetcher.NetErrorCode",
-      -net::ERR_ADDRESS_UNREACHABLE, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.RobotsRulesFetcher.ResponseCode", 0);
-  EXPECT_EQ(absl::nullopt, GetResponseErrorReceived());
-  EXPECT_THAT(GetRobotsRulesReceived(), testing::ElementsAre(absl::nullopt));
-
-  // Subsequent calls will return the response immediately.
-  GetRobotsRules();
-  GetRobotsRules();
-  EXPECT_THAT(
-      GetRobotsRulesReceived(),
-      testing::ElementsAre(absl::nullopt, absl::nullopt, absl::nullopt));
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/browser/subresource_redirect/subresource_redirect_observer.cc b/chrome/browser/subresource_redirect/subresource_redirect_observer.cc
deleted file mode 100644
index 9e834d3..0000000
--- a/chrome/browser/subresource_redirect/subresource_redirect_observer.cc
+++ /dev/null
@@ -1,397 +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.
-
-#include "chrome/browser/subresource_redirect/subresource_redirect_observer.h"
-
-#include "chrome/browser/login_detection/login_detection_keyed_service.h"
-#include "chrome/browser/login_detection/login_detection_keyed_service_factory.h"
-#include "chrome/browser/login_detection/login_detection_type.h"
-#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
-#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/subresource_redirect/origin_robots_rules_cache.h"
-#include "chrome/browser/subresource_redirect/subresource_redirect_util.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
-#include "components/optimization_guide/proto/public_image_metadata.pb.h"
-#include "components/subresource_redirect/common/subresource_redirect_features.h"
-#include "content/public/browser/global_routing_id.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/navigation_handle_user_data.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/web_contents.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
-#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
-#include "url/gurl.h"
-
-namespace subresource_redirect {
-
-namespace {
-
-// Returns the OptimizationGuideDecider when LiteMode and the subresource
-// redirect feature are enabled.
-optimization_guide::OptimizationGuideDecider*
-GetOptimizationGuideDeciderFromWebContents(content::WebContents* web_contents) {
-  DCHECK(ShouldEnablePublicImageHintsBasedCompression());
-  if (!web_contents)
-    return nullptr;
-
-  if (Profile* profile =
-          Profile::FromBrowserContext(web_contents->GetBrowserContext())) {
-    if (data_reduction_proxy::DataReductionProxySettings::
-            IsDataSaverEnabledByUser(profile->IsOffTheRecord(),
-                                     profile->GetPrefs())) {
-      return OptimizationGuideKeyedServiceFactory::GetForProfile(profile);
-    }
-  }
-  return nullptr;
-}
-
-// Pass down the |images_hints| to |render_frame_host|.
-void SetResourceLoadingImageHints(
-    content::RenderFrameHost* render_frame_host,
-    mojom::CompressPublicImagesHintsPtr images_hints) {
-  mojo::AssociatedRemote<mojom::SubresourceRedirectHintsReceiver>
-      hints_receiver;
-  DCHECK(ShouldEnablePublicImageHintsBasedCompression());
-
-  if (render_frame_host->GetRemoteAssociatedInterfaces()) {
-    render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
-        &hints_receiver);
-    hints_receiver->SetCompressPublicImagesHints(std::move(images_hints));
-  }
-}
-
-void UpdateRobotsRules(
-    mojom::SubresourceRedirectService::GetRobotsRulesCallback callback,
-    absl::optional<std::string> robots_rules_proto) {
-  std::move(callback).Run(robots_rules_proto);
-}
-
-class NavigationData
-    : public content::NavigationHandleUserData<NavigationData> {
- public:
-  ~NavigationData() override = default;
-
-  static bool IsAllowedByLoginState(content::NavigationHandle& handle) {
-    DCHECK(ShouldEnableRobotsRulesFetching());
-    if (auto* self = GetForNavigationHandle(handle))
-      return self->is_allowed_by_login_state_;
-    return false;
-  }
-
-  static void SetIsAllowedByLoginState(content::NavigationHandle& handle,
-                                       bool allowed) {
-    DCHECK(ShouldEnableRobotsRulesFetching());
-    GetOrCreateForNavigationHandle(handle)->is_allowed_by_login_state_ =
-        allowed;
-  }
-
- private:
-  explicit NavigationData(content::NavigationHandle&) {}
-  friend NavigationHandleUserData<NavigationData>;
-  NAVIGATION_HANDLE_USER_DATA_KEY_DECL();
-
-  // Whether login is allowed for the current navigation. Set when the
-  // navigation is ready to be committed.
-  bool is_allowed_by_login_state_ = false;
-};
-
-NAVIGATION_HANDLE_USER_DATA_KEY_IMPL(NavigationData);
-
-}  // namespace
-
-ImageCompressionAppliedDocument::ImageCompressionAppliedDocument(
-    content::RenderFrameHost* render_frame_host)
-    : content::DocumentUserData<ImageCompressionAppliedDocument>(
-          render_frame_host) {}
-
-ImageCompressionAppliedDocument::~ImageCompressionAppliedDocument() = default;
-
-DOCUMENT_USER_DATA_KEY_IMPL(ImageCompressionAppliedDocument);
-
-// static
-ImageCompressionAppliedDocument::State
-ImageCompressionAppliedDocument::GetState(content::RenderFrameHost* rfh) {
-  if (auto* self = GetForCurrentDocument(rfh))
-    return self->state();
-  return kDisabled;
-}
-
-// static
-void ImageCompressionAppliedDocument::SetState(content::RenderFrameHost* rfh,
-                                               State state) {
-  GetOrCreateForCurrentDocument(rfh)->set_state(state);
-}
-
-void ImageCompressionAppliedDocument::GetAndUpdateRobotsRules(
-    const url::Origin& origin,
-    OriginRobotsRulesCache* rules_cache,
-    mojom::SubresourceRedirectService::GetRobotsRulesCallback callback) {
-  DCHECK(ShouldEnableRobotsRulesFetching());
-  if (!rules_cache) {
-    std::move(callback).Run(absl::nullopt);
-    return;
-  }
-  rules_cache->GetRobotsRules(
-      origin, base::BindOnce(&UpdateRobotsRules, std::move(callback)));
-}
-
-// static
-void SubresourceRedirectObserver::MaybeCreateForWebContents(
-    content::WebContents* web_contents) {
-  if ((ShouldEnablePublicImageHintsBasedCompression() ||
-       ShouldEnableRobotsRulesFetching()) &&
-      IsLiteModeEnabled(web_contents)) {
-    SubresourceRedirectObserver::CreateForWebContents(web_contents);
-  }
-}
-
-// static
-bool SubresourceRedirectObserver::IsHttpsImageCompressionApplied(
-    content::WebContents* web_contents) {
-  if (!ShouldCompressRedirectSubresource())
-    return false;
-
-  switch (
-      ImageCompressionAppliedDocument::GetState(web_contents->GetMainFrame())) {
-    case ImageCompressionAppliedDocument::kDisabled:
-    case ImageCompressionAppliedDocument::kLoginRobotsRulesFetchingOnly:
-      return false;
-    case ImageCompressionAppliedDocument::kLoginRobotsCheckedEnabled:
-    case ImageCompressionAppliedDocument::kPublicImageHintsEnabled:
-      return true;
-  }
-}
-
-SubresourceRedirectObserver::SubresourceRedirectObserver(
-    content::WebContents* web_contents)
-    : content::WebContentsObserver(web_contents),
-      content::WebContentsUserData<SubresourceRedirectObserver>(*web_contents),
-      receivers_(web_contents, this) {
-  DCHECK(ShouldEnablePublicImageHintsBasedCompression() ||
-         ShouldEnableRobotsRulesFetching());
-  if (ShouldEnablePublicImageHintsBasedCompression()) {
-    if (auto* optimization_guide_decider =
-            GetOptimizationGuideDeciderFromWebContents(web_contents)) {
-      optimization_guide_decider->RegisterOptimizationTypes(
-          {optimization_guide::proto::COMPRESS_PUBLIC_IMAGES});
-    }
-  }
-}
-
-SubresourceRedirectObserver::~SubresourceRedirectObserver() = default;
-
-void SubresourceRedirectObserver::ReadyToCommitNavigation(
-    content::NavigationHandle* navigation_handle) {
-  DCHECK(navigation_handle);
-  if (navigation_handle->IsSameDocument() ||
-      !navigation_handle->GetRenderFrameHost()) {
-    return;
-  }
-  if (!IsLiteModeEnabled(web_contents()))
-    return;
-  if (!navigation_handle->GetURL().SchemeIsHTTPOrHTTPS())
-    return;
-
-  // Send the login state when robots rules fetching is enabled for image and
-  // src-video compression.
-  if (!ShouldEnableRobotsRulesFetching())
-    return;
-
-  mojo::AssociatedRemote<mojom::SubresourceRedirectHintsReceiver>
-      hints_receiver;
-  navigation_handle->GetRenderFrameHost()
-      ->GetRemoteAssociatedInterfaces()
-      ->GetInterface(&hints_receiver);
-  // Save the logged-in state based on which DidFinishNavigation() will create
-  // ImageCompressionAppliedDocument. Note that checking for logged-in state
-  // here in ReadyToCommitNavigation() instead of in DidFinishNavigation()
-  // misses some corner cases. For example, first time OAuth logins to a site
-  // are treated as not logged-in.
-  bool is_allowed_by_login_state =
-      IsAllowedForCurrentLoginState(navigation_handle);
-  NavigationData::SetIsAllowedByLoginState(*navigation_handle,
-                                           is_allowed_by_login_state);
-  hints_receiver->SetLoggedInState(!is_allowed_by_login_state);
-}
-
-void SubresourceRedirectObserver::BindSubresourceRedirectService(
-    mojo::PendingAssociatedReceiver<mojom::SubresourceRedirectService> receiver,
-    content::RenderFrameHost* rfh) {
-  auto* web_contents = content::WebContents::FromRenderFrameHost(rfh);
-  if (!web_contents)
-    return;
-  auto* tab_helper = SubresourceRedirectObserver::FromWebContents(web_contents);
-  if (!tab_helper)
-    return;
-  tab_helper->receivers_.Bind(rfh, std::move(receiver));
-}
-
-void SubresourceRedirectObserver::DidFinishNavigation(
-    content::NavigationHandle* navigation_handle) {
-  DCHECK(navigation_handle);
-  if (!navigation_handle->HasCommitted() ||
-      navigation_handle->IsSameDocument() ||
-      !navigation_handle->GetRenderFrameHost()) {
-    return;
-  }
-  if (!navigation_handle->IsInMainFrame() &&
-      !ShouldEnableRobotsRulesFetching()) {
-    return;
-  }
-  if (!IsLiteModeEnabled(web_contents()))
-    return;
-
-  if (!navigation_handle->GetURL().SchemeIsHTTPOrHTTPS())
-    return;
-
-  // Check and show the one-time infobar when image compression is enabled. This
-  // needs to be done for src video compressed navigations too when that gets
-  // enabled.
-  if ((ShouldEnablePublicImageHintsBasedCompression() ||
-       ShouldEnableLoginRobotsCheckedImageCompression()) &&
-      !ShowInfoBarAndGetImageCompressionState(web_contents(),
-                                              navigation_handle)) {
-    return;
-  }
-
-  // Handle login robots based compression mode.
-  if (ShouldEnableRobotsRulesFetching()) {
-    if (NavigationData::IsAllowedByLoginState(*navigation_handle)) {
-      ImageCompressionAppliedDocument::SetState(
-          navigation_handle->GetRenderFrameHost(),
-          ShouldEnableLoginRobotsCheckedImageCompression()
-              ? ImageCompressionAppliedDocument::kLoginRobotsCheckedEnabled
-              : ImageCompressionAppliedDocument::kLoginRobotsRulesFetchingOnly);
-    }
-    return;
-  }
-
-  // Handle public image hints based compression mode.
-  DCHECK(ShouldEnablePublicImageHintsBasedCompression());
-
-  auto* optimization_guide_decider = GetOptimizationGuideDeciderFromWebContents(
-      navigation_handle->GetWebContents());
-  if (!optimization_guide_decider)
-    return;
-
-  content::RenderFrameHost* render_frame_host =
-      navigation_handle->GetRenderFrameHost();
-  optimization_guide_decider->CanApplyOptimizationAsync(
-      navigation_handle, optimization_guide::proto::COMPRESS_PUBLIC_IMAGES,
-      base::BindOnce(
-          &SubresourceRedirectObserver::OnResourceLoadingImageHintsReceived,
-          weak_factory_.GetWeakPtr(),
-          content::GlobalRenderFrameHostId(
-              render_frame_host->GetProcess()->GetID(),
-              render_frame_host->GetRoutingID())));
-}
-
-void SubresourceRedirectObserver::OnResourceLoadingImageHintsReceived(
-    content::GlobalRenderFrameHostId render_frame_host_routing_id,
-    optimization_guide::OptimizationGuideDecision decision,
-    const optimization_guide::OptimizationMetadata& optimization_metadata) {
-  DCHECK(ShouldEnablePublicImageHintsBasedCompression());
-
-  content::RenderFrameHost* current_render_frame_host =
-      content::RenderFrameHost::FromID(render_frame_host_routing_id);
-  // Check if the same render frame host is still valid.
-  if (!current_render_frame_host)
-    return;
-
-  if (decision != optimization_guide::OptimizationGuideDecision::kTrue)
-    return;
-  if (!optimization_metadata.public_image_metadata())
-    return;
-
-  std::vector<std::string> public_image_urls;
-  const optimization_guide::proto::PublicImageMetadata public_image_metadata =
-      optimization_metadata.public_image_metadata().value();
-  public_image_urls.reserve(public_image_metadata.url_size());
-  for (const auto& url : public_image_metadata.url())
-    public_image_urls.push_back(url);
-
-  // Pass down the image URLs to renderer even if it could be empty. This acts
-  // as a signal that the image hint fetch has finished, for coverage metrics
-  // purposes.
-  SetResourceLoadingImageHints(
-      current_render_frame_host,
-      mojom::CompressPublicImagesHints::New(public_image_urls));
-  if (!public_image_urls.empty()) {
-    ImageCompressionAppliedDocument::SetState(
-        current_render_frame_host,
-        ImageCompressionAppliedDocument::kPublicImageHintsEnabled);
-  }
-}
-
-void SubresourceRedirectObserver::NotifyCompressedImageFetchFailed(
-    base::TimeDelta retry_after) {
-  subresource_redirect::NotifyCompressedImageFetchFailed(web_contents(),
-                                                         retry_after);
-}
-
-void SubresourceRedirectObserver::GetRobotsRules(
-    const url::Origin& origin,
-    mojom::SubresourceRedirectService::GetRobotsRulesCallback callback) {
-  DCHECK(ShouldEnableRobotsRulesFetching());
-  DCHECK(!origin.opaque());
-  if (!web_contents()) {
-    std::move(callback).Run(absl::nullopt);
-    return;
-  }
-
-  // ImageCompressionAppliedDocument could be null when suresource redirect is
-  // disabled for this document.
-  auto* subresource_redirect_document_host =
-      ImageCompressionAppliedDocument::GetForCurrentDocument(
-          web_contents()->GetMainFrame());
-  if (!subresource_redirect_document_host) {
-    std::move(callback).Run(absl::nullopt);
-    return;
-  }
-
-  subresource_redirect_document_host->GetAndUpdateRobotsRules(
-      origin, GetOriginRobotsRulesCache(web_contents()), std::move(callback));
-}
-
-bool SubresourceRedirectObserver::IsAllowedForCurrentLoginState(
-    content::NavigationHandle* navigation_handle) {
-  DCHECK(ShouldEnableRobotsRulesFetching());
-
-  auto* login_detection_keyed_service =
-      login_detection::LoginDetectionKeyedServiceFactory::GetForProfile(
-          Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
-  if (!login_detection_keyed_service)
-    return false;
-
-  if (login_detection_keyed_service->GetPersistentLoginDetection(
-          navigation_handle->GetURL()) !=
-      login_detection::LoginDetectionType::kNoLogin) {
-    return false;
-  }
-
-  // Check if any of the parent frames have disabled image compression.
-  content::RenderFrameHost* parent_render_frame_host =
-      navigation_handle->GetRenderFrameHost();
-  while ((parent_render_frame_host = parent_render_frame_host->GetParent())) {
-    if (!parent_render_frame_host->IsActive())
-      continue;
-    switch (
-        ImageCompressionAppliedDocument::GetState(parent_render_frame_host)) {
-      case ImageCompressionAppliedDocument::kDisabled:
-        return false;
-      case ImageCompressionAppliedDocument::kLoginRobotsRulesFetchingOnly:
-      case ImageCompressionAppliedDocument::kLoginRobotsCheckedEnabled:
-        break;
-      case ImageCompressionAppliedDocument::kPublicImageHintsEnabled:
-        NOTREACHED();
-    }
-  }
-  return true;
-}
-
-WEB_CONTENTS_USER_DATA_KEY_IMPL(SubresourceRedirectObserver);
-
-}  // namespace subresource_redirect
diff --git a/chrome/browser/subresource_redirect/subresource_redirect_observer.h b/chrome/browser/subresource_redirect/subresource_redirect_observer.h
deleted file mode 100644
index fae571a..0000000
--- a/chrome/browser/subresource_redirect/subresource_redirect_observer.h
+++ /dev/null
@@ -1,141 +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 CHROME_BROWSER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_OBSERVER_H_
-#define CHROME_BROWSER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_OBSERVER_H_
-
-#include "chrome/common/subresource_redirect_service.mojom.h"
-#include "components/optimization_guide/content/browser/optimization_guide_decider.h"
-#include "content/public/browser/document_user_data.h"
-#include "content/public/browser/render_frame_host_receiver_set.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
-#include "url/origin.h"
-
-namespace content {
-class NavigationHandle;
-class WebContents;
-}  // namespace content
-
-namespace subresource_redirect {
-
-class OriginRobotsRulesCache;
-
-// Contains the subresource redirect for scoped to document's lifetime. This
-// gets created when navigation commits and lives until a different navigation
-// happens or the web contents is destroyed. This should be created only when
-// subresource redirect compression is allowed for the document.
-class ImageCompressionAppliedDocument
-    : public content::DocumentUserData<ImageCompressionAppliedDocument> {
- public:
-  enum State {
-    kDisabled,
-    kLoginRobotsRulesFetchingOnly,
-    kLoginRobotsCheckedEnabled,
-    kPublicImageHintsEnabled,
-  };
-
-  ~ImageCompressionAppliedDocument() override;
-  ImageCompressionAppliedDocument(const ImageCompressionAppliedDocument&) =
-      delete;
-  ImageCompressionAppliedDocument& operator=(
-      const ImageCompressionAppliedDocument&) = delete;
-
-  State state() const { return state_; }
-  void set_state(State state) { state_ = state; }
-
-  static State GetState(content::RenderFrameHost* rfh);
-  static void SetState(content::RenderFrameHost* rfh, State state);
-
-  // Gets the robots rules for |origin| from the |rules_cache| and invokes the
-  // |callback|.
-  void GetAndUpdateRobotsRules(
-      const url::Origin& origin,
-      OriginRobotsRulesCache* rules_cache,
-      mojom::SubresourceRedirectService::GetRobotsRulesCallback callback);
-
- private:
-  explicit ImageCompressionAppliedDocument(
-      content::RenderFrameHost* render_frame_host);
-  friend class content::DocumentUserData<ImageCompressionAppliedDocument>;
-
-  // Maintains whether https image compression was attempted for the last
-  // navigation. Even though image compression was attempted, it doesn't mean at
-  // least one image will get compressed, since that depends on a public image
-  // present in this page. This is not an issue since most pages tend to have at
-  // least one public image even though they are fully private.
-  State state_ = kDisabled;
-
-  DOCUMENT_USER_DATA_KEY_DECL();
-};
-
-// Sends the public image URL hints to renderer.
-class SubresourceRedirectObserver
-    : public content::WebContentsObserver,
-      public content::WebContentsUserData<SubresourceRedirectObserver>,
-      public mojom::SubresourceRedirectService {
- public:
-  static void MaybeCreateForWebContents(content::WebContents* web_contents);
-
-  // Returns whether LiteMode https image compression was attempted on this
-  // page.
-  static bool IsHttpsImageCompressionApplied(
-      content::WebContents* web_contents);
-
-  ~SubresourceRedirectObserver() override;
-  SubresourceRedirectObserver(const SubresourceRedirectObserver&) = delete;
-  SubresourceRedirectObserver& operator=(const SubresourceRedirectObserver&) =
-      delete;
-
-  static void BindSubresourceRedirectService(
-      mojo::PendingAssociatedReceiver<mojom::SubresourceRedirectService>
-          receiver,
-      content::RenderFrameHost* rfh);
-
- private:
-  friend class content::WebContentsUserData<SubresourceRedirectObserver>;
-
-  explicit SubresourceRedirectObserver(content::WebContents* web_contents);
-
-  // content::WebContentsObserver.
-  void ReadyToCommitNavigation(
-      content::NavigationHandle* navigation_handle) override;
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override;
-
-  // mojom::SubresourceRedirectService
-  void NotifyCompressedImageFetchFailed(base::TimeDelta retry_after) override;
-  void GetRobotsRules(const url::Origin& origin,
-                      mojom::SubresourceRedirectService::GetRobotsRulesCallback
-                          callback) override;
-
-  // Invoked when the OptimizationGuideKeyedService has sufficient information
-  // to make a decision for whether we can send resource loading image hints.
-  // If |decision| is true, public image URLs contained in
-  // |optimization_metadata| will be sent to the render frame host as specified
-  // by |render_frame_host_routing_id| to later be compressed.
-  void OnResourceLoadingImageHintsReceived(
-      content::GlobalRenderFrameHostId render_frame_host_routing_id,
-      optimization_guide::OptimizationGuideDecision decision,
-      const optimization_guide::OptimizationMetadata& optimization_metadata);
-
-  // Returns whether the navigation is allowed to subresource redirect based on
-  // the current logged-in status. For the subframes to be considered allowed,
-  // all the parent frames should be allowed for subresource redirect too. Login
-  // detection feature should be enabled to retrieve logged-in status, otherwise
-  // subresource redirect will be disallowed.
-  bool IsAllowedForCurrentLoginState(
-      content::NavigationHandle* navigation_handle);
-
-  content::RenderFrameHostReceiverSet<mojom::SubresourceRedirectService>
-      receivers_;
-
-  base::WeakPtrFactory<SubresourceRedirectObserver> weak_factory_{this};
-
-  WEB_CONTENTS_USER_DATA_KEY_DECL();
-};
-
-}  // namespace subresource_redirect
-
-#endif  // CHROME_BROWSER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_OBSERVER_H_
diff --git a/chrome/browser/subresource_redirect/subresource_redirect_util.cc b/chrome/browser/subresource_redirect/subresource_redirect_util.cc
deleted file mode 100644
index 9dff0f6..0000000
--- a/chrome/browser/subresource_redirect/subresource_redirect_util.cc
+++ /dev/null
@@ -1,164 +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.
-
-#include "chrome/browser/subresource_redirect/subresource_redirect_util.h"
-
-#include "base/rand_util.h"
-#include "build/build_config.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
-#include "chrome/browser/subresource_redirect/https_image_compression_infobar_decider.h"
-#include "chrome/browser/subresource_redirect/litepages_service_bypass_decider.h"
-#include "chrome/browser/subresource_redirect/origin_robots_rules_cache.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
-#include "components/subresource_redirect/common/subresource_redirect_features.h"
-#include "content/public/browser/web_contents.h"
-#include "net/base/escape.h"
-#include "third_party/blink/public/common/features.h"
-
-#if defined(OS_ANDROID)
-#include "chrome/browser/subresource_redirect/android/previews_android_bridge.h"
-#endif
-
-namespace subresource_redirect {
-
-namespace {
-
-DataReductionProxyChromeSettings* GetDataReductionProxyChromeSettings(
-    content::WebContents* web_contents) {
-  DCHECK(web_contents);
-  return DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
-      web_contents->GetBrowserContext());
-}
-
-bool ShowInfoBarOnAndroid(content::WebContents* web_contents) {
-#if defined(OS_ANDROID)
-  return PreviewsAndroidBridge::CreateHttpsImageCompressionInfoBar(
-      web_contents);
-#else
-  return true;
-#endif
-}
-
-// Returns the litepage robots origin from one of the image or src video
-// subresource redirect features.
-GURL GetLitePageRobotsOrigin() {
-  auto lite_page_robots_origin = base::GetFieldTrialParamValueByFeature(
-      blink::features::kSubresourceRedirect, "lite_page_robots_origin");
-  if (lite_page_robots_origin.empty()) {
-    lite_page_robots_origin = base::GetFieldTrialParamValueByFeature(
-        blink::features::kSubresourceRedirectSrcVideo,
-        "lite_page_robots_origin");
-  }
-  if (lite_page_robots_origin.empty())
-    lite_page_robots_origin = "https://litepages.googlezip.net/";
-  return GURL(lite_page_robots_origin);
-}
-
-}  // namespace
-
-bool IsLiteModeEnabled(content::WebContents* web_contents) {
-  if (!web_contents)
-    return false;
-  const auto* data_reduction_proxy_settings =
-      GetDataReductionProxyChromeSettings(web_contents);
-  return data_reduction_proxy_settings &&
-         data_reduction_proxy_settings->IsDataReductionProxyEnabled();
-}
-
-bool ShowInfoBarAndGetImageCompressionState(
-    content::WebContents* web_contents,
-    content::NavigationHandle* navigation_handle) {
-  DCHECK(ShouldEnablePublicImageHintsBasedCompression() ||
-         ShouldEnableLoginRobotsCheckedImageCompression());
-
-  auto* data_reduction_proxy_settings =
-      GetDataReductionProxyChromeSettings(web_contents);
-  if (!data_reduction_proxy_settings->IsDataReductionProxyEnabled()) {
-    return false;
-  }
-
-  if (!data_reduction_proxy_settings->litepages_service_bypass_decider()
-           ->ShouldAllowNow()) {
-    return false;
-  }
-
-  auto* https_image_compression_infobar_decider =
-      data_reduction_proxy_settings->https_image_compression_infobar_decider();
-  if (!https_image_compression_infobar_decider ||
-      https_image_compression_infobar_decider->NeedToShowInfoBar()) {
-    if (https_image_compression_infobar_decider->CanShowInfoBar(
-            navigation_handle) &&
-        ShowInfoBarOnAndroid(web_contents)) {
-      https_image_compression_infobar_decider->SetUserHasSeenInfoBar();
-    }
-    // Do not enable image compression on this page.
-    return false;
-  }
-  return true;
-}
-
-void NotifyCompressedImageFetchFailed(content::WebContents* web_contents,
-                                      base::TimeDelta retry_after) {
-  GetDataReductionProxyChromeSettings(web_contents)
-      ->litepages_service_bypass_decider()
-      ->NotifyFetchFailure(retry_after);
-}
-
-GURL GetRobotsServerURL(const url::Origin& origin) {
-  DCHECK(ShouldEnableRobotsRulesFetching());
-  DCHECK(!origin.opaque());
-
-  GURL origin_url = origin.GetURL();
-  GURL::Replacements origin_replacement;
-  origin_replacement.SetPathStr("/robots.txt");
-  origin_url = origin_url.ReplaceComponents(origin_replacement);
-
-  GURL lite_page_robots_url = GetLitePageRobotsOrigin();
-  std::string query_str =
-      "u=" + net::EscapeQueryParamValue(origin_url.spec(), true /* use_plus */);
-
-  GURL::Replacements replacements;
-  replacements.SetPathStr("/robots");
-  replacements.SetQueryStr(query_str);
-
-  lite_page_robots_url = lite_page_robots_url.ReplaceComponents(replacements);
-  DCHECK(lite_page_robots_url.is_valid());
-  return lite_page_robots_url;
-}
-
-OriginRobotsRulesCache* GetOriginRobotsRulesCache(
-    content::WebContents* web_contents) {
-  DCHECK(web_contents);
-  if (const auto* data_reduction_proxy_settings =
-          GetDataReductionProxyChromeSettings(web_contents)) {
-    return data_reduction_proxy_settings->origin_robots_rules_cache();
-  }
-  return nullptr;
-}
-
-int MaxOriginRobotsRulesCacheSize() {
-  return base::GetFieldTrialParamByFeatureAsInt(
-      blink::features::kSubresourceRedirect,
-      "max_browser_origin_robots_rules_cache_size", 20);
-}
-
-base::TimeDelta GetLitePagesBypassRandomDuration() {
-  // Default is a random duration between 1 to 5 minutes.
-  return base::Seconds(
-      base::RandInt(base::GetFieldTrialParamByFeatureAsInt(
-                        blink::features::kSubresourceRedirect,
-                        "litepages_bypass_random_duration_min_secs", 60),
-                    base::GetFieldTrialParamByFeatureAsInt(
-                        blink::features::kSubresourceRedirect,
-                        "litepages_bypass_random_duration_max_secs", 300)));
-}
-
-base::TimeDelta GetLitePagesBypassMaxDuration() {
-  return base::Seconds(base::GetFieldTrialParamByFeatureAsInt(
-      blink::features::kSubresourceRedirect,
-      "litepages_bypass_max_duration_secs", 300));
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/browser/subresource_redirect/subresource_redirect_util.h b/chrome/browser/subresource_redirect/subresource_redirect_util.h
deleted file mode 100644
index 903a13753..0000000
--- a/chrome/browser/subresource_redirect/subresource_redirect_util.h
+++ /dev/null
@@ -1,56 +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 CHROME_BROWSER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_UTIL_H_
-#define CHROME_BROWSER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_UTIL_H_
-
-#include "base/time/time.h"
-#include "url/gurl.h"
-#include "url/origin.h"
-
-namespace content {
-class NavigationHandle;
-class WebContents;
-}  // namespace content
-
-namespace subresource_redirect {
-
-class OriginRobotsRulesCache;
-
-// Returns whether LiteMode is enabled for the profile associated with the
-// |web_contents|.
-bool IsLiteModeEnabled(content::WebContents* web_contents);
-
-// Returns whether image compression should be applied for this web_contents.
-// Also shows an one-time InfoBar on Android if needed.
-bool ShowInfoBarAndGetImageCompressionState(
-    content::WebContents* web_contents,
-    content::NavigationHandle* navigation_handle);
-
-// Notifies to LiteMode that image compression fetch had failed.
-void NotifyCompressedImageFetchFailed(content::WebContents* web_contents,
-                                      base::TimeDelta retry_after);
-
-// Returns the LitePages robots rules server endpoint URL to fetch for the given
-// |origin|.
-GURL GetRobotsServerURL(const url::Origin& origin);
-
-// Returns the robots rules cache for the profile of |web_contents|.
-OriginRobotsRulesCache* GetOriginRobotsRulesCache(
-    content::WebContents* web_contents);
-
-// Returns the maximum number of origin robots rules the browser should cache
-// in-memory to be sent to the renderers immediately.
-int MaxOriginRobotsRulesCacheSize();
-
-// Returns a random duration LitePages service should bypass for, when a
-// LitePages response fails without RetryAfter header.
-base::TimeDelta GetLitePagesBypassRandomDuration();
-
-// Returns the maximum duration LitePages service should be bypassed.
-base::TimeDelta GetLitePagesBypassMaxDuration();
-
-}  // namespace subresource_redirect
-
-#endif  // CHROME_BROWSER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_UTIL_H_
diff --git a/chrome/browser/subresource_redirect/subresource_redirect_util_unit_test.cc b/chrome/browser/subresource_redirect/subresource_redirect_util_unit_test.cc
deleted file mode 100644
index 64f54a9..0000000
--- a/chrome/browser/subresource_redirect/subresource_redirect_util_unit_test.cc
+++ /dev/null
@@ -1,63 +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.
-
-#include "base/strings/strcat.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/subresource_redirect/subresource_redirect_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/features.h"
-
-namespace subresource_redirect {
-
-namespace {
-
-// Naive function that escapes :, / characters in URL. Useful for simple tests.
-std::string EscapeURLForQueryParam(std::string url) {
-  base::ReplaceChars(url, ":", base::StringPrintf("%%%0X", ':'), &url);
-  base::ReplaceChars(url, "/", base::StringPrintf("%%%0X", '/'), &url);
-  return url;
-}
-
-}  // namespace
-
-TEST(SubresourceRedirectUtilTest, GetRobotsServerURL) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeaturesAndParameters(
-      {{blink::features::kSubresourceRedirect,
-        {{"enable_login_robots_based_compression", "true"},
-         {"enable_public_image_hints_based_compression", "false"},
-         {"enable_login_robots_for_low_memory", "true"}}}},
-      {});
-
-  for (auto* origin :
-       {"https://foo.com/", "https://www.foo.com/", "http://foo.com/"}) {
-    EXPECT_EQ(
-        GURL("https://litepages.googlezip.net/robots?u=" +
-             EscapeURLForQueryParam(base::StrCat({origin, "robots.txt"}))),
-        GetRobotsServerURL(url::Origin::Create(GURL(origin))));
-  }
-}
-
-TEST(SubresourceRedirectUtilTest, GetRobotsServerURL_ModifiedLitePagesOrigin) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeaturesAndParameters(
-      {{blink::features::kSubresourceRedirect,
-        {{"enable_login_robots_based_compression", "true"},
-         {"enable_public_image_hints_based_compression", "false"},
-         {"lite_page_robots_origin", "https://modified.litepages.com/"},
-         {"enable_login_robots_for_low_memory", "true"}}}},
-      {});
-
-  for (auto* origin :
-       {"https://foo.com/", "https://www.foo.com/", "http://foo.com/"}) {
-    EXPECT_EQ(
-        GURL("https://modified.litepages.com/robots?u=" +
-             EscapeURLForQueryParam(base::StrCat({origin, "robots.txt"}))),
-        GetRobotsServerURL(url::Origin::Create(GURL(origin))));
-  }
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/browser/supervised_user/supervised_user_service.cc b/chrome/browser/supervised_user/supervised_user_service.cc
index fda171ab..2c99f366 100644
--- a/chrome/browser/supervised_user/supervised_user_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_service.cc
@@ -942,8 +942,8 @@
     const std::string& version,
     ApprovedExtensionChange type) {
   PrefService* pref_service = GetPrefService();
-  DictionaryPrefUpdate update(pref_service,
-                              prefs::kSupervisedUserApprovedExtensions);
+  DictionaryPrefUpdateDeprecated update(
+      pref_service, prefs::kSupervisedUserApprovedExtensions);
   base::DictionaryValue* approved_extensions = update.Get();
   DCHECK(approved_extensions)
       << "kSupervisedUserApprovedExtensions pref not found";
diff --git a/chrome/browser/sync/test/integration/preferences_helper.cc b/chrome/browser/sync/test/integration/preferences_helper.cc
index 53811d8..7d9784b 100644
--- a/chrome/browser/sync/test/integration/preferences_helper.cc
+++ b/chrome/browser/sync/test/integration/preferences_helper.cc
@@ -60,7 +60,7 @@
 void ChangeListPref(int index,
                     const char* pref_name,
                     const base::ListValue& new_value) {
-  ListPrefUpdate update(GetPrefs(index), pref_name);
+  ListPrefUpdateDeprecated update(GetPrefs(index), pref_name);
   base::ListValue* list = update.Get();
   for (const auto& it : new_value.GetList()) {
     list->Append(it.Clone());
diff --git a/chrome/browser/sync/test/integration/sync_exponential_backoff_test.cc b/chrome/browser/sync/test/integration/sync_exponential_backoff_test.cc
index 116cc43d..6e54d21 100644
--- a/chrome/browser/sync/test/integration/sync_exponential_backoff_test.cc
+++ b/chrome/browser/sync/test/integration/sync_exponential_backoff_test.cc
@@ -37,13 +37,7 @@
   }
 };
 
-// Flaky on ChromeOS, crbug.com/1170609
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#define MAYBE_OfflineToOnline DISABLED_OfflineToOnline
-#else
-#define MAYBE_OfflineToOnline OfflineToOnline
-#endif
-IN_PROC_BROWSER_TEST_F(SyncExponentialBackoffTest, MAYBE_OfflineToOnline) {
+IN_PROC_BROWSER_TEST_F(SyncExponentialBackoffTest, OfflineToOnline) {
   const std::string kFolderTitle1 = "folder1";
   const std::string kFolderTitle2 = "folder2";
 
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index 81ed8ac8..aac9710 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -775,7 +775,7 @@
       // real clients, those are stored upon subscription with the
       // per-user-topic server. The pref name is defined in
       // per_user_topic_subscription_manager.cc.
-      DictionaryPrefUpdate update(
+      DictionaryPrefUpdateDeprecated update(
           GetProfile(index)->GetPrefs(),
           "invalidation.per_sender_registered_for_invalidation");
       update->SetKey(kInvalidationGCMSenderId,
@@ -791,7 +791,7 @@
                      base::Value("/private/" + notification_type +
                                  "-topic_server_user_id"));
       }
-      DictionaryPrefUpdate update_client_id(
+      DictionaryPrefUpdateDeprecated update_client_id(
           GetProfile(index)->GetPrefs(),
           invalidation::prefs::kInvalidationClientIDCache);
 
@@ -995,17 +995,12 @@
   // Closing all browsers created by this test. The calls here block until
   // they are closed. Other browsers created outside SyncTest setup should be
   // closed by the creator of that browser.
-  const size_t initial_total_browser_count = chrome::GetTotalBrowserCount();
-  size_t closed_browser_count = 0;
   for (Browser* browser : browsers_) {
     if (browser) {
       CloseBrowserSynchronously(browser);
-      closed_browser_count++;
     }
   }
   browsers_.clear();
-  ASSERT_EQ(chrome::GetTotalBrowserCount(),
-            initial_total_browser_count - closed_browser_count);
 #endif
   PlatformBrowserTest::TearDownOnMainThread();
 }
diff --git a/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_mac_win_linux.cc b/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_mac_win_linux.cc
index c4a79c3..e9a4c77 100644
--- a/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_mac_win_linux.cc
+++ b/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_mac_win_linux.cc
@@ -429,17 +429,9 @@
   helper_.CheckAppInListNotLocallyInstalled("SiteA");
 }
 
-// TODO(crbug.com/1282608): Test is flaky on Mac.
-#if BUILDFLAG(IS_MAC)
-#define MAYBE_WebAppIntegration_TurnSyncOff_InstMenuOptionSiteA_TurnSyncOn_SwitchProfileClientClient2_InListNotLclyInstSiteA \
-  DISABLED_WebAppIntegration_TurnSyncOff_InstMenuOptionSiteA_TurnSyncOn_SwitchProfileClientClient2_InListNotLclyInstSiteA
-#else
-#define MAYBE_WebAppIntegration_TurnSyncOff_InstMenuOptionSiteA_TurnSyncOn_SwitchProfileClientClient2_InListNotLclyInstSiteA \
-  WebAppIntegration_TurnSyncOff_InstMenuOptionSiteA_TurnSyncOn_SwitchProfileClientClient2_InListNotLclyInstSiteA
-#endif
 IN_PROC_BROWSER_TEST_F(
     TwoClientWebAppsIntegrationTestMacWinLinux,
-    MAYBE_WebAppIntegration_TurnSyncOff_InstMenuOptionSiteA_TurnSyncOn_SwitchProfileClientClient2_InListNotLclyInstSiteA) {
+    WebAppIntegration_TurnSyncOff_InstMenuOptionSiteA_TurnSyncOn_SwitchProfileClientClient2_InListNotLclyInstSiteA) {
   // Test contents are generated by script. Please do not modify!
   // See `chrome/test/webapps/README.md` for more info.
   // Sheriffs: Disabling this test is supported.
diff --git a/chrome/browser/tab/BUILD.gn b/chrome/browser/tab/BUILD.gn
index 2769c885..51e5466 100644
--- a/chrome/browser/tab/BUILD.gn
+++ b/chrome/browser/tab/BUILD.gn
@@ -24,6 +24,7 @@
     "java/src/org/chromium/chrome/browser/tab/TabObserver.java",
     "java/src/org/chromium/chrome/browser/tab/TabResolver.java",
     "java/src/org/chromium/chrome/browser/tab/TabState.java",
+    "java/src/org/chromium/chrome/browser/tab/TabStateAttributes.java",
     "java/src/org/chromium/chrome/browser/tab/TabViewManager.java",
     "java/src/org/chromium/chrome/browser/tab/TabViewProvider.java",
     "java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java",
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java
index 188f660..d9cdc11 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -273,14 +273,6 @@
     void goForward();
 
     /**
-     * Set whether the TabState representing this Tab has been updated.
-     * This method will ultimately be deprecated when the migration
-     * to CriticalPersistedTabData is complete.
-     * @param isDirty Whether the Tab's state has changed.
-     */
-    void setIsTabStateDirty(boolean isTabStateDirty);
-
-    /**
      * Set whether {@link Tab} metadata (specifically all {@link PersistedTabData})
      * will be saved. Not all Tabs need to be persisted across restarts.
      * The default value when a Tab is initialized is false.
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributes.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributes.java
new file mode 100644
index 0000000..098c9e7
--- /dev/null
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributes.java
@@ -0,0 +1,45 @@
+// Copyright 2022 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.tab;
+
+import org.chromium.base.UserData;
+import org.chromium.base.UserDataHost;
+
+/**
+ * Attributes related to {@link TabState}
+ */
+public class TabStateAttributes implements UserData {
+    private static final Class<TabStateAttributes> USER_DATA_KEY = TabStateAttributes.class;
+    /** Whether or not the TabState has changed. */
+    private boolean mIsTabStateDirty = true;
+
+    /**
+     * @return {@link TabStateAttributes} for a {@link Tab}
+     */
+    public static TabStateAttributes from(Tab tab) {
+        UserDataHost host = tab.getUserDataHost();
+        TabStateAttributes attrs = host.getUserData(USER_DATA_KEY);
+        return attrs != null ? attrs : host.setUserData(USER_DATA_KEY, new TabStateAttributes());
+    }
+
+    private TabStateAttributes() {}
+
+    /**
+     * @return true if the {@link TabState} has been changed
+     */
+    public boolean isTabStateDirty() {
+        return mIsTabStateDirty;
+    }
+
+    /**
+     * Set whether the TabState representing this Tab has been updated.
+     * This method will ultimately be deprecated when the migration
+     * to CriticalPersistedTabData is complete.
+     * @param isTabStateDirty whether the Tab's state has changed.
+     */
+    public void setIsTabStateDirty(boolean isTabStateDirty) {
+        mIsTabStateDirty = isTabStateDirty;
+    }
+}
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
index 2ee58b3..3bb80f5e 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/CriticalPersistedTabData.java
@@ -18,6 +18,7 @@
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabLaunchType;
+import org.chromium.chrome.browser.tab.TabStateAttributes;
 import org.chromium.chrome.browser.tab.TabUserAgent;
 import org.chromium.chrome.browser.tab.WebContentsState;
 import org.chromium.chrome.browser.tab.WebContentsStateBridge;
@@ -567,13 +568,13 @@
      * Set root id
      */
     public void setRootId(int rootId) {
-        if (mRootId == rootId) return;
+        if (mRootId == rootId || mTab.isDestroyed()) return;
         // TODO(crbug.com/1059640) add in setters for all mutable fields
         mRootId = rootId;
         for (CriticalPersistedTabDataObserver observer : mObservers) {
             observer.onRootIdChanged(mTab, rootId);
         }
-        mTab.setIsTabStateDirty(true);
+        TabStateAttributes.from(mTab).setIsTabStateDirty(true);
         save();
     }
 
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
index 6e188a9..c197afd1 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
@@ -477,7 +477,8 @@
                     if (tab.isDestroyed()
                             || getTimeSinceTabLastOpenedMs(tab)
                                     > TimeUnit.SECONDS.toMillis(getStaleTabThresholdSeconds())) {
-                        supplierCallback.onResult(null);
+                        PostTask.runOrPostTask(
+                                UiThreadTaskTraits.DEFAULT, () -> supplierCallback.onResult(null));
                         return;
                     }
                     PriceDataSnapshot previous = PersistedTabData.from(tab, USER_DATA_KEY) == null
@@ -485,7 +486,10 @@
                             : new PriceDataSnapshot(PersistedTabData.from(tab, USER_DATA_KEY));
                     ShoppingPersistedTabData.isShoppingPage(tab.getUrl(), (isShoppingPage) -> {
                         if (!isShoppingPage) {
-                            supplierCallback.onResult(getEmptyShoppingPersistedTabData(tab));
+                            PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT,
+                                    ()
+                                            -> supplierCallback.onResult(
+                                                    getEmptyShoppingPersistedTabData(tab)));
                             return;
                         }
 
@@ -496,7 +500,9 @@
                                             HintsProto.OptimizationType.PRICE_TRACKING,
                                             (decision, metadata) -> {
                                                 if (tab.isDestroyed()) {
-                                                    supplierCallback.onResult(null);
+                                                    PostTask.runOrPostTask(
+                                                            UiThreadTaskTraits.DEFAULT,
+                                                            () -> supplierCallback.onResult(null));
                                                     return;
                                                 }
                                                 if (decision != OptimizationGuideDecision.TRUE) {
@@ -504,25 +510,24 @@
                                                             getEmptyShoppingPersistedTabData(tab);
                                                     res.logPriceDropMetrics(
                                                             METRICS_IDENTIFIER_PREFIX);
-                                                    supplierCallback.onResult(res);
+                                                    PostTask.runOrPostTask(
+                                                            UiThreadTaskTraits.DEFAULT,
+                                                            () -> supplierCallback.onResult(res));
                                                     return;
                                                 }
                                                 try {
                                                     PriceTrackingData priceTrackingDataProto =
                                                             PriceTrackingData.parseFrom(
                                                                     metadata.getValue());
-                                                    ShoppingPersistedTabData
-                                                            shoppingPersistedTabData =
-                                                                    ShoppingPersistedTabData.from(
-                                                                            tab);
-                                                    shoppingPersistedTabData
-                                                            .parsePriceTrackingDataProto(tab,
-                                                                    priceTrackingDataProto,
-                                                                    previous);
-                                                    shoppingPersistedTabData.logPriceDropMetrics(
+                                                    ShoppingPersistedTabData sptd =
+                                                            ShoppingPersistedTabData.from(tab);
+                                                    sptd.parsePriceTrackingDataProto(
+                                                            tab, priceTrackingDataProto, previous);
+                                                    sptd.logPriceDropMetrics(
                                                             METRICS_IDENTIFIER_PREFIX);
-                                                    supplierCallback.onResult(
-                                                            shoppingPersistedTabData);
+                                                    PostTask.runOrPostTask(
+                                                            UiThreadTaskTraits.DEFAULT,
+                                                            () -> supplierCallback.onResult(sptd));
                                                 } catch (InvalidProtocolBufferException e) {
                                                     Log.i(TAG,
                                                             String.format(Locale.US,
@@ -532,13 +537,18 @@
                                                                             + "DataProto. "
                                                                             + "Details %s.",
                                                                     e));
-                                                    supplierCallback.onResult(null);
+                                                    PostTask.runOrPostTask(
+                                                            UiThreadTaskTraits.DEFAULT,
+                                                            () -> supplierCallback.onResult(null));
                                                 }
                                             });
                         } else {
                             sPageAnnotationsServiceFactory.getForLastUsedProfile().getAnnotations(
                                     tab.getUrl(), (result) -> {
-                                        supplierCallback.onResult(build(tab, result, previous));
+                                        PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT,
+                                                ()
+                                                        -> supplierCallback.onResult(
+                                                                build(tab, result, previous)));
                                     });
                         }
                     });
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc
index 62748c0..e1cf8da 100644
--- a/chrome/browser/themes/browser_theme_pack.cc
+++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -96,7 +96,7 @@
 // changed default theme assets, if you need themes to recreate their generated
 // images (which are cached), if you changed how missing values are
 // generated, or if you changed any constants.
-const int kThemePackVersion = 78;
+const int kThemePackVersion = 79;
 
 // IDs that are in the DataPack won't clash with the positive integer
 // uint16_t. kHeaderID should always have the maximum value because we want the
@@ -1506,6 +1506,15 @@
     SetColor(TP::COLOR_DOWNLOAD_SHELF, toolbar_color);
     SetColor(TP::COLOR_TAB_BACKGROUND_ACTIVE_FRAME_ACTIVE, toolbar_color);
     SetColor(TP::COLOR_TAB_BACKGROUND_ACTIVE_FRAME_INACTIVE, toolbar_color);
+
+    // If the toolbar color is set but the text color is not, ensure it has
+    // sufficient contrast.
+    SkColor toolbar_text_color =
+        TP::GetDefaultColor(TP::COLOR_TOOLBAR_TEXT, false);
+    toolbar_text_color =
+        color_utils::BlendForMinContrast(toolbar_text_color, toolbar_color)
+            .color;
+    SetColorIfUnspecified(TP::COLOR_TOOLBAR_TEXT, toolbar_text_color);
   }
   SkColor toolbar_button_icon_color;
   if (GetColor(TP::COLOR_TOOLBAR_BUTTON_ICON, &toolbar_button_icon_color)) {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 436aedb..5d30507 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3200,6 +3200,10 @@
       "browser_dialogs_desktop.cc",
       "frame/window_frame_util.cc",
       "frame/window_frame_util.h",
+      "signin_modal_dialog.cc",
+      "signin_modal_dialog.h",
+      "signin_modal_dialog_impl.cc",
+      "signin_modal_dialog_impl.h",
       "signin_view_controller.cc",
       "signin_view_controller.h",
       "signin_view_controller_delegate.cc",
@@ -4658,10 +4662,14 @@
       "views/toolbar/chrome_labs_bubble_view_model.h",
       "views/toolbar/chrome_labs_button.cc",
       "views/toolbar/chrome_labs_button.h",
+      "views/toolbar/chrome_labs_coordinator.cc",
+      "views/toolbar/chrome_labs_coordinator.h",
       "views/toolbar/chrome_labs_item_view.cc",
       "views/toolbar/chrome_labs_item_view.h",
       "views/toolbar/chrome_labs_utils.cc",
       "views/toolbar/chrome_labs_utils.h",
+      "views/toolbar/chrome_labs_view_controller.cc",
+      "views/toolbar/chrome_labs_view_controller.h",
       "views/toolbar/home_button.cc",
       "views/toolbar/home_button.h",
       "views/toolbar/reload_button.cc",
diff --git a/chrome/browser/ui/android/appmenu/internal/java/res/drawable/menu_action_bar_bg.xml b/chrome/browser/ui/android/appmenu/internal/java/res/drawable/menu_action_bar_bg.xml
index 3b34567..5977d6c 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/res/drawable/menu_action_bar_bg.xml
+++ b/chrome/browser/ui/android/appmenu/internal/java/res/drawable/menu_action_bar_bg.xml
@@ -13,5 +13,5 @@
         android:bottomLeftRadius="0dp"
         android:bottomRightRadius="0dp" />
     <solid
-        android:color="@color/menu_action_bar_bg_color"/>
+        android:color="@color/menu_action_bar_bg_color_baseline"/>
 </shape>
diff --git a/chrome/browser/ui/android/appmenu/internal/java/res/layout/menu_item.xml b/chrome/browser/ui/android/appmenu/internal/java/res/layout/menu_item.xml
index 841e535..734c622c 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/res/layout/menu_item.xml
+++ b/chrome/browser/ui/android/appmenu/internal/java/res/layout/menu_item.xml
@@ -27,6 +27,6 @@
         android:layout_height="match_parent"
         android:layout_gravity="end"
         android:gravity="center_vertical"
-        android:tint="?attr/default_icon_color_secondary"
+        android:tint="@macro/default_icon_color_secondary"
         android:duplicateParentState="true" />
 </LinearLayout>
diff --git a/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_arrow_downward_black_24dp.xml b/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_arrow_downward_black_24dp.xml
index edb4fb0..8f3b194f 100644
--- a/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_arrow_downward_black_24dp.xml
+++ b/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_arrow_downward_black_24dp.xml
@@ -3,8 +3,6 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_arrow_forward_black_24dp.xml b/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_arrow_forward_black_24dp.xml
index 86338fee..7a20e09d 100644
--- a/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_arrow_forward_black_24dp.xml
+++ b/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_arrow_forward_black_24dp.xml
@@ -3,8 +3,6 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_info_outline_black_24dp.xml b/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_info_outline_black_24dp.xml
index 5f801a6..c54307b 100644
--- a/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_info_outline_black_24dp.xml
+++ b/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_info_outline_black_24dp.xml
@@ -3,8 +3,6 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_more_vert_black_24dp.xml b/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_more_vert_black_24dp.xml
index f381a3b..2796f96 100644
--- a/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_more_vert_black_24dp.xml
+++ b/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_more_vert_black_24dp.xml
@@ -3,8 +3,6 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_refresh_black_24dp.xml b/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_refresh_black_24dp.xml
index 88c03d0..d029fda 100644
--- a/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_refresh_black_24dp.xml
+++ b/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_refresh_black_24dp.xml
@@ -3,8 +3,6 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_star_border_black_24dp.xml b/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_star_border_black_24dp.xml
index be562e0..6ad6bc5 100644
--- a/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_star_border_black_24dp.xml
+++ b/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_star_border_black_24dp.xml
@@ -3,8 +3,6 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_vintage_filter.xml b/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_vintage_filter.xml
index b617083..a5bb92ef 100644
--- a/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_vintage_filter.xml
+++ b/chrome/browser/ui/android/appmenu/internal/test/java/res/drawable/test_ic_vintage_filter.xml
@@ -3,8 +3,6 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceState.java b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceState.java
index 8a60d42..04d1f33 100644
--- a/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceState.java
+++ b/chrome/browser/ui/android/multiwindow/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceState.java
@@ -62,11 +62,12 @@
      * @param baseActivityName Predicate that tells if a given string is a legitimate name of
      *     the base activity of Chrome task.
      */
-    public static void maybeCreate(
+    public static MultiInstanceState maybeCreate(
             Supplier<List<AppTask>> appTaskSupplier, BaseActivityName baseActivityName) {
         if (sInstance == null) {
             sInstance = new MultiInstanceState(appTaskSupplier, baseActivityName);
         }
+        return sInstance;
     }
 
     private MultiInstanceState(
diff --git a/chrome/browser/ui/android/omnibox/java/res/drawable/ic_colorful_mic.xml b/chrome/browser/ui/android/omnibox/java/res/drawable/ic_colorful_mic.xml
index e1f58948..0eec111 100644
--- a/chrome/browser/ui/android/omnibox/java/res/drawable/ic_colorful_mic.xml
+++ b/chrome/browser/ui/android/omnibox/java/res/drawable/ic_colorful_mic.xml
@@ -3,8 +3,6 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        tools:targetApi="21"
         android:width="40dp"
         android:height="40dp"
         android:viewportWidth="56.0"
diff --git a/chrome/browser/ui/android/omnibox/java/res/drawable/ic_content_copy_black.xml b/chrome/browser/ui/android/omnibox/java/res/drawable/ic_content_copy_black.xml
index 41fbfc3..55b4437 100644
--- a/chrome/browser/ui/android/omnibox/java/res/drawable/ic_content_copy_black.xml
+++ b/chrome/browser/ui/android/omnibox/java/res/drawable/ic_content_copy_black.xml
@@ -1,6 +1,4 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        tools:targetApi="21"
         android:width="24dp"
         android:height="24dp"
         android:viewportWidth="24.0"
diff --git a/chrome/browser/ui/android/omnibox/java/res/drawable/ic_google_round.xml b/chrome/browser/ui/android/omnibox/java/res/drawable/ic_google_round.xml
index 0d9afcf..2e73f5a 100644
--- a/chrome/browser/ui/android/omnibox/java/res/drawable/ic_google_round.xml
+++ b/chrome/browser/ui/android/omnibox/java/res/drawable/ic_google_round.xml
@@ -5,8 +5,6 @@
 
 <vector
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:height="24dp"
     android:viewportHeight="36"
     android:viewportWidth="36"
diff --git a/chrome/browser/ui/android/omnibox/java/res/drawable/trending_up_black_24dp.xml b/chrome/browser/ui/android/omnibox/java/res/drawable/trending_up_black_24dp.xml
index b20c66c2..79682c8 100644
--- a/chrome/browser/ui/android/omnibox/java/res/drawable/trending_up_black_24dp.xml
+++ b/chrome/browser/ui/android/omnibox/java/res/drawable/trending_up_black_24dp.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        tools:targetApi="21"
         android:width="24dp"
         android:height="24dp"
         android:viewportWidth="24.0"
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index a9cdf42d4..78cf2565 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -589,10 +589,10 @@
         On-device encryption
       </message>
       <message name="IDS_ANDROID_TRUSTED_VAULT_BANNER_SUB_LABEL_OFFER_OPT_IN" desc="Sub-label for the on-device encryption banner when the user is offered to opt in.">
-        Encrypt passwords on your device before they‘re saved to Google Password Manager
+        For added safety, encrypt passwords on your device before they‘re saved to Google Password Manager
       </message>
       <message name="IDS_ANDROID_TRUSTED_VAULT_BANNER_SUB_LABEL_OPTED_IN" desc="Sub-label for the on-device encryption banner when the user is already opted in.">
-        Your password are encrypted on your device before they’re saved to Google Password Manager
+        Your passwords are encrypted on your device before they’re saved to Google Password Manager
       </message>
       <message name="IDS_SECTION_SAVED_PASSWORDS_EXCEPTIONS" desc="Header for the list of websites for which user selected to never save passwords. [CHAR_LIMIT=32]">
         Never saved
@@ -1432,12 +1432,6 @@
       <message name="IDS_DATA_REDUCTION_INITIAL_TITLE" desc="This title states that the below chart will contain the user's data savings after they have started browsing.">
         Your data savings will appear here
       </message>
-      <message name="IDS_DATA_REDUCTION_SAVED_LABEL" desc="Summary text for the menu item that states the amount of mobile data that was saved by Lite mode (i.e. XX MB saved). Lite mode allows users to to reduce their mobile data usage by compressing network traffic.">
-        <ph name="data">%1$s<ex>1.0 GB</ex></ph> saved
-      </message>
-      <message name="IDS_DATA_REDUCTION_DATE_LABEL" desc="Summary text for the menu item that states the beginning date when the displayed mobile data was saved (i.e. XX MB saved 'since Feb 28')">
-        since <ph name="date">%1$s<ex>Feb 28</ex></ph>
-      </message>
       <message name="IDS_DATA_REDUCTION_BENEFITS_DESCRIPTION_LITE_MODE" desc="Description text about the benefits of the Lite mode feature. Seen only before the user has enabled the feature.">
          In Lite mode, Chrome loads pages faster and uses up to 60 percent less data.
       </message>
@@ -5220,14 +5214,6 @@
         Viewing live page
       </message>
 
-      <!-- HTTPS image compression InfoBar -->
-      <message name="IDS_LITE_MODE_HTTPS_IMAGE_COMPRESSION_MESSAGE" desc="The text of the infobar notifying the user that Chrome's Lite mode will now also compresses HTTPS images.">
-        Lite mode now saves you more data by optimizing images on HTTPS pages.
-      </message>
-      <message name="IDS_LITE_MODE_HTTPS_IMAGE_COMPRESSION_SETTINGS_LINK" desc="This link opens the Lite Mode settings page to explain the feature and configure the settings.">
-        Manage
-      </message>
-
       <!-- Browser Promo Notification Strings -->
       <message name="IDS_CHROME_REENGAGEMENT_NOTIFICATION_1_TITLE" desc="The title of a notification shown to suggest that users use Chrome.  Users probably have not opened Chrome in a while.  Promotes relevant articles Chrome has.">
         Read today's news <ph name="NEWS_ICON">📰</ph>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_ANDROID_TRUSTED_VAULT_BANNER_LABEL.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_ANDROID_TRUSTED_VAULT_BANNER_LABEL.png.sha1
index ddcbe96..8d5a058 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_ANDROID_TRUSTED_VAULT_BANNER_LABEL.png.sha1
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_ANDROID_TRUSTED_VAULT_BANNER_LABEL.png.sha1
@@ -1 +1 @@
-5c8c39e592b62d13ae9c22ffb968bc80ba090260
\ No newline at end of file
+d28d4141f2fe2ce44e78f6896cadb5ecd86d4ffe
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_ANDROID_TRUSTED_VAULT_BANNER_SUB_LABEL_OFFER_OPT_IN.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_ANDROID_TRUSTED_VAULT_BANNER_SUB_LABEL_OFFER_OPT_IN.png.sha1
index ddcbe96..8d5a058 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_ANDROID_TRUSTED_VAULT_BANNER_SUB_LABEL_OFFER_OPT_IN.png.sha1
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_ANDROID_TRUSTED_VAULT_BANNER_SUB_LABEL_OFFER_OPT_IN.png.sha1
@@ -1 +1 @@
-5c8c39e592b62d13ae9c22ffb968bc80ba090260
\ No newline at end of file
+d28d4141f2fe2ce44e78f6896cadb5ecd86d4ffe
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_ANDROID_TRUSTED_VAULT_BANNER_SUB_LABEL_OPTED_IN.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_ANDROID_TRUSTED_VAULT_BANNER_SUB_LABEL_OPTED_IN.png.sha1
index 5d43d771..26df889 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_ANDROID_TRUSTED_VAULT_BANNER_SUB_LABEL_OPTED_IN.png.sha1
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_ANDROID_TRUSTED_VAULT_BANNER_SUB_LABEL_OPTED_IN.png.sha1
@@ -1 +1 @@
-14e7a6481c100f68c9b09cd8b591038199933e2a
\ No newline at end of file
+6eba108be0894a08ee8671f01afc350b282295c4
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LITE_MODE_HTTPS_IMAGE_COMPRESSION_MESSAGE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LITE_MODE_HTTPS_IMAGE_COMPRESSION_MESSAGE.png.sha1
deleted file mode 100644
index 02b778b6..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LITE_MODE_HTTPS_IMAGE_COMPRESSION_MESSAGE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-3f7399d55b217cb8223baee1ea1e5631eac9fe0c
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LITE_MODE_HTTPS_IMAGE_COMPRESSION_SETTINGS_LINK.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LITE_MODE_HTTPS_IMAGE_COMPRESSION_SETTINGS_LINK.png.sha1
deleted file mode 100644
index 02b778b6..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_LITE_MODE_HTTPS_IMAGE_COMPRESSION_SETTINGS_LINK.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-3f7399d55b217cb8223baee1ea1e5631eac9fe0c
\ No newline at end of file
diff --git a/chrome/browser/ui/android/toolbar/java/res/drawable/new_tab_icon.xml b/chrome/browser/ui/android/toolbar/java/res/drawable/new_tab_icon.xml
index 352e891..ba1fcc9 100644
--- a/chrome/browser/ui/android/toolbar/java/res/drawable/new_tab_icon.xml
+++ b/chrome/browser/ui/android/toolbar/java/res/drawable/new_tab_icon.xml
@@ -1,6 +1,4 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        tools:targetApi="21"
         android:width="24dp"
         android:height="24dp"
         android:viewportWidth="24.0"
diff --git a/chrome/browser/ui/app_list/app_context_menu_unittest.cc b/chrome/browser/ui/app_list/app_context_menu_unittest.cc
index c923215..a3cbf205d 100644
--- a/chrome/browser/ui/app_list/app_context_menu_unittest.cc
+++ b/chrome/browser/ui/app_list/app_context_menu_unittest.cc
@@ -247,13 +247,12 @@
                        : path.AppendASCII("hosted_app_absolute_options.json");
 
     JSONFileValueDeserializer deserializer(manifest_path);
-    std::unique_ptr<base::Value> manifest =
-        deserializer.Deserialize(nullptr, nullptr);
+    base::Value manifest = base::Value::FromUniquePtrValue(
+        deserializer.Deserialize(nullptr, nullptr));
 
-    base::Value value = base::Value(std::move(*manifest));
-    DCHECK(value.is_dict());
+    DCHECK(manifest.is_dict());
     const base::DictionaryValue* dictionary_manifest = nullptr;
-    value.GetAsDictionary(&dictionary_manifest);
+    manifest.GetAsDictionary(&dictionary_manifest);
     std::string error;
     return extensions::Extension::Create(
         path.DirName(), extensions::mojom::ManifestLocation::kInternal,
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc
index 5589e69..910798f 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
@@ -164,15 +164,15 @@
 
 void RemoveSyncItemFromLocalStorage(Profile* profile,
                                     const std::string& item_id) {
-  DictionaryPrefUpdate(profile->GetPrefs(), prefs::kAppListLocalState)
+  DictionaryPrefUpdateDeprecated(profile->GetPrefs(), prefs::kAppListLocalState)
       ->RemoveKey(item_id);
 }
 
 void UpdateSyncItemInLocalStorage(
     Profile* profile,
     const AppListSyncableService::SyncItem* sync_item) {
-  DictionaryPrefUpdate pref_update(profile->GetPrefs(),
-                                   prefs::kAppListLocalState);
+  DictionaryPrefUpdateDeprecated pref_update(profile->GetPrefs(),
+                                             prefs::kAppListLocalState);
   base::Value* dict_item = pref_update->FindKeyOfType(
       sync_item->item_id, base::Value::Type::DICTIONARY);
   if (!dict_item) {
@@ -1155,8 +1155,8 @@
   HandleUpdateStarted();
 
   // Reset local state and recreate from sync info.
-  DictionaryPrefUpdate pref_update(profile_->GetPrefs(),
-                                   prefs::kAppListLocalState);
+  DictionaryPrefUpdateDeprecated pref_update(profile_->GetPrefs(),
+                                             prefs::kAppListLocalState);
   pref_update->DictClear();
 
   sync_processor_ = std::move(sync_processor);
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index 62c4ab9..b27ad03 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -108,7 +108,7 @@
   explicit NotificationsEnabledDeferred(PrefService* prefs) : prefs_(prefs) {}
 
   void Put(const std::string& app_id, bool enabled) {
-    DictionaryPrefUpdate update(
+    DictionaryPrefUpdateDeprecated update(
         prefs_, arc::prefs::kArcSetNotificationsEnabledDeferred);
     base::DictionaryValue* const dict = update.Get();
     dict->SetKey(app_id, base::Value(enabled));
@@ -121,7 +121,7 @@
   }
 
   void Remove(const std::string& app_id) {
-    DictionaryPrefUpdate update(
+    DictionaryPrefUpdateDeprecated update(
         prefs_, arc::prefs::kArcSetNotificationsEnabledDeferred);
     base::DictionaryValue* const dict = update.Get();
     dict->RemoveKey(app_id);
@@ -1391,7 +1391,7 @@
   ScheduleAppFolderDeletion(app_id);
 
   // Remove from prefs.
-  DictionaryPrefUpdate update(prefs_, arc::prefs::kArcApps);
+  DictionaryPrefUpdateDeprecated update(prefs_, arc::prefs::kArcApps);
   base::DictionaryValue* apps = update.Get();
   const bool removed = apps->RemoveKey(app_id);
   DCHECK(removed);
@@ -1504,7 +1504,7 @@
 }
 
 void ArcAppListPrefs::RemovePackageFromPrefs(const std::string& package_name) {
-  DictionaryPrefUpdate(prefs_, arc::prefs::kArcPackages)
+  DictionaryPrefUpdateDeprecated(prefs_, arc::prefs::kArcPackages)
       .Get()
       ->RemoveKey(package_name);
 }
diff --git a/chrome/browser/ui/app_list/arc/arc_app_scoped_pref_update.cc b/chrome/browser/ui/app_list/arc/arc_app_scoped_pref_update.cc
index 502e9dd..f55eb930 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_scoped_pref_update.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_scoped_pref_update.cc
@@ -11,12 +11,12 @@
 ArcAppScopedPrefUpdate::ArcAppScopedPrefUpdate(PrefService* service,
                                                const std::string& id,
                                                const std::string& path)
-    : DictionaryPrefUpdate(service, path), id_(id) {}
+    : DictionaryPrefUpdateDeprecated(service, path), id_(id) {}
 
 ArcAppScopedPrefUpdate::~ArcAppScopedPrefUpdate() = default;
 
 base::DictionaryValue* ArcAppScopedPrefUpdate::Get() {
-  base::DictionaryValue* dict = DictionaryPrefUpdate::Get();
+  base::DictionaryValue* dict = DictionaryPrefUpdateDeprecated::Get();
   base::Value* dict_item =
       dict->FindKeyOfType(id_, base::Value::Type::DICTIONARY);
   if (!dict_item)
diff --git a/chrome/browser/ui/app_list/arc/arc_app_scoped_pref_update.h b/chrome/browser/ui/app_list/arc/arc_app_scoped_pref_update.h
index 28e993f..180f5ae 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_scoped_pref_update.h
+++ b/chrome/browser/ui/app_list/arc/arc_app_scoped_pref_update.h
@@ -14,7 +14,7 @@
 namespace arc {
 
 // Pref updater for ARC apps. Used in deferent pref sections.
-class ArcAppScopedPrefUpdate : public DictionaryPrefUpdate {
+class ArcAppScopedPrefUpdate : public DictionaryPrefUpdateDeprecated {
  public:
   // This is used in following cases:
   // |path| is "arc.apps" - To update ARC apps preferences. In this case |id|
@@ -32,7 +32,7 @@
 
   ~ArcAppScopedPrefUpdate() override;
 
-  // DictionaryPrefUpdate:
+  // DictionaryPrefUpdateDeprecated:
   base::DictionaryValue* Get() override;
 
  private:
diff --git a/chrome/browser/ui/app_list/arc/arc_default_app_list.cc b/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
index 9964ffa..c5184d0 100644
--- a/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
+++ b/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
@@ -78,41 +78,35 @@
 
     JSONFileValueDeserializer deserializer(file);
     std::string error_msg;
-    std::unique_ptr<base::Value> app_info =
-        deserializer.Deserialize(nullptr, &error_msg);
-    if (!app_info) {
+    auto app_info_ptr = deserializer.Deserialize(nullptr, &error_msg);
+    if (!app_info_ptr) {
       VLOG(2) << "Unable to deserialize json data: " << error_msg << " in file "
               << file.value() << ".";
       continue;
     }
+    base::Value app_info =
+        base::Value::FromUniquePtrValue(std::move(app_info_ptr));
 
-    std::unique_ptr<base::DictionaryValue> app_info_dictionary =
-        base::DictionaryValue::From(std::move(app_info));
-    CHECK(app_info_dictionary);
+    CHECK(app_info.is_dict());
 
-    std::string name;
-    std::string package_name;
-    std::string activity;
-    std::string app_path;
+    auto* name = app_info.FindStringKey(kName);
+    auto* package_name = app_info.FindStringKey(kPackageName);
+    auto* activity = app_info.FindStringKey(kActivity);
+    auto* app_path = app_info.FindStringKey(kAppPath);
+    bool oem = app_info.FindBoolPath(kOem).value_or(false);
 
-    app_info_dictionary->GetString(kName, &name);
-    app_info_dictionary->GetString(kPackageName, &package_name);
-    app_info_dictionary->GetString(kActivity, &activity);
-    app_info_dictionary->GetString(kAppPath, &app_path);
-    bool oem = app_info_dictionary->FindBoolPath(kOem).value_or(false);
-
-    if (name.empty() || package_name.empty() || activity.empty() ||
-        app_path.empty()) {
+    if (!name || !package_name || !activity || !app_path || name->empty() ||
+        package_name->empty() || activity->empty() || app_path->empty()) {
       VLOG(2) << "ARC app declaration is incomplete in file " << file.value()
               << ".";
       continue;
     }
 
     const std::string app_id =
-        ArcAppListPrefs::GetAppId(package_name, activity);
+        ArcAppListPrefs::GetAppId(*package_name, *activity);
     std::unique_ptr<ArcDefaultAppList::AppInfo> app =
         std::make_unique<ArcDefaultAppList::AppInfo>(
-            name, package_name, activity, oem, root_dir.Append(app_path));
+            *name, *package_name, *activity, oem, root_dir.Append(*app_path));
     apps.get()->insert(
         std::pair<std::string, std::unique_ptr<ArcDefaultAppList::AppInfo>>(
             app_id, std::move(app)));
diff --git a/chrome/browser/ui/app_list/search/omnibox_answer_result.cc b/chrome/browser/ui/app_list/search/omnibox_answer_result.cc
index d2d5ea7..7ffc142 100644
--- a/chrome/browser/ui/app_list/search/omnibox_answer_result.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_answer_result.cc
@@ -154,8 +154,7 @@
     bitmap_fetcher_ =
         std::make_unique<BitmapFetcher>(url, this, kOmniboxTrafficAnnotation);
   }
-  bitmap_fetcher_->Init(/*referrer=*/std::string(),
-                        net::ReferrerPolicy::NEVER_CLEAR,
+  bitmap_fetcher_->Init(net::ReferrerPolicy::NEVER_CLEAR,
                         network::mojom::CredentialsMode::kOmit);
   bitmap_fetcher_->Start(profile_->GetURLLoaderFactory().get());
 }
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.cc b/chrome/browser/ui/app_list/search/omnibox_result.cc
index 988c7ec..330543e 100644
--- a/chrome/browser/ui/app_list/search/omnibox_result.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_result.cc
@@ -349,8 +349,7 @@
     bitmap_fetcher_ =
         std::make_unique<BitmapFetcher>(url, this, kOmniboxTrafficAnnotation);
   }
-  bitmap_fetcher_->Init(/*referrer=*/std::string(),
-                        net::ReferrerPolicy::NEVER_CLEAR,
+  bitmap_fetcher_->Init(net::ReferrerPolicy::NEVER_CLEAR,
                         network::mojom::CredentialsMode::kOmit);
   bitmap_fetcher_->Start(profile_->GetURLLoaderFactory().get());
 }
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc
index f7bd37f0..b21356e4c 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc
@@ -64,7 +64,7 @@
     return;
 
   // Write the new finalized `items` to persistent storage.
-  ListPrefUpdate update(profile()->GetPrefs(), kPersistencePath);
+  ListPrefUpdateDeprecated update(profile()->GetPrefs(), kPersistencePath);
   for (const HoldingSpaceItem* item : items) {
     if (item->progress().IsComplete())
       update->Append(item->Serialize());
@@ -77,7 +77,7 @@
     return;
 
   // Remove the `items` from persistent storage.
-  ListPrefUpdate update(profile()->GetPrefs(), kPersistencePath);
+  ListPrefUpdateDeprecated update(profile()->GetPrefs(), kPersistencePath);
   update->EraseListValueIf([&items](const base::Value& persisted_item) {
     const std::string& persisted_item_id = HoldingSpaceItem::DeserializeId(
         base::Value::AsDictionaryValue(persisted_item));
@@ -99,7 +99,7 @@
     return;
 
   // Attempt to find the finalized `item` in persistent storage.
-  ListPrefUpdate update(profile()->GetPrefs(), kPersistencePath);
+  ListPrefUpdateDeprecated update(profile()->GetPrefs(), kPersistencePath);
   auto item_it = std::find_if(
       update->GetList().begin(), update->GetList().end(),
       [&item](const base::Value& persisted_item) {
diff --git a/chrome/browser/ui/ash/image_downloader_impl.cc b/chrome/browser/ui/ash/image_downloader_impl.cc
index 3ee3b0f9..1a2537a 100644
--- a/chrome/browser/ui/ash/image_downloader_impl.cc
+++ b/chrome/browser/ui/ash/image_downloader_impl.cc
@@ -62,9 +62,9 @@
     bitmap_fetcher_ =
         std::make_unique<BitmapFetcher>(url, this, annotation_tag);
 
-    bitmap_fetcher_->Init(
-        /*referrer=*/std::string(), net::ReferrerPolicy::NEVER_CLEAR,
-        network::mojom::CredentialsMode::kOmit, additional_headers);
+    bitmap_fetcher_->Init(net::ReferrerPolicy::NEVER_CLEAR,
+                          network::mojom::CredentialsMode::kOmit,
+                          additional_headers);
 
     bitmap_fetcher_->Start(profile->GetURLLoaderFactory().get());
   }
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl.cc b/chrome/browser/ui/ash/projector/projector_client_impl.cc
index fd63d61..b505cbb 100644
--- a/chrome/browser/ui/ash/projector/projector_client_impl.cc
+++ b/chrome/browser/ui/ash/projector/projector_client_impl.cc
@@ -74,8 +74,7 @@
 }
 
 void ProjectorClientImpl::StopSpeechRecognition() {
-  speech_recognizer_.reset();
-  recognizer_status_ = SPEECH_RECOGNIZER_OFF;
+  speech_recognizer_->Stop();
 }
 
 void ProjectorClientImpl::ShowSelfieCam() {
@@ -117,6 +116,12 @@
   recognizer_status_ = new_state;
 }
 
+void ProjectorClientImpl::OnSpeechRecognitionStopped() {
+  speech_recognizer_.reset();
+  recognizer_status_ = SPEECH_RECOGNIZER_OFF;
+  controller_->OnSpeechRecognitionStopped();
+}
+
 bool ProjectorClientImpl::GetDriveFsMountPointPath(
     base::FilePath* result) const {
   if (!IsDriveFsMounted())
diff --git a/chrome/browser/ui/ash/projector/projector_client_impl.h b/chrome/browser/ui/ash/projector/projector_client_impl.h
index b6009d4..1d7a207 100644
--- a/chrome/browser/ui/ash/projector/projector_client_impl.h
+++ b/chrome/browser/ui/ash/projector/projector_client_impl.h
@@ -60,6 +60,7 @@
   void OnSpeechSoundLevelChanged(int16_t level) override {}
   void OnSpeechRecognitionStateChanged(
       SpeechRecognizerStatus new_state) override;
+  void OnSpeechRecognitionStopped() override;
 
  private:
   ash::ProjectorController* const controller_;
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc
index 081c8d51..8ff4157 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_prefs.cc
@@ -598,7 +598,7 @@
     default_app_ids.push_back(default_app_id);
   InsertPinsAfterChromeAndBeforeFirstPinnedApp(syncable_service,
                                                default_app_ids);
-  ListPrefUpdate update(pref_service, GetShelfDefaultPinLayoutPref());
+  ListPrefUpdateDeprecated update(pref_service, GetShelfDefaultPinLayoutPref());
   update->Append(kDefaultPinnedAppsKey);
 }
 
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_browsertest.cc b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_browsertest.cc
index b3b4580e..7944ed2 100644
--- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_browsertest.cc
+++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_browsertest.cc
@@ -37,7 +37,7 @@
   }
 
   LegalMessageLines GetTestLegalMessage() {
-    std::unique_ptr<base::Value> value(base::JSONReader::ReadDeprecated(
+    absl::optional<base::Value> value(base::JSONReader::Read(
         "{"
         "  \"line\" : [ {"
         "     \"template\": \"The legal documents are: {0} and {1}.\","
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc
index 48617fb2..20b868c 100644
--- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc
@@ -107,8 +107,7 @@
       const std::string& message_json,
       AutofillClient::SaveCreditCardOptions options =
           AutofillClient::SaveCreditCardOptions().with_show_prompt()) {
-    std::unique_ptr<base::Value> value(
-        base::JSONReader::ReadDeprecated(message_json));
+    absl::optional<base::Value> value(base::JSONReader::Read(message_json));
     ASSERT_TRUE(value);
     base::DictionaryValue* dictionary;
     ASSERT_TRUE(value->GetAsDictionary(&dictionary));
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 10e4e20..e3858ca3 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -263,7 +263,7 @@
 
     CreationSource creation_source = CreationSource::kUnknown;
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if defined(OS_CHROMEOS)
     // The id from the restore data to restore the browser window.
     int32_t restore_id = kDefaultRestoreId;
 #endif
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index 0bbbb233..dfa2156 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -1334,6 +1334,7 @@
                                         dev_tools_enabled);
   command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS_TOGGLE,
                                         dev_tools_enabled);
+  command_updater_.UpdateCommandEnabled(IDC_VIEW_SOURCE, dev_tools_enabled);
 #if defined(OS_MAC)
   command_updater_.UpdateCommandEnabled(IDC_TOGGLE_JAVASCRIPT_APPLE_EVENTS,
                                         dev_tools_enabled);
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index 32186a6..73c784e 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -1732,10 +1732,18 @@
 }
 
 bool CanViewSource(const Browser* browser) {
-  return !browser->is_type_devtools() && browser->tab_strip_model()
-                                             ->GetActiveWebContents()
-                                             ->GetController()
-                                             .CanViewSource();
+  if (browser->is_type_devtools()) {
+    return false;
+  }
+
+  WebContents* web_contents =
+      browser->tab_strip_model()->GetActiveWebContents();
+
+  // Disallow ViewSource if DevTools are disabled.
+  if (!DevToolsWindow::AllowDevToolsFor(browser->profile(), web_contents)) {
+    return false;
+  }
+  return web_contents->GetController().CanViewSource();
 }
 
 bool CanToggleCaretBrowsing(Browser* browser) {
diff --git a/chrome/browser/ui/browser_window_state.cc b/chrome/browser/ui/browser_window_state.cc
index 9ce79d2..09bdf9d 100644
--- a/chrome/browser/ui/browser_window_state.cc
+++ b/chrome/browser/ui/browser_window_state.cc
@@ -48,11 +48,11 @@
   return true;
 }
 
-class WindowPlacementPrefUpdate : public DictionaryPrefUpdate {
+class WindowPlacementPrefUpdate : public DictionaryPrefUpdateDeprecated {
  public:
   WindowPlacementPrefUpdate(PrefService* service,
                             const std::string& window_name)
-      : DictionaryPrefUpdate(service, prefs::kAppWindowPlacement),
+      : DictionaryPrefUpdateDeprecated(service, prefs::kAppWindowPlacement),
         window_name_(window_name) {}
 
   WindowPlacementPrefUpdate(const WindowPlacementPrefUpdate&) = delete;
@@ -62,7 +62,8 @@
   ~WindowPlacementPrefUpdate() override {}
 
   base::DictionaryValue* Get() override {
-    base::DictionaryValue* all_apps_dict = DictionaryPrefUpdate::Get();
+    base::DictionaryValue* all_apps_dict =
+        DictionaryPrefUpdateDeprecated::Get();
     base::DictionaryValue* this_app_dict_weak = nullptr;
     if (!all_apps_dict->GetDictionary(window_name_, &this_app_dict_weak)) {
       auto this_app_dict = std::make_unique<base::DictionaryValue>();
@@ -95,15 +96,15 @@
   }
 }
 
-std::unique_ptr<DictionaryPrefUpdate> GetWindowPlacementDictionaryReadWrite(
-    const std::string& window_name,
-    PrefService* prefs) {
+std::unique_ptr<DictionaryPrefUpdateDeprecated>
+GetWindowPlacementDictionaryReadWrite(const std::string& window_name,
+                                      PrefService* prefs) {
   DCHECK(!window_name.empty());
-  // A normal DictionaryPrefUpdate will suffice for non-app windows.
+  // A normal DictionaryPrefUpdateDeprecated will suffice for non-app windows.
   if (prefs->FindPreference(window_name)) {
-    return std::make_unique<DictionaryPrefUpdate>(prefs, window_name);
+    return std::make_unique<DictionaryPrefUpdateDeprecated>(prefs, window_name);
   }
-  return std::unique_ptr<DictionaryPrefUpdate>(
+  return std::unique_ptr<DictionaryPrefUpdateDeprecated>(
       new WindowPlacementPrefUpdate(prefs, window_name));
 }
 
diff --git a/chrome/browser/ui/browser_window_state.h b/chrome/browser/ui/browser_window_state.h
index dda13105..8ec84a1 100644
--- a/chrome/browser/ui/browser_window_state.h
+++ b/chrome/browser/ui/browser_window_state.h
@@ -31,9 +31,9 @@
 // of the window that is stored in the given PrefService. If the window_name
 // isn't the name of a registered preference it is assumed to be the name of an
 // app and the AppWindowPlacement key is used to find the app's dictionary.
-std::unique_ptr<DictionaryPrefUpdate> GetWindowPlacementDictionaryReadWrite(
-    const std::string& window_name,
-    PrefService* prefs);
+std::unique_ptr<DictionaryPrefUpdateDeprecated>
+GetWindowPlacementDictionaryReadWrite(const std::string& window_name,
+                                      PrefService* prefs);
 // Returns NULL if the window corresponds to an app that doesn't have placement
 // information stored in the preferences system.
 const base::DictionaryValue* GetWindowPlacementDictionaryReadOnly(
diff --git a/chrome/browser/ui/cocoa/applescript/apple_event_util_unittest.mm b/chrome/browser/ui/cocoa/applescript/apple_event_util_unittest.mm
index becf43e..505d823 100644
--- a/chrome/browser/ui/cocoa/applescript/apple_event_util_unittest.mm
+++ b/chrome/browser/ui/cocoa/applescript/apple_event_util_unittest.mm
@@ -230,10 +230,10 @@
   };
 
   for (size_t i = 0; i < base::size(cases); ++i) {
-    std::unique_ptr<base::Value> value =
-        base::JSONReader::ReadDeprecated(cases[i].json_input);
+    absl::optional<base::Value> value =
+        base::JSONReader::Read(cases[i].json_input);
     NSAppleEventDescriptor* descriptor =
-        chrome::mac::ValueToAppleEventDescriptor(value.get());
+        chrome::mac::ValueToAppleEventDescriptor(&*value);
 
     EXPECT_EQ(cases[i].expected_aedesc_dump,
               AEDescToString([descriptor aeDesc]))
diff --git a/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm b/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm
index d7ddc8f8..91603add 100644
--- a/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm
+++ b/chrome/browser/ui/cocoa/task_manager_mac_browsertest.mm
@@ -83,8 +83,8 @@
     if (!local_state)
       FAIL();
 
-    DictionaryPrefUpdate dict_update(local_state,
-                                     prefs::kTaskManagerColumnVisibility);
+    DictionaryPrefUpdateDeprecated dict_update(
+        local_state, prefs::kTaskManagerColumnVisibility);
     dict_update->DictClear();
   }
 
diff --git a/chrome/browser/ui/cocoa/window_size_autosaver.mm b/chrome/browser/ui/cocoa/window_size_autosaver.mm
index 6938a16c..d091057f 100644
--- a/chrome/browser/ui/cocoa/window_size_autosaver.mm
+++ b/chrome/browser/ui/cocoa/window_size_autosaver.mm
@@ -53,7 +53,7 @@
 }
 
 - (void)save:(NSNotification*)notification {
-  DictionaryPrefUpdate update(_prefService, _path);
+  DictionaryPrefUpdateDeprecated update(_prefService, _path);
   base::Value* windowPrefs = update.Get();
   NSRect frame = [_window frame];
   if ([_window styleMask] & NSResizableWindowMask) {
@@ -87,7 +87,7 @@
     if (x2.value() - x1.value() < kMinWindowWidth ||
         y2.value() - y1.value() < kMinWindowHeight) {
       // Windows should never be very small.
-      DictionaryPrefUpdate update(_prefService, _path);
+      DictionaryPrefUpdateDeprecated update(_prefService, _path);
       base::Value* mutableWindowPrefs = update.Get();
       mutableWindowPrefs->RemoveKey("left");
       mutableWindowPrefs->RemoveKey("right");
diff --git a/chrome/browser/ui/cocoa/window_size_autosaver_unittest.mm b/chrome/browser/ui/cocoa/window_size_autosaver_unittest.mm
index 365ff61..d12a94c 100644
--- a/chrome/browser/ui/cocoa/window_size_autosaver_unittest.mm
+++ b/chrome/browser/ui/cocoa/window_size_autosaver_unittest.mm
@@ -180,7 +180,7 @@
   PrefService* pref = profile()->GetPrefs();
   ASSERT_TRUE(pref);
 
-  DictionaryPrefUpdate update(pref, path_);
+  DictionaryPrefUpdateDeprecated update(pref, path_);
   base::Value* windowPref = update.Get();
   windowPref->SetIntKey("left", 50);
   windowPref->SetIntKey("right", 50);
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index d08f99a..cc7f932d4 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -64,7 +64,7 @@
 #include "components/url_formatter/elide_url.h"
 #include "components/vector_icons/vector_icons.h"
 #include "content/public/browser/notification_service.h"
-#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/page.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/weak_document_ptr.h"
@@ -222,9 +222,8 @@
 }
 
 void ContentSettingSimpleBubbleModel::SetTitle() {
-  // TODO(https://crbug.com/1103176): Plumb the actual frame reference here
   PageSpecificContentSettings* content_settings =
-      PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+      PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
 
   static const ContentSettingsTypeIdEntry kBlockedTitleIDs[] = {
       {ContentSettingsType::COOKIES, IDS_BLOCKED_COOKIES_TITLE},
@@ -260,7 +259,7 @@
 
 void ContentSettingSimpleBubbleModel::SetMessage() {
   PageSpecificContentSettings* content_settings =
-      PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+      PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
 
   // TODO(https://crbug.com/978882): Make the two arrays below static again once
   // we no longer need to check base::FeatureList.
@@ -377,7 +376,7 @@
   if (mixed_content_settings) {
     // Update browser side settings to allow active mixed content.
     mixed_content_settings->AllowRunningOfInsecureContent(
-        *web_contents()->GetMainFrame());
+        GetPage().GetMainDocument());
   }
 
   // Update renderer side settings to allow active mixed content.
@@ -551,7 +550,7 @@
   std::u16string display_host = url_formatter::FormatUrlForSecurityDisplay(url);
 
   PageSpecificContentSettings* content_settings =
-      PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+      PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
   bool allowed = !content_settings->IsContentBlocked(content_type());
 
   // For the frame busting case the content is blocked but its content type is
@@ -820,7 +819,7 @@
   radio_item_setting_[1] = CONTENT_SETTING_BLOCK;
 
   PageSpecificContentSettings* content_settings =
-      PageSpecificContentSettings::GetForFrame(web_contents->GetMainFrame());
+      PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
   state_ = content_settings->GetMicrophoneCameraState();
   DCHECK(CameraAccessed() || MicrophoneAccessed());
 
@@ -956,7 +955,7 @@
 
 void ContentSettingMediaStreamBubbleModel::SetRadioGroup() {
   PageSpecificContentSettings* content_settings =
-      PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+      PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
   GURL url = content_settings->media_stream_access_origin();
   RadioGroup radio_group;
   radio_group.url = url;
@@ -994,7 +993,7 @@
     permissions::PermissionResult pan_tilt_zoom_permission =
         permission_manager->GetPermissionStatusForFrame(
             ContentSettingsType::CAMERA_PAN_TILT_ZOOM,
-            web_contents()->GetMainFrame(), url);
+            &GetPage().GetMainDocument(), url);
     bool has_pan_tilt_zoom_permission_granted =
         pan_tilt_zoom_permission.content_setting == CONTENT_SETTING_ALLOW;
     if (MicrophoneAccessed() && CameraAccessed()) {
@@ -1035,7 +1034,7 @@
 void ContentSettingMediaStreamBubbleModel::UpdateSettings(
     ContentSetting setting) {
   PageSpecificContentSettings* page_content_settings =
-      PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+      PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
   // The same urls must be used as in other places (e.g. the infobar) in
   // order to override the existing rule. Otherwise a new rule is created.
   // TODO(markusheintz): Extract to a helper so that there is only a single
@@ -1135,7 +1134,7 @@
 
 void ContentSettingMediaStreamBubbleModel::SetMediaMenus() {
   PageSpecificContentSettings* content_settings =
-      PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+      PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
   const std::string& requested_microphone =
       content_settings->media_stream_requested_audio_device();
   const std::string& requested_camera =
@@ -1205,7 +1204,7 @@
 
 void ContentSettingMediaStreamBubbleModel::SetCustomLink() {
   PageSpecificContentSettings* content_settings =
-      PageSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
+      PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
   if (content_settings->IsMicrophoneCameraStateChanged()) {
     set_custom_link(
         l10n_util::GetStringUTF16(IDS_MEDIASTREAM_SETTING_CHANGED_MESSAGE));
@@ -1238,7 +1237,7 @@
   SetCustomLink();
 #if defined(OS_MAC)
   PageSpecificContentSettings* content_settings =
-      PageSpecificContentSettings::GetForFrame(web_contents->GetMainFrame());
+      PageSpecificContentSettings::GetForFrame(&GetPage().GetMainDocument());
   if (!content_settings)
     return;
 
@@ -1313,7 +1312,7 @@
       web_contents()->GetBrowserContext());
   SettingInfo info;
   const GURL url =
-      web_contents()->GetMainFrame()->GetLastCommittedOrigin().GetURL();
+      GetPage().GetMainDocument().GetLastCommittedOrigin().GetURL();
   map->GetWebsiteSetting(url, url, ContentSettingsType::GEOLOCATION, &info);
   if (info.session_model == SessionModel::OneTime)
     set_custom_link(l10n_util::GetStringUTF16(IDS_GEOLOCATION_WILL_ASK_AGAIN));
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.h b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
index 3260a49..0962fdc2 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.h
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
@@ -35,6 +35,7 @@
 }
 
 namespace content {
+class Page;
 class WebContents;
 }
 
@@ -244,6 +245,7 @@
   Profile* GetProfile() const;
   Delegate* delegate() const { return delegate_; }
   int selected_item() const { return owner_->GetSelectedRadioOption(); }
+  content::Page& GetPage() const { return web_contents_->GetPrimaryPage(); }
 
   void set_title(const std::u16string& title) { bubble_content_.title = title; }
   void set_message(const std::u16string& message) {
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
index cf2a4ef..f1453c8 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
@@ -407,9 +407,8 @@
     auto* manager = PermissionManagerFactory::GetForProfile(
         exclusive_access_manager()->context()->GetProfile());
     if (!manager || !requesting_frame ||
-        manager->GetPermissionStatusForFrame(
-                   ContentSettingsType::WINDOW_PLACEMENT, requesting_frame,
-                   GetRequestingOrigin())
+        manager->GetPermissionStatusForCurrentDocument(
+                   ContentSettingsType::WINDOW_PLACEMENT, requesting_frame)
                 .content_setting != ContentSetting::CONTENT_SETTING_ALLOW) {
       display_id = display::kInvalidDisplayId;
     }
diff --git a/chrome/browser/ui/extensions/settings_overridden_params_providers_unittest.cc b/chrome/browser/ui/extensions/settings_overridden_params_providers_unittest.cc
index a0dbd01..8ad7271 100644
--- a/chrome/browser/ui/extensions/settings_overridden_params_providers_unittest.cc
+++ b/chrome/browser/ui/extensions/settings_overridden_params_providers_unittest.cc
@@ -43,13 +43,14 @@
   // Adds a new extension that overrides the NTP.
   const extensions::Extension* AddExtensionControllingNewTab(
       const char* name = "ntp override") {
-    std::unique_ptr<base::Value> chrome_url_overrides =
-        extensions::DictionaryBuilder().Set("newtab", "newtab.html").Build();
+    base::Value chrome_url_overrides = base::Value::FromUniquePtrValue(
+        extensions::DictionaryBuilder().Set("newtab", "newtab.html").Build());
     scoped_refptr<const extensions::Extension> extension =
         extensions::ExtensionBuilder(name)
             .SetLocation(extensions::mojom::ManifestLocation::kInternal)
-            .SetManifestKey("chrome_url_overrides",
-                            std::move(chrome_url_overrides))
+            .SetManifestKey(
+                "chrome_url_overrides",
+                base::Value::ToUniquePtrValue(std::move(chrome_url_overrides)))
             .Build();
 
     service()->AddExtension(extension.get());
diff --git a/chrome/browser/ui/global_media_controls/cast_media_notification_item.cc b/chrome/browser/ui/global_media_controls/cast_media_notification_item.cc
index 810bfd31..97c45118 100644
--- a/chrome/browser/ui/global_media_controls/cast_media_notification_item.cc
+++ b/chrome/browser/ui/global_media_controls/cast_media_notification_item.cc
@@ -334,9 +334,8 @@
                               url_, this, GetTrafficAnnotationTag())
                         : std::make_unique<BitmapFetcher>(
                               url_, this, GetTrafficAnnotationTag());
-  bitmap_fetcher_->Init(
-      /* referrer */ "", net::ReferrerPolicy::NEVER_CLEAR,
-      network::mojom::CredentialsMode::kOmit);
+  bitmap_fetcher_->Init(net::ReferrerPolicy::NEVER_CLEAR,
+                        network::mojom::CredentialsMode::kOmit);
   bitmap_fetcher_->Start(url_loader_factory_.get());
 }
 
diff --git a/chrome/browser/ui/global_media_controls/cast_media_notification_item_unittest.cc b/chrome/browser/ui/global_media_controls/cast_media_notification_item_unittest.cc
index ecaa4e5f..1183505 100644
--- a/chrome/browser/ui/global_media_controls/cast_media_notification_item_unittest.cc
+++ b/chrome/browser/ui/global_media_controls/cast_media_notification_item_unittest.cc
@@ -56,10 +56,10 @@
 
   MOCK_METHOD(void,
               Init,
-              (const std::string& referrer,
-               net::ReferrerPolicy referrer_policy,
+              (net::ReferrerPolicy referrer_policy,
                network::mojom::CredentialsMode credentials_mode,
-               const net::HttpRequestHeaders& additional_headers),
+               const net::HttpRequestHeaders& additional_headers,
+               const url::Origin& initiator),
               (override));
   MOCK_METHOD(void,
               Start,
@@ -293,7 +293,7 @@
             bitmap_fetcher_delegate = delegate;
 
             EXPECT_EQ(url, image_url);
-            EXPECT_CALL(*bitmap_fetcher, Init(_, _, _, _));
+            EXPECT_CALL(*bitmap_fetcher, Init);
             EXPECT_CALL(*bitmap_fetcher, Start(_));
             return bitmap_fetcher;
           });
diff --git a/chrome/browser/ui/hats/hats_service.cc b/chrome/browser/ui/hats/hats_service.cc
index c401c8b..e6dcc940 100644
--- a/chrome/browser/ui/hats/hats_service.cc
+++ b/chrome/browser/ui/hats/hats_service.cc
@@ -399,7 +399,8 @@
   UMA_HISTOGRAM_ENUMERATION(kHatsShouldShowSurveyReasonHistogram,
                             ShouldShowSurveyReasons::kYes);
 
-  DictionaryPrefUpdate update(profile_->GetPrefs(), prefs::kHatsSurveyMetadata);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kHatsSurveyMetadata);
   base::DictionaryValue* pref_data = update.Get();
   pref_data->SetIntPath(GetMajorVersionPath(trigger),
                         version_info::GetVersion().components()[0]);
@@ -416,7 +417,8 @@
 void HatsService::SetSurveyMetadataForTesting(
     const HatsService::SurveyMetadata& metadata) {
   const std::string& trigger = kHatsSurveyTriggerSettings;
-  DictionaryPrefUpdate update(profile_->GetPrefs(), prefs::kHatsSurveyMetadata);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kHatsSurveyMetadata);
   base::DictionaryValue* pref_data = update.Get();
   if (!metadata.last_major_version.has_value() &&
       !metadata.last_survey_started_time.has_value() &&
@@ -464,7 +466,8 @@
 void HatsService::GetSurveyMetadataForTesting(
     HatsService::SurveyMetadata* metadata) const {
   const std::string& trigger = kHatsSurveyTriggerSettings;
-  DictionaryPrefUpdate update(profile_->GetPrefs(), prefs::kHatsSurveyMetadata);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kHatsSurveyMetadata);
   base::DictionaryValue* pref_data = update.Get();
 
   absl::optional<int> last_major_version =
@@ -747,7 +750,8 @@
 
   // As soon as the HaTS Next dialog is created it will attempt to contact
   // the HaTS servers to check for a survey.
-  DictionaryPrefUpdate update(profile_->GetPrefs(), prefs::kHatsSurveyMetadata);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        prefs::kHatsSurveyMetadata);
   update->SetPath(GetLastSurveyCheckTime(trigger),
                   base::TimeToValue(base::Time::Now()));
 
diff --git a/chrome/browser/ui/messages/android/BUILD.gn b/chrome/browser/ui/messages/android/BUILD.gn
index 76e54b1..7c85925 100644
--- a/chrome/browser/ui/messages/android/BUILD.gn
+++ b/chrome/browser/ui/messages/android/BUILD.gn
@@ -5,8 +5,10 @@
 
 android_resources("java_resources") {
   sources = [
+    "java/res/drawable-v24/snackbar_background_tablet.xml",
     "java/res/drawable/snackbar_background_tablet.xml",
     "java/res/layout/snackbar.xml",
+    "java/res/values-night/dimens.xml",
     "java/res/values/dimens.xml",
   ]
 
@@ -33,6 +35,7 @@
     ":java_resources",
     "//base:base_java",
     "//chrome/browser/util:java",
+    "//components/browser_ui/styles/android:java",
     "//components/browser_ui/styles/android:java_resources",
     "//components/browser_ui/widget/android:java",
     "//components/infobars/android:infobar_android_enums_java",
diff --git a/chrome/browser/ui/messages/android/java/res/drawable-v24/snackbar_background_tablet.xml b/chrome/browser/ui/messages/android/java/res/drawable-v24/snackbar_background_tablet.xml
new file mode 100644
index 0000000..4ec3b1d1
--- /dev/null
+++ b/chrome/browser/ui/messages/android/java/res/drawable-v24/snackbar_background_tablet.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 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. -->
+
+<org.chromium.components.browser_ui.widget.SurfaceColorDrawable
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:shape="rectangle"
+    app:surfaceElevation="@dimen/snackbar_background_tablet_elev">
+  <corners
+      android:topLeftRadius="2dp"
+      android:topRightRadius="2dp"/>
+</org.chromium.components.browser_ui.widget.SurfaceColorDrawable>
diff --git a/chrome/browser/ui/messages/android/java/res/drawable/snackbar_background_tablet.xml b/chrome/browser/ui/messages/android/java/res/drawable/snackbar_background_tablet.xml
index 9a9d937..d91bd9e 100644
--- a/chrome/browser/ui/messages/android/java/res/drawable/snackbar_background_tablet.xml
+++ b/chrome/browser/ui/messages/android/java/res/drawable/snackbar_background_tablet.xml
@@ -6,10 +6,10 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle" >
 
-    <solid android:color="@color/snackbar_background_color" />
+    <solid android:color="@color/snackbar_background_color_baseline" />
 
     <corners
-        android:topLeftRadius="2dip"
-        android:topRightRadius="2dip"/>
+        android:topLeftRadius="2dp"
+        android:topRightRadius="2dp"/>
 
 </shape>
diff --git a/chrome/browser/ui/messages/android/java/res/values-night/dimens.xml b/chrome/browser/ui/messages/android/java/res/values-night/dimens.xml
new file mode 100644
index 0000000..3f24764
--- /dev/null
+++ b/chrome/browser/ui/messages/android/java/res/values-night/dimens.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 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 xmlns:tools="http://schemas.android.com/tools">
+  <!-- Snackbars -->
+  <dimen name="snackbar_background_tablet_elev">@dimen/default_elevation_4</dimen>
+</resources>
diff --git a/chrome/browser/ui/messages/android/java/res/values/dimens.xml b/chrome/browser/ui/messages/android/java/res/values/dimens.xml
index 29ad6b0..d74eba9 100644
--- a/chrome/browser/ui/messages/android/java/res/values/dimens.xml
+++ b/chrome/browser/ui/messages/android/java/res/values/dimens.xml
@@ -10,4 +10,5 @@
     <dimen name="snackbar_margin_tablet">24dp</dimen>
     <dimen name="snackbar_shadow_height">8dp</dimen>
     <dimen name="snackbar_text_view_margin">24dp</dimen>
+    <dimen name="snackbar_background_tablet_elev">@dimen/default_elevation_0</dimen>
 </resources>
diff --git a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java
index d01675c..31d1fe4 100644
--- a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java
+++ b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java
@@ -29,6 +29,7 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.ui.messages.R;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.widget.animation.Interpolators;
 import org.chromium.components.browser_ui.widget.text.TemplatePreservingTextView;
 import org.chromium.ui.base.DeviceFormFactor;
@@ -262,8 +263,7 @@
             return snackbar.getBackgroundColor();
         }
 
-        return ApiCompatibilityUtils.getColor(
-                view.getResources(), R.color.snackbar_background_color);
+        return SemanticColorUtils.getSnackbarBackgroundColor(view.getContext());
     }
 
     private static int getTextAppearance(Snackbar snackbar) {
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
index c8a90e8..979f6e8b 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
@@ -447,3 +447,10 @@
 
   shortcuts_backend->AddOrUpdateShortcut(text, match);
 }
+
+// static
+void ChromeOmniboxClient::OnFinishedNavigation(Profile* profile) {
+  AutocompleteActionPredictor* action_predictor =
+      predictors::AutocompleteActionPredictorFactory::GetForProfile(profile);
+  action_predictor->OnFinishedNavigation();
+}
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.h b/chrome/browser/ui/omnibox/chrome_omnibox_client.h
index 12ffda9..17c72a1c 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_client.h
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.h
@@ -88,6 +88,9 @@
                                      const std::u16string& text,
                                      const AutocompleteMatch& match);
 
+  // Called when a navigation finishes.
+  static void OnFinishedNavigation(Profile* profile);
+
  private:
   // Performs prerendering for |match|.
   void DoPrerender(const AutocompleteMatch& match);
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_navigation_observer.cc b/chrome/browser/ui/omnibox/chrome_omnibox_navigation_observer.cc
index bb052c1..afb7094 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_navigation_observer.cc
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_navigation_observer.cc
@@ -295,6 +295,8 @@
     ChromeOmniboxClient::OnSuccessfulNavigation(profile_, text_, match_);
   }
 
+  ChromeOmniboxClient::OnFinishedNavigation(profile_);
+
   if (navigation_handle->GetResponseHeaders()->response_code() == 404) {
     On404();
   }
diff --git a/chrome/browser/ui/passwords/account_avatar_fetcher.cc b/chrome/browser/ui/passwords/account_avatar_fetcher.cc
index 76a98a4..259bf8c 100644
--- a/chrome/browser/ui/passwords/account_avatar_fetcher.cc
+++ b/chrome/browser/ui/passwords/account_avatar_fetcher.cc
@@ -56,9 +56,11 @@
 AccountAvatarFetcher::~AccountAvatarFetcher() = default;
 
 void AccountAvatarFetcher::Start(
-    network::mojom::URLLoaderFactory* loader_factory) {
-  fetcher_.Init(std::string(), net::ReferrerPolicy::NEVER_CLEAR,
-                network::mojom::CredentialsMode::kOmit);
+    network::mojom::URLLoaderFactory* loader_factory,
+    const url::Origin& initiator) {
+  fetcher_.Init(net::ReferrerPolicy::NEVER_CLEAR,
+                network::mojom::CredentialsMode::kOmit,
+                net::HttpRequestHeaders(), initiator);
   fetcher_.Start(loader_factory);
 }
 
diff --git a/chrome/browser/ui/passwords/account_avatar_fetcher.h b/chrome/browser/ui/passwords/account_avatar_fetcher.h
index b2957e1..f2b9315 100644
--- a/chrome/browser/ui/passwords/account_avatar_fetcher.h
+++ b/chrome/browser/ui/passwords/account_avatar_fetcher.h
@@ -37,7 +37,8 @@
 
   ~AccountAvatarFetcher() override;
 
-  void Start(network::mojom::URLLoaderFactory* loader_factory);
+  void Start(network::mojom::URLLoaderFactory* loader_factory,
+             const url::Origin& initiator);
 
  private:
   // BitmapFetcherDelegate:
diff --git a/chrome/browser/ui/passwords/manage_passwords_view_utils.cc b/chrome/browser/ui/passwords/manage_passwords_view_utils.cc
index 856944c..34ecc7d 100644
--- a/chrome/browser/ui/passwords/manage_passwords_view_utils.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_view_utils.cc
@@ -32,6 +32,7 @@
 #include "components/sync/driver/sync_service.h"
 #include "components/sync/driver/sync_user_settings.h"
 #include "components/url_formatter/elide_url.h"
+#include "content/public/browser/web_contents.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/base/url_util.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -238,3 +239,12 @@
   Navigate(&params);
 }
 #endif  // !defined(OS_ANDROID)
+
+mojo::Remote<network::mojom::URLLoaderFactory> GetURLLoaderForMainFrame(
+    content::WebContents* web_contents) {
+  content::RenderFrameHost* frame = web_contents->GetMainFrame();
+  mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory;
+  frame->CreateNetworkServiceDefaultFactory(
+      url_loader_factory.BindNewPipeAndPassReceiver());
+  return url_loader_factory;
+}
diff --git a/chrome/browser/ui/passwords/manage_passwords_view_utils.h b/chrome/browser/ui/passwords/manage_passwords_view_utils.h
index 5e86f4d..c23b9d4 100644
--- a/chrome/browser/ui/passwords/manage_passwords_view_utils.h
+++ b/chrome/browser/ui/passwords/manage_passwords_view_utils.h
@@ -10,6 +10,12 @@
 
 #include "components/password_manager/core/browser/manage_passwords_referrer.h"
 #include "components/password_manager/core/browser/origin_credential_store.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
+
+namespace content {
+class WebContents;
+}  // namespace content
 
 namespace gfx {
 class ImageSkia;
@@ -105,4 +111,7 @@
 // Navigates to Passwords Checkup page.
 void NavigateToPasswordCheckupPage(Profile* profile);
 
+mojo::Remote<network::mojom::URLLoaderFactory> GetURLLoaderForMainFrame(
+    content::WebContents* web_contents);
+
 #endif  // CHROME_BROWSER_UI_PASSWORDS_MANAGE_PASSWORDS_VIEW_UTILS_H_
diff --git a/chrome/browser/ui/search/ntp_user_data_logger.cc b/chrome/browser/ui/search/ntp_user_data_logger.cc
index a226ab7..0ff40bb 100644
--- a/chrome/browser/ui/search/ntp_user_data_logger.cc
+++ b/chrome/browser/ui/search/ntp_user_data_logger.cc
@@ -21,7 +21,6 @@
 #include "components/ntp_tiles/metrics.h"
 #include "components/prefs/pref_service.h"
 #include "components/search/ntp_features.h"
-#include "extensions/common/constants.h"
 
 namespace {
 
@@ -264,12 +263,6 @@
   }
 }
 
-bool IsImpressionFromPreinstalledApp(
-    const ntp_tiles::NTPTileImpression& impression) {
-  return impression.url_for_rappor.is_valid() &&
-         impression.url_for_rappor.SchemeIs(extensions::kExtensionScheme) &&
-         extension_misc::IsPreinstalledAppId(impression.url_for_rappor.host());
-}
 }  // namespace
 
 // Helper macro to log a load time to UMA. There's no good reason why we don't
@@ -436,10 +429,6 @@
 void NTPUserDataLogger::LogMostVisitedNavigation(
     const ntp_tiles::NTPTileImpression& impression) {
   ntp_tiles::metrics::RecordTileClick(impression);
-  if (IsImpressionFromPreinstalledApp(impression)) {
-    base::RecordAction(
-        base::UserMetricsAction("NewTabPage.PreinstalledApps.Clicked"));
-  }
 
   // Records the action. This will be available as a time-stamped stream
   // server-side and can be used to compute time-to-long-dwell.
@@ -464,7 +453,6 @@
   }
 
   int tiles_count = 0;
-  int num_of_default_apps = 0;
   for (const absl::optional<ntp_tiles::NTPTileImpression>& impression :
        logged_impressions_) {
     if (!impression.has_value()) {
@@ -472,14 +460,8 @@
     }
     ntp_tiles::metrics::RecordTileImpression(*impression);
     ++tiles_count;
-
-    if (IsImpressionFromPreinstalledApp(*impression)) {
-      ++num_of_default_apps;
-    }
   }
   ntp_tiles::metrics::RecordPageImpression(tiles_count);
-  UMA_HISTOGRAM_COUNTS_100("NewTabPage.NumberOfPreinstalledApps",
-                           num_of_default_apps);
 
   DVLOG(1) << "Emitting NTP load time: " << load_time << ", "
            << "number of tiles: " << tiles_count;
diff --git a/chrome/browser/ui/search/ntp_user_data_logger_unittest.cc b/chrome/browser/ui/search/ntp_user_data_logger_unittest.cc
index 5ea8a2f..c34b7f5 100644
--- a/chrome/browser/ui/search/ntp_user_data_logger_unittest.cc
+++ b/chrome/browser/ui/search/ntp_user_data_logger_unittest.cc
@@ -46,15 +46,13 @@
 
 // Helper function that uses sensible defaults for irrelevant fields of
 // NTPTileImpression.
-ntp_tiles::NTPTileImpression MakeNTPTileImpression(
-    int index,
-    TileSource source,
-    TileTitleSource title_source,
-    TileVisualType visual_type,
-    GURL url_for_rapper = GURL()) {
+ntp_tiles::NTPTileImpression MakeNTPTileImpression(int index,
+                                                   TileSource source,
+                                                   TileTitleSource title_source,
+                                                   TileVisualType visual_type) {
   return ntp_tiles::NTPTileImpression(index, source, title_source, visual_type,
                                       favicon_base::IconType::kInvalid,
-                                      url_for_rapper);
+                                      /*url_for_rappor=*/GURL());
 }
 
 // Helper function that populates a list of expected impressions, each with the
@@ -110,54 +108,11 @@
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.NumberOfTiles"),
               ElementsAre(Bucket(ntp_tiles::kMaxNumTiles, 1)));
 
-  EXPECT_THAT(
-      histogram_tester.GetAllSamples("NewTabPage.NumberOfPreinstalledApps"),
-      ElementsAre(Bucket(0, 1)));
-
   // We should not log again for the same NTP.
   logger.LogMostVisitedLoaded(delta, /*using_most_visited=*/true,
                               /*is_visible=*/true);
   EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.NumberOfTiles"),
               ElementsAre(Bucket(ntp_tiles::kMaxNumTiles, 1)));
-
-  EXPECT_THAT(
-      histogram_tester.GetAllSamples("NewTabPage.NumberOfPreinstalledApps"),
-      ElementsAre(Bucket(0, 1)));
-}
-
-TEST_F(NTPUserDataLoggerTest, ShouldRecordPreinstalledApps) {
-  base::HistogramTester histogram_tester;
-
-  // Impressions increment the associated bins.
-  // Ensure non-zero statistics.
-  TestNTPUserDataLogger logger(GURL("chrome://newtab/"));
-
-  const base::TimeDelta delta = base::Milliseconds(73);
-
-  GURL gmail_extension_url(
-      "chrome-extension://pjkljhegncpnkpknbcohdijeoejaedia/index.html");
-  logger.LogMostVisitedImpression(
-      MakeNTPTileImpression(0, TileSource::TOP_SITES, TileTitleSource::INFERRED,
-                            TileVisualType::ICON_REAL, gmail_extension_url));
-
-  for (int i = 1; i < ntp_tiles::kMaxNumTiles; ++i) {
-    logger.LogMostVisitedImpression(MakeNTPTileImpression(
-        i, TileSource::TOP_SITES, TileTitleSource::UNKNOWN,
-        TileVisualType::ICON_REAL));
-  }
-  logger.LogMostVisitedLoaded(delta, /*using_most_visited=*/true,
-                              /*is_visible=*/true);
-
-  EXPECT_THAT(
-      histogram_tester.GetAllSamples("NewTabPage.NumberOfPreinstalledApps"),
-      ElementsAre(Bucket(1, 1)));
-
-  // We should not log again for the same NTP.
-  logger.LogMostVisitedLoaded(delta, /*using_most_visited=*/true,
-                              /*is_visible=*/true);
-  EXPECT_THAT(
-      histogram_tester.GetAllSamples("NewTabPage.NumberOfPreinstalledApps"),
-      ElementsAre(Bucket(1, 1)));
 }
 
 TEST_F(NTPUserDataLoggerTest, ShouldNotRecordImpressionsBeforeAllTilesLoaded) {
diff --git a/chrome/browser/ui/serial/serial_chooser_controller.cc b/chrome/browser/ui/serial/serial_chooser_controller.cc
index bba01de..940187b4 100644
--- a/chrome/browser/ui/serial/serial_chooser_controller.cc
+++ b/chrome/browser/ui/serial/serial_chooser_controller.cc
@@ -33,12 +33,10 @@
       filters_(std::move(filters)),
       callback_(std::move(callback)),
       frame_tree_node_id_(render_frame_host->GetFrameTreeNodeId()) {
-  auto* web_contents =
-      content::WebContents::FromRenderFrameHost(render_frame_host);
-  origin_ = web_contents->GetMainFrame()->GetLastCommittedOrigin();
+  origin_ = render_frame_host->GetMainFrame()->GetLastCommittedOrigin();
 
   auto* profile =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+      Profile::FromBrowserContext(render_frame_host->GetBrowserContext());
   chooser_context_ =
       SerialChooserContextFactory::GetForProfile(profile)->AsWeakPtr();
   DCHECK(chooser_context_);
diff --git a/chrome/browser/ui/signin_modal_dialog.cc b/chrome/browser/ui/signin_modal_dialog.cc
new file mode 100644
index 0000000..6e110e5
--- /dev/null
+++ b/chrome/browser/ui/signin_modal_dialog.cc
@@ -0,0 +1,24 @@
+// 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/signin_modal_dialog.h"
+
+#include <ostream>
+
+#include "base/check.h"
+
+SigninModalDialog::SigninModalDialog(base::OnceClosure on_close_callback)
+    : on_close_callback_(std::move(on_close_callback)) {
+  DCHECK(on_close_callback_);
+}
+
+SigninModalDialog::~SigninModalDialog() {
+  DCHECK(!on_close_callback_) << "NotifyModalDialogClosed() must have been "
+                                 "called before `this` is destroyed.";
+}
+
+void SigninModalDialog::NotifyModalDialogClosed() {
+  DCHECK(on_close_callback_);
+  std::move(on_close_callback_).Run();
+}
diff --git a/chrome/browser/ui/signin_modal_dialog.h b/chrome/browser/ui/signin_modal_dialog.h
new file mode 100644
index 0000000..380471c
--- /dev/null
+++ b/chrome/browser/ui/signin_modal_dialog.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_SIGNIN_MODAL_DIALOG_H_
+#define CHROME_BROWSER_UI_SIGNIN_MODAL_DIALOG_H_
+
+#include "base/callback.h"
+
+namespace content {
+class WebContents;
+}
+
+// Base class for a signin modal dialog.
+// SigninModalDialogImpl contains the default implementation that delegates
+// all work to SigninViewControllerDelegate.
+// Individual dialogs can extend this class to add some platform-agnostic logic.
+class SigninModalDialog {
+ public:
+  explicit SigninModalDialog(base::OnceClosure on_close_callback);
+
+  SigninModalDialog(const SigninModalDialog&) = delete;
+  SigninModalDialog& operator=(const SigninModalDialog&) = delete;
+
+  virtual ~SigninModalDialog();
+
+  // Closes the sign-in dialog. Note that this method may trigger destruction
+  // of this object, so the caller should no longer use this object after
+  // calling this method.
+  virtual void CloseModalDialog() = 0;
+
+  // Requests a resize of the native view hosting the web contents. `height` is
+  // the total height of the content, in pixels.
+  virtual void ResizeNativeView(int height) = 0;
+
+  // Returns the web contents of the modal dialog for testing.
+  virtual content::WebContents* GetModalDialogWebContentsForTesting() = 0;
+
+ protected:
+  // Calls `on_close_callback_` to notify that the dialog has been closed. Must
+  // be called exactly once per dialog's lifetime. The dialog may be destroyed
+  // after this call.
+  void NotifyModalDialogClosed();
+
+ private:
+  base::OnceClosure on_close_callback_;
+};
+
+#endif  // CHROME_BROWSER_UI_SIGNIN_MODAL_DIALOG_H_
diff --git a/chrome/browser/ui/signin_modal_dialog_impl.cc b/chrome/browser/ui/signin_modal_dialog_impl.cc
new file mode 100644
index 0000000..4bd7b13
--- /dev/null
+++ b/chrome/browser/ui/signin_modal_dialog_impl.cc
@@ -0,0 +1,31 @@
+// 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/signin_modal_dialog_impl.h"
+
+SigninModalDialogImpl::SigninModalDialogImpl(
+    SigninViewControllerDelegate* delegate,
+    base::OnceClosure on_close_callback)
+    : SigninModalDialog(std::move(on_close_callback)), delegate_(delegate) {
+  delegate_observation_.Observe(delegate_);
+}
+
+SigninModalDialogImpl::~SigninModalDialogImpl() = default;
+
+void SigninModalDialogImpl::CloseModalDialog() {
+  delegate_->CloseModalSignin();
+}
+
+void SigninModalDialogImpl::ResizeNativeView(int height) {
+  delegate_->ResizeNativeView(height);
+}
+
+content::WebContents*
+SigninModalDialogImpl::GetModalDialogWebContentsForTesting() {
+  return delegate_->GetWebContents();
+}
+
+void SigninModalDialogImpl::OnModalDialogClosed() {
+  NotifyModalDialogClosed();
+}
diff --git a/chrome/browser/ui/signin_modal_dialog_impl.h b/chrome/browser/ui/signin_modal_dialog_impl.h
new file mode 100644
index 0000000..51a4f4b3
--- /dev/null
+++ b/chrome/browser/ui/signin_modal_dialog_impl.h
@@ -0,0 +1,39 @@
+// 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_SIGNIN_MODAL_DIALOG_IMPL_H_
+#define CHROME_BROWSER_UI_SIGNIN_MODAL_DIALOG_IMPL_H_
+
+#include "base/callback_forward.h"
+#include "base/memory/raw_ptr.h"
+#include "base/scoped_observation.h"
+#include "chrome/browser/ui/signin_modal_dialog.h"
+#include "chrome/browser/ui/signin_view_controller_delegate.h"
+
+// Signin modal dialog that hosts a webUI in a native modal view.
+// Delegates actual work to SigninViewControllerDelegate.
+class SigninModalDialogImpl : public SigninModalDialog,
+                              public SigninViewControllerDelegate::Observer {
+ public:
+  explicit SigninModalDialogImpl(SigninViewControllerDelegate* delegate,
+                                 base::OnceClosure on_close_callback);
+
+  ~SigninModalDialogImpl() override;
+
+  // SigninModalDialog:
+  void CloseModalDialog() override;
+  void ResizeNativeView(int height) override;
+  content::WebContents* GetModalDialogWebContentsForTesting() override;
+
+  // SigninViewControllerDelegate::Observer:
+  void OnModalDialogClosed() override;
+
+ private:
+  raw_ptr<SigninViewControllerDelegate> delegate_;
+  base::ScopedObservation<SigninViewControllerDelegate,
+                          SigninViewControllerDelegate::Observer>
+      delegate_observation_{this};
+};
+
+#endif  // CHROME_BROWSER_UI_SIGNIN_MODAL_DIALOG_IMPL_H_
diff --git a/chrome/browser/ui/signin_reauth_view_controller.cc b/chrome/browser/ui/signin_reauth_view_controller.cc
index 19032de..3683437 100644
--- a/chrome/browser/ui/signin_reauth_view_controller.cc
+++ b/chrome/browser/ui/signin_reauth_view_controller.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/browser/ui/signin_modal_dialog.h"
 #include "chrome/browser/ui/webui/signin/signin_reauth_ui.h"
 #include "components/consent_auditor/consent_auditor.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -63,8 +64,10 @@
     Browser* browser,
     const CoreAccountId& account_id,
     signin_metrics::ReauthAccessPoint access_point,
+    base::OnceClosure on_close_callback,
     base::OnceCallback<void(signin::ReauthResult)> reauth_callback)
-    : browser_(browser),
+    : SigninModalDialog(std::move(on_close_callback)),
+      browser_(browser),
       account_id_(account_id),
       access_point_(access_point),
       reauth_callback_(std::move(reauth_callback)) {
@@ -99,7 +102,7 @@
     observer.OnReauthControllerDestroyed();
 }
 
-void SigninReauthViewController::CloseModalSignin() {
+void SigninReauthViewController::CloseModalDialog() {
   CompleteReauth(signin::ReauthResult::kCancelled);
 }
 
@@ -107,7 +110,8 @@
   NOTIMPLEMENTED();
 }
 
-content::WebContents* SigninReauthViewController::GetWebContents() {
+content::WebContents*
+SigninReauthViewController::GetModalDialogWebContentsForTesting() {
   // If the dialog is displayed, return its WebContents.
   if (dialog_delegate_)
     return dialog_delegate_->GetWebContents();
@@ -116,12 +120,7 @@
   return raw_reauth_web_contents_;
 }
 
-void SigninReauthViewController::SetWebContents(
-    content::WebContents* web_contents) {
-  NOTIMPLEMENTED();
-}
-
-void SigninReauthViewController::OnModalSigninClosed() {
+void SigninReauthViewController::OnModalDialogClosed() {
   DCHECK(
       dialog_delegate_observation_.IsObservingSource(dialog_delegate_.get()));
   dialog_delegate_observation_.Reset();
@@ -244,13 +243,13 @@
   if (reauth_callback_)
     std::move(reauth_callback_).Run(result);
 
-  NotifyModalSigninClosed();
+  // NotifyModalDialogClosed() will destroy the current instance.
+  // We cannot destroy `reauth_web_contents_` right now because this function
+  // can be triggered from `reauth_web_contents_`s observer method.
+  content::GetUIThreadTaskRunner({})->DeleteSoon(
+      FROM_HERE, std::move(reauth_web_contents_));
 
-  // Schedules an asynchronous deletion of the current instance.
-  // We cannot destroy |this| and in particular |reauth_web_contents_| right now
-  // because this function can be triggered from |reauth_web_contents_|'s
-  // observer method.
-  content::GetUIThreadTaskRunner({})->DeleteSoon(FROM_HERE, this);
+  NotifyModalDialogClosed();
 }
 
 void SigninReauthViewController::OnStateChanged() {
@@ -335,7 +334,7 @@
 void SigninReauthViewController::ShowGaiaReauthPageInNewTab() {
   DCHECK_EQ(ui_state_, UIState::kConfirmationDialog);
   ui_state_ = UIState::kGaiaReauthTab;
-  // Remove the observer to not trigger OnModalSigninClosed() that will abort
+  // Remove the observer to not trigger OnModalDialogClosed() that will abort
   // the reauth flow.
   DCHECK(
       dialog_delegate_observation_.IsObservingSource(dialog_delegate_.get()));
diff --git a/chrome/browser/ui/signin_reauth_view_controller.h b/chrome/browser/ui/signin_reauth_view_controller.h
index 1254584c..89bfb2e 100644
--- a/chrome/browser/ui/signin_reauth_view_controller.h
+++ b/chrome/browser/ui/signin_reauth_view_controller.h
@@ -11,6 +11,7 @@
 #include "base/observer_list_types.h"
 #include "base/scoped_observation.h"
 #include "base/time/time.h"
+#include "chrome/browser/ui/signin_modal_dialog.h"
 #include "chrome/browser/ui/signin_view_controller_delegate.h"
 #include "components/signin/public/base/signin_metrics.h"
 #include "components/sync/protocol/user_consent_types.pb.h"
@@ -42,8 +43,9 @@
 //
 // The Gaia reauth page is loaded in background and gets shown to the user only
 // after the user confirms the reauth confirmation dialog.
+// TODO(https://crbug.com/1282157): rename to SigninReauthDialog.
 class SigninReauthViewController
-    : public SigninViewControllerDelegate,
+    : public SigninModalDialog,
       public SigninViewControllerDelegate::Observer {
  public:
   enum class GaiaReauthType;
@@ -116,6 +118,7 @@
       Browser* browser,
       const CoreAccountId& account_id,
       signin_metrics::ReauthAccessPoint access_point,
+      base::OnceClosure on_close_callback,
       base::OnceCallback<void(signin::ReauthResult)> reauth_callback);
 
   SigninReauthViewController(const SigninReauthViewController&) = delete;
@@ -124,14 +127,13 @@
 
   ~SigninReauthViewController() override;
 
-  // SigninViewControllerDelegate:
-  void CloseModalSignin() override;
+  // SigninModalDialog:
+  void CloseModalDialog() override;
   void ResizeNativeView(int height) override;
-  content::WebContents* GetWebContents() override;
-  void SetWebContents(content::WebContents* web_contents) override;
+  content::WebContents* GetModalDialogWebContentsForTesting() override;
 
   // SigninViewControllerDelegate::Observer:
-  void OnModalSigninClosed() override;
+  void OnModalDialogClosed() override;
 
   // Called when the user clicks the confirm button in the reauth confirmation
   // dialog.
diff --git a/chrome/browser/ui/signin_reauth_view_controller_browsertest.cc b/chrome/browser/ui/signin_reauth_view_controller_browsertest.cc
index fdc22016..4f63a5e 100644
--- a/chrome/browser/ui/signin_reauth_view_controller_browsertest.cc
+++ b/chrome/browser/ui/signin_reauth_view_controller_browsertest.cc
@@ -214,7 +214,7 @@
   // The test cannot depend on Views implementation so it simulates clicking on
   // the close button through calling the close event.
   void SimulateCloseButtonClick() {
-    signin_reauth_view_controller()->OnModalSigninClosed();
+    signin_reauth_view_controller()->OnModalDialogClosed();
   }
 
   void ResetAbortHandle() { abort_handle_.reset(); }
@@ -232,7 +232,7 @@
         browser()->signin_view_controller();
     DCHECK(signin_view_controller->ShowsModalDialog());
     return static_cast<SigninReauthViewController*>(
-        signin_view_controller->GetModalDialogDelegateForTesting());
+        signin_view_controller->GetModalDialogForTesting());
   }
 
   base::HistogramTester* histogram_tester() { return &histogram_tester_; }
@@ -399,7 +399,7 @@
   target_content_observer.Wait();
 
   content::WebContents* target_contents =
-      signin_reauth_view_controller()->GetWebContents();
+      signin_reauth_view_controller()->GetModalDialogWebContentsForTesting();
   ASSERT_TRUE(content::ExecuteScript(
       target_contents, "document.getElementsByTagName('a')[0].click();"));
   EXPECT_EQ(WaitForReauthResult(), signin::ReauthResult::kSuccess);
@@ -435,7 +435,7 @@
   target_content_observer.Wait();
 
   content::WebContents* target_contents =
-      signin_reauth_view_controller()->GetWebContents();
+      signin_reauth_view_controller()->GetModalDialogWebContentsForTesting();
 
   SyncEncryptionKeysTabHelper* encryption_keys_tab_helper =
       SyncEncryptionKeysTabHelper::FromWebContents(target_contents);
@@ -471,7 +471,7 @@
   target_content_observer.Wait();
 
   content::WebContents* dialog_contents =
-      signin_reauth_view_controller()->GetWebContents();
+      signin_reauth_view_controller()->GetModalDialogWebContentsForTesting();
   content::TestNavigationObserver new_tab_observer(nullptr);
   new_tab_observer.StartWatchingNewWebContents();
   ASSERT_TRUE(content::ExecuteScript(
@@ -516,7 +516,9 @@
   content::WebContents* target_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   EXPECT_NE(target_contents, original_contents);
-  EXPECT_EQ(target_contents, signin_reauth_view_controller()->GetWebContents());
+  EXPECT_EQ(
+      target_contents,
+      signin_reauth_view_controller()->GetModalDialogWebContentsForTesting());
   EXPECT_EQ(target_contents->GetLastCommittedURL(), target_url);
 
   ASSERT_TRUE(content::ExecuteScript(
@@ -595,7 +597,7 @@
   ShowReauthPrompt(
       signin_metrics::ReauthAccessPoint::kPasswordSaveLocallyBubble);
   content::WebContents* confirmation_dialog_contents =
-      signin_reauth_view_controller()->GetWebContents();
+      signin_reauth_view_controller()->GetModalDialogWebContentsForTesting();
   content::TestNavigationObserver navigation_observer(
       confirmation_dialog_contents);
   navigation_observer.Wait();
@@ -620,7 +622,7 @@
   // before the reauth prompt was shown.
   ShowReauthPrompt(signin_metrics::ReauthAccessPoint::kPasswordSaveBubble);
   content::WebContents* confirmation_dialog_contents =
-      signin_reauth_view_controller()->GetWebContents();
+      signin_reauth_view_controller()->GetModalDialogWebContentsForTesting();
   content::TestNavigationObserver navigation_observer(
       confirmation_dialog_contents);
   navigation_observer.Wait();
@@ -659,7 +661,7 @@
                        ConfirmationDialogDarkModeDisabled) {
   ShowReauthPrompt();
   content::WebContents* confirmation_dialog_contents =
-      signin_reauth_view_controller()->GetWebContents();
+      signin_reauth_view_controller()->GetModalDialogWebContentsForTesting();
   content::TestNavigationObserver navigation_observer(
       confirmation_dialog_contents);
   navigation_observer.WaitForNavigationFinished();
diff --git a/chrome/browser/ui/signin_view_controller.cc b/chrome/browser/ui/signin_view_controller.cc
index 576e07f..9eb9b74b 100644
--- a/chrome/browser/ui/signin_view_controller.cc
+++ b/chrome/browser/ui/signin_view_controller.cc
@@ -4,8 +4,10 @@
 
 #include "chrome/browser/ui/signin_view_controller.h"
 
+#include <memory>
 #include <utility>
 
+#include "base/bind.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
@@ -13,7 +15,8 @@
 #include "chrome/browser/signin/signin_ui_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_dialogs.h"
-#include "chrome/browser/ui/signin_view_controller_delegate.h"
+#include "chrome/browser/ui/signin_modal_dialog.h"
+#include "chrome/browser/ui/signin_modal_dialog_impl.h"
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/base/signin_buildflags.h"
 #include "components/signin/public/identity_manager/account_info.h"
@@ -216,11 +219,9 @@
     return abort_handle;
   }
 
-  // The delegate will delete itself on request of the UI code when the widget
-  // is closed.
-  delegate_ = new SigninReauthViewController(
-      browser_, account_id, access_point, std::move(wrapped_reauth_callback));
-  delegate_observation_.Observe(delegate_.get());
+  dialog_ = std::make_unique<SigninReauthViewController>(
+      browser_, account_id, access_point, GetOnModalDialogClosedCallback(),
+      std::move(wrapped_reauth_callback));
   chrome::RecordDialogCreation(chrome::DialogIdentifier::SIGNIN_REAUTH);
   return abort_handle;
 }
@@ -228,11 +229,9 @@
 
 void SigninViewController::ShowModalSyncConfirmationDialog() {
   CloseModalSignin();
-  // The delegate will delete itself on request of the UI code when the widget
-  // is closed.
-  delegate_ =
-      SigninViewControllerDelegate::CreateSyncConfirmationDelegate(browser_);
-  delegate_observation_.Observe(delegate_.get());
+  dialog_ = std::make_unique<SigninModalDialogImpl>(
+      SigninViewControllerDelegate::CreateSyncConfirmationDelegate(browser_),
+      GetOnModalDialogClosedCallback());
   chrome::RecordDialogCreation(
       chrome::DialogIdentifier::SIGN_IN_SYNC_CONFIRMATION);
 }
@@ -244,16 +243,14 @@
 #if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
     BUILDFLAG(IS_CHROMEOS_LACROS)
   CloseModalSignin();
-  // The delegate will delete itself on request of the UI code when the widget
-  // is closed.
-  delegate_ =
+  dialog_ = std::make_unique<SigninModalDialogImpl>(
       SigninViewControllerDelegate::CreateEnterpriseConfirmationDelegate(
           browser_, account_info, profile_color,
           base::BindOnce(
               [](Browser* browser, base::OnceCallback<void(bool)> callback,
                  bool result) { std::move(callback).Run(result); },
-              base::Unretained(browser_), std::move(callback)));
-  delegate_observation_.Observe(delegate_.get());
+              base::Unretained(browser_), std::move(callback))),
+      GetOnModalDialogClosedCallback());
   chrome::RecordDialogCreation(
       chrome::DialogIdentifier::SIGNIN_ENTERPRISE_INTERCEPTION);
 #else
@@ -263,33 +260,30 @@
 
 void SigninViewController::ShowModalSigninErrorDialog() {
   CloseModalSignin();
-  // The delegate will delete itself on request of the UI code when the widget
-  // is closed.
-  delegate_ = SigninViewControllerDelegate::CreateSigninErrorDelegate(browser_);
-  delegate_observation_.Observe(delegate_.get());
+  dialog_ = std::make_unique<SigninModalDialogImpl>(
+      SigninViewControllerDelegate::CreateSigninErrorDelegate(browser_),
+      GetOnModalDialogClosedCallback());
   chrome::RecordDialogCreation(chrome::DialogIdentifier::SIGN_IN_ERROR);
 }
 
 bool SigninViewController::ShowsModalDialog() {
-  return delegate_ != nullptr;
+  return dialog_ != nullptr;
 }
 
 void SigninViewController::CloseModalSignin() {
-  if (delegate_)
-    delegate_->CloseModalSignin();
+  if (dialog_)
+    dialog_->CloseModalDialog();
 
-  DCHECK(!delegate_);
+  DCHECK(!dialog_);
 }
 
 void SigninViewController::SetModalSigninHeight(int height) {
-  if (delegate_)
-    delegate_->ResizeNativeView(height);
+  if (dialog_)
+    dialog_->ResizeNativeView(height);
 }
 
-void SigninViewController::OnModalSigninClosed() {
-  DCHECK(delegate_observation_.IsObservingSource(delegate_.get()));
-  delegate_observation_.Reset();
-  delegate_ = nullptr;
+void SigninViewController::OnModalDialogClosed() {
+  dialog_.reset();
 }
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
@@ -405,7 +399,6 @@
 
 void SigninViewController::ShowGaiaLogoutTab(
     signin_metrics::SourceForRefreshTokenOperation source) {
-
   // Since the user may be triggering navigation from another UI element such as
   // a menu, ensure the web contents (and therefore the page that is about to be
   // shown) is focused. (See crbug/926492 for motivation.)
@@ -429,16 +422,15 @@
 void SigninViewController::ShowModalSigninEmailConfirmationDialog(
     const std::string& last_email,
     const std::string& email,
-    base::OnceCallback<void(SigninEmailConfirmationDialog::Action)> callback) {
+    SigninEmailConfirmationDialog::Callback callback) {
   CloseModalSignin();
   content::WebContents* active_contents =
       browser_->tab_strip_model()->GetActiveWebContents();
-  // The delegate will delete itself on request of the UI code when the widget
-  // is closed.
-  delegate_ = SigninEmailConfirmationDialog::AskForConfirmation(
-      active_contents, browser_->profile(), last_email, email,
-      std::move(callback));
-  delegate_observation_.Observe(delegate_.get());
+  dialog_ = std::make_unique<SigninModalDialogImpl>(
+      SigninEmailConfirmationDialog::AskForConfirmation(
+          active_contents, browser_->profile(), last_email, email,
+          std::move(callback)),
+      GetOnModalDialogClosedCallback());
   chrome::RecordDialogCreation(
       chrome::DialogIdentifier::SIGN_IN_EMAIL_CONFIRMATION);
 }
@@ -446,12 +438,18 @@
 
 content::WebContents*
 SigninViewController::GetModalDialogWebContentsForTesting() {
-  DCHECK(delegate_);
-  return delegate_->GetWebContents();
+  DCHECK(dialog_);
+  return dialog_->GetModalDialogWebContentsForTesting();  // IN-TEST
 }
 
-SigninViewControllerDelegate*
-SigninViewController::GetModalDialogDelegateForTesting() {
-  DCHECK(delegate_);
-  return delegate_;
+SigninModalDialog* SigninViewController::GetModalDialogForTesting() {
+  return dialog_.get();
+}
+
+base::OnceClosure SigninViewController::GetOnModalDialogClosedCallback() {
+  return base::BindOnce(
+      &SigninViewController::OnModalDialogClosed,
+      base::Unretained(this)  // `base::Unretained()` is safe because
+                              // `dialog_` is owned by `this`.
+  );
 }
diff --git a/chrome/browser/ui/signin_view_controller.h b/chrome/browser/ui/signin_view_controller.h
index 788dd4b..7a343a5 100644
--- a/chrome/browser/ui/signin_view_controller.h
+++ b/chrome/browser/ui/signin_view_controller.h
@@ -11,10 +11,9 @@
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
-#include "base/scoped_observation.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/profile_chooser_constants.h"
-#include "chrome/browser/ui/signin_view_controller_delegate.h"
+#include "chrome/browser/ui/signin_modal_dialog.h"
 #include "components/signin/public/base/signin_buildflags.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "url/gurl.h"
@@ -56,7 +55,7 @@
 // error dialog, reauth prompt). Sync confirmation is used on
 // Win/Mac/Linux/Chrome OS. Sign-in is only used on Win/Mac/Linux because
 // Chrome OS has its own sign-in flow and doesn't use DICE.
-class SigninViewController : public SigninViewControllerDelegate::Observer {
+class SigninViewController {
  public:
   // Handle that will stop ongoing reauths upon destruction.
   class ReauthAbortHandle {
@@ -69,7 +68,7 @@
   SigninViewController(const SigninViewController&) = delete;
   SigninViewController& operator=(const SigninViewController&) = delete;
 
-  ~SigninViewController() override;
+  virtual ~SigninViewController();
 
   // Returns true if the signin flow should be shown for |mode|.
   static bool ShouldShowSigninForMode(profiles::BubbleViewMode mode);
@@ -109,7 +108,7 @@
   void ShowModalSigninEmailConfirmationDialog(
       const std::string& last_email,
       const std::string& email,
-      base::OnceCallback<void(SigninEmailConfirmationDialog::Action)> callback);
+      SigninEmailConfirmationDialog::Callback callback);
 
   // Shows the reauth prompt for |account_id| as either:
   // - a tab-modal dialog on top of the currently active tab, or
@@ -154,8 +153,8 @@
   // Sets the height of the modal signin dialog.
   void SetModalSigninHeight(int height);
 
-  // SigninViewControllerDelegate::Observer:
-  void OnModalSigninClosed() override;
+  // Called by a `dialog_`' when it closes.
+  void OnModalDialogClosed();
 
  private:
   FRIEND_TEST_ALL_PREFIXES(SignInViewControllerBrowserTest,
@@ -180,18 +179,18 @@
   // Returns the web contents of the modal dialog.
   content::WebContents* GetModalDialogWebContentsForTesting();
 
-  // Returns the modal dialog delegate.
-  SigninViewControllerDelegate* GetModalDialogDelegateForTesting();
+  // Returns the currently displayed modal dialog, or nullptr if no modal dialog
+  // is currently displayed.
+  SigninModalDialog* GetModalDialogForTesting();
+
+  // Helper to create an on close callback for `SigninModalDialog`.
+  base::OnceClosure GetOnModalDialogClosedCallback();
 
   // Browser owning this controller.
   raw_ptr<Browser> browser_;
 
-  // |delegate_| owns itself and calls OnModalSigninClosed() before being
-  // destroyed.
-  raw_ptr<SigninViewControllerDelegate> delegate_ = nullptr;
-  base::ScopedObservation<SigninViewControllerDelegate,
-                          SigninViewControllerDelegate::Observer>
-      delegate_observation_{this};
+  // Currently displayed modal dialog, or nullptr if none is displayed.
+  std::unique_ptr<SigninModalDialog> dialog_;
 
   base::WeakPtrFactory<SigninViewController> weak_ptr_factory_{this};
 };
diff --git a/chrome/browser/ui/signin_view_controller_delegate.cc b/chrome/browser/ui/signin_view_controller_delegate.cc
index f63b9f7..81991ecf 100644
--- a/chrome/browser/ui/signin_view_controller_delegate.cc
+++ b/chrome/browser/ui/signin_view_controller_delegate.cc
@@ -14,7 +14,7 @@
   observer_list_.RemoveObserver(observer);
 }
 
-void SigninViewControllerDelegate::NotifyModalSigninClosed() {
+void SigninViewControllerDelegate::NotifyModalDialogClosed() {
   for (auto& observer : observer_list_)
-    observer.OnModalSigninClosed();
+    observer.OnModalDialogClosed();
 }
diff --git a/chrome/browser/ui/signin_view_controller_delegate.h b/chrome/browser/ui/signin_view_controller_delegate.h
index 7dcae72e..e2a7bdd 100644
--- a/chrome/browser/ui/signin_view_controller_delegate.h
+++ b/chrome/browser/ui/signin_view_controller_delegate.h
@@ -30,13 +30,14 @@
 // as well as managing the navigation inside them.
 // Subclasses are responsible for deleting themselves when the window they're
 // managing closes.
+// TODO(https://crbug.com/1282157): rename to SigninModalDialogDelegate.
 class SigninViewControllerDelegate {
  public:
   class Observer : public base::CheckedObserver {
    public:
     // Called when a dialog controlled by this SigninViewControllerDelegate is
     // closed.
-    virtual void OnModalSigninClosed() = 0;
+    virtual void OnModalDialogClosed() = 0;
   };
 
   SigninViewControllerDelegate(const SigninViewControllerDelegate&) = delete;
@@ -97,7 +98,7 @@
   SigninViewControllerDelegate();
   virtual ~SigninViewControllerDelegate();
 
-  void NotifyModalSigninClosed();
+  void NotifyModalDialogClosed();
 
  private:
   base::ObserverList<Observer, true> observer_list_;
diff --git a/chrome/browser/ui/signin_view_controller_interactive_uitest.cc b/chrome/browser/ui/signin_view_controller_interactive_uitest.cc
index d26348c..be3acd89 100644
--- a/chrome/browser/ui/signin_view_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/signin_view_controller_interactive_uitest.cc
@@ -72,34 +72,6 @@
   absl::optional<LoginUIService::SyncConfirmationUIClosedResult> result_;
 };
 
-class SigninDialogClosedObserver
-    : public SigninViewControllerDelegate::Observer {
- public:
-  explicit SigninDialogClosedObserver(SigninViewControllerDelegate* delegate)
-      : delegate_(delegate) {
-    delegate_->AddObserver(this);
-  }
-
-  ~SigninDialogClosedObserver() override {
-    if (delegate_) {
-      delegate_->RemoveObserver(this);
-    }
-  }
-
-  void WaitForDialogClosed() { dialog_closed_run_loop_.Run(); }
-
- private:
-  // SigninViewControllerDelegate::Observer:
-  void OnModalSigninClosed() override {
-    delegate_->RemoveObserver(this);
-    delegate_ = nullptr;
-    dialog_closed_run_loop_.Quit();
-  }
-
-  base::RunLoop dialog_closed_run_loop_;
-  raw_ptr<SigninViewControllerDelegate> delegate_;
-};
-
 }  // namespace
 
 class SignInViewControllerBrowserTest : public InProcessBrowserTest {
@@ -205,14 +177,16 @@
   EXPECT_TRUE(browser()->signin_view_controller()->ShowsModalDialog());
   content_observer.Wait();
 
-  SigninDialogClosedObserver dialog_observer(
-      browser()->signin_view_controller()->GetModalDialogDelegateForTesting());
+  content::WebContentsDestroyedWatcher dialog_destroyed_watcher(
+      browser()
+          ->signin_view_controller()
+          ->GetModalDialogWebContentsForTesting());
   ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_RETURN,
                                               /*control=*/false,
                                               /*shift=*/false, /*alt=*/false,
                                               /*command=*/false));
   // Default action simply closes the dialog.
-  dialog_observer.WaitForDialogClosed();
+  dialog_destroyed_watcher.Wait();
   EXPECT_FALSE(browser()->signin_view_controller()->ShowsModalDialog());
 }
 
@@ -237,14 +211,16 @@
   EXPECT_TRUE(browser()->signin_view_controller()->ShowsModalDialog());
   content_observer.Wait();
 
-  SigninDialogClosedObserver dialog_observer(
-      browser()->signin_view_controller()->GetModalDialogDelegateForTesting());
+  content::WebContentsDestroyedWatcher dialog_destroyed_watcher(
+      browser()
+          ->signin_view_controller()
+          ->GetModalDialogWebContentsForTesting());
   ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_RETURN,
                                               /*control=*/false,
                                               /*shift=*/false, /*alt=*/false,
                                               /*command=*/false));
 
-  dialog_observer.WaitForDialogClosed();
+  dialog_destroyed_watcher.Wait();
   EXPECT_TRUE(result);
   EXPECT_FALSE(browser()->signin_view_controller()->ShowsModalDialog());
 }
diff --git a/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc b/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc
index 773f618f..f720e43 100644
--- a/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc
+++ b/chrome/browser/ui/startup/credential_provider_signin_dialog_win.cc
@@ -182,12 +182,11 @@
 
     // Build a result for the credential provider that includes only the abort
     // exit code.
-    std::unique_ptr<base::Value> result(
-        new base::Value(base::Value::Type::DICTIONARY));
-    result->SetKey(credential_provider::kKeyExitCode,
-                   base::Value(credential_provider::kUiecAbort));
+    base::Value result(base::Value::Type::DICTIONARY);
+    result.SetKey(credential_provider::kKeyExitCode,
+                  base::Value(credential_provider::kUiecAbort));
     base::ListValue args;
-    args.Append(std::move(result));
+    args.Append(base::Value::ToUniquePtrValue(std::move(result)));
     OnSigninComplete(&args);
   }
 
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
index d48117d..2a7f4e3 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -1637,14 +1637,7 @@
   return web_app::test::InstallWebApp(profile, std::move(web_app_info));
 }
 
-// TODO(crbug.com/1281009): Fix flakes and re-enable.
-#if defined(OS_WIN)
-#define MAYBE_ListAppsForAllProfiles DISABLED_ListAppsForAllProfiles
-#else
-#define MAYBE_ListAppsForAllProfiles ListAppsForAllProfiles
-#endif
-IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorTest,
-                       MAYBE_ListAppsForAllProfiles) {
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorTest, ListAppsForAllProfiles) {
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   base::FilePath user_data_dir = profile_manager->user_data_dir();
   Profile* profile1 = browser()->profile();
@@ -1738,27 +1731,20 @@
       command_line, base::FilePath(), chrome::startup::IsProcessStartup::kNo,
       browser()->profile(), {}));
 
-  {
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    while (!base::PathExists(output_path))
-      base::RunLoop().RunUntilIdle();
-    std::string file_contents;
-    base::ReadFileToString(output_path, &file_contents);
-    ASSERT_EQ(expected_info, file_contents);
-  }
-
   CloseBrowserSynchronously(app_browser1);
   CloseBrowserSynchronously(app_browser2);
+  CloseBrowserSynchronously(browser());
+
+  content::RunAllTasksUntilIdle();
+  {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    std::string file_contents;
+    ASSERT_TRUE(base::ReadFileToString(output_path, &file_contents));
+    ASSERT_EQ(expected_info, file_contents);
+  }
 }
 
-// TODO(crbug.com/1281009): Fix flakes and re-enable.
-#if defined(OS_WIN)
-#define MAYBE_ListAppsForGivenProfile DISABLED_ListAppsForGivenProfile
-#else
-#define MAYBE_ListAppsForGivenProfile ListAppsForGivenProfile
-#endif
-IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorTest,
-                       MAYBE_ListAppsForGivenProfile) {
+IN_PROC_BROWSER_TEST_F(StartupBrowserCreatorTest, ListAppsForGivenProfile) {
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   base::FilePath user_data_dir = profile_manager->user_data_dir();
   Profile* profile1 = browser()->profile();
@@ -1846,17 +1832,17 @@
       command_line, base::FilePath(), chrome::startup::IsProcessStartup::kNo,
       browser()->profile(), {}));
 
-  {
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    while (!base::PathExists(output_path))
-      base::RunLoop().RunUntilIdle();
-    std::string file_contents;
-    base::ReadFileToString(output_path, &file_contents);
-    ASSERT_EQ(expected_info, file_contents);
-  }
-
   CloseBrowserSynchronously(app_browser1);
   CloseBrowserSynchronously(app_browser2);
+  CloseBrowserSynchronously(browser());
+
+  content::RunAllTasksUntilIdle();
+  {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    std::string file_contents;
+    ASSERT_TRUE(base::ReadFileToString(output_path, &file_contents));
+    ASSERT_EQ(expected_info, file_contents);
+  }
 }
 #endif  // defined(OS_LINUX) || defined(OS_MAC) || defined(OS_WIN)
 
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 51e4808b..4f7c4fb 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -75,7 +75,6 @@
 #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_web_contents_helper_factory.h"
-#include "chrome/browser/subresource_redirect/subresource_redirect_observer.h"
 #include "chrome/browser/sync/sessions/sync_sessions_router_tab_helper.h"
 #include "chrome/browser/sync/sessions/sync_sessions_web_contents_router_factory.h"
 #include "chrome/browser/sync/sync_encryption_keys_tab_helper.h"
@@ -391,8 +390,6 @@
             profile));
   }
   SoundContentSettingObserver::CreateForWebContents(web_contents);
-  subresource_redirect::SubresourceRedirectObserver::MaybeCreateForWebContents(
-      web_contents);
   sync_sessions::SyncSessionsRouterTabHelper::CreateForWebContents(
       web_contents,
       sync_sessions::SyncSessionsWebContentsRouterFactory::GetForProfile(
diff --git a/chrome/browser/ui/tabs/pinned_tab_codec.cc b/chrome/browser/ui/tabs/pinned_tab_codec.cc
index 074a15db..1797912 100644
--- a/chrome/browser/ui/tabs/pinned_tab_codec.cc
+++ b/chrome/browser/ui/tabs/pinned_tab_codec.cc
@@ -87,7 +87,7 @@
   if (!prefs)
     return;
 
-  ListPrefUpdate update(prefs, prefs::kPinnedTabs);
+  ListPrefUpdateDeprecated update(prefs, prefs::kPinnedTabs);
   base::Value* values = update.Get();
   values->ClearList();
   for (const auto& tab : tabs)
diff --git a/chrome/browser/ui/task_manager/task_manager_table_model.cc b/chrome/browser/ui/task_manager/task_manager_table_model.cc
index f52b6d2..4f7f7de 100644
--- a/chrome/browser/ui/task_manager/task_manager_table_model.cc
+++ b/chrome/browser/ui/task_manager/task_manager_table_model.cc
@@ -863,8 +863,8 @@
   if (!local_state)
     return;
 
-  DictionaryPrefUpdate dict_update(local_state,
-                                   prefs::kTaskManagerColumnVisibility);
+  DictionaryPrefUpdateDeprecated dict_update(
+      local_state, prefs::kTaskManagerColumnVisibility);
 
   base::DictionaryValue::Iterator it(*columns_settings_);
   while (!it.IsAtEnd()) {
diff --git a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
index 5f49717..782b0cac 100644
--- a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
@@ -247,8 +247,9 @@
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
 
   {
-    ListPrefUpdate update(TestingBrowserProcess::GetGlobal()->local_state(),
-                          policy::policy_prefs::kSystemFeaturesDisableList);
+    ListPrefUpdateDeprecated update(
+        TestingBrowserProcess::GetGlobal()->local_state(),
+        policy::policy_prefs::kSystemFeaturesDisableList);
     base::ListValue* list = update.Get();
     list->Append(policy::SystemFeature::kBrowserSettings);
   }
@@ -261,8 +262,9 @@
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
 
   {
-    ListPrefUpdate update(TestingBrowserProcess::GetGlobal()->local_state(),
-                          policy::policy_prefs::kSystemFeaturesDisableList);
+    ListPrefUpdateDeprecated update(
+        TestingBrowserProcess::GetGlobal()->local_state(),
+        policy::policy_prefs::kSystemFeaturesDisableList);
     base::ListValue* list = update.Get();
     list->ClearList();
   }
diff --git a/chrome/browser/ui/user_education/feature_promo_snooze_service.cc b/chrome/browser/ui/user_education/feature_promo_snooze_service.cc
index 21fe71b..57de208b 100644
--- a/chrome/browser/ui/user_education/feature_promo_snooze_service.cc
+++ b/chrome/browser/ui/user_education/feature_promo_snooze_service.cc
@@ -174,7 +174,8 @@
 }
 
 void FeaturePromoSnoozeService::Reset(const base::Feature& iph_feature) {
-  DictionaryPrefUpdate update(profile_->GetPrefs(), kIPHSnoozeDataPath);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        kIPHSnoozeDataPath);
   base::DictionaryValue* pref_data = update.Get();
   pref_data->RemovePath(iph_feature.name);
 }
@@ -238,7 +239,8 @@
     const FeaturePromoSnoozeService::SnoozeData& snooze_data) {
   std::string path_prefix = std::string(iph_feature.name) + ".";
 
-  DictionaryPrefUpdate update(profile_->GetPrefs(), kIPHSnoozeDataPath);
+  DictionaryPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                        kIPHSnoozeDataPath);
   base::DictionaryValue* pref_data = update.Get();
 
   pref_data->SetBoolPath(path_prefix + kIPHIsDismissedPath,
diff --git a/chrome/browser/ui/user_education/tutorial/tutorial.cc b/chrome/browser/ui/user_education/tutorial/tutorial.cc
index 05df9769..4b6843e 100644
--- a/chrome/browser/ui/user_education/tutorial/tutorial.cc
+++ b/chrome/browser/ui/user_education/tutorial/tutorial.cc
@@ -15,7 +15,7 @@
 #include "ui/base/interaction/element_tracker.h"
 #include "ui/base/interaction/interaction_sequence.h"
 
-Tutorial::StepBuilder::StepBuilder() {}
+Tutorial::StepBuilder::StepBuilder() = default;
 Tutorial::StepBuilder::StepBuilder(const TutorialDescription::Step& step)
     : step_(step) {}
 Tutorial::StepBuilder::~StepBuilder() = default;
@@ -206,7 +206,7 @@
 
 // static
 std::unique_ptr<Tutorial> Tutorial::Builder::BuildFromDescription(
-    TutorialDescription description,
+    const TutorialDescription& description,
     TutorialService* tutorial_service,
     TutorialBubbleFactoryRegistry* bubble_factory_registry,
     ui::ElementContext context) {
diff --git a/chrome/browser/ui/user_education/tutorial/tutorial.h b/chrome/browser/ui/user_education/tutorial/tutorial.h
index 25009bb..c5c3d98 100644
--- a/chrome/browser/ui/user_education/tutorial/tutorial.h
+++ b/chrome/browser/ui/user_education/tutorial/tutorial.h
@@ -106,7 +106,7 @@
     ~Builder();
 
     static std::unique_ptr<Tutorial> BuildFromDescription(
-        TutorialDescription description,
+        const TutorialDescription& description,
         TutorialService* tutorial_service,
         TutorialBubbleFactoryRegistry* bubble_factory_registry,
         ui::ElementContext context);
diff --git a/chrome/browser/ui/user_education/tutorial/tutorial_description.cc b/chrome/browser/ui/user_education/tutorial/tutorial_description.cc
index 43a5f7d..43bc37e 100644
--- a/chrome/browser/ui/user_education/tutorial/tutorial_description.cc
+++ b/chrome/browser/ui/user_education/tutorial/tutorial_description.cc
@@ -10,13 +10,15 @@
 
 TutorialDescription::TutorialDescription() = default;
 TutorialDescription::~TutorialDescription() = default;
-TutorialDescription::TutorialDescription(
-    const TutorialDescription& description) = default;
+TutorialDescription::TutorialDescription(TutorialDescription&&) = default;
+TutorialDescription& TutorialDescription::operator=(TutorialDescription&&) =
+    default;
 
 TutorialDescription::Step::Step()
     : step_type(ui::InteractionSequence::StepType::kShown),
       arrow(TutorialDescription::Step::Arrow::NONE) {}
 TutorialDescription::Step::~Step() = default;
+
 TutorialDescription::Step::Step(
     absl::optional<std::u16string> title_text_,
     absl::optional<std::u16string> body_text_,
@@ -36,8 +38,10 @@
       must_remain_visible(must_remain_visible_),
       transition_only_on_event(transition_only_on_event_),
       name_elements_callback(name_elements_callback_) {}
-TutorialDescription::Step::Step(const TutorialDescription::Step& description) =
-    default;
+
+TutorialDescription::Step::Step(const TutorialDescription::Step&) = default;
+TutorialDescription::Step& TutorialDescription::Step::operator=(
+    const TutorialDescription::Step&) = default;
 
 bool TutorialDescription::Step::Step::ShouldShowBubble() const {
   return (element_id &&
diff --git a/chrome/browser/ui/user_education/tutorial/tutorial_description.h b/chrome/browser/ui/user_education/tutorial/tutorial_description.h
index 93d659f..b7b79ec 100644
--- a/chrome/browser/ui/user_education/tutorial/tutorial_description.h
+++ b/chrome/browser/ui/user_education/tutorial/tutorial_description.h
@@ -8,10 +8,93 @@
 #include <string>
 #include <vector>
 
+#include "base/metrics/histogram_macros.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/interaction/interaction_sequence.h"
 
+// Holds the data required to properly store histograms for a given tutorial.
+// Abstract base class because best practice is to statically declare
+// histograms and so we need some compile-time polymorphism to actually
+// implement the RecordXXX() calls.
+//
+// Use MakeTutorialHistograms() below to create a concrete instance of this
+// class.
+class TutorialHistograms {
+ public:
+  TutorialHistograms() = default;
+  TutorialHistograms(const TutorialHistograms& other) = delete;
+  virtual ~TutorialHistograms() = default;
+  void operator=(const TutorialHistograms& other) = delete;
+
+  // Records whether the tutorial was completed or not.
+  virtual void RecordComplete(bool value) = 0;
+
+  // Records the step on which the tutorial was aborted.
+  virtual void RecordAbort(int step) = 0;
+
+  // Records whether, when an IPH offered the tutorial, the user opted into
+  // seeing the tutorial or not.
+  virtual void RecordIphLinkClicked(bool value) = 0;
+};
+
+namespace internal {
+
+constexpr char kTutorialHistogramPrefix[] = "Tutorial.";
+
+template <const char histogram_name[]>
+class TutorialHistogramsImpl : public TutorialHistograms {
+ public:
+  explicit TutorialHistogramsImpl(int max_steps)
+      : histogram_name_(histogram_name),
+        completed_name_(kTutorialHistogramPrefix + histogram_name_ +
+                        ".Completion"),
+        aborted_name_(kTutorialHistogramPrefix + histogram_name_ +
+                      ".AbortStep"),
+        link_clicked_name_(kTutorialHistogramPrefix + histogram_name_ +
+                           ".IPHLinkClickedWhenShown"),
+        max_steps_(max_steps) {}
+  ~TutorialHistogramsImpl() override = default;
+
+ protected:
+  void RecordComplete(bool value) override {
+    UMA_HISTOGRAM_BOOLEAN(completed_name_, value);
+  }
+
+  void RecordAbort(int step) override {
+    UMA_HISTOGRAM_EXACT_LINEAR(aborted_name_, step, max_steps_);
+  }
+
+  void RecordIphLinkClicked(bool value) override {
+    UMA_HISTOGRAM_BOOLEAN(link_clicked_name_, value);
+  }
+
+ private:
+  const std::string histogram_name_;
+  const std::string completed_name_;
+  const std::string aborted_name_;
+  const std::string link_clicked_name_;
+  const int max_steps_;
+};
+
+}  // namespace internal
+
+// Call to create a tutorial-specific histograms object for use with the
+// tutorial. The template parameter should be a reference to a const char[]
+// that is a compile-time constant. Also remember to add a matching entry to
+// the "TutorialID" variant in histograms.xml corresponding to your tutorial.
+//
+// Example:
+//   const char kMyTutorialName[] = "MyTutorial";
+//   tutorial_descriptions.histograms =
+//       MakeTutorialHistograms<kMyTutorialName>(
+//           tutorial_description.steps.size());
+template <const char* histogram_name>
+std::unique_ptr<TutorialHistograms> MakeTutorialHistograms(int max_steps) {
+  return std::make_unique<internal::TutorialHistogramsImpl<histogram_name>>(
+      max_steps);
+}
+
 // A Struct that provides all of the data necessary to construct a Tutorial.
 // A Tutorial Description is a list of Steps for a tutorial. Each step has info
 // for constructing the InteractionSequence::Step from the
@@ -23,7 +106,8 @@
 
   TutorialDescription();
   ~TutorialDescription();
-  TutorialDescription(const TutorialDescription& description);
+  TutorialDescription(TutorialDescription&& other);
+  TutorialDescription& operator=(TutorialDescription&& other);
 
   struct Step {
     enum Arrow {
@@ -45,8 +129,8 @@
          absl::optional<bool> must_remain_visible_ = absl::nullopt,
          bool transition_only_on_event_ = false,
          NameElementsCallback name_elements_callback_ = NameElementsCallback());
-    Step(const Step& step);
-    Step& operator=(const Step& step) = default;
+    Step(const Step& other);
+    Step& operator=(const Step& other);
     ~Step();
 
     absl::optional<std::u16string> title_text;
@@ -96,6 +180,10 @@
 
   // the list of TutorialDescription steps
   std::vector<Step> steps;
+
+  // The histogram data to use. Use MakeTutorialHistograms() above to create a
+  // value to use, if you want to record specific histograms for this tutorial.
+  std::unique_ptr<TutorialHistograms> histograms;
 };
 
 #endif  // CHROME_BROWSER_UI_USER_EDUCATION_TUTORIAL_TUTORIAL_DESCRIPTION_H_
diff --git a/chrome/browser/ui/user_education/tutorial/tutorial_registry.cc b/chrome/browser/ui/user_education/tutorial/tutorial_registry.cc
index ca8fe07..1d364dc7 100644
--- a/chrome/browser/ui/user_education/tutorial/tutorial_registry.cc
+++ b/chrome/browser/ui/user_education/tutorial/tutorial_registry.cc
@@ -38,5 +38,5 @@
 
 void TutorialRegistry::AddTutorial(TutorialIdentifier id,
                                    TutorialDescription description) {
-  tutorial_registry_.insert(std::make_pair(id, description));
+  tutorial_registry_.emplace(id, std::move(description));
 }
diff --git a/chrome/browser/ui/user_education/tutorial/tutorial_unittest.cc b/chrome/browser/ui/user_education/tutorial/tutorial_unittest.cc
index ce620d5e2..bc36c583 100644
--- a/chrome/browser/ui/user_education/tutorial/tutorial_unittest.cc
+++ b/chrome/browser/ui/user_education/tutorial/tutorial_unittest.cc
@@ -99,7 +99,7 @@
         u"title", u"description", ui::InteractionSequence::StepType::kShown,
         kTestIdentifier1, std::string(),
         TutorialDescription::Step::Arrow::NONE));
-    registry->AddTutorial(kTestTutorial1, description);
+    registry->AddTutorial(kTestTutorial1, std::move(description));
   }
 
   std::unique_ptr<TutorialBubbleFactoryRegistry> bubble_factory_registry =
@@ -132,7 +132,7 @@
       ui::InteractionSequence::StepType::kShown, kTestIdentifier1, "",
       TutorialDescription::Step::Arrow::NONE));
 
-  tutorial_registry->AddTutorial(kTestTutorial1, description);
+  tutorial_registry->AddTutorial(kTestTutorial1, std::move(description));
 
   EXPECT_CALL_IN_SCOPE(
       completed, Run,
diff --git a/chrome/browser/ui/views/chrome_views_delegate.cc b/chrome/browser/ui/views/chrome_views_delegate.cc
index 5484dad..a6474ed 100644
--- a/chrome/browser/ui/views/chrome_views_delegate.cc
+++ b/chrome/browser/ui/views/chrome_views_delegate.cc
@@ -88,7 +88,7 @@
   if (!prefs)
     return;
 
-  std::unique_ptr<DictionaryPrefUpdate> pref_update =
+  std::unique_ptr<DictionaryPrefUpdateDeprecated> pref_update =
       chrome::GetWindowPlacementDictionaryReadWrite(window_name, prefs);
   base::DictionaryValue* window_preferences = pref_update->Get();
   window_preferences->SetInteger("left", bounds.x());
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc b/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
index ee4e857..4123cfc 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
@@ -103,10 +103,11 @@
   PageActionIconView* intent_picker_view = GetIntentPickerIcon();
   EXPECT_TRUE(intent_picker_view->GetVisible());
 
-  if (!base::FeatureList::IsEnabled(features::kIntentPickerPWAPersistence)) {
-    EXPECT_FALSE(intent_picker_bubble());
-    GetIntentPickerIcon()->ExecuteForTesting();
-  }
+#if !defined(OS_CHROMEOS)
+  // On Chrome OS, the picker bubble will appear automatically.
+  EXPECT_FALSE(intent_picker_bubble());
+  GetIntentPickerIcon()->ExecuteForTesting();
+#endif
 
   waiter.WaitIfNeededAndGet();
   ASSERT_TRUE(intent_picker_bubble());
@@ -139,20 +140,13 @@
   EXPECT_EQ(nullptr, intent_picker_bubble());
 }
 
+#if defined(OS_CHROMEOS)
 // Tests that clicking a link from an app browser to either within or outside
 // the scope of an installed app does not show the intent picker, even when an
 // outside of scope link is opened within the context of the PWA.
-// Flaky on Linux: https://crbug.com/1186613
-#if defined(OS_LINUX)
-#define MAYBE_NavigationInAppWindowToInScopeLinkDoesNotShowIntentPicker \
-  DISABLED_NavigationInAppWindowToInScopeLinkDoesNotShowIntentPicker
-#else
-#define MAYBE_NavigationInAppWindowToInScopeLinkDoesNotShowIntentPicker \
-  NavigationInAppWindowToInScopeLinkDoesNotShowIntentPicker
-#endif
 IN_PROC_BROWSER_TEST_P(
     IntentPickerBubbleViewBrowserTest,
-    MAYBE_NavigationInAppWindowToInScopeLinkDoesNotShowIntentPicker) {
+    NavigationInAppWindowToInScopeLinkDoesNotShowIntentPicker) {
   InstallTestWebApp();
 
   // No intent picker should be seen when first opening the web app.
@@ -183,6 +177,7 @@
     EXPECT_EQ(nullptr, intent_picker_bubble());
   }
 }
+#endif
 
 // Tests that the intent icon updates its visibiliy when switching between
 // tabs.
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
index d8e155f..67abba18 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
@@ -587,7 +587,7 @@
   // Show state label for user managed permission, indicating that permission
   // is in the default ask state now. Autoblocked permission doesn't change.
   EXPECT_FALSE(api_->GetStateLabelAt(0));
-  EXPECT_EQ(u"Can ask to open and place windows on your screens",
+  EXPECT_EQ(u"Can ask to use info about your screens",
             api_->GetStateLabelAt(1)->GetText());
 
   // In the ask state, the toggle is in the off state, indicating that
diff --git a/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc b/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc
index 04f7338..da21236 100644
--- a/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc
+++ b/chrome/browser/ui/views/passwords/account_chooser_dialog_view.cc
@@ -119,10 +119,8 @@
                 &AccountChooserDialogView::CredentialsItemPressed,
                 base::Unretained(this), base::Unretained(form.get())),
             titles.first, titles.second, form.get(),
-            web_contents_->GetBrowserContext()
-                ->GetDefaultStoragePartition()
-                ->GetURLLoaderFactoryForBrowserProcess()
-                .get()));
+            GetURLLoaderForMainFrame(web_contents_).get(),
+            web_contents_->GetMainFrame()->GetLastCommittedOrigin()));
     credential_view->SetStoreIndicatorIcon(form->in_store);
     ChromeLayoutProvider* layout_provider = ChromeLayoutProvider::Get();
     gfx::Insets insets =
diff --git a/chrome/browser/ui/views/passwords/auto_signin_first_run_dialog_view.cc b/chrome/browser/ui/views/passwords/auto_signin_first_run_dialog_view.cc
index f446c22..564e92cf 100644
--- a/chrome/browser/ui/views/passwords/auto_signin_first_run_dialog_view.cc
+++ b/chrome/browser/ui/views/passwords/auto_signin_first_run_dialog_view.cc
@@ -37,7 +37,7 @@
   auto call_controller = [](AutoSigninFirstRunDialogView* dialog,
                             ControllerCallbackFn func) {
     if (dialog->controller_) {
-      (dialog->controller_->*func)();
+      (dialog->controller_.get()->*func)();
     }
   };
   SetAcceptCallback(
diff --git a/chrome/browser/ui/views/passwords/credentials_item_view.cc b/chrome/browser/ui/views/passwords/credentials_item_view.cc
index 47a9da46..3ca4066 100644
--- a/chrome/browser/ui/views/passwords/credentials_item_view.cc
+++ b/chrome/browser/ui/views/passwords/credentials_item_view.cc
@@ -70,6 +70,7 @@
     const std::u16string& lower_text,
     const password_manager::PasswordForm* form,
     network::mojom::URLLoaderFactory* loader_factory,
+    const url::Origin& initiator,
     int upper_text_style,
     int lower_text_style)
     : Button(std::move(callback)) {
@@ -96,7 +97,7 @@
     // Fetch the actual avatar.
     AccountAvatarFetcher* fetcher = new AccountAvatarFetcher(
         form->icon_url, weak_ptr_factory_.GetWeakPtr());
-    fetcher->Start(loader_factory);
+    fetcher->Start(loader_factory, initiator);
   }
   AddChildView(std::move(image_view));
 
diff --git a/chrome/browser/ui/views/passwords/credentials_item_view.h b/chrome/browser/ui/views/passwords/credentials_item_view.h
index 683b001..02c9611 100644
--- a/chrome/browser/ui/views/passwords/credentials_item_view.h
+++ b/chrome/browser/ui/views/passwords/credentials_item_view.h
@@ -42,6 +42,7 @@
                       const std::u16string& lower_text,
                       const password_manager::PasswordForm* form,
                       network::mojom::URLLoaderFactory* loader_factory,
+                      const url::Origin& initiator,
                       int upper_text_style = views::style::STYLE_PRIMARY,
                       int lower_text_style = views::style::STYLE_SECONDARY);
   CredentialsItemView(const CredentialsItemView&) = delete;
diff --git a/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc b/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc
index 7674735..50175e5 100644
--- a/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc
+++ b/chrome/browser/ui/views/passwords/password_auto_sign_in_view.cc
@@ -9,6 +9,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/passwords/manage_passwords_view_utils.h"
 #include "chrome/browser/ui/passwords/password_dialog_prompts.h"
 #include "chrome/browser/ui/passwords/passwords_model_delegate.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
@@ -47,10 +48,8 @@
           views::Button::PressedCallback(),
           l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_AUTO_SIGNIN_TITLE_MD),
           form.username_value, &form,
-          controller_.GetProfile()
-              ->GetDefaultStoragePartition()
-              ->GetURLLoaderFactoryForBrowserProcess()
-              .get(),
+          GetURLLoaderForMainFrame(web_contents).get(),
+          web_contents->GetMainFrame()->GetLastCommittedOrigin(),
           views::style::STYLE_HINT, views::style::STYLE_PRIMARY));
   credential->SetEnabled(false);
 
diff --git a/chrome/browser/ui/views/passwords/password_save_update_view.cc b/chrome/browser/ui/views/passwords/password_save_update_view.cc
index 9b5a3b93..57f3891 100644
--- a/chrome/browser/ui/views/passwords/password_save_update_view.cc
+++ b/chrome/browser/ui/views/passwords/password_save_update_view.cc
@@ -351,10 +351,8 @@
     AddChildView(std::make_unique<CredentialsItemView>(
                      views::Button::PressedCallback(), titles.first,
                      titles.second, &password_form,
-                     controller_.GetProfile()
-                         ->GetDefaultStoragePartition()
-                         ->GetURLLoaderFactoryForBrowserProcess()
-                         .get()))
+                     GetURLLoaderForMainFrame(web_contents).get(),
+                     web_contents->GetMainFrame()->GetLastCommittedOrigin()))
         ->SetEnabled(false);
   } else {
     std::unique_ptr<views::EditableCombobox> username_dropdown =
diff --git a/chrome/browser/ui/views/payments/payment_request_blob_url_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_blob_url_browsertest.cc
index d2c77ab..68421467 100644
--- a/chrome/browser/ui/views/payments/payment_request_blob_url_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_blob_url_browsertest.cc
@@ -16,11 +16,19 @@
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestBlobUrlTest, ConnectionTerminated) {
   NavigateTo("/payment_request_blob_url_test.html");
-  ResetEventWaiter(DialogEvent::DIALOG_CLOSED);
+
+  // Trigger the Blob URL load, and wait for it to finish.
   ASSERT_TRUE(content::ExecuteScript(
       GetActiveWebContents(),
       "(function() { document.getElementById('buy').click(); })();"));
+  WaitForLoadStop(GetActiveWebContents());
+
+  // Trigger the PaymentRequest, which should be rejected.
+  ResetEventWaiter(DialogEvent::DIALOG_CLOSED);
+  ASSERT_TRUE(content::ExecuteScript(
+      GetActiveWebContents(), "triggerPaymentRequest();"));
   WaitForObservedEvent();
+
   ExpectBodyContains({"Rejected: NotSupportedError"});
 }
 
diff --git a/chrome/browser/ui/views/payments/payment_request_completion_status_metrics_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_completion_status_metrics_browsertest.cc
index f7f3b94..151f8852 100644
--- a/chrome/browser/ui/views/payments/payment_request_completion_status_metrics_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_completion_status_metrics_browsertest.cc
@@ -624,4 +624,504 @@
                JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_FALSE);
 }
 
+IN_PROC_BROWSER_TEST_F(
+    PaymentRequestCompletionStatusMetricsWithBasicCardDisabledTest,
+    MerchantAborted_Reload) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  NavigateTo("/payment_request_can_make_payment_metrics_test.html");
+  base::HistogramTester histogram_tester;
+
+  // Start the Payment Request.
+  ResetEventWaiterForDialogOpened();
+  ASSERT_EQ("success",
+            content::EvalJs(GetActiveWebContents(),
+                            content::JsReplace(
+                                "noQueryShowWithMethods([{supportedMethods:$1}"
+                                ", {supportedMethods:$2}])",
+                                a_method_name, b_method_name)));
+  WaitForObservedEvent();
+
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.TransactionAmount.Triggered", kRegularTransaction, 1);
+
+  // The merchant reloads the page.
+  ResetEventWaiter(DialogEvent::DIALOG_CLOSED);
+  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(),
+                                     "(function() { location.reload(); })();"));
+  WaitForObservedEvent();
+
+  // Make sure the metrics are logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.CheckoutFunnel.Aborted",
+      JourneyLogger::ABORT_REASON_MERCHANT_NAVIGATION, 1);
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.TimeToCheckout.OtherAborted", 1);
+
+  // Make sure PaymentRequest.TransactionAmount.Completed is not logged
+  // since the request got aborted.
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.TransactionAmount.Completed", 0);
+
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_FALSE);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    PaymentRequestCompletionStatusMetricsWithBasicCardDisabledTest,
+    MerchantAborted_Navigation) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  NavigateTo("/payment_request_can_make_payment_metrics_test.html");
+  base::HistogramTester histogram_tester;
+
+  // Start the Payment Request.
+  ResetEventWaiterForDialogOpened();
+  ASSERT_EQ("success",
+            content::EvalJs(GetActiveWebContents(),
+                            content::JsReplace(
+                                "noQueryShowWithMethods([{supportedMethods:$1}"
+                                ", {supportedMethods:$2}])",
+                                a_method_name, b_method_name)));
+  WaitForObservedEvent();
+
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.TransactionAmount.Triggered", kRegularTransaction, 1);
+
+  // The merchant navigates away.
+  ResetEventWaiter(DialogEvent::DIALOG_CLOSED);
+  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(),
+                                     "(function() { window.location.href = "
+                                     "'/payment_request_email_test.html'; "
+                                     "})();"));
+  WaitForObservedEvent();
+
+  // Make sure the metrics are logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.CheckoutFunnel.Aborted",
+      JourneyLogger::ABORT_REASON_MERCHANT_NAVIGATION, 1);
+
+  // Make sure PaymentRequest.TransactionAmount.Completed is not logged
+  // since the request got aborted.
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.TransactionAmount.Completed", 0);
+
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_FALSE);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    PaymentRequestCompletionStatusMetricsWithBasicCardDisabledTest,
+    MerchantAborted_Abort) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+  NavigateTo("/payment_request_can_make_payment_metrics_test.html");
+  base::HistogramTester histogram_tester;
+
+  // Start the Payment Request.
+  ResetEventWaiterForDialogOpened();
+  ASSERT_EQ("success",
+            content::EvalJs(GetActiveWebContents(),
+                            content::JsReplace(
+                                "noQueryShowWithMethods([{supportedMethods:$1}"
+                                ", {supportedMethods:$2}])",
+                                a_method_name, b_method_name)));
+  WaitForObservedEvent();
+
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.TransactionAmount.Triggered", kRegularTransaction, 1);
+
+  // The merchant aborts the Payment Request.
+  ResetEventWaiterForSequence(
+      {DialogEvent::ABORT_CALLED, DialogEvent::DIALOG_CLOSED});
+  const std::string click_buy_button_js =
+      "(function() { document.getElementById('abort').click(); })();";
+  ASSERT_TRUE(
+      content::ExecuteScript(GetActiveWebContents(), click_buy_button_js));
+  WaitForObservedEvent();
+
+  // Make sure the metrics are logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.CheckoutFunnel.Aborted",
+      JourneyLogger::ABORT_REASON_ABORTED_BY_MERCHANT, 1);
+
+  // Make sure PaymentRequest.TransactionAmount.Completed is not logged
+  // since the request got aborted.
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.TransactionAmount.Completed", 0);
+
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_FALSE);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    PaymentRequestCompletionStatusMetricsWithBasicCardDisabledTest,
+    UserAborted_Navigation) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  NavigateTo("/payment_request_can_make_payment_metrics_test.html");
+  base::HistogramTester histogram_tester;
+
+  // Start the Payment Request.
+  ResetEventWaiterForDialogOpened();
+  ASSERT_EQ("success",
+            content::EvalJs(GetActiveWebContents(),
+                            content::JsReplace(
+                                "noQueryShowWithMethods([{supportedMethods:$1}"
+                                ", {supportedMethods:$2}])",
+                                a_method_name, b_method_name)));
+  WaitForObservedEvent();
+
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.TransactionAmount.Triggered", kRegularTransaction, 1);
+
+  // Navigate away.
+  NavigateTo("/payment_request_email_test.html");
+
+  // Make sure the metrics are logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.CheckoutFunnel.Aborted",
+      JourneyLogger::ABORT_REASON_USER_NAVIGATION, 1);
+
+  histogram_tester.ExpectTotalCount("PaymentRequest.TimeToCheckout.UserAborted",
+                                    1);
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.TimeToCheckout.UserAborted.Shown", 1);
+
+  // Make sure PaymentRequest.TransactionAmount.Completed is not logged
+  // since the request got aborted.
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.TransactionAmount.Completed", 0);
+
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_FALSE);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    PaymentRequestCompletionStatusMetricsWithBasicCardDisabledTest,
+    UserAborted_CancelButton) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  NavigateTo("/payment_request_can_make_payment_metrics_test.html");
+  base::HistogramTester histogram_tester;
+
+  // Start the Payment Request.
+  ResetEventWaiterForDialogOpened();
+  ASSERT_EQ("success",
+            content::EvalJs(GetActiveWebContents(),
+                            content::JsReplace(
+                                "noQueryShowWithMethods([{supportedMethods:$1}"
+                                ", {supportedMethods:$2}])",
+                                a_method_name, b_method_name)));
+  WaitForObservedEvent();
+
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.TransactionAmount.Triggered", kRegularTransaction, 1);
+
+  // Click on the cancel button.
+  ClickOnCancel();
+
+  // Make sure the metrics are logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.CheckoutFunnel.Aborted",
+      JourneyLogger::ABORT_REASON_ABORTED_BY_USER, 1);
+
+  // Make sure PaymentRequest.TransactionAmount.Completed is not logged
+  // since the request got aborted.
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.TransactionAmount.Completed", 0);
+
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_FALSE);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    PaymentRequestCompletionStatusMetricsWithBasicCardDisabledTest,
+    UserAborted_TabClosed) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  NavigateTo("/payment_request_can_make_payment_metrics_test.html");
+  base::HistogramTester histogram_tester;
+
+  // Start the Payment Request.
+  ResetEventWaiterForDialogOpened();
+  ASSERT_EQ("success",
+            content::EvalJs(GetActiveWebContents(),
+                            content::JsReplace(
+                                "noQueryShowWithMethods([{supportedMethods:$1}"
+                                ", {supportedMethods:$2}])",
+                                a_method_name, b_method_name)));
+  WaitForObservedEvent();
+
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.TransactionAmount.Triggered", kRegularTransaction, 1);
+
+  // Close the tab containing the Payment Request.
+  ResetEventWaiterForSequence({DialogEvent::DIALOG_CLOSED});
+  chrome::CloseTab(browser());
+  WaitForObservedEvent();
+
+  // Make sure the metrics are logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.CheckoutFunnel.Aborted",
+      JourneyLogger::ABORT_REASON_ABORTED_BY_USER, 1);
+
+  // Make sure PaymentRequest.TransactionAmount.Completed is not logged
+  // since the request got aborted.
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.TransactionAmount.Completed", 0);
+
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_FALSE);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    PaymentRequestCompletionStatusMetricsWithBasicCardDisabledTest,
+    UserAborted_Reload) {
+  // Installs two apps so that the Payment Request UI will be shown.
+  std::string a_method_name;
+  InstallPaymentApp("a.com", "payment_request_success_responder.js",
+                    &a_method_name);
+  std::string b_method_name;
+  InstallPaymentApp("b.com", "payment_request_success_responder.js",
+                    &b_method_name);
+
+  NavigateTo("/payment_request_can_make_payment_metrics_test.html");
+  base::HistogramTester histogram_tester;
+
+  // Start the Payment Request.
+  ResetEventWaiterForDialogOpened();
+  ASSERT_EQ("success",
+            content::EvalJs(GetActiveWebContents(),
+                            content::JsReplace(
+                                "noQueryShowWithMethods([{supportedMethods:$1}"
+                                ", {supportedMethods:$2}])",
+                                a_method_name, b_method_name)));
+  WaitForObservedEvent();
+
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.TransactionAmount.Triggered", kRegularTransaction, 1);
+
+  // Reload the page containing the Payment Request.
+  ResetEventWaiterForSequence({DialogEvent::DIALOG_CLOSED});
+  chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
+  WaitForObservedEvent();
+
+  // Make sure the metrics are logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "PaymentRequest.CheckoutFunnel.Aborted",
+      JourneyLogger::ABORT_REASON_USER_NAVIGATION, 1);
+
+  // Make sure PaymentRequest.TransactionAmount.Completed is not logged
+  // since the request got aborted.
+  histogram_tester.ExpectTotalCount(
+      "PaymentRequest.TransactionAmount.Completed", 0);
+
+  // Make sure the correct events were logged.
+  std::vector<base::Bucket> buckets =
+      histogram_tester.GetAllSamples("PaymentRequest.Events");
+  ASSERT_EQ(1U, buckets.size());
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_SHOWN);
+  EXPECT_TRUE(buckets[0].min & JourneyLogger::EVENT_USER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_OTHER_ABORTED);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_PAY_CLICKED);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_SKIPPED_SHOW);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_COMPLETED);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_INITIAL_FORM_OF_PAYMENT);
+  EXPECT_TRUE(buckets[0].min &
+              JourneyLogger::EVENT_HAD_NECESSARY_COMPLETE_SUGGESTIONS);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_SHIPPING);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_NAME);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_PHONE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_REQUEST_PAYER_EMAIL);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_FALSE);
+  EXPECT_FALSE(buckets[0].min & JourneyLogger::EVENT_CAN_MAKE_PAYMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_TRUE);
+  EXPECT_FALSE(buckets[0].min &
+               JourneyLogger::EVENT_HAS_ENROLLED_INSTRUMENT_FALSE);
+}
 }  // namespace payments
diff --git a/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
index 2e25ddcc..6412278 100644
--- a/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
@@ -26,14 +26,14 @@
 #include "ui/views/controls/button/md_text_button.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/focus/focus_search.h"
-#include "ui/views/layout/box_layout.h"
-#include "ui/views/layout/fill_layout.h"
-#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/box_layout_view.h"
+#include "ui/views/layout/table_layout_view.h"
+#include "ui/views/metadata/view_factory.h"
 #include "ui/views/painter.h"
 
 namespace payments {
 
-namespace {
+namespace internal {
 
 // This class is the actual sheet that gets pushed on the view_stack_. It
 // implements views::FocusTraversable to trap focus within its hierarchy. This
@@ -43,7 +43,7 @@
 // view should be focused first (through SetFirstFocusableView). If no initial
 // view is specified, the first view added to the hierarchy will get focus when
 // this SheetView's RequestFocus() is called.
-class SheetView : public views::View, public views::FocusTraversable {
+class SheetView : public views::BoxLayoutView, public views::FocusTraversable {
  public:
   METADATA_HEADER(SheetView);
   explicit SheetView(const base::RepeatingCallback<void(bool*)>&
@@ -76,7 +76,7 @@
   void RequestFocus() override {
     // In accessibility contexts, we want to focus the title of the sheet.
     views::View* title =
-        GetViewByID(static_cast<int>(DialogViewID::SHEET_TITLE));
+        GetViewByID(static_cast<int>(payments::DialogViewID::SHEET_TITLE));
     views::FocusManager* focus = GetFocusManager();
     DCHECK(focus);
 
@@ -130,9 +130,12 @@
   base::RepeatingCallback<void(bool*)> enter_key_accelerator_callback_;
 };
 
-BEGIN_METADATA(SheetView, views::View)
+BEGIN_METADATA(SheetView, views::BoxLayoutView)
 END_METADATA
 
+BEGIN_VIEW_BUILDER(, SheetView, views::BoxLayoutView)
+END_VIEW_BUILDER
+
 // A scroll view that displays a separator on the bounds where content is
 // scrolled out of view. For example, if the view can be scrolled up to reveal
 // more content, the top of the content area will display a separator.
@@ -205,7 +208,13 @@
 ADD_READONLY_PROPERTY_METADATA(bool, BottomBorder)
 END_METADATA
 
-}  // namespace
+}  // namespace internal
+
+}  // namespace payments
+
+DEFINE_VIEW_BUILDER(, payments::internal::SheetView)
+
+namespace payments {
 
 PaymentRequestSheetController::PaymentRequestSheetController(
     base::WeakPtr<PaymentRequestSpec> spec,
@@ -220,77 +229,89 @@
   // before creating the sheet view. This way, it's possible to determine
   // whether there's something to do when the user hits enter.
   std::unique_ptr<views::View> footer = CreateFooterView();
-  auto view = std::make_unique<SheetView>(
-      primary_button_
-          ? base::BindRepeating(
-                &PaymentRequestSheetController::PerformPrimaryButtonAction,
-                weak_ptr_factory_.GetWeakPtr())
-          : base::RepeatingCallback<void(bool*)>());
+  auto view =
+      views::Builder<internal::SheetView>(
+          std::make_unique<internal::SheetView>(
+              primary_button_
+                  ? base::BindRepeating(&PaymentRequestSheetController::
+                                            PerformPrimaryButtonAction,
+                                        weak_ptr_factory_.GetWeakPtr())
+                  : base::RepeatingCallback<void(bool*)>()))
+          .SetOrientation(views::BoxLayout::Orientation::kVertical)
+          .CustomConfigure(base::BindOnce(
+              [](PaymentRequestSheetController* controller,
+                 internal::SheetView* sheet_view) {
+                DialogViewID sheet_id;
+                if (controller->GetSheetId(&sheet_id))
+                  sheet_view->SetID(static_cast<int>(sheet_id));
 
-  DialogViewID sheet_id;
-  if (GetSheetId(&sheet_id))
-    view->SetID(static_cast<int>(sheet_id));
+                sheet_view->SetBackground(views::CreateThemedSolidBackground(
+                    sheet_view, ui::kColorDialogBackground));
 
-  view->SetBackground(views::CreateThemedSolidBackground(
-      view.get(), ui::kColorDialogBackground));
+                // Paint the sheets to layers, otherwise the MD buttons (which
+                // do paint to a layer) won't do proper clipping.
+                sheet_view->SetPaintToLayer();
+              },
+              base::Unretained(this)))
+          .AddChildren(
+              views::Builder<views::View>()
+                  .CopyAddressTo(&header_view_)
+                  .CustomConfigure(base::BindOnce(
+                      [](PaymentRequestSheetController* controller,
+                         views::View* view) {
+                        PopulateSheetHeaderView(
+                            controller->ShouldShowHeaderBackArrow(),
+                            controller->CreateHeaderContentView(view),
+                            base::BindRepeating(&PaymentRequestSheetController::
+                                                    BackButtonPressed,
+                                                base::Unretained(controller)),
+                            view, controller->GetHeaderBackground(view));
+                      },
+                      base::Unretained(this))),
+              views::Builder<views::View>()
+                  .CopyAddressTo(&header_content_separator_container_)
+                  .SetUseDefaultFillLayout(true),
+              // |content_view| will go into a views::ScrollView so it needs to
+              // be sized now otherwise it'll be sized to the ScrollView's
+              // viewport height, preventing the scroll bar from ever being
+              // shown.
+              views::Builder<views::ScrollView>(
+                  DisplayDynamicBorderForHiddenContents()
+                      ? std::make_unique<internal::BorderedScrollView>()
+                      : std::make_unique<views::ScrollView>())
+                  .CopyAddressTo(&scroll_)
+                  .SetHorizontalScrollBarMode(
+                      views::ScrollView::ScrollBarMode::kDisabled)
+                  .SetContents(
+                      views::Builder<views::TableLayoutView>()
+                          .CopyAddressTo(&pane_)
+                          .AddColumn(views::LayoutAlignment::kStretch,
+                                     views::LayoutAlignment::kStart,
+                                     views::TableLayout::kFixedSize,
+                                     views::TableLayout::ColumnSize::kFixed,
+                                     dialog_->GetActualDialogWidth(),
+                                     dialog_->GetActualDialogWidth())
+                          .AddRows(1, views::TableLayout::kFixedSize, 0)
+                          .AddChild(
+                              views::Builder<views::View>()
+                                  .CopyAddressTo(&content_view_)
+                                  .SetID(static_cast<int>(
+                                      DialogViewID::CONTENT_VIEW))
+                                  .CustomConfigure(base::BindOnce(
+                                      [](views::View* content_view) {
+                                        content_view->SetPaintToLayer();
+                                        content_view->layer()
+                                            ->SetFillsBoundsOpaquely(true);
+                                        content_view->SetBackground(
+                                            views::CreateThemedSolidBackground(
+                                                content_view,
+                                                ui::kColorDialogBackground));
+                                      })))))
+          .Build();
 
-  // Paint the sheets to layers, otherwise the MD buttons (which do paint to a
-  // layer) won't do proper clipping.
-  view->SetPaintToLayer();
-
-  views::GridLayout* layout =
-      view->SetLayoutManager(std::make_unique<views::GridLayout>());
-
-  // Note: each view is responsible for its own padding (insets).
-  views::ColumnSet* columns = layout->AddColumnSet(0);
-  columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1.0,
-                     views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
-
-  layout->StartRow(views::GridLayout::kFixedSize, 0);
-  header_view_ = layout->AddView(std::make_unique<views::View>());
-  PopulateSheetHeaderView(
-      ShouldShowHeaderBackArrow(), CreateHeaderContentView(header_view_),
-      base::BindRepeating(&PaymentRequestSheetController::BackButtonPressed,
-                          base::Unretained(this)),
-      header_view_, GetHeaderBackground(header_view_));
-
-  layout->StartRow(views::GridLayout::kFixedSize, 0);
-  header_content_separator_container_ =
-      layout->AddView(std::make_unique<views::View>());
-  header_content_separator_container_->SetLayoutManager(
-      std::make_unique<views::FillLayout>());
-
-  layout->StartRow(1.0, 0);
-  // |content_view| will go into a views::ScrollView so it needs to be sized now
-  // otherwise it'll be sized to the ScrollView's viewport height, preventing
-  // the scroll bar from ever being shown.
-  scroll_ = layout->AddView(DisplayDynamicBorderForHiddenContents()
-                                ? std::make_unique<BorderedScrollView>()
-                                : std::make_unique<views::ScrollView>());
-  scroll_->SetHorizontalScrollBarMode(
-      views::ScrollView::ScrollBarMode::kDisabled);
-  pane_ = scroll_->SetContents(std::make_unique<views::View>());
-  views::GridLayout* pane_layout =
-      pane_->SetLayoutManager(std::make_unique<views::GridLayout>());
-  views::ColumnSet* pane_columns = pane_layout->AddColumnSet(0);
-  pane_columns->AddColumn(
-      views::GridLayout::Alignment::FILL, views::GridLayout::Alignment::LEADING,
-      views::GridLayout::kFixedSize, views::GridLayout::ColumnSize::kFixed,
-      dialog_->GetActualDialogWidth(), dialog_->GetActualDialogWidth());
-  pane_layout->StartRow(views::GridLayout::kFixedSize, 0);
-  // This is owned by its parent. It's the container passed to FillContentView.
-  content_view_ = pane_layout->AddView(std::make_unique<views::View>());
-  content_view_->SetPaintToLayer();
-  content_view_->layer()->SetFillsBoundsOpaquely(true);
-  content_view_->SetBackground(views::CreateThemedSolidBackground(
-      content_view_, ui::kColorDialogBackground));
-  content_view_->SetID(static_cast<int>(DialogViewID::CONTENT_VIEW));
-  pane_->SizeToPreferredSize();
-
-  if (footer) {
-    layout->StartRow(views::GridLayout::kFixedSize, 0);
-    layout->AddView(std::move(footer));
-  }
+  view->SetFlexForView(scroll_, 1.0);
+  if (footer)
+    view->AddChildView(std::move(footer));
 
   UpdateContentView();
 
@@ -319,14 +340,14 @@
       base::BindRepeating(&PaymentRequestSheetController::BackButtonPressed,
                           base::Unretained(this)),
       header_view_, GetHeaderBackground(header_view_));
-  header_view_->Layout();
+  header_view_->InvalidateLayout();
   header_view_->SchedulePaint();
 }
 
 void PaymentRequestSheetController::UpdateFocus(views::View* focused_view) {
   DialogViewID sheet_id;
   if (GetSheetId(&sheet_id)) {
-    SheetView* sheet_view = static_cast<SheetView*>(
+    internal::SheetView* sheet_view = static_cast<internal::SheetView*>(
         dialog()->GetViewByID(static_cast<int>(sheet_id)));
     // This will be null on first call since it's not been set until CreateView
     // returns, and the first call to UpdateFocus() comes from CreateView.
@@ -342,11 +363,11 @@
   if (!is_active_)
     return;
 
-  content_view_->Layout();
+  content_view_->InvalidateLayout();
   pane_->SizeToPreferredSize();
   // Now that the content and its surrounding pane are updated, force a Layout
   // on the ScrollView so that it updates its scroll bars now.
-  scroll_->Layout();
+  scroll_->InvalidateLayout();
 }
 
 bool PaymentRequestSheetController::ShouldShowPrimaryButton() {
@@ -409,12 +430,13 @@
 std::unique_ptr<views::View>
 PaymentRequestSheetController::CreateHeaderContentView(
     views::View* header_view) {
-  auto title_label = std::make_unique<views::Label>(
-      GetSheetTitle(), views::style::CONTEXT_DIALOG_TITLE);
-  title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  title_label->SetID(static_cast<int>(DialogViewID::SHEET_TITLE));
-  title_label->SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
-  return title_label;
+  return views::Builder<views::Label>()
+      .SetText(GetSheetTitle())
+      .SetTextContext(views::style::CONTEXT_DIALOG_TITLE)
+      .SetHorizontalAlignment(gfx::ALIGN_LEFT)
+      .SetID(static_cast<int>(DialogViewID::SHEET_TITLE))
+      .SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY)
+      .Build();
 }
 
 std::unique_ptr<views::Background>
@@ -424,53 +446,51 @@
 }
 
 std::unique_ptr<views::View> PaymentRequestSheetController::CreateFooterView() {
-  auto container = std::make_unique<views::View>();
-
-  // The distance between the elements and the dialog borders.
-  container->SetBorder(views::CreateEmptyBorder(gfx::Insets(16)));
-
-  views::GridLayout* layout =
-      container->SetLayoutManager(std::make_unique<views::GridLayout>());
-
-  views::ColumnSet* columns = layout->AddColumnSet(0);
-  columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
-                     views::GridLayout::kFixedSize,
-                     views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
-  columns->AddPaddingColumn(1.0, 0);
-  columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER,
-                     views::GridLayout::kFixedSize,
-                     views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
-
-  layout->StartRow(views::GridLayout::kFixedSize, 0);
   std::unique_ptr<views::View> extra_view = CreateExtraFooterView();
-  if (extra_view)
-    layout->AddView(std::move(extra_view));
-  else
-    layout->SkipColumns(1);
+  views::BoxLayoutView* trailing_buttons_container = nullptr;
+  bool has_extra_view = !!extra_view;
 
-  auto trailing_buttons_container = std::make_unique<views::View>();
-  trailing_buttons_container->SetLayoutManager(
-      std::make_unique<views::BoxLayout>(
-          views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
-          kPaymentRequestButtonSpacing));
-
+  auto container =
+      views::Builder<views::TableLayoutView>()
+          .SetBorder(views::CreateEmptyBorder(gfx::Insets(16)))
+          .AddColumn(views::LayoutAlignment::kStart,
+                     views::LayoutAlignment::kCenter,
+                     views::TableLayout::kFixedSize,
+                     views::TableLayout::ColumnSize::kUsePreferred, 0, 0)
+          .AddPaddingColumn(1.0, 0)
+          .AddColumn(views::LayoutAlignment::kEnd,
+                     views::LayoutAlignment::kCenter,
+                     views::TableLayout::kFixedSize,
+                     views::TableLayout::ColumnSize::kUsePreferred, 0, 0)
+          .AddRows(1, views::TableLayout::kFixedSize, 0)
+          .AddChildren(
+              views::Builder<views::View>(
+                  extra_view ? std::move(extra_view)
+                             : std::make_unique<views::View>()),
+              views::Builder<views::BoxLayoutView>()
+                  .CopyAddressTo(&trailing_buttons_container)
+                  .SetOrientation(views::BoxLayout::Orientation::kHorizontal)
+                  .SetBetweenChildSpacing(kPaymentRequestButtonSpacing)
+                  .CustomConfigure(base::BindOnce(
+                      [](PaymentRequestSheetController* controller,
+                         views::BoxLayoutView* container) {
 #if defined(OS_MAC)
-  AddSecondaryButton(trailing_buttons_container.get());
-  AddPrimaryButton(trailing_buttons_container.get());
+                        controller->AddSecondaryButton(container);
+                        controller->AddPrimaryButton(container);
 #else
-  AddPrimaryButton(trailing_buttons_container.get());
-  AddSecondaryButton(trailing_buttons_container.get());
+                        controller->AddPrimaryButton(container);
+                        controller->AddSecondaryButton(container);
 #endif  // defined(OS_MAC)
+                      },
+                      base::Unretained(this))))
+          .Build();
 
-  if (container->children().empty() &&
-      trailing_buttons_container->children().empty()) {
+  if (!has_extra_view && trailing_buttons_container->children().empty()) {
     // If there's no extra view and no button, return null to signal that no
     // footer should be rendered.
     return nullptr;
   }
 
-  layout->AddView(std::move(trailing_buttons_container));
-
   return container;
 }
 
@@ -500,23 +520,29 @@
 
 void PaymentRequestSheetController::AddPrimaryButton(views::View* container) {
   if (ShouldShowPrimaryButton()) {
-    primary_button_ =
-        container->AddChildView(std::make_unique<views::MdTextButton>(
-            GetPrimaryButtonCallback(), GetPrimaryButtonLabel()));
-    primary_button_->SetID(GetPrimaryButtonId());
-    primary_button_->SetEnabled(GetPrimaryButtonEnabled());
-    primary_button_->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
-    primary_button_->SetProminent(true);
+    views::Builder<views::View>(container)
+        .AddChild(views::Builder<views::MdTextButton>()
+                      .CopyAddressTo(&primary_button_)
+                      .SetCallback(GetPrimaryButtonCallback())
+                      .SetText(GetPrimaryButtonLabel())
+                      .SetID(GetPrimaryButtonId())
+                      .SetEnabled(GetPrimaryButtonEnabled())
+                      .SetFocusBehavior(views::View::FocusBehavior::ALWAYS)
+                      .SetProminent(true))
+        .BuildChildren();
   }
 }
 
 void PaymentRequestSheetController::AddSecondaryButton(views::View* container) {
   if (ShouldShowSecondaryButton()) {
-    secondary_button_ =
-        container->AddChildView(std::make_unique<views::MdTextButton>(
-            GetSecondaryButtonCallback(), GetSecondaryButtonLabel()));
-    secondary_button_->SetID(GetSecondaryButtonId());
-    secondary_button_->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
+    views::Builder<views::View>(container)
+        .AddChild(views::Builder<views::MdTextButton>()
+                      .CopyAddressTo(&secondary_button_)
+                      .SetCallback(GetSecondaryButtonCallback())
+                      .SetText(GetSecondaryButtonLabel())
+                      .SetID(GetSecondaryButtonId())
+                      .SetFocusBehavior(views::View::FocusBehavior::ALWAYS))
+        .BuildChildren();
   }
 }
 
diff --git a/chrome/browser/ui/views/plugin_vm/plugin_vm_installer_view_browsertest.cc b/chrome/browser/ui/views/plugin_vm/plugin_vm_installer_view_browsertest.cc
index 961719e5..654ba398 100644
--- a/chrome/browser/ui/views/plugin_vm/plugin_vm_installer_view_browsertest.cc
+++ b/chrome/browser/ui/views/plugin_vm/plugin_vm_installer_view_browsertest.cc
@@ -110,8 +110,8 @@
   }
 
   void SetPluginVmImagePref(std::string url, std::string hash) {
-    DictionaryPrefUpdate update(browser()->profile()->GetPrefs(),
-                                plugin_vm::prefs::kPluginVmImage);
+    DictionaryPrefUpdateDeprecated update(browser()->profile()->GetPrefs(),
+                                          plugin_vm::prefs::kPluginVmImage);
     base::DictionaryValue* plugin_vm_image = update.Get();
     plugin_vm_image->SetKey("url", base::Value(url));
     plugin_vm_image->SetKey("hash", base::Value(hash));
diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
index 8509b023..785ead1 100644
--- a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
+++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
@@ -129,7 +129,7 @@
 }
 
 void SigninViewControllerDelegateViews::CloseModalSignin() {
-  NotifyModalSigninClosed();
+  NotifyModalDialogClosed();
   // Either `modal_signin_widget_` or `owned_content_view_` is nullptr.
   if (modal_signin_widget_) {
     DCHECK(!owned_content_view_);
@@ -232,7 +232,7 @@
   SetModalType(dialog_modal_type);
 
   RegisterDeleteDelegateCallback(base::BindOnce(
-      &SigninViewControllerDelegateViews::NotifyModalSigninClosed,
+      &SigninViewControllerDelegateViews::NotifyModalDialogClosed,
       base::Unretained(this)));
 
   if (!wait_for_size)
diff --git a/chrome/browser/ui/views/reader_mode/reader_mode_icon_view.cc b/chrome/browser/ui/views/reader_mode/reader_mode_icon_view.cc
index edfeb6b4..f653a90a 100644
--- a/chrome/browser/ui/views/reader_mode/reader_mode_icon_view.cc
+++ b/chrome/browser/ui/views/reader_mode/reader_mode_icon_view.cc
@@ -62,6 +62,9 @@
 
 void ReaderModeIconView::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
+  if (!navigation_handle->IsInPrimaryMainFrame())
+    return;
+
   if (GetVisible())
     views::InkDrop::Get(this)->AnimateToState(views::InkDropState::HIDDEN,
                                               nullptr);
diff --git a/chrome/browser/ui/views/reader_mode/reader_mode_icon_view_browsertest.cc b/chrome/browser/ui/views/reader_mode/reader_mode_icon_view_browsertest.cc
index 08584ae..f0658995 100644
--- a/chrome/browser/ui/views/reader_mode/reader_mode_icon_view_browsertest.cc
+++ b/chrome/browser/ui/views/reader_mode/reader_mode_icon_view_browsertest.cc
@@ -29,6 +29,7 @@
 #include "content/public/test/prerender_test_util.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/animation/ink_drop.h"
 #include "ui/views/test/button_test_api.h"
 
 namespace {
@@ -172,6 +173,54 @@
   EXPECT_TRUE(observer.IsDistillabilityDriverTimerRunning());
 }
 
+IN_PROC_BROWSER_TEST_F(ReaderModeIconViewPrerenderBrowserTest,
+                       InkDropStateNotAffectedByPrerendering) {
+  dom_distiller::TestDistillabilityObserver observer(
+      browser()->tab_strip_model()->GetActiveWebContents());
+  dom_distiller::DistillabilityResult expected_result;
+  expected_result.is_distillable = true;
+  expected_result.is_last = false;
+  expected_result.is_mobile_friendly = false;
+
+  // Navigate to a distillable page.
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), https_server_secure()->GetURL(kSimpleArticlePath)));
+  observer.WaitForResult(expected_result);
+
+  // The icon should be visible for the distillable page.
+  EXPECT_TRUE(reader_mode_icon_->GetVisible());
+
+  // Force the ink drop to activate for testing.
+  views::InkDrop::Get(reader_mode_icon_)
+      ->AnimateToState(views::InkDropState::ACTIVATED, nullptr);
+
+  // Prerender a page that is not distillable.
+  prerender_helper_.AddPrerender(
+      https_server_secure()->GetURL(kNonArticlePath));
+
+  // Prerendering does not affect the visibility of the icon and the ink drop
+  // state. So the icon should be still visible and the ink drop should be still
+  // activated.
+  EXPECT_TRUE(reader_mode_icon_->GetVisible());
+  EXPECT_EQ(views::InkDropState::ACTIVATED,
+            views::InkDrop::Get(reader_mode_icon_)
+                ->GetInkDrop()
+                ->GetTargetInkDropState());
+
+  // Make the prerendering page primary.
+  prerender_helper_.NavigatePrimaryPage(
+      https_server_secure()->GetURL(kNonArticlePath));
+  expected_result.is_distillable = false;
+  observer.WaitForResult(expected_result);
+
+  // The new primary page is not distillable. The icon should be invisible and
+  // the ink drop should be hidden.
+  EXPECT_FALSE(reader_mode_icon_->GetVisible());
+  EXPECT_EQ(views::InkDropState::HIDDEN, views::InkDrop::Get(reader_mode_icon_)
+                                             ->GetInkDrop()
+                                             ->GetTargetInkDropState());
+}
+
 class ReaderModeIconViewBrowserTestWithSettings
     : public ReaderModeIconViewBrowserTest {
  public:
diff --git a/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_view_impl_unittest.cc b/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_view_impl_unittest.cc
index 1e99dfd..e9b6c54 100644
--- a/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_view_impl_unittest.cc
+++ b/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_view_impl_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/test/simple_test_clock.h"
+#include "build/build_config.h"
 #include "chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble_controller.h"
 #include "chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_device_button.h"
 #include "chrome/test/base/testing_profile.h"
@@ -108,7 +109,13 @@
             container->children()[2]->GetGroup());
 }
 
-TEST_F(SendTabToSelfBubbleViewImplTest, ButtonPressed) {
+// TODO(crbug.com/1285538): Flaky on Linux TSAN
+#if defined(OS_LINUX) && defined(THREAD_SANITIZER)
+#define MAYBE_ButtonPressed DISABLED_ButtonPressed
+#else
+#define MAYBE_ButtonPressed ButtonPressed
+#endif
+TEST_F(SendTabToSelfBubbleViewImplTest, MAYBE_ButtonPressed) {
   EXPECT_CALL(*controller_, OnDeviceSelected("Device_3", "device_guid_3"));
   const views::View* button_container = bubble_->GetButtonContainerForTesting();
   ASSERT_EQ(3U, button_container->children().size());
diff --git a/chrome/browser/ui/views/sync/inline_login_ui_browsertest.cc b/chrome/browser/ui/views/sync/inline_login_ui_browsertest.cc
index 74f0bc77..ebeeed6f 100644
--- a/chrome/browser/ui/views/sync/inline_login_ui_browsertest.cc
+++ b/chrome/browser/ui/views/sync/inline_login_ui_browsertest.cc
@@ -274,8 +274,8 @@
 void InlineLoginUIBrowserTest::AddEmailToOneClickRejectedList(
     const std::string& email) {
   PrefService* pref_service = browser()->profile()->GetPrefs();
-  ListPrefUpdate updater(pref_service,
-                         prefs::kReverseAutologinRejectedEmailList);
+  ListPrefUpdateDeprecated updater(pref_service,
+                                   prefs::kReverseAutologinRejectedEmailList);
   if (!base::Contains(updater->GetList(), base::Value(email)))
     updater->Append(email);
 }
diff --git a/chrome/browser/ui/views/tabs/tab_unittest.cc b/chrome/browser/ui/views/tabs/tab_unittest.cc
index 5ba5b84..f077984 100644
--- a/chrome/browser/ui/views/tabs/tab_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_unittest.cc
@@ -606,8 +606,8 @@
   tab->SetData(data);
   EXPECT_FALSE(icon->GetShowingLoadingAnimation());
 
-  // Simulate a tab load starting and stopping during tab dragging (or with
-  // stacked tabs): no layer painting.
+  // Simulate a tab load starting and stopping during tab dragging:
+  // no layer painting.
   tab_controller->set_paint_throbber_to_layer(false);
   data.network_state = TabNetworkState::kWaiting;
   tab->SetData(data);
diff --git a/chrome/browser/ui/views/task_manager_view_browsertest.cc b/chrome/browser/ui/views/task_manager_view_browsertest.cc
index 38a15dd..35025de 100644
--- a/chrome/browser/ui/views/task_manager_view_browsertest.cc
+++ b/chrome/browser/ui/views/task_manager_view_browsertest.cc
@@ -86,8 +86,8 @@
     if (!local_state)
       FAIL();
 
-    DictionaryPrefUpdate dict_update(local_state,
-                                     prefs::kTaskManagerColumnVisibility);
+    DictionaryPrefUpdateDeprecated dict_update(
+        local_state, prefs::kTaskManagerColumnVisibility);
     dict_update->DictClear();
   }
 
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 be20e9d..c3a52c6 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.cc
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.cc
@@ -5,23 +5,19 @@
 #include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h"
 
 #include "base/bind.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/timer/elapsed_timer.h"
+#include "base/callback.h"
+#include "base/callback_list.h"
 #include "build/build_config.h"
 #include "build/buildflag.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/about_flags.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/flag_descriptions.h"
-#include "chrome/browser/lifetime/application_lifetime.h"
-#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.h"
 #include "chrome/browser/ui/views/toolbar/chrome_labs_button.h"
-#include "chrome/browser/ui/views/toolbar/chrome_labs_utils.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_item_view.h"
 #include "chrome/browser/ui/webui/flags/flags_ui.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/google_chrome_strings.h"
-#include "components/flags_ui/pref_service_flags_storage.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -36,77 +32,15 @@
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/layout/flex_layout_types.h"
+#include "ui/views/layout/flex_layout_view.h"
 #include "ui/views/layout/layout_provider.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#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/about_flags.h"
-#endif
-
 namespace {
 
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class ChromeLabsSelectedLab {
-  kUnspecifiedSelected = 0,
-  // kReadLaterSelected = 1,
-  // kTabSearchSelected = 2,
-  kTabScrollingSelected = 3,
-  kSidePanelSelected = 4,
-  kLensRegionSearchSelected = 5,
-  kWebUITabStripSelected = 6,
-  kMaxValue = kWebUITabStripSelected,
-};
-
-void EmitToHistogram(const std::u16string& selected_lab_state,
-                     const std::string& internal_name) {
-  const auto get_histogram_name = [](const std::u16string& selected_lab_state) {
-    if (selected_lab_state == base::ASCIIToUTF16(base::StringPiece(
-                                  flags_ui::kGenericExperimentChoiceDefault))) {
-      return "Toolbar.ChromeLabs.DefaultLabAction";
-    } else if (selected_lab_state ==
-               base::ASCIIToUTF16(base::StringPiece(
-                   flags_ui::kGenericExperimentChoiceEnabled))) {
-      return "Toolbar.ChromeLabs.EnableLabAction";
-    } else if (selected_lab_state ==
-               base::ASCIIToUTF16(base::StringPiece(
-                   flags_ui::kGenericExperimentChoiceDisabled))) {
-      return "Toolbar.ChromeLabs.DisableLabAction";
-    } else {
-      return "";
-    }
-  };
-
-  const auto get_enum = [](const std::string& internal_name) {
-    if (internal_name == flag_descriptions::kScrollableTabStripFlagId) {
-      return ChromeLabsSelectedLab::kTabScrollingSelected;
-    } else if (internal_name == flag_descriptions::kSidePanelFlagId) {
-      return ChromeLabsSelectedLab::kSidePanelSelected;
-    } else if (internal_name ==
-               flag_descriptions::kEnableLensRegionSearchFlagId) {
-      return ChromeLabsSelectedLab::kLensRegionSearchSelected;
-#if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP) && \
-    (defined(OS_WIN) || BUILDFLAG(IS_CHROMEOS_ASH))
-    } else if (internal_name == flag_descriptions::kWebUITabStripFlagId) {
-      return ChromeLabsSelectedLab::kWebUITabStripSelected;
-#endif
-    } else {
-      return ChromeLabsSelectedLab::kUnspecifiedSelected;
-    }
-  };
-
-  const std::string histogram_name = get_histogram_name(selected_lab_state);
-  if (!histogram_name.empty())
-    base::UmaHistogramEnumeration(histogram_name, get_enum(internal_name));
-}
-
-ChromeLabsBubbleView* g_chrome_labs_bubble = nullptr;
-
 class ChromeLabsFooter : public views::View {
  public:
   METADATA_HEADER(ChromeLabsFooter);
-  explicit ChromeLabsFooter(ChromeLabsBubbleView* bubble) {
+  explicit ChromeLabsFooter(base::RepeatingClosure restart_callback) {
     SetLayoutManager(std::make_unique<views::FlexLayout>())
         ->SetOrientation(views::LayoutOrientation::kVertical)
         .SetCrossAxisAlignment(views::LayoutAlignment::kStart);
@@ -129,11 +63,7 @@
             .Build());
     AddChildView(views::Builder<views::MdTextButton>()
                      .CopyAddressTo(&restart_button_)
-                     .SetCallback(base::BindRepeating(
-                         [](ChromeLabsBubbleView* bubble_view) {
-                           bubble_view->RestartToApplyFlags();
-                         },
-                         bubble))
+                     .SetCallback(restart_callback)
                      .SetText(l10n_util::GetStringUTF16(
                          IDS_CHROMELABS_RELAUNCH_BUTTON_LABEL))
                      .SetProminent(true)
@@ -158,48 +88,10 @@
 
 }  // namespace
 
-// static
-void ChromeLabsBubbleView::Show(ChromeLabsButton* anchor_view,
-                                Browser* browser,
-                                const ChromeLabsBubbleViewModel* model,
-                                bool user_is_chromeos_owner) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (anchor_view->GetAshOwnerCheckTimer()) {
-    UmaHistogramMediumTimes("Toolbar.ChromeLabs.AshOwnerCheckTime",
-                            anchor_view->GetAshOwnerCheckTimer()->Elapsed());
-  }
-#endif
-  g_chrome_labs_bubble = new ChromeLabsBubbleView(anchor_view, browser, model,
-                                                  user_is_chromeos_owner);
-  views::Widget* const widget =
-      BubbleDialogDelegateView::CreateBubble(g_chrome_labs_bubble);
-  widget->Show();
-}
-
-// static
-bool ChromeLabsBubbleView::IsShowing() {
-  return g_chrome_labs_bubble != nullptr &&
-         g_chrome_labs_bubble->GetWidget() != nullptr;
-}
-
-// static
-void ChromeLabsBubbleView::Hide() {
-  if (IsShowing())
-    g_chrome_labs_bubble->GetWidget()->Close();
-}
-
-ChromeLabsBubbleView::~ChromeLabsBubbleView() {
-  g_chrome_labs_bubble = nullptr;
-}
-
-ChromeLabsBubbleView::ChromeLabsBubbleView(
-    ChromeLabsButton* anchor_view,
-    Browser* browser,
-    const ChromeLabsBubbleViewModel* model,
-    bool user_is_chromeos_owner)
+ChromeLabsBubbleView::ChromeLabsBubbleView(ChromeLabsButton* anchor_view,
+                                           Browser* browser)
     : BubbleDialogDelegateView(anchor_view,
-                               views::BubbleBorder::Arrow::TOP_RIGHT),
-      model_(model) {
+                               views::BubbleBorder::Arrow::TOP_RIGHT) {
   SetButtons(ui::DIALOG_BUTTON_NONE);
   SetShowCloseButton(true);
   SetTitle(l10n_util::GetStringUTF16(IDS_WINDOW_TITLE_EXPERIMENTS));
@@ -215,25 +107,6 @@
   // behavior was confusing.
   SetAccessibleRole(ax::mojom::Role::kDialog);
 
-// TODO(elainechien): Take care of additional cases 1) kSafeMode switch is
-// present 2) user is secondary user.
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  profile_ = browser->profile()->GetOriginalProfile();
-  if (user_is_chromeos_owner) {
-    ash::OwnerSettingsServiceAsh* service =
-        ash::OwnerSettingsServiceAshFactory::GetForBrowserContext(profile_);
-    flags_storage_ = std::make_unique<ash::about_flags::OwnerFlagsStorage>(
-        profile_->GetPrefs(), service);
-  } else {
-    flags_storage_ = std::make_unique<flags_ui::PrefServiceFlagsStorage>(
-        profile_->GetPrefs());
-  }
-#else
-  flags_storage_ = std::make_unique<flags_ui::PrefServiceFlagsStorage>(
-      g_browser_process->local_state());
-#endif
-  flags_state_ = about_flags::GetCurrentFlagsState();
-
   // TODO(crbug.com/1259763): Currently basing this off what extension menu uses
   // for sizing as suggested as an initial fix by UI. Discuss a more formal
   // solution.
@@ -258,87 +131,45 @@
   scroll_view->ClipHeightTo(0, kMaxChromeLabsHeightDp);
   AddChildView(std::move(scroll_view));
 
-  // Create each lab item.
-  const std::vector<LabInfo>& all_labs = model_->GetLabInfo();
-  for (const auto& lab : all_labs) {
-    const flags_ui::FeatureEntry* entry =
-        flags_state_->FindFeatureEntryByName(lab.internal_name);
-    if (IsChromeLabsFeatureValid(lab, browser->profile())) {
-      bool valid_entry_type =
-          entry->type == flags_ui::FeatureEntry::FEATURE_VALUE ||
-          entry->type == flags_ui::FeatureEntry::FEATURE_WITH_PARAMS_VALUE;
-      DCHECK(valid_entry_type);
-      int default_index = GetIndexOfEnabledLabState(entry);
-      menu_item_container_->AddChildView(
-          CreateLabItem(lab, default_index, entry, browser));
-    }
-  }
-  // ChromeLabsButton should not appear in the toolbar if there are no
-  // experiments to show. Therefore ChromeLabsBubble should not be created.
-  DCHECK(menu_item_container_->children().size() >= 1);
-
-  // Hide dot indicator once bubble has been opened.
-  anchor_view->HideDotIndicator();
-
-  restart_prompt_ = AddChildView(std::make_unique<ChromeLabsFooter>(this));
+  /* base::Unretained is safe here because NotifyRestartCallback will notify a
+   * CallbackListSubscription, and ChromeLabsFooter is a child on
+   * ChromeLabsBubbleView. ChromeLabsBubbleView will outlive the
+   * ChromeLabsFooter and the CallbackListSubscription will ensure the restart
+   * callback is valid.. */
+  restart_prompt_ = AddChildView(std::make_unique<ChromeLabsFooter>(
+      base::BindRepeating(&ChromeLabsBubbleView::NotifyRestartCallback,
+                          base::Unretained(this))));
   restart_prompt_->SetVisible(about_flags::IsRestartNeededToCommitChanges());
 }
 
-std::unique_ptr<ChromeLabsItemView> ChromeLabsBubbleView::CreateLabItem(
+ChromeLabsBubbleView::~ChromeLabsBubbleView() = default;
+
+ChromeLabsItemView* ChromeLabsBubbleView::AddLabItem(
     const LabInfo& lab,
     int default_index,
     const flags_ui::FeatureEntry* entry,
-    Browser* browser) {
-  auto combobox_callback = [](ChromeLabsBubbleView* bubble_view,
-                              std::string internal_name,
-                              ChromeLabsItemView* item_view) {
-    int selected_index = item_view->GetSelectedIndex();
-    about_flags::SetFeatureEntryEnabled(
-        bubble_view->flags_storage_.get(),
-        internal_name + flags_ui::kMultiSeparatorChar +
-            base::NumberToString(selected_index),
-        true);
-    bubble_view->ShowRelaunchPrompt();
-    EmitToHistogram(
-        item_view->GetFeatureEntry()->DescriptionForOption(selected_index),
-        internal_name);
-  };
-
+    Browser* browser,
+    base::RepeatingCallback<void(ChromeLabsItemView* item_view)>
+        combobox_callback) {
   std::unique_ptr<ChromeLabsItemView> item_view =
       std::make_unique<ChromeLabsItemView>(
-          lab, default_index, entry,
-          base::BindRepeating(combobox_callback, this, lab.internal_name),
-          browser);
+          lab, default_index, entry, std::move(combobox_callback), browser);
 
   item_view->SetProperty(
       views::kFlexBehaviorKey,
       views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
                                views::MaximumFlexSizeRule::kPreferred, true));
-  return item_view;
+
+  return menu_item_container_->AddChildView(std::move(item_view));
 }
 
-int ChromeLabsBubbleView::GetIndexOfEnabledLabState(
-    const flags_ui::FeatureEntry* entry) {
-  std::set<std::string> enabled_entries;
-  flags_state_->GetSanitizedEnabledFlags(flags_storage_.get(),
-                                         &enabled_entries);
-  for (int i = 0; i < entry->NumOptions(); i++) {
-    const std::string name = entry->NameForOption(i);
-    if (enabled_entries.count(name) > 0)
-      return i;
-  }
-  return 0;
+size_t ChromeLabsBubbleView::GetNumLabItems() {
+  return menu_item_container_->children().size();
 }
 
-void ChromeLabsBubbleView::RestartToApplyFlags() {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  // On Chrome OS be less intrusive and restart inside the user session after
-  // we apply the newly selected flags.
-  VLOG(1) << "Restarting to apply per-session flags...";
-  ash::about_flags::FeatureFlagsUpdate(*flags_storage_, profile_->GetPrefs())
-      .UpdateSessionManager();
-#endif
-  chrome::AttemptRestart();
+base::CallbackListSubscription ChromeLabsBubbleView::RegisterRestartCallback(
+    base::RepeatingClosureList::CallbackType callback) {
+  return restart_callback_list_.Add(std::move(callback));
 }
 
 void ChromeLabsBubbleView::ShowRelaunchPrompt() {
@@ -353,18 +184,11 @@
   }
 #endif
 
-  DCHECK_EQ(g_chrome_labs_bubble, this);
-  g_chrome_labs_bubble->SizeToContents();
+  SizeToContents();
 }
 
-// static
-ChromeLabsBubbleView*
-ChromeLabsBubbleView::GetChromeLabsBubbleViewForTesting() {
-  return g_chrome_labs_bubble;
-}
-
-flags_ui::FlagsState* ChromeLabsBubbleView::GetFlagsStateForTesting() {
-  return flags_state_;
+void ChromeLabsBubbleView::NotifyRestartCallback() {
+  restart_callback_list_.Notify();
 }
 
 views::View* ChromeLabsBubbleView::GetMenuItemContainerForTesting() {
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h
index 3960d4a..40be00f 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h
@@ -5,75 +5,58 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_BUBBLE_VIEW_H_
 #define CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_BUBBLE_VIEW_H_
 
+#include "base/callback.h"
+#include "base/callback_list.h"
 #include "base/memory/raw_ptr.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.h"
-#include "chrome/browser/ui/views/toolbar/chrome_labs_item_view.h"
 #include "components/flags_ui/feature_entry.h"
-#include "components/flags_ui/flags_state.h"
-#include "components/flags_ui/flags_storage.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
-#include "ui/views/layout/flex_layout_view.h"
 
 class Browser;
 class ChromeLabsButton;
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-class Profile;
-#endif
+class ChromeLabsItemView;
+struct LabInfo;
+
+namespace views {
+class FlexLayoutView;
+}
 
 // TODO(elainechien): Use composition instead of inheritance.
 class ChromeLabsBubbleView : public views::BubbleDialogDelegateView {
  public:
   METADATA_HEADER(ChromeLabsBubbleView);
-  static void Show(ChromeLabsButton* anchor_view,
-                   Browser* browser,
-                   const ChromeLabsBubbleViewModel* model,
-                   bool user_is_chromeos_owner);
-
-  static bool IsShowing();
-
-  static void Hide();
-
-  void RestartToApplyFlags();
-
+  ChromeLabsBubbleView(ChromeLabsButton* anchor_view, Browser* browser);
   ~ChromeLabsBubbleView() override;
 
+  ChromeLabsItemView* AddLabItem(
+      const LabInfo& lab,
+      int default_index,
+      const flags_ui::FeatureEntry* entry,
+      Browser* browser,
+      base::RepeatingCallback<void(ChromeLabsItemView* item_view)>
+          combobox_callback);
+
+  size_t GetNumLabItems();
+
+  base::CallbackListSubscription RegisterRestartCallback(
+      base::RepeatingClosureList::CallbackType callback);
+
+  void ShowRelaunchPrompt();
+
   // Getter functions for testing.
   static ChromeLabsBubbleView* GetChromeLabsBubbleViewForTesting();
-  flags_ui::FlagsState* GetFlagsStateForTesting();
   views::View* GetMenuItemContainerForTesting();
   bool IsRestartPromptVisibleForTesting();
 
  private:
-  ChromeLabsBubbleView(ChromeLabsButton* anchor_view,
-                       Browser* browser,
-                       const ChromeLabsBubbleViewModel* model,
-                       bool user_is_chromeos_owner);
-
-  std::unique_ptr<ChromeLabsItemView> CreateLabItem(
-      const LabInfo& lab,
-      int default_index,
-      const flags_ui::FeatureEntry* entry,
-      Browser* browser);
-
-  int GetIndexOfEnabledLabState(const flags_ui::FeatureEntry* entry);
-
-  void ShowRelaunchPrompt();
-
-  std::unique_ptr<flags_ui::FlagsStorage> flags_storage_;
-
-  raw_ptr<flags_ui::FlagsState> flags_state_;
+  void NotifyRestartCallback();
 
   // This view will hold all the child lab items.
   raw_ptr<views::FlexLayoutView> menu_item_container_;
 
-  raw_ptr<const ChromeLabsBubbleViewModel> model_;
-
   raw_ptr<views::View> restart_prompt_;
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  Profile* profile_ = nullptr;
-#endif
+  base::RepeatingClosureList restart_callback_list_;
 };
 #endif  // CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_BUBBLE_VIEW_H_
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_unittest.cc b/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_unittest.cc
deleted file mode 100644
index 84cdbfc9..0000000
--- a/chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_unittest.cc
+++ /dev/null
@@ -1,503 +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.
-
-#include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h"
-
-#include "base/containers/cxx20_erase_vector.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/task_environment.h"
-#include "base/time/time.h"
-#include "chrome/browser/about_flags.h"
-#include "chrome/browser/ui/toolbar/chrome_labs_prefs.h"
-#include "chrome/browser/ui/ui_features.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/frame/test_with_browser_view.h"
-#include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.h"
-#include "chrome/browser/ui/views/toolbar/chrome_labs_button.h"
-#include "chrome/browser/ui/views/toolbar/chrome_labs_utils.h"
-#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
-#include "chrome/browser/ui/views/user_education/new_badge_label.h"
-#include "chrome/browser/unexpire_flags.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "components/flags_ui/feature_entry_macros.h"
-#include "components/flags_ui/flags_state.h"
-#include "components/flags_ui/pref_service_flags_storage.h"
-#include "components/version_info/channel.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/events/event_utils.h"
-#include "ui/views/test/button_test_api.h"
-#include "ui/views/test/combobox_test_api.h"
-#include "ui/views/test/widget_test.h"
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "base/memory/ptr_util.h"
-#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
-#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/about_flags.h"
-#include "chromeos/cryptohome/cryptohome_parameters.h"
-#include "chromeos/dbus/session_manager/fake_session_manager_client.h"
-#include "components/user_manager/scoped_user_manager.h"
-#include "components/user_manager/user_manager.h"
-#endif
-
-namespace {
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-constexpr char kFakeUserName[] = "test@example.com";
-constexpr char kFakeGaiaId[] = "1234567890";
-#endif
-
-const char kFirstTestFeatureId[] = "feature-1";
-const char kTestFeatureWithVariationId[] = "feature-2";
-const char kThirdTestFeatureId[] = "feature-3";
-const char kExpiredFlagTestFeatureId[] = "expired-feature";
-
-const base::Feature kTestFeature1{"FeatureName1",
-                                  base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kTestFeature2{"FeatureName2",
-                                  base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kTestFeature3{"FeatureName3",
-                                  base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::Feature kExpiredFlagTestFeature{"Expired",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
-
-const flags_ui::FeatureEntry::FeatureParam kTestVariationOther2[] = {
-    {"Param1", "Value"}};
-const flags_ui::FeatureEntry::FeatureVariation kTestVariations2[] = {
-    {"Description", kTestVariationOther2, 1, nullptr}};
-
-// Experiment platform to use for feature flags.
-unsigned short GetPlatformToUse() {
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-  return flags_ui::FlagsState::GetCurrentPlatform() | flags_ui::kOsCrOS;
-#else
-  return flags_ui::FlagsState::GetCurrentPlatform();
-#endif
-}
-
-}  // namespace
-
-class ChromeLabsBubbleTest : public TestWithBrowserView {
- public:
-  ChromeLabsBubbleTest()
-      : TestWithBrowserView(
-            base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME),
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-        user_manager_(new ash::FakeChromeUserManager()),
-        user_manager_enabler_(base::WrapUnique(user_manager_)),
-#endif
-        scoped_feature_entries_(
-            {{kFirstTestFeatureId, "", "", GetPlatformToUse(),
-              FEATURE_VALUE_TYPE(kTestFeature1)},
-             {kTestFeatureWithVariationId, "", "", GetPlatformToUse(),
-              FEATURE_WITH_PARAMS_VALUE_TYPE(kTestFeature2,
-                                             kTestVariations2,
-                                             "TestTrial")},
-             // kThirdTestFeatureId will be the Id of a FeatureEntry that is not
-             // compatible with the current platform.
-             {kThirdTestFeatureId, "", "", 0,
-              FEATURE_VALUE_TYPE(kTestFeature3)},
-             {kExpiredFlagTestFeatureId, "", "", GetPlatformToUse(),
-              FEATURE_VALUE_TYPE(kExpiredFlagTestFeature)}}) {
-    // Set expiration milestone such that the flag is expired.
-    flags::testing::SetFlagExpiration(kExpiredFlagTestFeatureId, 0);
-  }
-
-  void SetUp() override {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-    const AccountId account_id(
-        AccountId::FromUserEmailGaiaId(kFakeUserName, kFakeGaiaId));
-    user_manager_->AddUser(account_id);
-    user_manager_->LoginUser(account_id);
-#endif
-
-    scoped_feature_list_.InitAndEnableFeature(features::kChromeLabs);
-
-    // Set up test data on the model.
-    scoped_chrome_labs_model_data_.SetModelDataForTesting(TestLabInfo());
-
-    TestWithBrowserView::SetUp();
-    profile()->GetPrefs()->SetBoolean(chrome_labs_prefs::kBrowserLabsEnabled,
-                                      true);
-
-    ChromeLabsButton* button = chrome_labs_button();
-    ChromeLabsBubbleView::Show(button, browser_view()->browser(),
-                               chrome_labs_model(),
-                               /*user_is_chromeos_owner=*/false);
-  }
-
-  void TearDown() override {
-    chrome_labs_bubble()->GetFlagsStateForTesting()->Reset();
-    TestWithBrowserView::TearDown();
-  }
-
-  ChromeLabsBubbleView* chrome_labs_bubble() {
-    return ChromeLabsBubbleView::GetChromeLabsBubbleViewForTesting();
-  }
-
-  ChromeLabsButton* chrome_labs_button() {
-    return browser_view()->toolbar()->chrome_labs_button();
-  }
-
-  views::View* chrome_labs_menu_item_container() {
-    return ChromeLabsBubbleView::GetChromeLabsBubbleViewForTesting()
-        ->GetMenuItemContainerForTesting();
-  }
-
-  ChromeLabsBubbleViewModel* chrome_labs_model() {
-    return browser_view()->toolbar()->chrome_labs_model();
-  }
-
-  flags_ui::FlagsState* flags_state() {
-    return ChromeLabsBubbleView::GetChromeLabsBubbleViewForTesting()
-        ->GetFlagsStateForTesting();
-  }
-
-  ChromeLabsItemView* first_lab_item() {
-    views::View* menu_items = chrome_labs_menu_item_container();
-    return static_cast<ChromeLabsItemView*>(menu_items->children().front());
-  }
-
-  // This corresponds with the feature of type FEATURE_WITH_PARAMS_VALUE
-  ChromeLabsItemView* second_lab_item() {
-    views::View* menu_items = chrome_labs_menu_item_container();
-    return static_cast<ChromeLabsItemView*>(menu_items->children()[1]);
-  }
-
-  // Returns true if the option at index |option_index| is the enabled feature
-  // state in the FlagsStorage we expect the entry to be in.
-  bool IsSelected(int option_index,
-                  const flags_ui::FeatureEntry* entry,
-                  flags_ui::FlagsStorage* expected_flags_storage) {
-    std::string internal_name = std::string(entry->internal_name) + "@" +
-                                base::NumberToString(option_index);
-    std::set<std::string> enabled_entries;
-    flags_state()->GetSanitizedEnabledFlags(expected_flags_storage,
-                                            &enabled_entries);
-    for (int i = 0; i < entry->NumOptions(); i++) {
-      const std::string name = entry->NameForOption(i);
-      if (internal_name == name && enabled_entries.count(name) > 0) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  // Returns true if none of the entry's options have been enabled.
-  bool IsDefault(const flags_ui::FeatureEntry* entry,
-                 flags_ui::FlagsStorage* expected_flags_storage) {
-    std::set<std::string> enabled_entries;
-    flags_state()->GetSanitizedEnabledFlags(expected_flags_storage,
-                                            &enabled_entries);
-    for (int i = 0; i < entry->NumOptions(); i++) {
-      const std::string name = entry->NameForOption(i);
-      if (enabled_entries.count(name) > 0) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  std::vector<LabInfo> TestLabInfo() {
-    std::vector<LabInfo> test_feature_info;
-    test_feature_info.emplace_back(LabInfo(kFirstTestFeatureId, u"", u"", "",
-                                           version_info::Channel::STABLE));
-
-    std::vector<std::u16string> variation_descriptions = {u"Description"};
-
-    test_feature_info.emplace_back(
-        LabInfo(kTestFeatureWithVariationId, u"", u"", "",
-                version_info::Channel::STABLE, variation_descriptions));
-
-    test_feature_info.emplace_back(LabInfo(kThirdTestFeatureId, u"", u"", "",
-                                           version_info::Channel::STABLE));
-
-    test_feature_info.emplace_back(LabInfo(kExpiredFlagTestFeatureId, u"", u"",
-                                           "", version_info::Channel::STABLE));
-
-    return test_feature_info;
-  }
-
- protected:
-  ScopedChromeLabsModelDataForTesting scoped_chrome_labs_model_data_;
-
- private:
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  ash::FakeChromeUserManager* user_manager_;
-  user_manager::ScopedUserManager user_manager_enabler_;
-#endif
-
-  about_flags::testing::ScopedFeatureEntries scoped_feature_entries_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-class ChromeLabsFeatureTest : public ChromeLabsBubbleTest,
-                              public testing::WithParamInterface<int> {
- public:
-  ChromeLabsFeatureTest() = default;
-};
-
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
-// This test checks that selecting an option through the combobox on a lab will
-// enable the corresponding option on the feature.
-TEST_P(ChromeLabsFeatureTest, ChangeSelectedOption) {
-  int row = GetParam();
-
-  // FeatureEntry of type FEATURE_VALUE
-  ChromeLabsItemView* lab_item = first_lab_item();
-  views::Combobox* lab_item_combobox =
-      lab_item->GetLabStateComboboxForTesting();
-
-  lab_item_combobox->SetSelectedRow(row);
-
-  const flags_ui::FeatureEntry* feature_entry = lab_item->GetFeatureEntry();
-  std::unique_ptr<flags_ui::PrefServiceFlagsStorage> flags_storage =
-      std::make_unique<flags_ui::PrefServiceFlagsStorage>(
-          TestingBrowserProcess::GetGlobal()->local_state());
-  EXPECT_TRUE(IsSelected(row, feature_entry, flags_storage.get()));
-
-  // FeatureEntry of type FEATURE_WITH_PARAMS_VALUE
-  ChromeLabsItemView* lab_item_with_params = second_lab_item();
-  views::Combobox* lab_item_with_params_combobox =
-      lab_item_with_params->GetLabStateComboboxForTesting();
-  lab_item_with_params_combobox->SetSelectedRow(row);
-
-  const flags_ui::FeatureEntry* feature_entry_with_params =
-      lab_item_with_params->GetFeatureEntry();
-  EXPECT_TRUE(IsSelected(row, feature_entry_with_params, flags_storage.get()));
-}
-
-// For FeatureEntries of type FEATURE_VALUE, the option at index 1 corresponds
-// to "Enabled" and the option at index 2 corresponds to "Disabled". For
-// FeatureEntries of type FEATURE_WITH_PARAMS_VALUE, the option at index 1
-// corresponds to "Enabled" and the option at index 2 corresponds to the first
-// additional parameter.
-INSTANTIATE_TEST_SUITE_P(All, ChromeLabsFeatureTest, testing::Values(1, 2));
-
-// This test checks that selecting row 0 will reset the feature to it's Default
-// state.
-TEST_F(ChromeLabsBubbleTest, ResetToDefault) {
-  ChromeLabsItemView* lab_item = first_lab_item();
-  views::Combobox* lab_item_combobox =
-      lab_item->GetLabStateComboboxForTesting();
-
-  // Selects an option and then attempts to reset the lab to Default by
-  // selecting 0.
-  const flags_ui::FeatureEntry* feature_entry = lab_item->GetFeatureEntry();
-  lab_item_combobox->SetSelectedRow(1);
-  std::unique_ptr<flags_ui::PrefServiceFlagsStorage> flags_storage =
-      std::make_unique<flags_ui::PrefServiceFlagsStorage>(
-          TestingBrowserProcess::GetGlobal()->local_state());
-  EXPECT_FALSE(IsDefault(feature_entry, flags_storage.get()));
-  lab_item_combobox->SetSelectedRow(0);
-  EXPECT_TRUE(IsDefault(feature_entry, flags_storage.get()));
-}
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
-
-// Ash versions of the above tests.
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-
-namespace ash {
-
-class ChromeLabsAshFeatureTest : public ChromeLabsFeatureTest {
- public:
-  ChromeLabsAshFeatureTest()
-      : ChromeLabsFeatureTest(),
-        user_manager_(new FakeChromeUserManager()),
-        user_manager_enabler_(base::WrapUnique(user_manager_)) {
-    SessionManagerClient::InitializeFakeInMemory();
-    FakeSessionManagerClient::Get()->set_supports_browser_restart(true);
-    const AccountId account_id(
-        AccountId::FromUserEmailGaiaId(kFakeUserName, kFakeGaiaId));
-    user_manager_->AddUser(account_id);
-    user_manager_->LoginUser(account_id);
-  }
-
- private:
-  FakeChromeUserManager* user_manager_;
-  user_manager::ScopedUserManager user_manager_enabler_;
-};
-
-TEST_P(ChromeLabsAshFeatureTest, ChangeSelectedOption) {
-  int row = GetParam();
-
-  // FeatureEntry of type FEATURE_VALUE
-  ChromeLabsItemView* lab_item = first_lab_item();
-  views::Combobox* lab_item_combobox =
-      lab_item->GetLabStateComboboxForTesting();
-
-  lab_item_combobox->SetSelectedRow(row);
-
-  const flags_ui::FeatureEntry* feature_entry = lab_item->GetFeatureEntry();
-  // On ash-chrome we expect the PrefService from the profile to be used.
-  std::unique_ptr<flags_ui::PrefServiceFlagsStorage> flags_storage =
-      std::make_unique<flags_ui::PrefServiceFlagsStorage>(
-          profile()->GetPrefs());
-  EXPECT_TRUE(IsSelected(row, feature_entry, flags_storage.get()));
-
-  // FeatureEntry of type FEATURE_WITH_PARAMS_VALUE
-  ChromeLabsItemView* lab_item_with_params = second_lab_item();
-  views::Combobox* lab_item_with_params_combobox =
-      lab_item_with_params->GetLabStateComboboxForTesting();
-  lab_item_with_params_combobox->SetSelectedRow(row);
-
-  const flags_ui::FeatureEntry* feature_entry_with_params =
-      lab_item_with_params->GetFeatureEntry();
-  EXPECT_TRUE(IsSelected(row, feature_entry_with_params, flags_storage.get()));
-
-  // Make sure flags have been set since ChromeOS should apply flags through
-  // the session manager.
-  AccountId user_id =
-      user_manager::UserManager::Get()->GetActiveUser()->GetAccountId();
-  std::vector<std::string> raw_flags;
-  FakeSessionManagerClient* session_manager = FakeSessionManagerClient::Get();
-  chrome_labs_bubble()->RestartToApplyFlags();
-  const bool has_user_flags = session_manager->GetFlagsForUser(
-      cryptohome::CreateAccountIdentifierFromAccountId(user_id), &raw_flags);
-  EXPECT_TRUE(has_user_flags);
-}
-
-INSTANTIATE_TEST_SUITE_P(All, ChromeLabsAshFeatureTest, testing::Values(1, 2));
-
-// OwnerFlagsStorage on build bots works the same way as the non-owner version
-// since we don't have the session manager daemon to write and sign the proto
-// blob. This test just opens the bubble to make sure there are no crashes.
-TEST_F(ChromeLabsBubbleTest, ShowBubbleWhenUserIsOwner) {
-  ChromeLabsBubbleView::Hide();
-  ChromeLabsBubbleView::Show(chrome_labs_button(), browser_view()->browser(),
-                             chrome_labs_model(),
-                             /*user_is_chromeos_owner=*/true);
-}
-
-TEST_F(ChromeLabsBubbleTest, ResetToDefault) {
-  ChromeLabsItemView* lab_item = first_lab_item();
-  views::Combobox* lab_item_combobox =
-      lab_item->GetLabStateComboboxForTesting();
-
-  // Selects an option and then attempts to reset the lab to Default by
-  // selecting 0.
-  const flags_ui::FeatureEntry* feature_entry = lab_item->GetFeatureEntry();
-  lab_item_combobox->SetSelectedRow(1);
-  // On ash-chrome we expect the PrefService from the profile to be used.
-  std::unique_ptr<flags_ui::PrefServiceFlagsStorage> flags_storage =
-      std::make_unique<flags_ui::PrefServiceFlagsStorage>(
-          profile()->GetPrefs());
-  EXPECT_FALSE(IsDefault(feature_entry, flags_storage.get()));
-  lab_item_combobox->SetSelectedRow(0);
-  EXPECT_TRUE(IsDefault(feature_entry, flags_storage.get()));
-}
-
-}  // namespace ash
-
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-// This test checks that only the two features that are supported on the current
-// platform and do not have expired flags are added to the bubble.
-TEST_F(ChromeLabsBubbleTest, OnlyCompatibleFeaturesShow) {
-  EXPECT_TRUE(chrome_labs_menu_item_container()->children().size() == 2);
-}
-
-// This test checks that the restart prompt becomes visible when a lab state is
-// changed.
-TEST_F(ChromeLabsBubbleTest, RestartPromptShows) {
-  ChromeLabsBubbleView* bubble_view = chrome_labs_bubble();
-  ChromeLabsItemView* lab_item = first_lab_item();
-  views::Combobox* lab_item_combobox =
-      lab_item->GetLabStateComboboxForTesting();
-  EXPECT_FALSE(bubble_view->IsRestartPromptVisibleForTesting());
-  lab_item_combobox->SetSelectedRow(1);
-  EXPECT_TRUE(bubble_view->IsRestartPromptVisibleForTesting());
-  views::test::WidgetDestroyedWaiter destroyed_waiter(bubble_view->GetWidget());
-  ChromeLabsBubbleView::Hide();
-  destroyed_waiter.Wait();
-  ChromeLabsBubbleView::Show(chrome_labs_button(), browser_view()->browser(),
-                             chrome_labs_model(),
-                             /*user_is_chromeos_owner=*/false);
-  ChromeLabsBubbleView* bubble_view_after_restart = chrome_labs_bubble();
-  EXPECT_TRUE(bubble_view_after_restart->IsRestartPromptVisibleForTesting());
-}
-
-// This test checks that the restart prompt does not show when the lab state has
-// not changed.
-// TODO(elainechien): This currently only works for default. This will be
-// changed to work for all states. See design doc in crbug/1145666.
-TEST_F(ChromeLabsBubbleTest, SelectDefaultTwiceNoRestart) {
-  ChromeLabsBubbleView* bubble_view = chrome_labs_bubble();
-  ChromeLabsItemView* lab_item = first_lab_item();
-  views::Combobox* lab_item_combobox =
-      lab_item->GetLabStateComboboxForTesting();
-  // Select default state when the originally instantiated state was already
-  // default.
-  lab_item_combobox->SetSelectedRow(0);
-  EXPECT_FALSE(bubble_view->IsRestartPromptVisibleForTesting());
-}
-
-// TODO(crbug.com/1128855)
-#if !BUILDFLAG(IS_CHROMEOS_LACROS)
-TEST_F(ChromeLabsBubbleTest, ShowFeedbackPage) {
-  // TODO(b/185480535): Fix the test for WebUIFeedback
-  if (base::FeatureList::IsEnabled(features::kWebUIFeedback))
-    GTEST_SKIP() << "Skipped due to crash with webui feedback.";
-
-  base::HistogramTester histogram_tester;
-
-  views::MdTextButton* feedback_button =
-      first_lab_item()->GetFeedbackButtonForTesting();
-  ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
-                   ui::EventTimeForNow(), 0, 0);
-  views::test::ButtonTestApi test_api(feedback_button);
-  test_api.NotifyClick(e);
-
-  histogram_tester.ExpectTotalCount("Feedback.RequestSource", 1);
-}
-#endif
-
-// This test checks the new badge shows and that after 8 days the new badge is
-// not showing anymore.
-TEST_F(ChromeLabsBubbleTest, NewBadgeTest) {
-  EXPECT_TRUE(first_lab_item()->GetNewBadgeForTesting()->GetDisplayNewBadge());
-  ChromeLabsBubbleView::Hide();
-  constexpr base::TimeDelta kDelay = base::Days(8);
-  task_environment()->AdvanceClock(kDelay);
-  ChromeLabsBubbleView::Show(chrome_labs_button(), browser_view()->browser(),
-                             chrome_labs_model(),
-                             /*user_is_chromeos_owner=*/false);
-  EXPECT_FALSE(first_lab_item()->GetNewBadgeForTesting()->GetDisplayNewBadge());
-}
-
-// This test checks that experiments that are removed from the model will be
-// removed from the PrefService when updating new badge prefs.
-TEST_F(ChromeLabsBubbleTest, CleanUpNewBadgePrefsTest) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  const base::Value* new_badge_prefs =
-      browser_view()->browser()->profile()->GetPrefs()->GetDictionary(
-          chrome_labs_prefs::kChromeLabsNewBadgeDictAshChrome);
-#else
-  const base::Value* new_badge_prefs =
-      g_browser_process->local_state()->GetDictionary(
-          chrome_labs_prefs::kChromeLabsNewBadgeDict);
-#endif
-
-  EXPECT_TRUE(new_badge_prefs->FindKey(kFirstTestFeatureId));
-  EXPECT_TRUE(new_badge_prefs->FindKey(kTestFeatureWithVariationId));
-
-  // Remove two experiments.
-  std::vector<LabInfo> test_experiments = TestLabInfo();
-  base::EraseIf(test_experiments, [](const auto& lab) {
-    return lab.internal_name == kFirstTestFeatureId;
-  });
-  base::EraseIf(test_experiments, [](const auto& lab) {
-    return lab.internal_name == kTestFeatureWithVariationId;
-  });
-
-  scoped_chrome_labs_model_data_.SetModelDataForTesting(test_experiments);
-
-  UpdateChromeLabsNewBadgePrefs(browser_view()->browser()->profile(),
-                                chrome_labs_model());
-  EXPECT_FALSE(new_badge_prefs->FindKey(kFirstTestFeatureId));
-  EXPECT_FALSE(new_badge_prefs->FindKey(kTestFeatureWithVariationId));
-}
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_button.cc b/chrome/browser/ui/views/toolbar/chrome_labs_button.cc
index 0ffed00..ba2e311 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_button.cc
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_button.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/ui/toolbar/chrome_labs_prefs.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_coordinator.h"
 #include "chrome/browser/ui/views/toolbar/chrome_labs_utils.h"
 #include "chrome/browser/ui/webui/flags/flags_ui.h"
 #include "chrome/grit/generated_resources.h"
@@ -45,12 +46,12 @@
   GetViewAccessibility().OverrideHasPopup(ax::mojom::HasPopup::kDialog);
   new_experiments_indicator_ = views::DotIndicator::Install(image());
   UpdateDotIndicator();
+
+  chrome_labs_coordinator_ = std::make_unique<ChromeLabsCoordinator>(
+      this, browser_view_->browser(), model_);
 }
 
-ChromeLabsButton::~ChromeLabsButton() {
-  // Make sure the bubble is destroyed if the button is being destroyed.
-  ChromeLabsBubbleView::Hide();
-}
+ChromeLabsButton::~ChromeLabsButton() = default;
 
 void ChromeLabsButton::Layout() {
   ToolbarButton::Layout();
@@ -77,8 +78,8 @@
   }
 #endif
 
-  if (ChromeLabsBubbleView::IsShowing()) {
-    ChromeLabsBubbleView::Hide();
+  if (chrome_labs_coordinator_->BubbleExists()) {
+    chrome_labs_coordinator_->Hide();
     return;
   }
 
@@ -103,29 +104,32 @@
     is_waiting_to_show = true;
     service->IsOwnerAsync(base::BindOnce(
         [](ChromeLabsButton* button, base::WeakPtr<BrowserView> browser_view,
-           const ChromeLabsBubbleViewModel* model, bool is_owner) {
+           ChromeLabsCoordinator* coordinator, bool is_owner) {
+          // BrowserView may have been destroyed before async function returns
           if (!browser_view)
             return;
-          ChromeLabsBubbleView::Show(button, browser_view->browser(), model,
-                                     is_owner);
+          is_owner
+              ? coordinator->Show(
+                    ChromeLabsCoordinator::ShowUserType::kChromeOsOwnerUserType)
+              : coordinator->Show();
           button->is_waiting_to_show = false;
         },
-        this, browser_view_->GetAsWeakPtr(), model_));
+        this, browser_view_->GetAsWeakPtr(), chrome_labs_coordinator_.get()));
     return;
   }
 #endif
-  ChromeLabsBubbleView::Show(this, browser_view_->browser(), model_,
-                             /*user_is_chromeos_owner=*/false);
+  chrome_labs_coordinator_->Show();
 }
 
 void ChromeLabsButton::UpdateDotIndicator() {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  DictionaryPrefUpdate update(
+  DictionaryPrefUpdateDeprecated update(
       browser_view_->browser()->profile()->GetPrefs(),
       chrome_labs_prefs::kChromeLabsNewBadgeDictAshChrome);
 #else
-  DictionaryPrefUpdate update(g_browser_process->local_state(),
-                              chrome_labs_prefs::kChromeLabsNewBadgeDict);
+  DictionaryPrefUpdateDeprecated update(
+      g_browser_process->local_state(),
+      chrome_labs_prefs::kChromeLabsNewBadgeDict);
 #endif
 
   base::DictionaryValue* new_badge_prefs = update.Get();
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_button.h b/chrome/browser/ui/views/toolbar/chrome_labs_button.h
index d375011..9bec66d 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_button.h
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_button.h
@@ -8,6 +8,7 @@
 #include "base/memory/raw_ptr.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_coordinator.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/controls/dot_indicator.h"
@@ -50,6 +51,10 @@
     return new_experiments_indicator_->GetVisible();
   }
 
+  ChromeLabsCoordinator* GetChromeLabsCoordinatorForTesting() {
+    return chrome_labs_coordinator_.get();
+  }
+
  private:
   void ButtonPressed();
 
@@ -73,6 +78,8 @@
   raw_ptr<const ChromeLabsBubbleViewModel> model_;
 
   raw_ptr<views::DotIndicator> new_experiments_indicator_;
+
+  std::unique_ptr<ChromeLabsCoordinator> chrome_labs_coordinator_ = nullptr;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_BUTTON_H_
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_button_unittest.cc b/chrome/browser/ui/views/toolbar/chrome_labs_button_unittest.cc
index 8219a75..95429fb8 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_button_unittest.cc
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_button_unittest.cc
@@ -106,6 +106,8 @@
 TEST_F(ChromeLabsButtonTest, ShowAndHideChromeLabsBubbleOnPress) {
   ChromeLabsButton* labs_button =
       browser_view()->toolbar()->chrome_labs_button();
+  ChromeLabsCoordinator* coordinator =
+      labs_button->GetChromeLabsCoordinatorForTesting();
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   ash::OwnerSettingsServiceAsh* service_ =
@@ -113,7 +115,7 @@
   labs_button->SetShouldCircumventDeviceCheckForTesting(true);
 #endif
 
-  EXPECT_FALSE(ChromeLabsBubbleView::IsShowing());
+  EXPECT_FALSE(coordinator->BubbleExists());
   ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                    ui::EventTimeForNow(), 0, 0);
   views::test::ButtonTestApi test_api(labs_button);
@@ -121,13 +123,13 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   service_->RunPendingIsOwnerCallbacksForTesting(/*is_owner=*/false);
 #endif
-  EXPECT_TRUE(ChromeLabsBubbleView::IsShowing());
+  EXPECT_TRUE(coordinator->BubbleExists());
 
   views::test::WidgetDestroyedWaiter destroyed_waiter(
-      ChromeLabsBubbleView::GetChromeLabsBubbleViewForTesting()->GetWidget());
+      coordinator->GetChromeLabsBubbleViewForTesting()->GetWidget());
   test_api.NotifyClick(e);
   destroyed_waiter.Wait();
-  EXPECT_FALSE(ChromeLabsBubbleView::IsShowing());
+  EXPECT_FALSE(coordinator->BubbleExists());
 }
 
 TEST_F(ChromeLabsButtonTest, ShouldButtonShowTest) {
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_coordinator.cc b/chrome/browser/ui/views/toolbar/chrome_labs_coordinator.cc
new file mode 100644
index 0000000..497055e
--- /dev/null
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_coordinator.cc
@@ -0,0 +1,104 @@
+// 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/toolbar/chrome_labs_coordinator.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "build/chromeos_buildflags.h"
+#include "chrome/browser/about_flags.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_button.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_view_controller.h"
+#include "components/flags_ui/pref_service_flags_storage.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#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/about_flags.h"
+#endif
+
+ChromeLabsCoordinator::ChromeLabsCoordinator(
+    ChromeLabsButton* anchor_view,
+    Browser* browser,
+    const ChromeLabsBubbleViewModel* model)
+    : anchor_view_(anchor_view), browser_(browser), chrome_labs_model_(model) {}
+
+ChromeLabsCoordinator::~ChromeLabsCoordinator() {
+  if (BubbleExists()) {
+    chrome_labs_bubble_view_->GetWidget()->Close();
+    chrome_labs_bubble_view_ = nullptr;
+  }
+}
+
+bool ChromeLabsCoordinator::BubbleExists() {
+  return chrome_labs_bubble_view_ != nullptr;
+}
+
+void ChromeLabsCoordinator::Show(ShowUserType user_type) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  if (anchor_view_->GetAshOwnerCheckTimer()) {
+    UmaHistogramMediumTimes("Toolbar.ChromeLabs.AshOwnerCheckTime",
+                            anchor_view_->GetAshOwnerCheckTimer()->Elapsed());
+  }
+
+  // Bypass possible incognito profile same as chrome://flags does.
+  Profile* original_profile = browser_->profile()->GetOriginalProfile();
+  if (user_type == ShowUserType::kChromeOsOwnerUserType) {
+    ash::OwnerSettingsServiceAsh* service =
+        ash::OwnerSettingsServiceAshFactory::GetForBrowserContext(
+            original_profile);
+    flags_storage_ = std::make_unique<ash::about_flags::OwnerFlagsStorage>(
+        original_profile->GetPrefs(), service);
+  } else {
+    flags_storage_ = std::make_unique<flags_ui::PrefServiceFlagsStorage>(
+        original_profile->GetPrefs());
+  }
+#else
+  flags_storage_ = std::make_unique<flags_ui::PrefServiceFlagsStorage>(
+      g_browser_process->local_state());
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+  flags_state_ = about_flags::GetCurrentFlagsState();
+
+  auto chrome_labs_bubble_view =
+      std::make_unique<ChromeLabsBubbleView>(anchor_view_, browser_);
+  chrome_labs_bubble_view_ = chrome_labs_bubble_view.get();
+  chrome_labs_bubble_view_->View::AddObserver(this);
+
+  controller_ = std::make_unique<ChromeLabsViewController>(
+      chrome_labs_model_, chrome_labs_bubble_view_, browser_, flags_state_,
+      flags_storage_.get());
+
+  // ChromeLabsButton should not appear in the toolbar if there are no
+  // experiments to show. Therefore ChromeLabsBubble should not be created.
+  DCHECK_GE(chrome_labs_bubble_view_->GetNumLabItems(), 1u);
+
+  views::Widget* const widget = views::BubbleDialogDelegateView::CreateBubble(
+      std::move(chrome_labs_bubble_view));
+  widget->Show();
+
+  // Hide dot indicator once bubble has been shown.
+  anchor_view_->HideDotIndicator();
+}
+
+void ChromeLabsCoordinator::Hide() {
+  if (BubbleExists()) {
+    // TODO(elainechien): Replace Close with ClosedWithReason and add an
+    // additional enum to ClosedReason
+    chrome_labs_bubble_view_->GetWidget()->Close();
+    // Closing the widget will eventually result in chrome_labs_bubble_view_
+    // being set to nullptr, but we also set it to nullptr here since we know
+    // the widget will now be destroyed and we shouldn't be accessing
+    // chrome_labs_bubble_ anymore.
+    chrome_labs_bubble_view_ = nullptr;
+  }
+}
+
+void ChromeLabsCoordinator::OnViewIsDeleting(views::View* observed_view) {
+  chrome_labs_bubble_view_ = nullptr;
+}
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_coordinator.h b/chrome/browser/ui/views/toolbar/chrome_labs_coordinator.h
new file mode 100644
index 0000000..b5915af
--- /dev/null
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_coordinator.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_COORDINATOR_H_
+#define CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_COORDINATOR_H_
+
+#include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.h"
+
+#include "base/memory/raw_ptr.h"
+#include "components/flags_ui/flags_state.h"
+#include "components/flags_ui/flags_storage.h"
+#include "ui/views/view_observer.h"
+
+class Browser;
+class ChromeLabsButton;
+class ChromeLabsBubbleView;
+class ChromeLabsViewController;
+
+class ChromeLabsCoordinator : public views::ViewObserver {
+ public:
+  enum class ShowUserType {
+    // The default user type that accounts for most users.
+    kDefaultUserType,
+    // Indicates that the user is the device owner on ChromeOS. The
+    // OwnerFlagsStorage will be used in this case.
+    kChromeOsOwnerUserType,
+  };
+
+  ChromeLabsCoordinator(ChromeLabsButton* anchor_view,
+                        Browser* browser,
+                        const ChromeLabsBubbleViewModel* model);
+  ~ChromeLabsCoordinator() override;
+
+  bool BubbleExists();
+
+  void Show(ShowUserType user_type = ShowUserType::kDefaultUserType);
+
+  void Hide();
+
+  ChromeLabsBubbleView* GetChromeLabsBubbleViewForTesting() {
+    return chrome_labs_bubble_view_;
+  }
+
+  flags_ui::FlagsState* GetFlagsStateForTesting() { return flags_state_; }
+
+  ChromeLabsViewController* GetViewControllerForTesting() {
+    return controller_.get();
+  }
+
+ private:
+  // views::ViewObserver
+  void OnViewIsDeleting(views::View* observed_view) override;
+
+  raw_ptr<ChromeLabsButton> anchor_view_;
+  raw_ptr<Browser> browser_;
+  raw_ptr<const ChromeLabsBubbleViewModel> chrome_labs_model_;
+  raw_ptr<ChromeLabsBubbleView> chrome_labs_bubble_view_ = nullptr;
+
+  std::unique_ptr<flags_ui::FlagsStorage> flags_storage_;
+  raw_ptr<flags_ui::FlagsState> flags_state_;
+  std::unique_ptr<ChromeLabsViewController> controller_;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_COORDINATOR_H_
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_item_view.cc b/chrome/browser/ui/views/toolbar/chrome_labs_item_view.cc
index 38c0c1e..c993de61 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_item_view.cc
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_item_view.cc
@@ -3,24 +3,18 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ui/views/toolbar/chrome_labs_item_view.h"
+#include "base/callback_list.h"
 #include "base/memory/raw_ptr.h"
-#include "base/time/time.h"
-#include "base/values.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/flag_descriptions.h"
-#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/toolbar/chrome_labs_prefs.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/chrome_typography.h"
 #include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.h"
-#include "chrome/browser/ui/views/toolbar/chrome_labs_utils.h"
 #include "chrome/browser/ui/views/user_education/new_badge_label.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/prefs/scoped_user_pref_update.h"
+#include "components/flags_ui/feature_entry.h"
 #include "extensions/browser/api/feedback_private/feedback_private_api.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -28,6 +22,7 @@
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/button/md_text_button.h"
+#include "ui/views/controls/combobox/combobox.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/layout/flex_layout_types.h"
@@ -50,12 +45,6 @@
       /* extra_diagnostics=*/std::string());
 }
 
-// Returns the number of days since epoch (1970-01-01) in the local timezone.
-uint32_t GetCurrentDay() {
-  base::TimeDelta delta = base::Time::Now() - base::Time::UnixEpoch();
-  return base::saturated_cast<uint32_t>(delta.InDays());
-}
-
 }  // namespace
 
 class LabsComboboxModel : public ui::ComboboxModel {
@@ -129,8 +118,11 @@
 
   experiment_name_ =
       AddChildView(std::make_unique<NewBadgeLabel>(lab.visible_name));
-  experiment_name_->SetDisplayNewBadge(
-      ShouldShowNewBadge(browser->profile(), lab));
+  // The NewBadgeLabel’s default visibility is true. However, we only want the
+  // new badge to show if PrefService conditions are met. Here we set the
+  // default to false. Then, when the bubble is being shown the view controller
+  // will set the NewBadgeLabel’s visibility to true if applicable.
+  experiment_name_->SetDisplayNewBadge(false);
   experiment_name_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   experiment_name_->SetBadgePlacement(
       NewBadgeLabel::BadgePlacement::kImmediatelyAfterText);
@@ -231,45 +223,20 @@
           .Build());
 }
 
+ChromeLabsItemView::~ChromeLabsItemView() = default;
+
 int ChromeLabsItemView::GetSelectedIndex() const {
   return lab_state_combobox_->GetSelectedIndex();
 }
 
-const flags_ui::FeatureEntry* ChromeLabsItemView::GetFeatureEntry() {
-  return feature_entry_;
+// Same as NewBadgeLabel::SetDisplayNewBadge this should only be called before
+// the label is shown.
+void ChromeLabsItemView::ShowNewBadge() {
+  experiment_name_->SetDisplayNewBadge(true);
 }
 
-bool ChromeLabsItemView::ShouldShowNewBadge(Profile* profile,
-                                            const LabInfo& lab) {
-  // This experiment was added before adding the new badge and is not new.
-  if (lab.internal_name == flag_descriptions::kScrollableTabStripFlagId) {
-    return false;
-  }
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  DictionaryPrefUpdate update(
-      profile->GetPrefs(), chrome_labs_prefs::kChromeLabsNewBadgeDictAshChrome);
-#else
-  DictionaryPrefUpdate update(g_browser_process->local_state(),
-                              chrome_labs_prefs::kChromeLabsNewBadgeDict);
-#endif
-
-  base::DictionaryValue* new_badge_prefs = update.Get();
-
-  DCHECK(new_badge_prefs->FindIntKey(lab.internal_name));
-  int start_day = *new_badge_prefs->FindIntKey(lab.internal_name);
-  if (start_day == chrome_labs_prefs::kChromeLabsNewExperimentPrefValue) {
-    // Set the dictionary value of this experiment to the number of days since
-    // epoch (1970-01-01). This value is the first day the user sees the new
-    // experiment in Chrome Labs and will be used to determine whether or not to
-    // show the new badge.
-    new_badge_prefs->SetInteger(lab.internal_name, GetCurrentDay());
-    return true;
-  }
-  int days_elapsed = GetCurrentDay() - start_day;
-  // Show the new badge for 7 days. If the users sets the clock such that the
-  // current day is now before |start_day| don’t show the new badge.
-  return (days_elapsed < 7) && (days_elapsed >= 0);
+const flags_ui::FeatureEntry* ChromeLabsItemView::GetFeatureEntry() {
+  return feature_entry_;
 }
 
 BEGIN_METADATA(ChromeLabsItemView, views::View)
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_item_view.h b/chrome/browser/ui/views/toolbar/chrome_labs_item_view.h
index 191daab8..0a597677 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_item_view.h
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_item_view.h
@@ -7,23 +7,27 @@
 
 #include "base/callback.h"
 #include "base/memory/raw_ptr.h"
-#include "components/flags_ui/feature_entry.h"
 #include "ui/base/metadata/metadata_header_macros.h"
-#include "ui/views/controls/combobox/combobox.h"
 #include "ui/views/view.h"
 
 class Browser;
 class NewBadgeLabel;
-class Profile;
 struct LabInfo;
 
+namespace flags_ui {
+struct FeatureEntry;
+}
+
 namespace views {
+class Combobox;
 class MdTextButton;
 }  // namespace views
 
 class ChromeLabsItemView : public views::View {
  public:
   METADATA_HEADER(ChromeLabsItemView);
+  // TODO(elainechien): Have the mediator extract all LabInfo so that views do
+  // not need to have ChromeLabsModel structures in their dependencies.
   ChromeLabsItemView(
       const LabInfo& lab,
       int default_index,
@@ -32,8 +36,12 @@
           combobox_callback,
       Browser* browser);
 
+  ~ChromeLabsItemView() override;
+
   int GetSelectedIndex() const;
 
+  void ShowNewBadge();
+
   views::Combobox* GetLabStateComboboxForTesting() {
     return lab_state_combobox_;
   }
@@ -47,8 +55,6 @@
   const flags_ui::FeatureEntry* GetFeatureEntry();
 
  private:
-  bool ShouldShowNewBadge(Profile* profile, const LabInfo& lab);
-
   raw_ptr<NewBadgeLabel> experiment_name_;
 
   // Combobox with selected state of the lab.
@@ -57,6 +63,8 @@
   raw_ptr<const flags_ui::FeatureEntry> feature_entry_;
 
   views::MdTextButton* feedback_button_;
+
+  base::RepeatingClosureList combobox_callback_list_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_ITEM_VIEW_H_
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_unittest.cc b/chrome/browser/ui/views/toolbar/chrome_labs_unittest.cc
new file mode 100644
index 0000000..527200b
--- /dev/null
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_unittest.cc
@@ -0,0 +1,646 @@
+// 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.
+
+#include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h"
+
+#include "base/containers/cxx20_erase_vector.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "chrome/browser/about_flags.h"
+#include "chrome/browser/ui/toolbar/chrome_labs_prefs.h"
+#include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/test_with_browser_view.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_button.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_coordinator.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_item_view.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_utils.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_view_controller.h"
+#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
+#include "chrome/browser/ui/views/user_education/new_badge_label.h"
+#include "chrome/browser/unexpire_flags.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "components/flags_ui/feature_entry_macros.h"
+#include "components/flags_ui/flags_state.h"
+#include "components/flags_ui/pref_service_flags_storage.h"
+#include "components/version_info/channel.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event_utils.h"
+#include "ui/views/controls/combobox/combobox.h"
+#include "ui/views/test/button_test_api.h"
+#include "ui/views/test/combobox_test_api.h"
+#include "ui/views/test/widget_test.h"
+#include "ui/views/widget/widget.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
+#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/about_flags.h"
+#include "chromeos/cryptohome/cryptohome_parameters.h"
+#include "chromeos/dbus/session_manager/fake_session_manager_client.h"
+#include "components/user_manager/scoped_user_manager.h"
+#include "components/user_manager/user_manager.h"
+#endif
+
+namespace {
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+constexpr char kFakeUserName[] = "test@example.com";
+constexpr char kFakeGaiaId[] = "1234567890";
+#endif
+
+const char kFirstTestFeatureId[] = "feature-1";
+const char kTestFeatureWithVariationId[] = "feature-2";
+const char kThirdTestFeatureId[] = "feature-3";
+const char kExpiredFlagTestFeatureId[] = "expired-feature";
+
+const base::Feature kTestFeature1{"FeatureName1",
+                                  base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kTestFeature2{"FeatureName2",
+                                  base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kTestFeature3{"FeatureName3",
+                                  base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kExpiredFlagTestFeature{"Expired",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
+const flags_ui::FeatureEntry::FeatureParam kTestVariationOther2[] = {
+    {"Param1", "Value"}};
+const flags_ui::FeatureEntry::FeatureVariation kTestVariations2[] = {
+    {"Description", kTestVariationOther2, 1, nullptr}};
+
+// Experiment platform to use for feature flags.
+unsigned short GetPlatformToUse() {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  return flags_ui::FlagsState::GetCurrentPlatform() | flags_ui::kOsCrOS;
+#else
+  return flags_ui::FlagsState::GetCurrentPlatform();
+#endif
+}
+
+std::vector<LabInfo> TestLabInfo() {
+  std::vector<LabInfo> test_feature_info;
+  test_feature_info.emplace_back(LabInfo(kFirstTestFeatureId, u"", u"", "",
+                                         version_info::Channel::STABLE));
+
+  std::vector<std::u16string> variation_descriptions = {u"Description"};
+
+  test_feature_info.emplace_back(LabInfo(kTestFeatureWithVariationId, u"", u"",
+                                         "", version_info::Channel::STABLE,
+                                         variation_descriptions));
+
+  test_feature_info.emplace_back(LabInfo(kThirdTestFeatureId, u"", u"", "",
+                                         version_info::Channel::STABLE));
+
+  test_feature_info.emplace_back(LabInfo(kExpiredFlagTestFeatureId, u"", u"",
+                                         "", version_info::Channel::STABLE));
+
+  return test_feature_info;
+}
+
+}  // namespace
+
+class ChromeLabsCoordinatorTest : public TestWithBrowserView {
+ public:
+  ChromeLabsCoordinatorTest()
+      : TestWithBrowserView(
+            base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME),
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+        user_manager_(new ash::FakeChromeUserManager()),
+        user_manager_enabler_(base::WrapUnique(user_manager_)),
+#endif
+        scoped_feature_entries_(
+            {{kFirstTestFeatureId, "", "", GetPlatformToUse(),
+              FEATURE_VALUE_TYPE(kTestFeature1)},
+             {kTestFeatureWithVariationId, "", "", GetPlatformToUse(),
+              FEATURE_WITH_PARAMS_VALUE_TYPE(kTestFeature2,
+                                             kTestVariations2,
+                                             "TestTrial")},
+             // kThirdTestFeatureId will be the Id of a FeatureEntry that is not
+             // compatible with the current platform.
+             {kThirdTestFeatureId, "", "", 0,
+              FEATURE_VALUE_TYPE(kTestFeature3)},
+             {kExpiredFlagTestFeatureId, "", "", GetPlatformToUse(),
+              FEATURE_VALUE_TYPE(kExpiredFlagTestFeature)}}) {
+    // Set expiration milestone such that the flag is expired.
+    flags::testing::SetFlagExpiration(kExpiredFlagTestFeatureId, 0);
+  }
+
+  void SetUp() override {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    const AccountId account_id(
+        AccountId::FromUserEmailGaiaId(kFakeUserName, kFakeGaiaId));
+    user_manager_->AddUser(account_id);
+    user_manager_->LoginUser(account_id);
+#endif
+
+    scoped_feature_list_.InitAndEnableFeature(features::kChromeLabs);
+
+    // Set up test data on the model.
+    scoped_chrome_labs_model_data_.SetModelDataForTesting(TestLabInfo());
+
+    TestWithBrowserView::SetUp();
+    profile()->GetPrefs()->SetBoolean(chrome_labs_prefs::kBrowserLabsEnabled,
+                                      true);
+
+    ChromeLabsButton* button = browser_view()->toolbar()->chrome_labs_button();
+    chrome_labs_coordinator_ = std::make_unique<ChromeLabsCoordinator>(
+        button, browser_view()->browser(), chrome_labs_model());
+  }
+
+  void TearDown() override {
+    about_flags::GetCurrentFlagsState()->Reset();
+    TestWithBrowserView::TearDown();
+  }
+
+  views::View* chrome_labs_menu_item_container() {
+    return chrome_labs_coordinator_->GetChromeLabsBubbleViewForTesting()
+        ->GetMenuItemContainerForTesting();
+  }
+
+  ChromeLabsBubbleViewModel* chrome_labs_model() {
+    return browser_view()->toolbar()->chrome_labs_model();
+  }
+
+  ChromeLabsItemView* first_lab_item() {
+    views::View* menu_items = chrome_labs_menu_item_container();
+    return static_cast<ChromeLabsItemView*>(menu_items->children().front());
+  }
+
+ protected:
+  ScopedChromeLabsModelDataForTesting scoped_chrome_labs_model_data_;
+  std::unique_ptr<ChromeLabsCoordinator> chrome_labs_coordinator_;
+
+ private:
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  ash::FakeChromeUserManager* user_manager_;
+  user_manager::ScopedUserManager user_manager_enabler_;
+#endif
+
+  about_flags::testing::ScopedFeatureEntries scoped_feature_entries_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(ChromeLabsCoordinatorTest, ShowBubbleTest) {
+  chrome_labs_coordinator_->Show();
+  EXPECT_TRUE(chrome_labs_coordinator_->BubbleExists());
+
+  views::test::WidgetDestroyedWaiter first_destroyed_waiter(
+      chrome_labs_coordinator_->GetChromeLabsBubbleViewForTesting()
+          ->GetWidget());
+  chrome_labs_coordinator_->Hide();
+  first_destroyed_waiter.Wait();
+  EXPECT_FALSE(chrome_labs_coordinator_->BubbleExists());
+  chrome_labs_coordinator_->Show();
+  // The bubble can be closed by the user clicking off of the bubble.
+  views::test::WidgetDestroyedWaiter second_destroyed_waiter(
+      chrome_labs_coordinator_->GetChromeLabsBubbleViewForTesting()
+          ->GetWidget());
+  chrome_labs_coordinator_->GetChromeLabsBubbleViewForTesting()
+      ->GetWidget()
+      ->Close();
+  second_destroyed_waiter.Wait();
+  EXPECT_FALSE(chrome_labs_coordinator_->BubbleExists());
+}
+
+// This test checks the new badge shows and that after 8 days the new badge is
+// not showing anymore.
+TEST_F(ChromeLabsCoordinatorTest, NewBadgeTest) {
+  chrome_labs_coordinator_->Show();
+  EXPECT_TRUE(first_lab_item()->GetNewBadgeForTesting()->GetDisplayNewBadge());
+  views::test::WidgetDestroyedWaiter destroyed_waiter(
+      chrome_labs_coordinator_->GetChromeLabsBubbleViewForTesting()
+          ->GetWidget());
+  chrome_labs_coordinator_->Hide();
+  destroyed_waiter.Wait();
+  constexpr base::TimeDelta kDelay = base::Days(8);
+  task_environment()->AdvanceClock(kDelay);
+  chrome_labs_coordinator_->Show();
+  EXPECT_FALSE(first_lab_item()->GetNewBadgeForTesting()->GetDisplayNewBadge());
+}
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+
+// OwnerFlagsStorage on build bots works the same way as the non-owner version
+// since we don't have the session manager daemon to write and sign the proto
+// blob. This test just opens and closes the bubble to make sure there are no
+// crashes.
+TEST_F(ChromeLabsCoordinatorTest, ShowBubbleWhenUserIsOwner) {
+  chrome_labs_coordinator_->Show(
+      ChromeLabsCoordinator::ShowUserType::kChromeOsOwnerUserType);
+  views::test::WidgetDestroyedWaiter destroyed_waiter(
+      chrome_labs_coordinator_->GetChromeLabsBubbleViewForTesting()
+          ->GetWidget());
+  chrome_labs_coordinator_->Hide();
+  destroyed_waiter.Wait();
+  chrome_labs_coordinator_->Show(
+      ChromeLabsCoordinator::ShowUserType::kChromeOsOwnerUserType);
+}
+
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+class ChromeLabsViewControllerTest : public TestWithBrowserView {
+ public:
+  ChromeLabsViewControllerTest()
+      : TestWithBrowserView(
+            base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME),
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+        user_manager_(new ash::FakeChromeUserManager()),
+        user_manager_enabler_(base::WrapUnique(user_manager_)),
+#endif
+        scoped_feature_entries_(
+            {{kFirstTestFeatureId, "", "", GetPlatformToUse(),
+              FEATURE_VALUE_TYPE(kTestFeature1)},
+             {kTestFeatureWithVariationId, "", "", GetPlatformToUse(),
+              FEATURE_WITH_PARAMS_VALUE_TYPE(kTestFeature2,
+                                             kTestVariations2,
+                                             "TestTrial")},
+             // kThirdTestFeatureId will be the Id of a FeatureEntry that is not
+             // compatible with the current platform.
+             {kThirdTestFeatureId, "", "", 0,
+              FEATURE_VALUE_TYPE(kTestFeature3)},
+             {kExpiredFlagTestFeatureId, "", "", GetPlatformToUse(),
+              FEATURE_VALUE_TYPE(kExpiredFlagTestFeature)}}) {
+    // Set expiration milestone such that the flag is expired.
+    flags::testing::SetFlagExpiration(kExpiredFlagTestFeatureId, 0);
+  }
+
+  void SetUp() override {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    const AccountId account_id(
+        AccountId::FromUserEmailGaiaId(kFakeUserName, kFakeGaiaId));
+    user_manager_->AddUser(account_id);
+    user_manager_->LoginUser(account_id);
+#endif
+
+    scoped_feature_list_.InitAndEnableFeature(features::kChromeLabs);
+
+    // Set up test data on the model.
+    scoped_chrome_labs_model_data_.SetModelDataForTesting(TestLabInfo());
+
+    TestWithBrowserView::SetUp();
+    profile()->GetPrefs()->SetBoolean(chrome_labs_prefs::kBrowserLabsEnabled,
+                                      true);
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    // On ash-chrome we expect the PrefService from the profile to be used.
+    flags_storage_ = std::make_unique<flags_ui::PrefServiceFlagsStorage>(
+        profile()->GetPrefs());
+#else  // !BUILDFLAG(IS_CHROMEOS_ASH)
+    flags_storage_ = std::make_unique<flags_ui::PrefServiceFlagsStorage>(
+        TestingBrowserProcess::GetGlobal()->local_state());
+#endif
+
+    std::unique_ptr<ChromeLabsBubbleView> bubble_view =
+        std::make_unique<ChromeLabsBubbleView>(chrome_labs_button(),
+                                               browser_view()->browser());
+    bubble_view_ = bubble_view.get();
+    bubble_widget_ =
+        views::BubbleDialogDelegateView::CreateBubble(std::move(bubble_view));
+  }
+
+  void TearDown() override {
+    about_flags::GetCurrentFlagsState()->Reset();
+    bubble_widget_->CloseWithReason(views::Widget::ClosedReason::kUnspecified);
+    TestWithBrowserView::TearDown();
+  }
+
+  ChromeLabsBubbleView* chrome_labs_bubble() { return bubble_view_; }
+
+  ChromeLabsButton* chrome_labs_button() {
+    return browser_view()->toolbar()->chrome_labs_button();
+  }
+
+  views::View* chrome_labs_menu_item_container() {
+    return chrome_labs_bubble()->GetMenuItemContainerForTesting();
+  }
+
+  ChromeLabsBubbleViewModel* chrome_labs_model() {
+    return browser_view()->toolbar()->chrome_labs_model();
+  }
+
+  flags_ui::FlagsState* flags_state() {
+    return about_flags::GetCurrentFlagsState();
+  }
+
+  ChromeLabsItemView* first_lab_item() {
+    views::View* menu_items = chrome_labs_menu_item_container();
+    return static_cast<ChromeLabsItemView*>(menu_items->children().front());
+  }
+
+  // This corresponds with the feature of type FEATURE_WITH_PARAMS_VALUE
+  ChromeLabsItemView* second_lab_item() {
+    views::View* menu_items = chrome_labs_menu_item_container();
+    return static_cast<ChromeLabsItemView*>(menu_items->children()[1]);
+  }
+
+  // Returns true if the option at index |option_index| is the enabled feature
+  // state in the FlagsStorage we expect the entry to be in.
+  bool IsSelected(int option_index,
+                  const flags_ui::FeatureEntry* entry,
+                  flags_ui::FlagsStorage* expected_flags_storage) {
+    std::string internal_name = std::string(entry->internal_name) + "@" +
+                                base::NumberToString(option_index);
+    std::set<std::string> enabled_entries;
+    flags_state()->GetSanitizedEnabledFlags(expected_flags_storage,
+                                            &enabled_entries);
+    for (int i = 0; i < entry->NumOptions(); i++) {
+      const std::string name = entry->NameForOption(i);
+      if (internal_name == name && enabled_entries.count(name) > 0) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  // Returns true if none of the entry's options have been enabled.
+  bool IsDefault(const flags_ui::FeatureEntry* entry,
+                 flags_ui::FlagsStorage* expected_flags_storage) {
+    std::set<std::string> enabled_entries;
+    flags_state()->GetSanitizedEnabledFlags(expected_flags_storage,
+                                            &enabled_entries);
+    for (int i = 0; i < entry->NumOptions(); i++) {
+      const std::string name = entry->NameForOption(i);
+      if (enabled_entries.count(name) > 0) {
+        return false;
+      }
+    }
+    return true;
+  }
+  std::unique_ptr<ChromeLabsViewController> CreateViewController() {
+    std::unique_ptr<ChromeLabsViewController> view_controller =
+        std::make_unique<ChromeLabsViewController>(
+            chrome_labs_model(), chrome_labs_bubble(),
+            browser_view()->browser(), flags_state(), flags_storage_.get());
+    return view_controller;
+  }
+
+  flags_ui::PrefServiceFlagsStorage* GetFlagsStorage() {
+    return flags_storage_.get();
+  }
+
+ protected:
+  ScopedChromeLabsModelDataForTesting scoped_chrome_labs_model_data_;
+  ChromeLabsBubbleView* bubble_view_;
+  views::Widget* bubble_widget_;
+
+ private:
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  ash::FakeChromeUserManager* user_manager_;
+  user_manager::ScopedUserManager user_manager_enabler_;
+#endif
+
+  about_flags::testing::ScopedFeatureEntries scoped_feature_entries_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+  std::unique_ptr<flags_ui::PrefServiceFlagsStorage> flags_storage_;
+};
+
+class ChromeLabsFeatureTest : public ChromeLabsViewControllerTest,
+                              public testing::WithParamInterface<int> {
+ public:
+  ChromeLabsFeatureTest() = default;
+};
+
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+// This test checks that selecting an option through the combobox on a lab will
+// enable the corresponding option on the feature.
+TEST_P(ChromeLabsFeatureTest, ChangeSelectedOption) {
+  int row = GetParam();
+  std::unique_ptr<ChromeLabsViewController> view_controller =
+      CreateViewController();
+
+  // FeatureEntry of type FEATURE_VALUE
+  ChromeLabsItemView* lab_item = first_lab_item();
+  views::Combobox* lab_item_combobox =
+      lab_item->GetLabStateComboboxForTesting();
+
+  lab_item_combobox->SetSelectedRow(row);
+  const flags_ui::FeatureEntry* feature_entry = lab_item->GetFeatureEntry();
+  EXPECT_TRUE(IsSelected(row, feature_entry, GetFlagsStorage()));
+
+  // FeatureEntry of type FEATURE_WITH_PARAMS_VALUE
+  ChromeLabsItemView* lab_item_with_params = second_lab_item();
+  views::Combobox* lab_item_with_params_combobox =
+      lab_item_with_params->GetLabStateComboboxForTesting();
+  lab_item_with_params_combobox->SetSelectedRow(row);
+
+  const flags_ui::FeatureEntry* feature_entry_with_params =
+      lab_item_with_params->GetFeatureEntry();
+  EXPECT_TRUE(IsSelected(row, feature_entry_with_params, GetFlagsStorage()));
+}
+
+// For FeatureEntries of type FEATURE_VALUE, the option at index 1 corresponds
+// to "Enabled" and the option at index 2 corresponds to "Disabled". For
+// FeatureEntries of type FEATURE_WITH_PARAMS_VALUE, the option at index 1
+// corresponds to "Enabled" and the option at index 2 corresponds to the first
+// additional parameter.
+INSTANTIATE_TEST_SUITE_P(All, ChromeLabsFeatureTest, testing::Values(1, 2));
+
+// This test checks that selecting row 0 will reset the feature to it's Default
+// state.
+TEST_F(ChromeLabsViewControllerTest, ResetToDefault) {
+  std::unique_ptr<ChromeLabsViewController> view_controller =
+      CreateViewController();
+
+  ChromeLabsItemView* lab_item = first_lab_item();
+  views::Combobox* lab_item_combobox =
+      lab_item->GetLabStateComboboxForTesting();
+
+  // Selects an option and then attempts to reset the lab to Default by
+  // selecting 0.
+  const flags_ui::FeatureEntry* feature_entry = lab_item->GetFeatureEntry();
+  lab_item_combobox->SetSelectedRow(1);
+  EXPECT_FALSE(IsDefault(feature_entry, GetFlagsStorage()));
+  lab_item_combobox->SetSelectedRow(0);
+  EXPECT_TRUE(IsDefault(feature_entry, GetFlagsStorage()));
+}
+
+#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+
+// Ash versions of the above tests.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+
+namespace ash {
+
+class ChromeLabsAshFeatureTest : public ChromeLabsFeatureTest {
+ public:
+  ChromeLabsAshFeatureTest()
+      : ChromeLabsFeatureTest(),
+        user_manager_(new FakeChromeUserManager()),
+        user_manager_enabler_(base::WrapUnique(user_manager_)) {
+    SessionManagerClient::InitializeFakeInMemory();
+    FakeSessionManagerClient::Get()->set_supports_browser_restart(true);
+    const AccountId account_id(
+        AccountId::FromUserEmailGaiaId(kFakeUserName, kFakeGaiaId));
+    user_manager_->AddUser(account_id);
+    user_manager_->LoginUser(account_id);
+  }
+
+ private:
+  FakeChromeUserManager* user_manager_;
+  user_manager::ScopedUserManager user_manager_enabler_;
+};
+
+TEST_P(ChromeLabsAshFeatureTest, ChangeSelectedOption) {
+  int row = GetParam();
+  std::unique_ptr<ChromeLabsViewController> view_controller =
+      CreateViewController();
+
+  // FeatureEntry of type FEATURE_VALUE
+  ChromeLabsItemView* lab_item = first_lab_item();
+  views::Combobox* lab_item_combobox =
+      lab_item->GetLabStateComboboxForTesting();
+
+  lab_item_combobox->SetSelectedRow(row);
+
+  const flags_ui::FeatureEntry* feature_entry = lab_item->GetFeatureEntry();
+
+  EXPECT_TRUE(IsSelected(row, feature_entry, GetFlagsStorage()));
+
+  // FeatureEntry of type FEATURE_WITH_PARAMS_VALUE
+  ChromeLabsItemView* lab_item_with_params = second_lab_item();
+  views::Combobox* lab_item_with_params_combobox =
+      lab_item_with_params->GetLabStateComboboxForTesting();
+  lab_item_with_params_combobox->SetSelectedRow(row);
+
+  const flags_ui::FeatureEntry* feature_entry_with_params =
+      lab_item_with_params->GetFeatureEntry();
+  EXPECT_TRUE(IsSelected(row, feature_entry_with_params, GetFlagsStorage()));
+
+  // Make sure flags have been set since ChromeOS should apply flags through
+  // the session manager.
+  AccountId user_id =
+      user_manager::UserManager::Get()->GetActiveUser()->GetAccountId();
+  std::vector<std::string> raw_flags;
+  FakeSessionManagerClient* session_manager = FakeSessionManagerClient::Get();
+  view_controller->RestartToApplyFlagsForTesting();
+  const bool has_user_flags = session_manager->GetFlagsForUser(
+      cryptohome::CreateAccountIdentifierFromAccountId(user_id), &raw_flags);
+  EXPECT_TRUE(has_user_flags);
+}
+
+INSTANTIATE_TEST_SUITE_P(All, ChromeLabsAshFeatureTest, testing::Values(1, 2));
+
+TEST_F(ChromeLabsViewControllerTest, ResetToDefault) {
+  std::unique_ptr<ChromeLabsViewController> view_controller =
+      CreateViewController();
+
+  ChromeLabsItemView* lab_item = first_lab_item();
+  views::Combobox* lab_item_combobox =
+      lab_item->GetLabStateComboboxForTesting();
+
+  // Selects an option and then attempts to reset the lab to Default by
+  // selecting 0.
+  const flags_ui::FeatureEntry* feature_entry = lab_item->GetFeatureEntry();
+  lab_item_combobox->SetSelectedRow(1);
+
+  EXPECT_FALSE(IsDefault(feature_entry, GetFlagsStorage()));
+  lab_item_combobox->SetSelectedRow(0);
+  EXPECT_TRUE(IsDefault(feature_entry, GetFlagsStorage()));
+}
+
+}  // namespace ash
+
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+// This test checks that only the two features that are supported on the current
+// platform and do not have expired flags are added to the bubble.
+TEST_F(ChromeLabsViewControllerTest, OnlyCompatibleFeaturesShow) {
+  std::unique_ptr<ChromeLabsViewController> view_controller =
+      CreateViewController();
+  EXPECT_TRUE(chrome_labs_menu_item_container()->children().size() == 2);
+}
+
+// This test checks that the restart prompt becomes visible when a lab state is
+// changed.
+TEST_F(ChromeLabsViewControllerTest, RestartPromptShows) {
+  std::unique_ptr<ChromeLabsViewController> view_controller =
+      CreateViewController();
+  ChromeLabsBubbleView* bubble_view = chrome_labs_bubble();
+  ChromeLabsItemView* lab_item = first_lab_item();
+  views::Combobox* lab_item_combobox =
+      lab_item->GetLabStateComboboxForTesting();
+  EXPECT_FALSE(bubble_view->IsRestartPromptVisibleForTesting());
+  lab_item_combobox->SetSelectedRow(1);
+  EXPECT_TRUE(bubble_view->IsRestartPromptVisibleForTesting());
+  // Check that restart information has been propagated to flags state.
+  EXPECT_TRUE(about_flags::IsRestartNeededToCommitChanges());
+}
+
+// This test checks that the restart prompt does not show when the lab state has
+// not changed.
+// TODO(elainechien): This currently only works for default. This will be
+// changed to work for all states. See design doc in crbug/1145666.
+TEST_F(ChromeLabsViewControllerTest, SelectDefaultTwiceNoRestart) {
+  std::unique_ptr<ChromeLabsViewController> view_controller =
+      CreateViewController();
+  ChromeLabsBubbleView* bubble_view = chrome_labs_bubble();
+  ChromeLabsItemView* lab_item = first_lab_item();
+  views::Combobox* lab_item_combobox =
+      lab_item->GetLabStateComboboxForTesting();
+  // Select default state when the originally instantiated state was already
+  // default.
+  lab_item_combobox->SetSelectedRow(0);
+  EXPECT_FALSE(bubble_view->IsRestartPromptVisibleForTesting());
+}
+
+// TODO(crbug.com/1128855)
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
+TEST_F(ChromeLabsViewControllerTest, ShowFeedbackPage) {
+  std::unique_ptr<ChromeLabsViewController> view_controller =
+      CreateViewController();
+  // TODO(b/185480535): Fix the test for WebUIFeedback
+  if (base::FeatureList::IsEnabled(features::kWebUIFeedback))
+    GTEST_SKIP() << "Skipped due to crash with webui feedback.";
+
+  base::HistogramTester histogram_tester;
+
+  views::MdTextButton* feedback_button =
+      first_lab_item()->GetFeedbackButtonForTesting();
+  ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                   ui::EventTimeForNow(), 0, 0);
+  views::test::ButtonTestApi test_api(feedback_button);
+  test_api.NotifyClick(e);
+
+  histogram_tester.ExpectTotalCount("Feedback.RequestSource", 1);
+}
+#endif
+
+// This test checks that experiments that are removed from the model will be
+// removed from the PrefService when updating new badge prefs.
+TEST_F(ChromeLabsViewControllerTest, CleanUpNewBadgePrefsTest) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  const base::Value* new_badge_prefs =
+      browser_view()->browser()->profile()->GetPrefs()->GetDictionary(
+          chrome_labs_prefs::kChromeLabsNewBadgeDictAshChrome);
+#else
+  const base::Value* new_badge_prefs =
+      g_browser_process->local_state()->GetDictionary(
+          chrome_labs_prefs::kChromeLabsNewBadgeDict);
+#endif
+
+  EXPECT_TRUE(new_badge_prefs->FindKey(kFirstTestFeatureId));
+  EXPECT_TRUE(new_badge_prefs->FindKey(kTestFeatureWithVariationId));
+
+  // Remove two experiments.
+  std::vector<LabInfo> test_experiments = TestLabInfo();
+  base::EraseIf(test_experiments, [](const auto& lab) {
+    return lab.internal_name == kFirstTestFeatureId;
+  });
+  base::EraseIf(test_experiments, [](const auto& lab) {
+    return lab.internal_name == kTestFeatureWithVariationId;
+  });
+
+  scoped_chrome_labs_model_data_.SetModelDataForTesting(test_experiments);
+
+  UpdateChromeLabsNewBadgePrefs(browser_view()->browser()->profile(),
+                                chrome_labs_model());
+  EXPECT_FALSE(new_badge_prefs->FindKey(kFirstTestFeatureId));
+  EXPECT_FALSE(new_badge_prefs->FindKey(kTestFeatureWithVariationId));
+}
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_utils.cc b/chrome/browser/ui/views/toolbar/chrome_labs_utils.cc
index c03b885..e5b619c 100644
--- a/chrome/browser/ui/views/toolbar/chrome_labs_utils.cc
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_utils.cc
@@ -50,11 +50,12 @@
 void UpdateChromeLabsNewBadgePrefs(Profile* profile,
                                    const ChromeLabsBubbleViewModel* model) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  DictionaryPrefUpdate update(
+  DictionaryPrefUpdateDeprecated update(
       profile->GetPrefs(), chrome_labs_prefs::kChromeLabsNewBadgeDictAshChrome);
 #else
-  DictionaryPrefUpdate update(g_browser_process->local_state(),
-                              chrome_labs_prefs::kChromeLabsNewBadgeDict);
+  DictionaryPrefUpdateDeprecated update(
+      g_browser_process->local_state(),
+      chrome_labs_prefs::kChromeLabsNewBadgeDict);
 #endif
 
   base::Value* new_badge_prefs = update.Get();
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_view_controller.cc b/chrome/browser/ui/views/toolbar/chrome_labs_view_controller.cc
new file mode 100644
index 0000000..b70ff67
--- /dev/null
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_view_controller.cc
@@ -0,0 +1,218 @@
+// 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/toolbar/chrome_labs_view_controller.h"
+
+#include "base/callback_list.h"
+#include "base/metrics/histogram_functions.h"
+#include "chrome/browser/about_flags.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/flag_descriptions.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/toolbar/chrome_labs_prefs.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_bubble_view_model.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_item_view.h"
+#include "chrome/browser/ui/views/toolbar/chrome_labs_utils.h"
+#include "components/flags_ui/feature_entry.h"
+#include "components/flags_ui/flags_state.h"
+#include "components/flags_ui/flags_storage.h"
+#include "components/prefs/scoped_user_pref_update.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/settings/about_flags.h"
+#endif
+
+namespace {
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class ChromeLabsSelectedLab {
+  kUnspecifiedSelected = 0,
+  // kReadLaterSelected = 1,
+  // kTabSearchSelected = 2,
+  kTabScrollingSelected = 3,
+  kSidePanelSelected = 4,
+  kLensRegionSearchSelected = 5,
+  kWebUITabStripSelected = 6,
+  kMaxValue = kWebUITabStripSelected,
+};
+
+void EmitToHistogram(const std::u16string& selected_lab_state,
+                     const std::string& internal_name) {
+  const auto get_histogram_name = [](const std::u16string& selected_lab_state) {
+    if (selected_lab_state == base::ASCIIToUTF16(base::StringPiece(
+                                  flags_ui::kGenericExperimentChoiceDefault))) {
+      return "Toolbar.ChromeLabs.DefaultLabAction";
+    } else if (selected_lab_state ==
+               base::ASCIIToUTF16(base::StringPiece(
+                   flags_ui::kGenericExperimentChoiceEnabled))) {
+      return "Toolbar.ChromeLabs.EnableLabAction";
+    } else if (selected_lab_state ==
+               base::ASCIIToUTF16(base::StringPiece(
+                   flags_ui::kGenericExperimentChoiceDisabled))) {
+      return "Toolbar.ChromeLabs.DisableLabAction";
+    } else {
+      return "";
+    }
+  };
+
+  const auto get_enum = [](const std::string& internal_name) {
+    if (internal_name == flag_descriptions::kScrollableTabStripFlagId) {
+      return ChromeLabsSelectedLab::kTabScrollingSelected;
+    } else if (internal_name == flag_descriptions::kSidePanelFlagId) {
+      return ChromeLabsSelectedLab::kSidePanelSelected;
+    } else if (internal_name ==
+               flag_descriptions::kEnableLensRegionSearchFlagId) {
+      return ChromeLabsSelectedLab::kLensRegionSearchSelected;
+#if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP) && \
+    (defined(OS_WIN) || BUILDFLAG(IS_CHROMEOS_ASH))
+    } else if (internal_name == flag_descriptions::kWebUITabStripFlagId) {
+      return ChromeLabsSelectedLab::kWebUITabStripSelected;
+#endif
+    } else {
+      return ChromeLabsSelectedLab::kUnspecifiedSelected;
+    }
+  };
+
+  const std::string histogram_name = get_histogram_name(selected_lab_state);
+  if (!histogram_name.empty())
+    base::UmaHistogramEnumeration(histogram_name, get_enum(internal_name));
+}
+
+// Returns the number of days since epoch (1970-01-01) in the local timezone.
+uint32_t GetCurrentDay() {
+  base::TimeDelta delta = base::Time::Now() - base::Time::UnixEpoch();
+  return base::saturated_cast<uint32_t>(delta.InDays());
+}
+
+}  // namespace
+
+ChromeLabsViewController::ChromeLabsViewController(
+    const ChromeLabsBubbleViewModel* model,
+    ChromeLabsBubbleView* chrome_labs_bubble_view,
+    Browser* browser,
+    flags_ui::FlagsState* flags_state,
+    flags_ui::FlagsStorage* flags_storage)
+    : model_(model),
+      chrome_labs_bubble_view_(chrome_labs_bubble_view),
+      browser_(browser),
+      flags_state_(flags_state),
+      flags_storage_(flags_storage) {
+  ParseModelDataAndAddLabs();
+  SetRestartCallback();
+}
+
+int ChromeLabsViewController::GetIndexOfEnabledLabState(
+    const flags_ui::FeatureEntry* entry,
+    flags_ui::FlagsState* flags_state,
+    flags_ui::FlagsStorage* flags_storage) {
+  std::set<std::string> enabled_entries;
+  flags_state->GetSanitizedEnabledFlags(flags_storage, &enabled_entries);
+  for (int i = 0; i < entry->NumOptions(); i++) {
+    const std::string name = entry->NameForOption(i);
+    if (enabled_entries.count(name) > 0)
+      return i;
+  }
+  return 0;
+}
+
+void ChromeLabsViewController::ParseModelDataAndAddLabs() {
+  // Create each lab item.
+  const std::vector<LabInfo>& all_labs = model_->GetLabInfo();
+  for (const auto& lab : all_labs) {
+    const flags_ui::FeatureEntry* entry =
+        flags_state_->FindFeatureEntryByName(lab.internal_name);
+    if (IsChromeLabsFeatureValid(lab, browser_->profile())) {
+      bool valid_entry_type =
+          entry->type == flags_ui::FeatureEntry::FEATURE_VALUE ||
+          entry->type == flags_ui::FeatureEntry::FEATURE_WITH_PARAMS_VALUE;
+      DCHECK(valid_entry_type);
+      int default_index =
+          GetIndexOfEnabledLabState(entry, flags_state_, flags_storage_);
+      ChromeLabsItemView* lab_item = chrome_labs_bubble_view_->AddLabItem(
+          lab, default_index, entry, browser_,
+          base::BindRepeating(
+              [](ChromeLabsBubbleView* bubble_view, std::string internal_name,
+                 flags_ui::FlagsStorage* flags_storage,
+                 ChromeLabsItemView* item_view) {
+                int selected_index = item_view->GetSelectedIndex();
+                about_flags::SetFeatureEntryEnabled(
+                    flags_storage,
+                    internal_name + flags_ui::kMultiSeparatorChar +
+                        base::NumberToString(selected_index),
+                    true);
+
+                bubble_view->ShowRelaunchPrompt();
+                EmitToHistogram(
+                    item_view->GetFeatureEntry()->DescriptionForOption(
+                        selected_index),
+                    internal_name);
+              },
+              chrome_labs_bubble_view_.get(), lab.internal_name,
+              flags_storage_));
+      if (ShouldLabShowNewBadge(browser_->profile(), lab)) {
+        lab_item->ShowNewBadge();
+      }
+    }
+  }
+}
+
+void ChromeLabsViewController::RestartToApplyFlags() {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // On Chrome OS be less intrusive and restart inside the user session after
+  // we apply the newly selected flags.
+  VLOG(1) << "Restarting to apply per-session flags...";
+  ash::about_flags::FeatureFlagsUpdate(
+      *flags_storage_, browser_->profile()->GetOriginalProfile()->GetPrefs())
+      .UpdateSessionManager();
+#endif
+  chrome::AttemptRestart();
+}
+
+void ChromeLabsViewController::SetRestartCallback() {
+  restart_callback_ = chrome_labs_bubble_view_->RegisterRestartCallback(
+      base::BindRepeating(&ChromeLabsViewController::RestartToApplyFlags,
+                          base::Unretained(this)));
+}
+
+bool ChromeLabsViewController::ShouldLabShowNewBadge(Profile* profile,
+                                                     const LabInfo& lab) {
+  // This experiment was added before adding the new badge and is not new.
+  if (lab.internal_name == flag_descriptions::kScrollableTabStripFlagId) {
+    return false;
+  }
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  DictionaryPrefUpdateDeprecated update(
+      profile->GetPrefs(), chrome_labs_prefs::kChromeLabsNewBadgeDictAshChrome);
+#else
+  DictionaryPrefUpdateDeprecated update(
+      g_browser_process->local_state(),
+      chrome_labs_prefs::kChromeLabsNewBadgeDict);
+#endif
+
+  base::DictionaryValue* new_badge_prefs = update.Get();
+
+  DCHECK(new_badge_prefs->FindIntKey(lab.internal_name));
+  int start_day = *new_badge_prefs->FindIntKey(lab.internal_name);
+  if (start_day == chrome_labs_prefs::kChromeLabsNewExperimentPrefValue) {
+    // Set the dictionary value of this experiment to the number of days since
+    // epoch (1970-01-01). This value is the first day the user sees the new
+    // experiment in Chrome Labs and will be used to determine whether or not to
+    // show the new badge.
+    new_badge_prefs->SetInteger(lab.internal_name, GetCurrentDay());
+    return true;
+  }
+  int days_elapsed = GetCurrentDay() - start_day;
+  // Show the new badge for 7 days. If the users sets the clock such that the
+  // current day is now before |start_day| don’t show the new badge.
+  return (days_elapsed < 7) && (days_elapsed >= 0);
+}
+
+void ChromeLabsViewController::RestartToApplyFlagsForTesting() {
+  RestartToApplyFlags();
+}
diff --git a/chrome/browser/ui/views/toolbar/chrome_labs_view_controller.h b/chrome/browser/ui/views/toolbar/chrome_labs_view_controller.h
new file mode 100644
index 0000000..3149f63
--- /dev/null
+++ b/chrome/browser/ui/views/toolbar/chrome_labs_view_controller.h
@@ -0,0 +1,55 @@
+// 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_TOOLBAR_CHROME_LABS_VIEW_CONTROLLER_H_
+#define CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_VIEW_CONTROLLER_H_
+
+#include "base/callback_list.h"
+#include "base/memory/raw_ptr.h"
+
+class Browser;
+class ChromeLabsBubbleViewModel;
+class ChromeLabsBubbleView;
+struct LabInfo;
+class Profile;
+
+namespace flags_ui {
+class FlagsState;
+class FlagsStorage;
+struct FeatureEntry;
+}  // namespace flags_ui
+
+class ChromeLabsViewController {
+ public:
+  ChromeLabsViewController(const ChromeLabsBubbleViewModel* model,
+                           ChromeLabsBubbleView* chrome_labs_bubble_view,
+                           Browser* browser,
+                           flags_ui::FlagsState* flags_state,
+                           flags_ui::FlagsStorage* flags_storage);
+  ~ChromeLabsViewController() = default;
+
+  void RestartToApplyFlagsForTesting();
+
+ private:
+  int GetIndexOfEnabledLabState(const flags_ui::FeatureEntry* entry,
+                                flags_ui::FlagsState* flags_state,
+                                flags_ui::FlagsStorage* flags_storage);
+
+  void ParseModelDataAndAddLabs();
+
+  void RestartToApplyFlags();
+
+  void SetRestartCallback();
+
+  bool ShouldLabShowNewBadge(Profile* profile, const LabInfo& lab);
+
+  raw_ptr<const ChromeLabsBubbleViewModel> model_;
+  raw_ptr<ChromeLabsBubbleView> chrome_labs_bubble_view_;
+  base::CallbackListSubscription restart_callback_;
+  raw_ptr<Browser> browser_;
+  raw_ptr<flags_ui::FlagsState> flags_state_;
+  raw_ptr<flags_ui::FlagsStorage> flags_storage_;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_TOOLBAR_CHROME_LABS_VIEW_CONTROLLER_H_
diff --git a/chrome/browser/ui/views/user_education/browser_tutorial_service_factory.cc b/chrome/browser/ui/views/user_education/browser_tutorial_service_factory.cc
index f07e883..ade171d 100644
--- a/chrome/browser/ui/views/user_education/browser_tutorial_service_factory.cc
+++ b/chrome/browser/ui/views/user_education/browser_tutorial_service_factory.cc
@@ -96,7 +96,7 @@
         u"Right Click on a Tab and select \"Add Tab To new Group\".",
         ui::InteractionSequence::StepType::kShown, kTabStripElementId,
         std::string(), TutorialDescription::Step::Arrow::TOP, absl::nullopt);
-    description.steps.emplace_back(step1);
+    description.steps.emplace_back(std::move(step1));
 
     TutorialDescription::Step step2(
         absl::nullopt, u"Select \"Enter a name for your Tab Group\".",
@@ -120,6 +120,7 @@
         TutorialDescription::Step::Arrow::TOP, absl::nullopt);
     description.steps.emplace_back(std::move(step4));
 
-    tutorial_registry->AddTutorial("Tab Group Tutorial", description);
+    tutorial_registry->AddTutorial("Tab Group Tutorial",
+                                   std::move(description));
   }
 }
diff --git a/chrome/browser/ui/views/user_education/feature_promo_controller_views_unittest.cc b/chrome/browser/ui/views/user_education/feature_promo_controller_views_unittest.cc
index ba3099fe..7b2e351 100644
--- a/chrome/browser/ui/views/user_education/feature_promo_controller_views_unittest.cc
+++ b/chrome/browser/ui/views/user_education/feature_promo_controller_views_unittest.cc
@@ -538,7 +538,7 @@
   desc.steps.emplace_back(std::move(step2));
 
   TutorialServiceManager::GetInstance()->tutorial_registry()->AddTutorial(
-      kTestTutorialIdentifier, desc);
+      kTestTutorialIdentifier, std::move(desc));
 
   // Launch a feature promo that has a tutorial.
   EXPECT_CALL(*mock_tracker_, ShouldTriggerHelpUI(Ref(kTestIPHFeature)))
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
index 007fe39..a31c010 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "build/build_config.h"
-#include "build/buildflag.h"
 #include "chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
 #include "content/public/test/browser_test.h"
 
 namespace web_app {
@@ -25,6 +25,20 @@
   helper_.CheckLaunchIconShown();
 }
 
+IN_PROC_BROWSER_TEST_F(WebAppIntegrationBrowserTest, VerifyWindowModeChanged) {
+  helper_.InstallCreateShortcutWindowed("SiteA");
+  helper_.CheckAppWindowMode("SiteA", apps::mojom::WindowMode::kWindow);
+  // Change to tabbed mode
+  helper_.ChangeAppSettingsWindowMode("SiteA",
+                                      apps::mojom::WindowMode::kTabbedWindow);
+#if defined(OS_CHROMEOS)
+  helper_.CheckAppWindowMode("SiteA", apps::mojom::WindowMode::kWindow);
+#else
+  // Verify change is propagated in W/M/L cases
+  helper_.CheckAppWindowMode("SiteA", apps::mojom::WindowMode::kTabbedWindow);
+#endif
+}
+
 // Automated tests:
 
 // TODO(crbug.com/1279704): Test is consistently failing on Mac and Win7.
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
index 50ec7be..a55052f 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
@@ -23,6 +23,7 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/banners/test_app_banner_manager_desktop.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -42,6 +43,9 @@
 #include "chrome/browser/ui/web_applications/web_app_dialog_utils.h"
 #include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
 #include "chrome/browser/ui/web_applications/web_app_menu_model.h"
+#include "chrome/browser/ui/webui/app_management/app_management_page_handler.h"
+#include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
+#include "chrome/browser/web_applications/app_service/web_app_publisher_helper.h"
 #include "chrome/browser/web_applications/manifest_update_manager.h"
 #include "chrome/browser/web_applications/policy/web_app_policy_constants.h"
 #include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
@@ -58,6 +62,7 @@
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/prefs/scoped_user_pref_update.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -74,6 +79,7 @@
 #include "third_party/re2/src/re2/re2.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/widget/any_widget_observer.h"
+#include "ui/webui/resources/cr_components/app_management/app_management.mojom-forward.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
@@ -292,6 +298,7 @@
 AppState::AppState(web_app::AppId app_id,
                    const std::string app_name,
                    const GURL app_scope,
+                   const apps::mojom::WindowMode& window_mode,
                    const blink::mojom::DisplayMode& effective_display_mode,
                    const blink::mojom::DisplayMode& user_display_mode,
                    bool installed_locally,
@@ -299,6 +306,7 @@
     : id(app_id),
       name(app_name),
       scope(app_scope),
+      window_mode(window_mode),
       effective_display_mode(effective_display_mode),
       user_display_mode(user_display_mode),
       is_installed_locally(installed_locally),
@@ -307,6 +315,7 @@
 AppState::AppState(const AppState&) = default;
 bool AppState::operator==(const AppState& other) const {
   return id == other.id && name == other.name && scope == other.scope &&
+         window_mode == other.window_mode &&
          effective_display_mode == other.effective_display_mode &&
          user_display_mode == other.user_display_mode &&
          is_installed_locally == other.is_installed_locally &&
@@ -457,6 +466,25 @@
 #endif
 }
 
+void WebAppIntegrationTestDriver::ChangeAppSettingsWindowMode(
+    const std::string& site_mode,
+    apps::mojom::WindowMode window_mode) {
+#if !defined(OS_CHROMEOS)
+  BeforeStateChangeAction();
+  absl::optional<AppState> app_state = GetAppBySiteMode(
+      before_state_change_action_state_.get(), profile(), site_mode);
+  ASSERT_TRUE(app_state.has_value())
+      << "No app installed for site: " << site_mode;
+  mojo::PendingReceiver<app_management::mojom::Page> page;
+  mojo::Remote<app_management::mojom::PageHandler> handler;
+  AppManagementPageHandler app_management_page_handler(
+      handler.BindNewPipeAndPassReceiver(), page.InitWithNewPipeAndPassRemote(),
+      profile());
+  app_management_page_handler.SetWindowMode(app_state->id, window_mode);
+  AfterStateChangeAction();
+#endif
+}
+
 void WebAppIntegrationTestDriver::CloseCustomToolbar() {
   BeforeStateChangeAction();
   ASSERT_TRUE(app_browser());
@@ -735,9 +763,20 @@
   ASSERT_TRUE(app_state.has_value())
       << "No app installed for site: " << site_mode;
   auto app_id = app_state->id;
+  // Will need to add feature flag based condition for web app settings page
+#if defined(OS_CHROMEOS)
   auto& sync_bridge = WebAppProvider::GetForTest(profile())->sync_bridge();
   sync_bridge.SetAppUserDisplayMode(app_id, blink::mojom::DisplayMode::kBrowser,
                                     true);
+#else
+  mojo::PendingReceiver<app_management::mojom::Page> page;
+  mojo::Remote<app_management::mojom::PageHandler> handler;
+  AppManagementPageHandler app_management_page_handler(
+      handler.BindNewPipeAndPassReceiver(), page.InitWithNewPipeAndPassRemote(),
+      profile());
+  app_management_page_handler.SetWindowMode(app_id,
+                                            apps::mojom::WindowMode::kBrowser);
+#endif
   AfterStateChangeAction();
 }
 
@@ -749,9 +788,20 @@
   ASSERT_TRUE(app_state.has_value())
       << "No app installed for site: " << site_mode;
   auto app_id = app_state->id;
+  // Will need to add feature flag based condition for web app settings page.
+#if defined(OS_CHROMEOS)
   auto& sync_bridge = WebAppProvider::GetForTest(profile())->sync_bridge();
   sync_bridge.SetAppUserDisplayMode(
       app_id, blink::mojom::DisplayMode::kStandalone, true);
+#else
+  mojo::PendingReceiver<app_management::mojom::Page> page;
+  mojo::Remote<app_management::mojom::PageHandler> handler;
+  AppManagementPageHandler app_management_page_handler(
+      handler.BindNewPipeAndPassReceiver(), page.InitWithNewPipeAndPassRemote(),
+      profile());
+  app_management_page_handler.SetWindowMode(app_id,
+                                            apps::mojom::WindowMode::kWindow);
+#endif
   AfterStateChangeAction();
 }
 
@@ -892,8 +942,8 @@
           run_loop.Quit();
       }));
   {
-    ListPrefUpdate update(profile()->GetPrefs(),
-                          prefs::kWebAppInstallForceList);
+    ListPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                    prefs::kWebAppInstallForceList);
     size_t removed_count =
         update->EraseListValueIf([&](const base::Value& item) {
           const base::Value* url_value = item.FindKey(kUrlKey);
@@ -992,6 +1042,17 @@
   AfterStateCheckAction();
 }
 
+void WebAppIntegrationTestDriver::CheckAppWindowMode(
+    const std::string& site_mode,
+    apps::mojom::WindowMode window_mode) {
+  BeforeStateCheckAction();
+  absl::optional<AppState> app_state = GetAppBySiteMode(
+      after_state_change_action_state_.get(), profile(), site_mode);
+  ASSERT_TRUE(app_state);
+  EXPECT_EQ(app_state->window_mode, window_mode);
+  AfterStateCheckAction();
+}
+
 void WebAppIntegrationTestDriver::CheckInstallable() {
   BeforeStateCheckAction();
   absl::optional<BrowserState> browser_state = GetStateForBrowser(
@@ -1303,11 +1364,16 @@
     WebAppRegistrar& registrar = GetProviderForProfile(profile)->registrar();
     auto app_ids = registrar.GetAppIds();
     base::flat_map<AppId, AppState> app_state;
+    WebAppPublisherHelper web_app_publisher_helper(profile, provider(),
+                                                   apps::mojom::AppType::kWeb,
+                                                   /*delegate=*/nullptr, true);
     for (const auto& app_id : app_ids) {
       app_state.emplace(
           app_id,
           AppState(app_id, registrar.GetAppShortName(app_id),
                    registrar.GetAppScope(app_id),
+                   web_app_publisher_helper.ConvertDisplayModeToWindowMode(
+                       registrar.GetAppUserDisplayMode(app_id)),
                    registrar.GetAppEffectiveDisplayMode(app_id),
                    registrar.GetAppUserDisplayMode(app_id),
                    registrar.IsLocallyInstalled(app_id),
@@ -1369,8 +1435,8 @@
     item.SetKey(kDefaultLaunchContainerKey,
                 std::move(default_launch_container));
     item.SetKey(kCreateDesktopShortcutKey, base::Value(create_shortcut));
-    ListPrefUpdate update(profile()->GetPrefs(),
-                          prefs::kWebAppInstallForceList);
+    ListPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                    prefs::kWebAppInstallForceList);
     update->Append(item.Clone());
   }
   active_app_id_ = observer.Wait();
@@ -1394,8 +1460,8 @@
       }));
   std::string url_spec = provider()->registrar().GetAppStartUrl(id).spec();
   {
-    ListPrefUpdate update(profile()->GetPrefs(),
-                          prefs::kWebAppInstallForceList);
+    ListPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                    prefs::kWebAppInstallForceList);
     size_t removed_count =
         update->EraseListValueIf([&](const base::Value& item) {
           const base::Value* url_value = item.FindKey(kUrlKey);
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
index 7aa15ea..9f1a2d3 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
@@ -20,6 +20,7 @@
 #include "chrome/browser/web_applications/web_app_id.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
@@ -70,6 +71,7 @@
   AppState(AppId app_id,
            const std::string app_name,
            const GURL app_scope,
+           const apps::mojom::WindowMode& window_mode,
            const blink::mojom::DisplayMode& effective_display_mode,
            const blink::mojom::DisplayMode& user_display_mode,
            bool is_installed_locally,
@@ -81,6 +83,7 @@
   AppId id;
   std::string name;
   GURL scope;
+  apps::mojom::WindowMode window_mode;
   blink::mojom::DisplayMode effective_display_mode;
   blink::mojom::DisplayMode user_display_mode;
   bool is_installed_locally;
@@ -140,6 +143,8 @@
   // https://docs.google.com/spreadsheets/d/1d3iAOAnojp4_WrPky9exz1-mjkeulOJVUav5QYG99MQ/edit#gid=2008870403
 
   // State change actions:
+  void ChangeAppSettingsWindowMode(const std::string& site_mode,
+                                   apps::mojom::WindowMode window_mode);
   void CloseCustomToolbar();
   void ClosePwa();
   void InstallCreateShortcutTabbed(const std::string& site_mode);
@@ -177,6 +182,8 @@
   void CheckAppNotInList(const std::string& site_mode);
   void CheckAppShortcutExists(const std::string& site_mode);
   void CheckAppShortcutNotExists(const std::string& site_mode);
+  void CheckAppWindowMode(const std::string& site_mode,
+                          apps::mojom::WindowMode window_mode);
   void CheckInstallable();
   void CheckInstallIconShown();
   void CheckInstallIconNotShown();
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index 6b94a0d..3af50a1 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -312,6 +312,17 @@
   EXPECT_EQ(provider->registrar().GetAppBackgroundColor(app_id), SK_ColorBLUE);
 }
 
+IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, ManifestWithColor) {
+  const GURL app_url =
+      https_server()->GetURL("/banners/no-sw-with-colors.html");
+  const AppId app_id = InstallWebAppFromPage(browser(), app_url);
+  auto* provider = WebAppProvider::GetForTest(profile());
+
+  EXPECT_EQ(provider->registrar().GetAppBackgroundColor(app_id),
+            SK_ColorYELLOW);
+  EXPECT_EQ(provider->registrar().GetAppThemeColor(app_id), SK_ColorGREEN);
+}
+
 // Base class for background color change browser tests parameterized by whether
 // to use a SWA or a non-SWA.
 class WebAppBackgroundColorChangeBrowserTest
@@ -1851,8 +1862,8 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-// TODO(crbug.com/1270961): Flaky.
-#if defined(OS_WIN) || (defined(OS_MAC) && defined(ARCH_CPU_ARM64))
+// TODO(crbug.com/1270961): Flaky on Win and Mac.
+#if defined(OS_WIN) || defined(OS_MAC)
 #define MAYBE_WebAppFileHandler DISABLED_WebAppFileHandler
 #else
 #define MAYBE_WebAppFileHandler WebAppFileHandler
diff --git a/chrome/browser/ui/webid/identity_dialog_controller.cc b/chrome/browser/ui/webid/identity_dialog_controller.cc
index b91fa3f..ceb6b4f 100644
--- a/chrome/browser/ui/webid/identity_dialog_controller.cc
+++ b/chrome/browser/ui/webid/identity_dialog_controller.cc
@@ -114,7 +114,10 @@
   // IDP scheme is expected to always be `https://`.
   CHECK(idp_url.SchemeIs(url::kHttpsScheme));
 #if !defined(OS_ANDROID)
-  std::move(on_selected).Run(accounts[0].account_id);
+  std::move(on_selected)
+      .Run(accounts[0].account_id,
+           accounts[0].login_state ==
+               content::IdentityRequestAccount::LoginState::kSignIn);
 #else
   rp_web_contents_ = rp_web_contents;
   on_account_selection_ = std::move(on_selected);
@@ -129,14 +132,17 @@
 }
 
 void IdentityDialogController::OnAccountSelected(const Account& account) {
-  std::move(on_account_selection_).Run(account.account_id);
+  std::move(on_account_selection_)
+      .Run(account.account_id,
+           account.login_state ==
+               content::IdentityRequestAccount::LoginState::kSignIn);
 }
 
 void IdentityDialogController::OnDismiss() {
   // |OnDismiss| can be called after |OnAccountSelected| which sets the callback
   // to null.
   if (on_account_selection_)
-    std::move(on_account_selection_).Run(std::string());
+    std::move(on_account_selection_).Run(std::string(), false);
 }
 
 gfx::NativeView IdentityDialogController::GetNativeView() {
diff --git a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.cc b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.cc
index 0a4df544..5955b976 100644
--- a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.cc
+++ b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.cc
@@ -6,18 +6,42 @@
 
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/task/task_runner_util.h"
 #include "chrome/browser/media/router/discovery/access_code/access_code_media_sink_util.h"
+#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
+#include "chrome/browser/media/router/discovery/media_sink_discovery_metrics.h"
+#include "chrome/browser/media/router/providers/cast/dual_media_sink_service.h"
 #include "components/media_router/common/discovery/media_sink_internal.h"
+#include "components/media_router/common/discovery/media_sink_service_base.h"
 
 using ::media_router::CreateAccessCodeMediaSink;
+using SinkSource = ::media_router::CastDeviceCountMetrics::SinkSource;
+
+// TODO(b/213324920): Remove WebUI from the media_router namespace after
+// expiration module has been completed.
+namespace media_router {
 
 AccessCodeCastHandler::AccessCodeCastHandler(
     mojo::PendingReceiver<access_code_cast::mojom::PageHandler> page_handler,
     mojo::PendingRemote<access_code_cast::mojom::Page> page,
     Profile* profile)
+    : AccessCodeCastHandler(std::move(page_handler),
+                            std::move(page),
+                            profile,
+                            media_router::DualMediaSinkService::GetInstance()
+                                ->GetCastMediaSinkServiceImpl()) {
+  DCHECK(profile_);
+}
+
+AccessCodeCastHandler::AccessCodeCastHandler(
+    mojo::PendingReceiver<access_code_cast::mojom::PageHandler> page_handler,
+    mojo::PendingRemote<access_code_cast::mojom::Page> page,
+    Profile* profile,
+    CastMediaSinkServiceImpl* cast_media_sink_service_impl)
     : page_(std::move(page)),
       receiver_(this, std::move(page_handler)),
-      profile_(profile) {
+      profile_(profile),
+      cast_media_sink_service_impl_(cast_media_sink_service_impl) {
   DCHECK(profile_);
 }
 
@@ -56,9 +80,15 @@
     std::move(add_sink_callback_).Run(AddSinkResultCode::SINK_CREATION_ERROR);
     return;
   }
-  std::move(add_sink_callback_)
-      .Run(AccessCodeCastHandler::AddSinkToMediaRouter(
-          creation_result.first.value()));
+  // Check to see if the media sink already exists in the media router.
+  base::PostTaskAndReplyWithResult(
+      cast_media_sink_service_impl_->task_runner().get(), FROM_HERE,
+      base::BindOnce(&CastMediaSinkServiceImpl::HasSink,
+                     base::Unretained(cast_media_sink_service_impl_),
+                     creation_result.first.value().id()),
+      base::BindOnce(&AccessCodeCastHandler::HandleSinkPresentInMediaRouter,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     creation_result.first.value()));
 }
 
 void AccessCodeCastHandler::SetSinkCallbackForTesting(
@@ -66,14 +96,30 @@
   add_sink_callback_ = std::move(callback);
 }
 
-AddSinkResultCode AccessCodeCastHandler::AddSinkToMediaRouter(
-    MediaSinkInternal media_sink) {
-  // TODO (b/201430609): Complete addition to media_router.
-  NOTIMPLEMENTED();
-  return AddSinkResultCode::OK;
+void AccessCodeCastHandler::HandleSinkPresentInMediaRouter(
+    MediaSinkInternal media_sink,
+    bool has_sink) {
+  if (has_sink) {
+    std::move(add_sink_callback_).Run(AddSinkResultCode::OK);
+    return;
+  }
+  AccessCodeCastHandler::AddSinkToMediaRouter(media_sink);
+}
+
+void AccessCodeCastHandler::AddSinkToMediaRouter(MediaSinkInternal media_sink) {
+  // TODO (b/201430609): Complete addition to media_router. Additionally must
+  // add an observer to the cast_media_sink_service_impl that waits for the
+  // actual channel to successfully open. The observer will trigger the
+  // AddSinkCallback stored in the class.
+  cast_media_sink_service_impl_->task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&CastMediaSinkServiceImpl::OpenChannel,
+                                base::Unretained(cast_media_sink_service_impl_),
+                                media_sink, nullptr, SinkSource::kAccessCode));
+  std::move(add_sink_callback_).Run(AddSinkResultCode::OK);
 }
 
 void AccessCodeCastHandler::CastToSink(CastToSinkCallback callback) {
   // TODO (b/204572061): Complete casting implementation.
   NOTIMPLEMENTED();
 }
+}  // namespace media_router
diff --git a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.h b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.h
index 0593a40..b2262a4 100644
--- a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.h
+++ b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.h
@@ -8,6 +8,7 @@
 #include "base/scoped_observation.h"
 #include "chrome/browser/media/router/discovery/access_code/access_code_cast_discovery_interface.h"
 #include "chrome/browser/media/router/discovery/access_code/discovery_resources.pb.h"
+#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
 #include "chrome/browser/media/router/discovery/mdns/media_sink_util.h"
 #include "chrome/browser/ui/app_list/search/search_controller.h"
 #include "chrome/browser/ui/webui/access_code_cast/access_code_cast.mojom.h"
@@ -20,6 +21,10 @@
 using ::media_router::CreateCastMediaSinkResult;
 using ::media_router::MediaSinkInternal;
 
+// TODO(b/213324920): Remove WebUI from the media_router namespace after
+// expiration module has been completed.
+namespace media_router {
+
 class AccessCodeCastHandler : public access_code_cast::mojom::PageHandler {
  public:
   using DiscoveryDevice = chrome_browser_media::proto::DiscoveryDevice;
@@ -28,6 +33,14 @@
       mojo::PendingReceiver<access_code_cast::mojom::PageHandler> page_handler,
       mojo::PendingRemote<access_code_cast::mojom::Page> page,
       Profile* profile);
+
+  // Constructor that is used for testing.
+  AccessCodeCastHandler(
+      mojo::PendingReceiver<access_code_cast::mojom::PageHandler> page_handler,
+      mojo::PendingRemote<access_code_cast::mojom::Page> page,
+      Profile* profile,
+      CastMediaSinkServiceImpl* cast_media_sink_service_impl);
+
   ~AccessCodeCastHandler() override;
 
   // access_code_cast::mojom::PageHandler overrides:
@@ -41,13 +54,18 @@
   void SetSinkCallbackForTesting(AddSinkCallback callback);
 
  private:
+  friend class AccessCodeCastHandlerTest;
   FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest,
                            DiscoveryDeviceMissingWithOk);
   FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest,
                            ValidDiscoveryDeviceAndCode);
   FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest, InvalidDiscoveryDevice);
   FRIEND_TEST_ALL_PREFIXES(AccessCodeCastHandlerTest, NonOKResultCode);
-  AddSinkResultCode AddSinkToMediaRouter(MediaSinkInternal media_sink);
+
+  void AddSinkToMediaRouter(MediaSinkInternal media_sink);
+
+  void HandleSinkPresentInMediaRouter(MediaSinkInternal media_sink,
+                                      bool has_sink);
 
   void OnAccessCodeValidated(
       absl::optional<DiscoveryDevice> discovery_device,
@@ -63,11 +81,15 @@
   const std::string recent_sink_id;
 
   // Used to fetch OAuth2 access tokens.
-  Profile* const profile_;
+  raw_ptr<Profile> const profile_;
 
   AddSinkCallback add_sink_callback_;
 
+  raw_ptr<CastMediaSinkServiceImpl> const cast_media_sink_service_impl_;
+
   base::WeakPtrFactory<AccessCodeCastHandler> weak_ptr_factory_{this};
 };
 
+}  // namespace media_router
+
 #endif  // CHROME_BROWSER_UI_WEBUI_ACCESS_CODE_CAST_ACCESS_CODE_CAST_HANDLER_H_
diff --git a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc
index dd26326..5de2e3f 100644
--- a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc
+++ b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc
@@ -5,13 +5,25 @@
 #include "chrome/browser/ui/webui/access_code_cast/access_code_cast_handler.h"
 
 #include "base/test/bind.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/test/test_simple_task_runner.h"
 #include "base/time/time.h"
 #include "chrome/browser/media/router/discovery/access_code/access_code_test_util.h"
+#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
+#include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_test_helpers.h"
+#include "chrome/browser/media/router/providers/cast/cast_session_tracker.h"
+#include "chrome/browser/media/router/providers/cast/dual_media_sink_service.h"
+#include "chrome/browser/media/router/test/provider_test_helpers.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #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/cast_channel/cast_socket.h"
+#include "components/cast_channel/cast_socket_service.h"
+#include "components/cast_channel/cast_test_util.h"
+#include "components/media_router/common/test/test_helper.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -20,8 +32,13 @@
 using DiscoveryDevice = chrome_browser_media::proto::DiscoveryDevice;
 using access_code_cast::mojom::AddSinkResultCode;
 using MockAddSinkCallback =
-    base::MockCallback<AccessCodeCastHandler::AddSinkCallback>;
+    base::MockCallback<media_router::AccessCodeCastHandler::AddSinkCallback>;
 using ::testing::_;
+using ::testing::InvokeWithoutArgs;
+
+// TODO(b/213324920): Remove WebUI from the media_router namespace after
+// expiration module has been completed.
+namespace media_router {
 
 namespace {
 class MockPage : public access_code_cast::mojom::Page {
@@ -35,19 +52,36 @@
   }
   mojo::Receiver<access_code_cast::mojom::Page> receiver_{this};
 };
+
 }  // namespace
 
 class AccessCodeCastHandlerTest : public testing::Test {
  protected:
   AccessCodeCastHandlerTest()
-      : profile_manager_(TestingBrowserProcess::GetGlobal()) {}
+      : mock_time_task_runner_(new base::TestMockTimeTaskRunner()),
+        mock_cast_socket_service_(
+            new cast_channel::MockCastSocketService(mock_time_task_runner_)),
+        message_handler_(mock_cast_socket_service_.get()),
+        session_tracker_(
+            new CastSessionTracker(&dual_media_sink_service_,
+                                   &message_handler_,
+                                   mock_cast_socket_service_->task_runner())),
+        profile_manager_(TestingBrowserProcess::GetGlobal()),
+        mock_cast_media_sink_service_impl_(
+            new MockCastMediaSinkServiceImpl(mock_sink_discovered_cb_.Get(),
+                                             mock_cast_socket_service_.get(),
+                                             discovery_network_monitor_.get(),
+                                             &dual_media_sink_service_)) {
+    mock_cast_socket_service_->SetTaskRunnerForTest(mock_time_task_runner_);
+  }
 
   void SetUp() override {
     ASSERT_TRUE(profile_manager_.SetUp());
     handler_ = std::make_unique<AccessCodeCastHandler>(
         mojo::PendingReceiver<access_code_cast::mojom::PageHandler>(),
         page_.BindAndGetRemote(),
-        profile_manager()->CreateTestingProfile("foo_email"));
+        profile_manager()->CreateTestingProfile("foo_email"),
+        mock_cast_media_sink_service_impl_.get());
   }
   void TearDown() override { handler_.reset(); }
   AccessCodeCastHandler* handler() { return handler_.get(); }
@@ -55,11 +89,35 @@
   TestingProfileManager* profile_manager() { return &profile_manager_; }
 
  private:
-  std::unique_ptr<AccessCodeCastHandler> handler_;
   // Everything must be called on Chrome_UIThread.
   content::BrowserTaskEnvironment task_environment_;
+
+  scoped_refptr<base::TestMockTimeTaskRunner> mock_time_task_runner_;
+
+  static std::vector<DiscoveryNetworkInfo> GetFakeNetworkInfo() {
+    return {
+        DiscoveryNetworkInfo{std::string("enp0s2"), std::string("ethernet1")}};
+    ;
+  }
+
+  std::unique_ptr<DiscoveryNetworkMonitor> discovery_network_monitor_ =
+      DiscoveryNetworkMonitor::CreateInstanceForTest(&GetFakeNetworkInfo);
+
+  std::unique_ptr<AccessCodeCastHandler> handler_;
+
+  base::MockCallback<OnSinksDiscoveredCallback> mock_sink_discovered_cb_;
+
+  TestMediaSinkService dual_media_sink_service_;
+  std::unique_ptr<cast_channel::MockCastSocketService>
+      mock_cast_socket_service_;
+
+  testing::NiceMock<cast_channel::MockCastMessageHandler> message_handler_;
+  std::unique_ptr<media_router::CastSessionTracker> session_tracker_;
   testing::StrictMock<MockPage> page_;
   TestingProfileManager profile_manager_;
+
+  std::unique_ptr<MockCastMediaSinkServiceImpl>
+      mock_cast_media_sink_service_impl_;
 };
 
 TEST_F(AccessCodeCastHandlerTest, DiscoveryDeviceMissingWithOk) {
@@ -84,6 +142,9 @@
   handler()->SetSinkCallbackForTesting(mock_callback.Get());
   handler()->OnAccessCodeValidated(discovery_device_proto,
                                    AddSinkResultCode::OK);
+
+  MediaSinkInternal cast_sink1 = CreateCastSink(1);
+  handler()->HandleSinkPresentInMediaRouter(cast_sink1, true);
 }
 
 TEST_F(AccessCodeCastHandlerTest, InvalidDiscoveryDevice) {
@@ -111,3 +172,4 @@
   handler()->OnAccessCodeValidated(absl::nullopt,
                                    AddSinkResultCode::AUTH_ERROR);
 }
+}  // namespace media_router
diff --git a/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc b/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc
index 3047403..9523f4e 100644
--- a/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc
+++ b/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.cc
@@ -21,6 +21,8 @@
 #include "content/public/common/bindings_policy.h"
 #include "ui/base/webui/web_ui_util.h"
 
+using media_router::AccessCodeCastHandler;
+
 ///////////////////////////////////////////////////////////////////////////////
 //  AccessCodeCast dialog:
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.h b/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.h
index 97a21cb..7318d8f 100644
--- a/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.h
+++ b/chrome/browser/ui/webui/access_code_cast/access_code_cast_ui.h
@@ -72,7 +72,7 @@
       mojo::PendingReceiver<access_code_cast::mojom::PageHandler> page_handler)
       override;
 
-  std::unique_ptr<AccessCodeCastHandler> page_handler_;
+  std::unique_ptr<media_router::AccessCodeCastHandler> page_handler_;
   mojo::Receiver<access_code_cast::mojom::PageHandlerFactory> factory_receiver_{
       this};
 
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
index b6da902..853d24c 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
@@ -262,6 +262,17 @@
   std::move(callback).Run(std::move(app_ids).extract());
 }
 
+void AppManagementPageHandler::SetWindowMode(
+    const std::string& app_id,
+    apps::mojom::WindowMode window_mode) {
+#if defined(OS_CHROMEOS)
+  NOTREACHED();
+#else
+  apps::AppServiceProxyFactory::GetForProfile(profile_)->SetWindowMode(
+      app_id, std::move(window_mode));
+#endif
+}
+
 app_management::mojom::AppPtr AppManagementPageHandler::CreateUIAppPtr(
     const apps::AppUpdate& update) {
   base::flat_map<apps::mojom::PermissionType, apps::mojom::PermissionPtr>
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.h b/chrome/browser/ui/webui/app_management/app_management_page_handler.h
index 60047d3..d9aa893 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.h
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.h
@@ -53,6 +53,8 @@
   void GetOverlappingPreferredApps(
       const std::string& app_id,
       GetOverlappingPreferredAppsCallback callback) override;
+  void SetWindowMode(const std::string& app_id,
+                     apps::mojom::WindowMode window_mode) override;
 
  private:
   app_management::mojom::AppPtr CreateUIAppPtr(const apps::AppUpdate& update);
diff --git a/chrome/browser/ui/webui/browser_switch/browser_switch_ui.cc b/chrome/browser/ui/webui/browser_switch/browser_switch_ui.cc
index 1c1deea..027389b 100644
--- a/chrome/browser/ui/webui/browser_switch/browser_switch_ui.cc
+++ b/chrome/browser/ui/webui/browser_switch/browser_switch_ui.cc
@@ -65,19 +65,18 @@
 //   "sitelist": ["example.com", ...],
 //   "greylist": ["example.net", ...]
 // }
-std::unique_ptr<base::Value> RuleSetToDict(
-    const browser_switcher::RuleSet& ruleset) {
-  auto sitelist = std::make_unique<base::ListValue>();
+base::Value RuleSetToDict(const browser_switcher::RuleSet& ruleset) {
+  base::Value sitelist(base::Value::Type::LIST);
   for (const auto& rule : ruleset.sitelist)
-    sitelist->Append(rule->ToString());
+    sitelist.Append(rule->ToString());
 
-  auto greylist = std::make_unique<base::ListValue>();
+  base::Value greylist(base::Value::Type::LIST);
   for (const auto& rule : ruleset.greylist)
-    greylist->Append(rule->ToString());
+    greylist.Append(rule->ToString());
 
-  auto dict = std::make_unique<base::DictionaryValue>();
-  dict->Set("sitelist", std::move(sitelist));
-  dict->Set("greylist", std::move(greylist));
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey("sitelist", std::move(sitelist));
+  dict.SetKey("greylist", std::move(greylist));
 
   return dict;
 }
@@ -381,17 +380,17 @@
 
   auto* service = GetBrowserSwitcherService(web_ui());
 
-  base::DictionaryValue retval;
+  base::Value retval(base::Value::Type::DICTIONARY);
   auto gpo_dict = RuleSetToDict(service->prefs().GetRules());
-  retval.Set("gpo", std::move(gpo_dict));
+  retval.SetKey("gpo", std::move(gpo_dict));
   auto ieem_dict = RuleSetToDict(*service->sitelist()->GetIeemSitelist());
-  retval.Set("ieem", std::move(ieem_dict));
+  retval.SetKey("ieem", std::move(ieem_dict));
   auto external_sitelist_dict =
       RuleSetToDict(*service->sitelist()->GetExternalSitelist());
-  retval.Set("external_sitelist", std::move(external_sitelist_dict));
+  retval.SetKey("external_sitelist", std::move(external_sitelist_dict));
   auto external_greylist_dict =
       RuleSetToDict(*service->sitelist()->GetExternalGreylist());
-  retval.Set("external_greylist", std::move(external_greylist_dict));
+  retval.SetKey("external_greylist", std::move(external_greylist_dict));
 
   ResolveJavascriptCallback(args->GetList()[0], retval);
 }
@@ -475,14 +474,12 @@
 
   base::DictionaryValue retval;
   for (const auto& source : sources) {
-    std::unique_ptr<base::Value> val;
+    base::Value val;
     if (source.url.is_valid())
-      val = std::make_unique<base::Value>(source.url.spec());
-    else
-      val = std::make_unique<base::Value>();
+      val = base::Value(source.url.spec());
     // |pref_name| is something like "browser_switcher.blah", so this will be in
     // a nested object.
-    retval.Set(source.pref_name, std::move(val));
+    retval.SetKey(source.pref_name, std::move(val));
   }
   ResolveJavascriptCallback(args->GetList()[0], retval);
 }
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 28306c79e..90d19b2 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -209,6 +209,7 @@
 #include "chrome/browser/ash/web_applications/media_app/chrome_media_app_ui_delegate.h"
 #include "chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_theme_provider.h"
 #include "chrome/browser/ash/web_applications/personalization_app/chrome_personalization_app_wallpaper_provider.h"
+#include "chrome/browser/ash/web_applications/personalization_app/personalization_app_user_provider_impl.h"
 #include "chrome/browser/feedback/feedback_dialog_utils.h"
 #include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h"
@@ -584,9 +585,12 @@
                                                      const GURL& url) {
   auto theme_provider =
       std::make_unique<ChromePersonalizationAppThemeProvider>(web_ui);
+  auto user_provider =
+      std::make_unique<PersonalizationAppUserProviderImpl>(web_ui);
   auto wallpaper_provider =
       std::make_unique<ChromePersonalizationAppWallpaperProvider>(web_ui);
   return new ash::PersonalizationAppUI(web_ui, std::move(theme_provider),
+                                       std::move(user_provider),
                                        std::move(wallpaper_provider));
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index 43f4705..695c55f6 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -638,6 +638,11 @@
                IDS_SAML_SECURITY_TOKEN_PIN_DIALOG_SUBTITLE);
 }
 
+void GaiaScreenHandler::GetAdditionalParameters(base::DictionaryValue* dict) {
+  dict->SetKey("isRedirectToDefaultIdPEnabled",
+               base::Value(features::IsRedirectToDefaultIdPEnabled()));
+}
+
 void GaiaScreenHandler::Initialize() {
   initialized_ = true;
   // This should be called only once on page load.
@@ -1158,7 +1163,10 @@
 }
 
 void GaiaScreenHandler::Show() {
-  ShowScreen(GaiaView::kScreenId);
+  base::DictionaryValue data;
+  data.SetBoolean("hasUserPods",
+                  LoginDisplayHost::default_host()->HasUserPods());
+  ShowScreenWithData(GaiaView::kScreenId, &data);
   elapsed_timer_ = std::make_unique<base::ElapsedTimer>();
   hidden_ = false;
 }
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
index 33164043..2e3ec34 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
@@ -194,6 +194,7 @@
   // BaseScreenHandler implementation:
   void DeclareLocalizedValues(
       ::login::LocalizedValuesBuilder* builder) override;
+  void GetAdditionalParameters(base::DictionaryValue* dict) override;
   void Initialize() override;
 
   // WebUIMessageHandler implementation:
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 6be341c..59e286c 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -14,6 +14,7 @@
 #include "ash/components/login/auth/key.h"
 #include "ash/components/login/auth/user_context.h"
 #include "ash/components/proximity_auth/screenlock_bridge.h"
+#include "ash/constants/ash_features.h"
 #include "ash/public/mojom/tray_action.mojom.h"
 #include "base/bind.h"
 #include "base/i18n/number_formatting.h"
@@ -234,8 +235,14 @@
   // Used by SAML password dialog.
   builder->Add("nextButtonText", IDS_OFFLINE_LOGIN_NEXT_BUTTON_TEXT);
 
-  builder->Add("samlNotice", IDS_LOGIN_SAML_NOTICE);
+  builder->Add("samlNotice", features::IsRedirectToDefaultIdPEnabled()
+                                 ? IDS_LOGIN_SAML_NOTICE_SHORT
+                                 : IDS_LOGIN_SAML_NOTICE);
   builder->Add("samlNoticeWithVideo", IDS_LOGIN_SAML_NOTICE_WITH_VIDEO);
+  builder->Add("samlChangeProviderMessage",
+               IDS_LOGIN_SAML_CHANGE_PROVIDER_MESSAGE);
+  builder->Add("samlChangeProviderButton",
+               IDS_LOGIN_SAML_CHANGE_PROVIDER_BUTTON);
   builder->AddF("confirmPasswordTitle", IDS_LOGIN_CONFIRM_PASSWORD_TITLE,
                 ui::GetChromeOSDeviceName());
   builder->Add("manualPasswordTitle", IDS_LOGIN_MANUAL_PASSWORD_TITLE);
diff --git a/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.cc b/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.cc
index 1083297..40e9f9a 100644
--- a/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.cc
+++ b/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.cc
@@ -34,8 +34,7 @@
       /*visual_type=*/
       ntp_tiles::TileVisualType::ICON_REAL /* unused on desktop */,
       /*icon_type=*/favicon_base::IconType::kInvalid /* unused on desktop */,
-      /*url_for_rappor=*/
-      tile.url /* used on desktop for logging */);
+      /*url_for_rappor=*/GURL() /* unused */);
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/webui/extensions/extensions_internals_source.cc b/chrome/browser/ui/webui/extensions/extensions_internals_source.cc
index 84b02e7..a0d0583 100644
--- a/chrome/browser/ui/webui/extensions/extensions_internals_source.cc
+++ b/chrome/browser/ui/webui/extensions/extensions_internals_source.cc
@@ -344,8 +344,7 @@
 base::Value FormatDetailedPermissionSet(const T& permissions) {
   base::Value value_list(base::Value::Type::LIST);
   for (const auto& permission : permissions) {
-    std::unique_ptr<base::Value> detail(permission->ToValue());
-    if (detail) {
+    if (auto detail = permission->ToValue()) {
       base::Value tmp(base::Value::Type::DICTIONARY);
       tmp.SetKey(permission->name(),
                  base::Value::FromUniquePtrValue(std::move(detail)));
diff --git a/chrome/browser/ui/webui/feedback/child_web_dialog.cc b/chrome/browser/ui/webui/feedback/child_web_dialog.cc
index 40998bd..a259b8a 100644
--- a/chrome/browser/ui/webui/feedback/child_web_dialog.cc
+++ b/chrome/browser/ui/webui/feedback/child_web_dialog.cc
@@ -22,7 +22,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 // ChildWebDialog, public:
 
-ChildWebDialog::ChildWebDialog(views::Widget* parent_widget,
+ChildWebDialog::ChildWebDialog(Profile* profile,
+                               views::Widget* parent_widget,
                                const GURL& url,
                                const std::u16string& title,
                                ui::ModalType modal_type,
@@ -30,7 +31,8 @@
                                int dialog_height,
                                bool can_resize,
                                bool can_minimize)
-    : parent_widget_(parent_widget),
+    : profile_(profile),
+      parent_widget_(parent_widget),
       title_(title),
       url_(url),
       modal_type_(modal_type),
@@ -43,8 +45,7 @@
 ChildWebDialog::~ChildWebDialog() = default;
 
 void ChildWebDialog::Show() {
-  chrome::ShowWebDialog(parent_widget_->GetNativeView(),
-                        ProfileManager::GetActiveUserProfile(), this);
+  chrome::ShowWebDialog(parent_widget_->GetNativeView(), profile_, this);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/webui/feedback/child_web_dialog.h b/chrome/browser/ui/webui/feedback/child_web_dialog.h
index ada039c..fb69cb57 100644
--- a/chrome/browser/ui/webui/feedback/child_web_dialog.h
+++ b/chrome/browser/ui/webui/feedback/child_web_dialog.h
@@ -14,6 +14,7 @@
 #include "ui/web_dialogs/web_dialog_delegate.h"
 #include "url/gurl.h"
 
+class Profile;
 namespace views {
 class Widget;
 }
@@ -21,7 +22,8 @@
 // Launches a child web dialog with specified URL and title.
 class ChildWebDialog : public ui::WebDialogDelegate {
  public:
-  ChildWebDialog(views::Widget* parent_widget,
+  ChildWebDialog(Profile* profile,
+                 views::Widget* parent_widget,
                  const GURL& url,
                  const std::u16string& title,
                  ui::ModalType modal_type = ui::MODAL_TYPE_WINDOW,
@@ -53,7 +55,8 @@
   bool ShouldShowDialogTitle() const override;
 
  private:
-  raw_ptr<views::Widget> parent_widget_;
+  const raw_ptr<Profile> profile_;
+  const raw_ptr<views::Widget> parent_widget_;
   const std::u16string title_;
   const GURL url_;
   const ui::ModalType modal_type_;
diff --git a/chrome/browser/ui/webui/feedback/feedback_dialog.cc b/chrome/browser/ui/webui/feedback/feedback_dialog.cc
index 85bfb86..ba65679 100644
--- a/chrome/browser/ui/webui/feedback/feedback_dialog.cc
+++ b/chrome/browser/ui/webui/feedback/feedback_dialog.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/json/json_writer.h"
+#include "base/logging.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -46,15 +47,26 @@
 
 // static
 void FeedbackDialog::CreateOrShow(
+    Profile* profile,
     const extensions::api::feedback_private::FeedbackInfo& info) {
-  // Focus the window hosting the dialog that has already been created.
   if (current_instance_) {
     DCHECK(current_instance_->widget_);
-    current_instance_->widget_->Show();
-    return;
+    const Profile* current_profile =
+        current_instance_->profile_keep_alive_.profile();
+    if (profile == current_profile) {
+      // Focus the window hosting the dialog that has already been created.
+      current_instance_->widget_->Show();
+      return;
+    } else {
+      // Close the existing window and create a new one for `profile`.
+      VLOG(1) << "Re-opening the feedback dialog for profile \""
+              << profile->GetDebugName() << "\" (was \""
+              << current_profile->GetDebugName() << "\")";
+      current_instance_->attached_to_current_instance_ = false;
+      current_instance_->widget_->Close();
+    }
   }
 
-  Profile* profile = ProfileManager::GetActiveUserProfile();
   current_instance_ = new FeedbackDialog(profile, info);
   gfx::NativeWindow window =
       chrome::ShowWebDialog(nullptr, profile, current_instance_,
@@ -76,7 +88,8 @@
 
 FeedbackDialog::~FeedbackDialog() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  current_instance_ = nullptr;
+  if (attached_to_current_instance_)
+    current_instance_ = nullptr;
 }
 
 ui::ModalType FeedbackDialog::GetDialogModalType() const {
@@ -143,7 +156,7 @@
 }
 
 void FeedbackDialog::OnDialogClosed(const std::string& json_retval) {
-  DCHECK(this == current_instance_);
+  DCHECK(this == current_instance_ || !attached_to_current_instance_);
   delete this;
 }
 
diff --git a/chrome/browser/ui/webui/feedback/feedback_dialog.h b/chrome/browser/ui/webui/feedback/feedback_dialog.h
index e4f82f643..493e077 100644
--- a/chrome/browser/ui/webui/feedback/feedback_dialog.h
+++ b/chrome/browser/ui/webui/feedback/feedback_dialog.h
@@ -15,9 +15,12 @@
 #include "ui/views/widget/widget.h"
 #include "ui/web_dialogs/web_dialog_delegate.h"
 
+class Profile;
+
 class FeedbackDialog : public ui::WebDialogDelegate {
  public:
   static void CreateOrShow(
+      Profile* profile,
       const extensions::api::feedback_private::FeedbackInfo& info);
 
   FeedbackDialog(const FeedbackDialog&) = delete;
@@ -61,6 +64,8 @@
   // Widget for the Feedback WebUI.
   raw_ptr<views::Widget> widget_;
   static FeedbackDialog* current_instance_;
+  // Whether `this` should correspond to `current_instance_`.
+  bool attached_to_current_instance_ = true;
 
   // Prevent Profile destruction until the dialog is closed, to prevent a
   // dangling RenderProcessHost crash.
diff --git a/chrome/browser/ui/webui/feedback/feedback_handler.cc b/chrome/browser/ui/webui/feedback/feedback_handler.cc
index 35c04195..6016550 100644
--- a/chrome/browser/ui/webui/feedback/feedback_handler.cc
+++ b/chrome/browser/ui/webui/feedback/feedback_handler.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/strings/strcat.h"
 #include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/feedback/child_web_dialog.h"
 #include "chrome/browser/ui/webui/feedback/feedback_dialog.h"
 #include "chrome/common/webui_url_constants.h"
@@ -23,7 +24,8 @@
 
 namespace {
 
-void ShowChildPage(const FeedbackDialog* dialog,
+void ShowChildPage(Profile* profile,
+                   const FeedbackDialog* dialog,
                    const GURL& url,
                    const std::u16string& title,
                    int dialog_width = 640,
@@ -33,7 +35,7 @@
   bool isParentModal = dialog->GetWidget()->IsModal();
   // when the dialog is closed, it will delete itself
   ChildWebDialog* child_dialog = new ChildWebDialog(
-      dialog->GetWidget(), url, title,
+      profile, dialog->GetWidget(), url, title,
       /*modal_type=*/
       isParentModal ? ui::MODAL_TYPE_WINDOW : ui::MODAL_TYPE_NONE, dialog_width,
       dialog_height, can_resize, can_minimize);
@@ -80,24 +82,26 @@
 
 #if defined(OS_CHROMEOS)
 void FeedbackHandler::HandleShowAssistantLogsInfo(const base::ListValue* args) {
-  ShowChildPage(dialog_, ChildPageURL("html/assistant_logs_info.html"),
-                std::u16string(),
+  ShowChildPage(Profile::FromWebUI(web_ui()), dialog_,
+                ChildPageURL("html/assistant_logs_info.html"), std::u16string(),
                 /*dialog_width=*/400, /*dialog_height=*/120,
                 /*can_resize=*/false, /*can_minimize=*/false);
 }
 void FeedbackHandler::HandleShowBluetoothLogsInfo(const base::ListValue* args) {
-  ShowChildPage(dialog_, ChildPageURL("html/bluetooth_logs_info.html"),
-                std::u16string(),
+  ShowChildPage(Profile::FromWebUI(web_ui()), dialog_,
+                ChildPageURL("html/bluetooth_logs_info.html"), std::u16string(),
                 /*dialog_width=*/400, /*dialog_height=*/120,
                 /*can_resize=*/false, /*can_minimize=*/false);
 }
 #endif  // defined(OS_CHROMEOS)
 
 void FeedbackHandler::HandleShowSystemInfo(const base::ListValue* args) {
-  ShowChildPage(dialog_, ChildPageURL("html/sys_info.html"),
+  ShowChildPage(Profile::FromWebUI(web_ui()), dialog_,
+                ChildPageURL("html/sys_info.html"),
                 l10n_util::GetStringUTF16(IDS_FEEDBACK_SYSINFO_PAGE_TITLE));
 }
 
 void FeedbackHandler::HandleShowMetrics(const base::ListValue* args) {
-  ShowChildPage(dialog_, GURL("chrome://histograms"), std::u16string());
+  ShowChildPage(Profile::FromWebUI(web_ui()), dialog_,
+                GURL("chrome://histograms"), std::u16string());
 }
diff --git a/chrome/browser/ui/webui/help/version_updater_mac.mm b/chrome/browser/ui/webui/help/version_updater_mac.mm
index 0e788aa5..4616cf9 100644
--- a/chrome/browser/ui/webui/help/version_updater_mac.mm
+++ b/chrome/browser/ui/webui/help/version_updater_mac.mm
@@ -229,7 +229,7 @@
 
     case kAutoupdateRegisterFailed:
       enable_promote_button = false;
-      FALLTHROUGH;
+      [[fallthrough]];
     case kAutoupdateCheckFailed:
     case kAutoupdateInstallFailed:
     case kAutoupdatePromoteFailed:
@@ -328,7 +328,7 @@
 
   switch (update_state.state) {
     case updater::UpdateService::UpdateState::State::kCheckingForUpdates:
-      FALLTHROUGH;
+      [[fallthrough]];
     case updater::UpdateService::UpdateState::State::kUpdateAvailable:
       status = VersionUpdater::Status::CHECKING;
       enable_promote_button = false;
@@ -336,7 +336,7 @@
     case updater::UpdateService::UpdateState::State::kDownloading:
       progress = GetDownloadProgress(update_state.downloaded_bytes,
                                      update_state.total_bytes);
-      FALLTHROUGH;
+      [[fallthrough]];
     case updater::UpdateService::UpdateState::State::kInstalling:
       status = VersionUpdater::Status::UPDATING;
       enable_promote_button = false;
@@ -355,7 +355,7 @@
           update_state.error_code, update_state.extra_code1);
       break;
     case updater::UpdateService::UpdateState::State::kNotStarted:
-      FALLTHROUGH;
+      [[fallthrough]];
     case updater::UpdateService::UpdateState::State::kUnknown:
       return;
   }
diff --git a/chrome/browser/ui/webui/history/foreign_session_handler.cc b/chrome/browser/ui/webui/history/foreign_session_handler.cc
index 75cda14..e607b4e 100644
--- a/chrome/browser/ui/webui/history/foreign_session_handler.cc
+++ b/chrome/browser/ui/webui/history/foreign_session_handler.cc
@@ -309,8 +309,9 @@
     // Use a pref to keep track of sessions that were collapsed by the user.
     // To prevent the pref from accumulating stale sessions, clear it each time
     // and only add back sessions that are still current.
-    DictionaryPrefUpdate pref_update(Profile::FromWebUI(web_ui())->GetPrefs(),
-                                     prefs::kNtpCollapsedForeignSessions);
+    DictionaryPrefUpdateDeprecated pref_update(
+        Profile::FromWebUI(web_ui())->GetPrefs(),
+        prefs::kNtpCollapsedForeignSessions);
     base::DictionaryValue* current_collapsed_sessions = pref_update.Get();
     std::unique_ptr<base::DictionaryValue> collapsed_sessions(
         current_collapsed_sessions->DeepCopy());
@@ -449,7 +450,8 @@
   // Store session tags for collapsed sessions in a preference so that the
   // collapsed state persists.
   PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
-  DictionaryPrefUpdate update(prefs, prefs::kNtpCollapsedForeignSessions);
+  DictionaryPrefUpdateDeprecated update(prefs,
+                                        prefs::kNtpCollapsedForeignSessions);
   if (is_collapsed)
     update.Get()->SetBoolean(session_tag, true);
   else
diff --git a/chrome/browser/ui/webui/local_state/local_state_ui.cc b/chrome/browser/ui/webui/local_state/local_state_ui.cc
index c2d9ca5..000b57b 100644
--- a/chrome/browser/ui/webui/local_state/local_state_ui.cc
+++ b/chrome/browser/ui/webui/local_state/local_state_ui.cc
@@ -28,7 +28,7 @@
 // user accounts which we don't want to expose to other users. Use an allowlist
 // to only show variations and UMA related fields which don't contain PII.
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#define ENABLE_FILTERING true
+#define ENABLE_FILTERING false
 #else
 #define ENABLE_FILTERING false
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/ui/webui/management/management_ui_browsertest.cc b/chrome/browser/ui/webui/management/management_ui_browsertest.cc
index 0e6e0416..42d2916 100644
--- a/chrome/browser/ui/webui/management/management_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/management/management_ui_browsertest.cc
@@ -78,8 +78,8 @@
   ASSERT_TRUE(content::ExecuteScriptAndExtractString(contents, javascript,
                                                      &unmanaged_json));
 
-  std::unique_ptr<base::Value> unmanaged_value_ptr =
-      base::JSONReader::ReadDeprecated(unmanaged_json);
+  absl::optional<base::Value> unmanaged_value_ptr =
+      base::JSONReader::Read(unmanaged_json);
   std::map<std::string, std::u16string> expected_unmanaged_values{
       {"browserManagementNotice",
        l10n_util::GetStringFUTF16(
@@ -93,7 +93,7 @@
        l10n_util::GetStringUTF16(IDS_MANAGEMENT_MANAGED_WEBSITES_EXPLANATION)},
   };
 
-  VerifyTexts(unmanaged_value_ptr.get(), expected_unmanaged_values);
+  VerifyTexts(&*unmanaged_value_ptr, expected_unmanaged_values);
 
   // The browser is managed.
   profile_policy_connector()->OverrideIsManagedForTesting(true);
@@ -111,8 +111,8 @@
   ASSERT_TRUE(content::ExecuteScriptAndExtractString(contents, javascript,
                                                      &managed_json));
 
-  std::unique_ptr<base::Value> managed_value_ptr =
-      base::JSONReader::ReadDeprecated(managed_json);
+  absl::optional<base::Value> managed_value_ptr =
+      base::JSONReader::Read(managed_json);
   std::map<std::string, std::u16string> expected_managed_values{
       {"browserManagementNotice",
        l10n_util::GetStringFUTF16(
@@ -124,6 +124,6 @@
       {"managedWebsitesSubtitle",
        l10n_util::GetStringUTF16(IDS_MANAGEMENT_MANAGED_WEBSITES_EXPLANATION)}};
 
-  VerifyTexts(managed_value_ptr.get(), expected_managed_values);
+  VerifyTexts(&*managed_value_ptr, expected_managed_values);
 }
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/ui/webui/management/management_ui_handler.cc b/chrome/browser/ui/webui/management/management_ui_handler.cc
index 25f8db3..84fef7e 100644
--- a/chrome/browser/ui/webui/management/management_ui_handler.cc
+++ b/chrome/browser/ui/webui/management/management_ui_handler.cc
@@ -868,7 +868,7 @@
   if (!url.empty() && GURL(url) != logo_url_) {
     icon_fetcher_ = std::make_unique<BitmapFetcher>(
         GURL(url), this, GetManagementUICustomerLogoAnnotation());
-    icon_fetcher_->Init(std::string(), net::ReferrerPolicy::NEVER_CLEAR,
+    icon_fetcher_->Init(net::ReferrerPolicy::NEVER_CLEAR,
                         network::mojom::CredentialsMode::kOmit);
     auto* profile = Profile::FromWebUI(web_ui());
     icon_fetcher_->Start(profile->GetDefaultStoragePartition()
diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc b/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc
index 599a2c1..a00c455 100644
--- a/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/net_internals/net_internals_ui_browsertest.cc
@@ -203,8 +203,8 @@
   ASSERT_TRUE(net_internals_test_->StartTestServer());
   const std::string& path = list_value->GetList()[0].GetString();
   GURL url = net_internals_test_->embedded_test_server()->GetURL(path);
-  std::unique_ptr<base::Value> url_value(new base::Value(url.spec()));
-  RunJavascriptCallback(url_value.get());
+  base::Value url_value(url.spec());
+  RunJavascriptCallback(&url_value);
 }
 
 void NetInternalsTest::MessageHandler::SetUpTestReportURI(
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
index 8956cc8..0dacad6 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
@@ -580,7 +580,8 @@
 
 void NewTabPageHandler::SetModuleDisabled(const std::string& module_id,
                                           bool disabled) {
-  ListPrefUpdate update(profile_->GetPrefs(), prefs::kNtpDisabledModules);
+  ListPrefUpdateDeprecated update(profile_->GetPrefs(),
+                                  prefs::kNtpDisabledModules);
   base::Value module_id_value(module_id);
   if (disabled) {
     if (!base::Contains(update->GetList(), module_id_value))
diff --git a/chrome/browser/ui/webui/ntp/app_icon_webui_handler.cc b/chrome/browser/ui/webui/ntp/app_icon_webui_handler.cc
index f3cd4d6..812508e 100644
--- a/chrome/browser/ui/webui/ntp/app_icon_webui_handler.cc
+++ b/chrome/browser/ui/webui/ntp/app_icon_webui_handler.cc
@@ -19,13 +19,13 @@
 
 namespace {
 
-std::unique_ptr<base::Value> GetDominantColorCssString(
+base::Value GetDominantColorCssString(
     scoped_refptr<base::RefCountedMemory> png) {
   color_utils::GridSampler sampler;
   SkColor color = color_utils::CalculateKMeanColorOfPNG(png);
-  return std::make_unique<base::Value>(
-      base::StringPrintf("rgb(%d, %d, %d)", SkColorGetR(color),
-                         SkColorGetG(color), SkColorGetB(color)));
+  return base::Value(base::StringPrintf("rgb(%d, %d, %d)", SkColorGetR(color),
+                                        SkColorGetG(color),
+                                        SkColorGetB(color)));
 }
 
 }  // namespace
@@ -65,9 +65,8 @@
     return;
   scoped_refptr<base::RefCountedStaticMemory> bits_mem(
       new base::RefCountedStaticMemory(&bits.front(), bits.size()));
-  std::unique_ptr<base::Value> color_value =
-      GetDominantColorCssString(bits_mem);
+  base::Value color_value = GetDominantColorCssString(bits_mem);
   base::Value id(extension_id);
   web_ui()->CallJavascriptFunctionUnsafe("ntp.setFaviconDominantColor", id,
-                                         *color_value);
+                                         color_value);
 }
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
index 386d144..01471b2 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -634,7 +634,7 @@
 
   const base::Value* app_page_names = prefs->GetList(prefs::kNtpAppPageNames);
   if (!app_page_names || !app_page_names->GetList().size()) {
-    ListPrefUpdate update(prefs, prefs::kNtpAppPageNames);
+    ListPrefUpdateDeprecated update(prefs, prefs::kNtpAppPageNames);
     base::ListValue* list = update.Get();
     list->Append(l10n_util::GetStringUTF16(IDS_APP_DEFAULT_PAGE_NAME));
     dictionary->SetKey("appPageNames", list->Clone());
@@ -1074,7 +1074,7 @@
 
   base::AutoReset<bool> auto_reset(&ignore_changes_, true);
   PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
-  ListPrefUpdate update(prefs, prefs::kNtpAppPageNames);
+  ListPrefUpdateDeprecated update(prefs, prefs::kNtpAppPageNames);
   base::ListValue* list = update.Get();
   if (page_index < list->GetList().size()) {
     list->GetList()[page_index] = base::Value(name);
diff --git a/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc b/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc
index f2e0052..879fa59 100644
--- a/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc
@@ -318,9 +318,8 @@
   std::string json;
   ASSERT_TRUE(
       content::ExecuteScriptAndExtractString(contents, javascript, &json));
-  std::unique_ptr<base::Value> value_ptr =
-      base::JSONReader::ReadDeprecated(json);
-  ASSERT_TRUE(value_ptr.get());
+  absl::optional<base::Value> value_ptr = base::JSONReader::Read(json);
+  ASSERT_TRUE(value_ptr);
   ASSERT_TRUE(value_ptr->is_list());
   base::Value::ConstListView actual_policies = value_ptr->GetList();
 
@@ -364,11 +363,10 @@
   EXPECT_TRUE(
       base::ReadFileToString(export_policies_test_file_path, &file_contents));
 
-  std::unique_ptr<base::Value> value_ptr =
-      base::JSONReader::ReadDeprecated(file_contents);
+  absl::optional<base::Value> value_ptr = base::JSONReader::Read(file_contents);
 
   // Check that the file contains a valid dictionary.
-  EXPECT_TRUE(value_ptr.get());
+  EXPECT_TRUE(value_ptr);
   EXPECT_TRUE(value_ptr->is_dict());
 
   // Since Chrome Metadata has a lot of variations based on platform, OS,
diff --git a/chrome/browser/ui/webui/quota_internals/quota_internals_handler.cc b/chrome/browser/ui/webui/quota_internals/quota_internals_handler.cc
index 3e89160..67c570a 100644
--- a/chrome/browser/ui/webui/quota_internals/quota_internals_handler.cc
+++ b/chrome/browser/ui/webui/quota_internals/quota_internals_handler.cc
@@ -56,8 +56,8 @@
 }
 
 void QuotaInternalsHandler::ReportGlobalInfo(const GlobalStorageInfo& data) {
-  std::unique_ptr<base::Value> value(data.NewValue());
-  FireWebUIListener("GlobalInfoUpdated", *value);
+  base::Value value(data.NewValue());
+  FireWebUIListener("GlobalInfoUpdated", value);
 }
 
 void QuotaInternalsHandler::ReportPerHostInfo(
diff --git a/chrome/browser/ui/webui/quota_internals/quota_internals_types.cc b/chrome/browser/ui/webui/quota_internals/quota_internals_types.cc
index 7ec48113..958719d4 100644
--- a/chrome/browser/ui/webui/quota_internals/quota_internals_types.cc
+++ b/chrome/browser/ui/webui/quota_internals/quota_internals_types.cc
@@ -37,18 +37,18 @@
 
 GlobalStorageInfo::~GlobalStorageInfo() {}
 
-std::unique_ptr<base::Value> GlobalStorageInfo::NewValue() const {
+base::Value GlobalStorageInfo::NewValue() const {
   // TODO(tzik): Add CreateLongIntegerValue to base/values.h and remove
   // all static_cast<double> in this file.
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
-  dict->SetString("type", StorageTypeToString(type_));
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetStringKey("type", StorageTypeToString(type_));
   if (usage_ >= 0)
-    dict->SetDoubleKey("usage", static_cast<double>(usage_));
+    dict.SetDoubleKey("usage", static_cast<double>(usage_));
   if (unlimited_usage_ >= 0)
-    dict->SetDoubleKey("unlimitedUsage", static_cast<double>(unlimited_usage_));
+    dict.SetDoubleKey("unlimitedUsage", static_cast<double>(unlimited_usage_));
   if (quota_ >= 0)
-    dict->SetDoubleKey("quota", static_cast<double>(quota_));
-  return std::move(dict);
+    dict.SetDoubleKey("quota", static_cast<double>(quota_));
+  return dict;
 }
 
 PerHostStorageInfo::PerHostStorageInfo(const std::string& host,
@@ -57,16 +57,16 @@
 
 PerHostStorageInfo::~PerHostStorageInfo() {}
 
-std::unique_ptr<base::Value> PerHostStorageInfo::NewValue() const {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+base::Value PerHostStorageInfo::NewValue() const {
+  base::Value dict(base::Value::Type::DICTIONARY);
   DCHECK(!host_.empty());
-  dict->SetString("host", host_);
-  dict->SetString("type", StorageTypeToString(type_));
+  dict.SetStringKey("host", host_);
+  dict.SetStringKey("type", StorageTypeToString(type_));
   if (usage_ >= 0)
-    dict->SetDoubleKey("usage", static_cast<double>(usage_));
+    dict.SetDoubleKey("usage", static_cast<double>(usage_));
   if (quota_ >= 0)
-    dict->SetDoubleKey("quota", static_cast<double>(quota_));
-  return std::move(dict);
+    dict.SetDoubleKey("quota", static_cast<double>(quota_));
+  return dict;
 }
 
 PerOriginStorageInfo::PerOriginStorageInfo(const GURL& origin,
@@ -81,23 +81,22 @@
 
 PerOriginStorageInfo::~PerOriginStorageInfo() {}
 
-std::unique_ptr<base::Value> PerOriginStorageInfo::NewValue() const {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+base::Value PerOriginStorageInfo::NewValue() const {
+  base::Value dict(base::Value::Type::DICTIONARY);
   DCHECK(!origin_.is_empty());
   DCHECK(!host_.empty());
-  dict->SetString("origin", origin_.spec());
-  dict->SetString("type", StorageTypeToString(type_));
-  dict->SetString("host", host_);
+  dict.SetStringKey("origin", origin_.spec());
+  dict.SetStringKey("type", StorageTypeToString(type_));
+  dict.SetStringKey("host", host_);
   if (used_count_ >= 0)
-    dict->SetInteger("usedCount", used_count_);
+    dict.SetIntKey("usedCount", used_count_);
   if (!last_access_time_.is_null())
-    dict->SetDoubleKey("lastAccessTime",
-                       last_access_time_.ToDoubleT() * 1000.0);
+    dict.SetDoubleKey("lastAccessTime", last_access_time_.ToDoubleT() * 1000.0);
   if (!last_modified_time_.is_null()) {
-    dict->SetDoubleKey("lastModifiedTime",
-                       last_modified_time_.ToDoubleT() * 1000.0);
+    dict.SetDoubleKey("lastModifiedTime",
+                      last_modified_time_.ToDoubleT() * 1000.0);
   }
-  return std::move(dict);
+  return dict;
 }
 
 }  // namespace quota_internals
diff --git a/chrome/browser/ui/webui/quota_internals/quota_internals_types.h b/chrome/browser/ui/webui/quota_internals/quota_internals_types.h
index 19796798..873bc86 100644
--- a/chrome/browser/ui/webui/quota_internals/quota_internals_types.h
+++ b/chrome/browser/ui/webui/quota_internals/quota_internals_types.h
@@ -36,7 +36,7 @@
   void set_quota(int64_t quota) { quota_ = quota; }
 
   // Create new Value for passing to WebUI page.
-  std::unique_ptr<base::Value> NewValue() const;
+  base::Value NewValue() const;
 
  private:
   blink::mojom::StorageType type_;
@@ -57,7 +57,7 @@
   void set_quota(int64_t quota) { quota_ = quota; }
 
   // Create new Value for passing to WebUI page.
-  std::unique_ptr<base::Value> NewValue() const;
+  base::Value NewValue() const;
 
  private:
   std::string host_;
@@ -87,7 +87,7 @@
   }
 
   // Create new Value for passing to WebUI page.
-  std::unique_ptr<base::Value> NewValue() const;
+  base::Value NewValue() const;
 
  private:
   GURL origin_;
diff --git a/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc b/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc
index b46f265..ced9ba7 100644
--- a/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc
@@ -340,6 +340,9 @@
        IDS_BLUETOOTH_DEVICE_LIST_PREVIOUSLY_CONNECTED},
       {"bluetoothDeviceListNoConnectedDevices",
        IDS_BLUETOOTH_DEVICE_LIST_NO_CONNECTED_DEVICES},
+      {"bluetoothEnabledA11YLabel", IDS_SETTINGS_BLUETOOTH_ENABLED_A11Y_LABEL},
+      {"bluetoothDisabledA11YLabel", IDS_SETTINGS_BLUETOOTH_DISABLED_A11Y_LABEL}
+
   };
   html_source->AddLocalizedStrings(kLocalizedStrings);
   html_source->AddBoolean("enableFastPairFlag", features::IsFastPairEnabled());
diff --git a/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc b/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
index ac633afc6..6a44f6d2e 100644
--- a/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
@@ -41,8 +41,7 @@
   DCHECK_LE(static_cast<int>(fingerprints_list.size()),
             kMaxAllowedFingerprints);
   for (auto& fingerprint_name: fingerprints_list) {
-    std::unique_ptr<base::Value> str =
-        std::make_unique<base::Value>(fingerprint_name);
+    base::Value str(fingerprint_name);
     fingerprints.Append(std::move(str));
   }
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc b/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc
index 2215ba2..1a7aad0d 100644
--- a/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc
@@ -237,8 +237,7 @@
       gms_core_notifications_state_tracker_
           ->GetGmsCoreNotificationsDisabledDeviceNames();
   for (const auto& device_name : device_names) {
-    device_names_without_notifications_.emplace_back(
-        std::make_unique<base::Value>(device_name));
+    device_names_without_notifications_.emplace_back(base::Value(device_name));
   }
   SendGmsCoreNotificationsDisabledDeviceNames();
 }
@@ -249,7 +248,7 @@
 
   base::ListValue device_names_value;
   for (const auto& device_name : device_names_without_notifications_)
-    device_names_value.Append(device_name->Clone());
+    device_names_value.Append(device_name.Clone());
 
   FireWebUIListener(kSendGmsCoreNotificationsDisabledDeviceNames,
                     device_names_value);
diff --git a/chrome/browser/ui/webui/settings/chromeos/internet_handler.h b/chrome/browser/ui/webui/settings/chromeos/internet_handler.h
index 52c567b1..db4eb91 100644
--- a/chrome/browser/ui/webui/settings/chromeos/internet_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/internet_handler.h
@@ -61,7 +61,7 @@
       chromeos::tether::GmsCoreNotificationsStateTracker*
           gms_core_notifications_state_tracker);
 
-  std::vector<std::unique_ptr<base::Value>> device_names_without_notifications_;
+  std::vector<base::Value> device_names_without_notifications_;
 
   Profile* const profile_;
 
diff --git a/chrome/browser/ui/webui/settings/on_startup_handler.cc b/chrome/browser/ui/webui/settings/on_startup_handler.cc
index b806f604..6b6ee44 100644
--- a/chrome/browser/ui/webui/settings/on_startup_handler.cc
+++ b/chrome/browser/ui/webui/settings/on_startup_handler.cc
@@ -53,30 +53,29 @@
     content::BrowserContext* browser_context,
     const extensions::Extension* extension,
     extensions::UnloadedExtensionReason reason) {
-  FireWebUIListener(kOnStartupNtpExtensionEventName, *GetNtpExtension());
+  FireWebUIListener(kOnStartupNtpExtensionEventName, GetNtpExtension());
 }
 
 void OnStartupHandler::OnExtensionReady(
     content::BrowserContext* browser_context,
     const extensions::Extension* extension) {
-  FireWebUIListener(kOnStartupNtpExtensionEventName, *GetNtpExtension());
+  FireWebUIListener(kOnStartupNtpExtensionEventName, GetNtpExtension());
 }
 
-std::unique_ptr<base::Value> OnStartupHandler::GetNtpExtension() {
+base::Value OnStartupHandler::GetNtpExtension() {
   const extensions::Extension* ntp_extension =
       extensions::GetExtensionOverridingNewTabPage(profile_);
   if (!ntp_extension) {
-    std::unique_ptr<base::Value> none(new base::Value);
-    return none;
+    return base::Value();
   }
 
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
-  dict->SetString("id", ntp_extension->id());
-  dict->SetString("name", ntp_extension->name());
-  dict->SetBoolean("canBeDisabled",
-                   !extensions::ExtensionSystem::Get(profile_)
-                        ->management_policy()
-                        ->MustRemainEnabled(ntp_extension, nullptr));
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetStringKey("id", ntp_extension->id());
+  dict.SetStringKey("name", ntp_extension->name());
+  dict.SetBoolKey("canBeDisabled",
+                  !extensions::ExtensionSystem::Get(profile_)
+                       ->management_policy()
+                       ->MustRemainEnabled(ntp_extension, nullptr));
   return dict;
 }
 
@@ -84,7 +83,7 @@
   const base::Value& callback_id = args->GetList()[0];
   AllowJavascript();
 
-  ResolveJavascriptCallback(callback_id, *GetNtpExtension());
+  ResolveJavascriptCallback(callback_id, GetNtpExtension());
 }
 
 void OnStartupHandler::HandleValidateStartupPage(const base::ListValue* args) {
diff --git a/chrome/browser/ui/webui/settings/on_startup_handler.h b/chrome/browser/ui/webui/settings/on_startup_handler.h
index 42cf979..c296750 100644
--- a/chrome/browser/ui/webui/settings/on_startup_handler.h
+++ b/chrome/browser/ui/webui/settings/on_startup_handler.h
@@ -5,8 +5,6 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_ON_STARTUP_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_SETTINGS_ON_STARTUP_HANDLER_H_
 
-#include <memory>
-
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/scoped_observation.h"
@@ -47,7 +45,7 @@
                            HandleValidateStartupPage_Invalid);
 
   // Info for extension controlling the NTP or empty value.
-  std::unique_ptr<base::Value> GetNtpExtension();
+  base::Value GetNtpExtension();
 
   // Handler for the "getNtpExtension" message. No arguments.
   void HandleGetNtpExtension(const base::ListValue* /*args*/);
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index d63ac6d..b57e10c0 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -2633,12 +2633,16 @@
     {"siteSettingsWindowPlacement", IDS_SITE_SETTINGS_TYPE_WINDOW_PLACEMENT},
     {"siteSettingsWindowPlacementMidSentence",
      IDS_SITE_SETTINGS_TYPE_WINDOW_PLACEMENT_MID_SENTENCE},
+    {"siteSettingsWindowPlacementDescription",
+     IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_DESCRIPTION},
     {"siteSettingsWindowPlacementAsk",
      IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK},
-    {"siteSettingsWindowPlacementAskRecommended",
-     IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK_RECOMMENDED},
-    {"siteSettingsWindowPlacementBlock",
-     IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCK},
+    {"siteSettingsWindowPlacementBlocked",
+     IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCKED},
+    {"siteSettingsWindowPlacementAskExceptions",
+     IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK_EXCEPTIONS},
+    {"siteSettingsWindowPlacementBlockedExceptions",
+     IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCKED_EXCEPTIONS},
     {"siteSettingsFontAccessMidSentence",
      IDS_SITE_SETTINGS_TYPE_FONT_ACCESS_MID_SENTENCE},
     {"siteSettingsFontAccessAsk", IDS_SETTINGS_SITE_SETTINGS_FONT_ACCESS_ASK},
@@ -2863,6 +2867,19 @@
       {"securityKeysSetPinButton", IDS_SETTINGS_SECURITY_KEYS_SET_PIN_BUTTON},
       {"securityKeysSamePINAsCurrent",
        IDS_SETTINGS_SECURITY_KEYS_SAME_PIN_AS_CURRENT},
+      {"securityKeysPhoneEditDialogTitle",
+       IDS_SETTINGS_SECURITY_KEYS_PHONE_EDIT_DIALOG_TITLE},
+      {"securityKeysPhonesYourDevices",
+       IDS_SETTINGS_SECURITY_KEYS_PHONES_YOUR_DEVICES},
+      {"securityKeysPhonesSyncedDesc",
+       IDS_SETTINGS_SECURITY_KEYS_PHONES_SYNCED_DESC},
+      {"securityKeysPhonesLinkedDevices",
+       IDS_SETTINGS_SECURITY_KEYS_PHONES_LINKED_DEVICES},
+      {"securityKeysPhonesLinkedDesc",
+       IDS_SETTINGS_SECURITY_KEYS_PHONES_LINKED_DESC},
+      {"securityKeysPhonesManage", IDS_SETTINGS_SECURITY_KEYS_PHONES_MANAGE},
+      {"securityKeysPhonesManageDesc",
+       IDS_SETTINGS_SECURITY_KEYS_PHONES_MANAGE_DESC},
   };
   html_source->AddLocalizedStrings(kSecurityKeysStrings);
   bool win_native_api_available = false;
@@ -2875,6 +2892,9 @@
                           !win_native_api_available);
   html_source->AddBoolean("enableSecurityKeysBioEnrollment",
                           !win_native_api_available);
+  html_source->AddBoolean(
+      "enableSecurityKeysPhonesSubpage",
+      base::FeatureList::IsEnabled(device::kWebAuthPhoneSupport));
 }
 
 void AddIPHStrings(content::WebUIDataSource* html_source) {
diff --git a/chrome/browser/ui/webui/settings/settings_security_key_handler.cc b/chrome/browser/ui/webui/settings/settings_security_key_handler.cc
index 7efb7f2..16a67901 100644
--- a/chrome/browser/ui/webui/settings/settings_security_key_handler.cc
+++ b/chrome/browser/ui/webui/settings/settings_security_key_handler.cc
@@ -4,18 +4,26 @@
 
 #include "chrome/browser/ui/webui/settings/settings_security_key_handler.h"
 
+#include <algorithm>
+#include <string>
 #include <utility>
+#include <vector>
 
+#include "base/base64.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/containers/contains.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 #include "chrome/browser/ui/webui/settings/settings_security_key_handler.h"
+#include "chrome/browser/webauthn/cablev2_devices.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
+#include "device/fido/cable/cable_discovery_data.h"
 #include "device/fido/credential_management.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/pin.h"
@@ -24,6 +32,7 @@
 #include "device/fido/reset_request_handler.h"
 #include "device/fido/set_pin_request_handler.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/icu/source/common/unicode/locid.h"
 #include "ui/base/l10n/l10n_util.h"
 
 using content::BrowserThread;
@@ -51,6 +60,17 @@
   return value;
 }
 
+bool DecodePublicKey(const std::string& value,
+                     std::array<uint8_t, device::kP256X962Length>* out) {
+  std::string bytes;
+  if (!base::Base64Decode(value, &bytes) || bytes.size() != out->size()) {
+    return false;
+  }
+
+  std::copy(bytes.begin(), bytes.end(), out->begin());
+  return true;
+}
+
 }  // namespace
 
 namespace settings {
@@ -984,4 +1004,126 @@
   bio_->CancelEnrollment();
 }
 
+SecurityKeysPhonesHandler::SecurityKeysPhonesHandler() = default;
+SecurityKeysPhonesHandler::~SecurityKeysPhonesHandler() = default;
+
+void SecurityKeysPhonesHandler::OnJavascriptAllowed() {}
+void SecurityKeysPhonesHandler::OnJavascriptDisallowed() {}
+
+void SecurityKeysPhonesHandler::RegisterMessages() {
+  web_ui()->RegisterDeprecatedMessageCallback(
+      "securityKeyPhonesEnumerate",
+      base::BindRepeating(&SecurityKeysPhonesHandler::HandleEnumerate,
+                          base::Unretained(this)));
+  web_ui()->RegisterDeprecatedMessageCallback(
+      "securityKeyPhonesDelete",
+      base::BindRepeating(&SecurityKeysPhonesHandler::HandleDelete,
+                          base::Unretained(this)));
+  web_ui()->RegisterDeprecatedMessageCallback(
+      "securityKeyPhonesRename",
+      base::BindRepeating(&SecurityKeysPhonesHandler::HandleRename,
+                          base::Unretained(this)));
+}
+
+void SecurityKeysPhonesHandler::HandleEnumerate(const base::ListValue* args) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_EQ(1u, args->GetList().size());
+
+  AllowJavascript();
+  DoEnumerate(args->GetList()[0]);
+}
+
+void SecurityKeysPhonesHandler::HandleDelete(const base::ListValue* args) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_EQ(2u, args->GetList().size());
+
+  AllowJavascript();
+  const std::string public_key_base64 = args->GetList()[1].GetString();
+  std::array<uint8_t, device::kP256X962Length> public_key;
+  const bool ok = DecodePublicKey(public_key_base64, &public_key);
+  DCHECK(ok);
+
+  PrefService* const prefs =
+      Profile::FromBrowserContext(
+          web_ui()->GetWebContents()->GetBrowserContext())
+          ->GetPrefs();
+  cablev2::DeletePairingByPublicKey(prefs, public_key);
+
+  DoEnumerate(args->GetList()[0]);
+}
+
+void SecurityKeysPhonesHandler::HandleRename(const base::ListValue* args) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_EQ(3u, args->GetList().size());
+
+  AllowJavascript();
+  const std::string public_key_base64 = args->GetList()[1].GetString();
+  const std::string new_name = args->GetList()[2].GetString();
+  content::BrowserContext* const browser_ctx =
+      web_ui()->GetWebContents()->GetBrowserContext();
+
+  std::array<uint8_t, device::kP256X962Length> public_key;
+  const bool ok = DecodePublicKey(public_key_base64, &public_key);
+  DCHECK(ok);
+
+  // `existing_names` is built without calling `cablev2::MergeDevices` because
+  // that function will discard linked entries with duplicate public keys, which
+  // can hide some names that we would still like to avoid colliding with.
+  std::unique_ptr<cablev2::KnownDevices> known_devices =
+      cablev2::KnownDevices::FromProfile(
+          Profile::FromBrowserContext(browser_ctx));
+
+  // Remove the device that is getting renamed from the set of linked devices.
+  auto new_end = std::remove_if(
+      known_devices->linked_devices.begin(),
+      known_devices->linked_devices.end(),
+      [&public_key](const std::unique_ptr<device::cablev2::Pairing>& device)
+          -> bool { return device->peer_public_key_x962 == public_key; });
+  known_devices->linked_devices.erase(new_end,
+                                      known_devices->linked_devices.end());
+
+  PrefService* const prefs =
+      Profile::FromBrowserContext(browser_ctx)->GetPrefs();
+  cablev2::RenamePairing(prefs, public_key, new_name, known_devices->Names());
+
+  ResolveJavascriptCallback(args->GetList()[0], base::Value());
+}
+
+void SecurityKeysPhonesHandler::DoEnumerate(const base::Value& callback_id) {
+  const std::vector<std::unique_ptr<device::cablev2::Pairing>> pairings =
+      cablev2::MergeDevices(
+          cablev2::KnownDevices::FromProfile(Profile::FromBrowserContext(
+              web_ui()->GetWebContents()->GetBrowserContext())),
+          &icu::Locale::getDefault());
+
+  std::vector<base::Value> synced;
+  std::vector<base::Value> linked;
+  absl::optional<std::string> last_synced_device_name;
+  for (const auto& pairing : pairings) {
+    base::Value dict(base::Value::Type::DICTIONARY);
+    dict.SetKey("name", base::Value(pairing->name));
+    dict.SetKey("publicKey",
+                base::Value(base::Base64Encode(pairing->peer_public_key_x962)));
+
+    if (pairing->from_sync_deviceinfo) {
+      // Synced devices can have duplicate names. (E.g. if two or more
+      // channels are syncing from the same phone.) These are deduplicated
+      // here.
+      if (!last_synced_device_name ||
+          *last_synced_device_name != pairing->name) {
+        synced.emplace_back(std::move(dict));
+      }
+      last_synced_device_name = pairing->name;
+    } else {
+      linked.emplace_back(std::move(dict));
+    }
+  }
+
+  std::vector<base::Value> result;
+  result.emplace_back(std::move(synced));
+  result.emplace_back(std::move(linked));
+
+  ResolveJavascriptCallback(callback_id, base::ListValue(std::move(result)));
+}
+
 }  // namespace settings
diff --git a/chrome/browser/ui/webui/settings/settings_security_key_handler.h b/chrome/browser/ui/webui/settings/settings_security_key_handler.h
index 3dfe3d4..8ac8244 100644
--- a/chrome/browser/ui/webui/settings/settings_security_key_handler.h
+++ b/chrome/browser/ui/webui/settings/settings_security_key_handler.h
@@ -252,6 +252,24 @@
   base::WeakPtrFactory<SecurityKeysBioEnrollmentHandler> weak_factory_{this};
 };
 
+class SecurityKeysPhonesHandler : public SettingsPageUIHandler {
+ public:
+  SecurityKeysPhonesHandler();
+  ~SecurityKeysPhonesHandler() override;
+
+ protected:
+  void RegisterMessages() override;
+  void OnJavascriptAllowed() override;
+  void OnJavascriptDisallowed() override;
+
+ private:
+  void HandleEnumerate(const base::ListValue* args);
+  void HandleDelete(const base::ListValue* args);
+  void HandleRename(const base::ListValue* args);
+
+  void DoEnumerate(const base::Value& callback_id);
+};
+
 }  // namespace settings
 
 #endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_SECURITY_KEY_HANDLER_H_
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index 7271a46..d06bd15 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -228,6 +228,7 @@
   AddSettingsPageUIHandler(std::make_unique<SecurityKeysCredentialHandler>());
   AddSettingsPageUIHandler(
       std::make_unique<SecurityKeysBioEnrollmentHandler>());
+  AddSettingsPageUIHandler(std::make_unique<SecurityKeysPhonesHandler>());
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   InitBrowserSettingsWebUIHandlers();
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index 9fcd702..8db269e5 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -227,8 +227,9 @@
     return;
   }
   // Case 2:
-  if (etld_plus1_cookie_url)
+  if (etld_plus1_cookie_url && !is_partitioned) {
     return;
+  }
   // Case 3:
   if (url_is_origin_with_cookies) {
     // Cookies ignore schemes, so try and see if a https schemed version
@@ -1040,7 +1041,7 @@
         origin_info.SetKey(kNumCookies,
                            base::Value(origin_cookie_num_it->second));
         // Add cookies numbers for origins that isn't an eTLD+1.
-        if (origin_url.host() != etld_plus1)
+        if (origin_url.host() != etld_plus1 || is_partitioned)
           cookie_num += origin_cookie_num_it->second;
       }
     }
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
index a8e1168..71b94e2 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -494,6 +494,11 @@
         net::CookiePartitionKey::FromURLForTesting(
             GURL("https://google.com.au")));
     mock_browsing_data_cookie_helper->AddCookieSamples(
+        GURL("https://google.com.au"),
+        "__Host-A=1; Path=/; Partitioned; Secure;",
+        net::CookiePartitionKey::FromURLForTesting(
+            GURL("https://google.com.au")));
+    mock_browsing_data_cookie_helper->AddCookieSamples(
         GURL("https://www.another-example.com"),
         "__Host-A=1; Path=/; Partitioned; Secure;",
         net::CookiePartitionKey::FromURLForTesting(
@@ -963,20 +968,22 @@
     ASSERT_TRUE(site_group.FindStringKey("etldPlus1"));
     ASSERT_EQ("google.com.au", *site_group.FindStringKey("etldPlus1"));
 
-    EXPECT_EQ(3, site_group.FindKey("numCookies")->GetDouble());
+    EXPECT_EQ(4, site_group.FindKey("numCookies")->GetDouble());
 
     const base::Value* origin_list = site_group.FindListKey("origins");
     ASSERT_TRUE(origin_list && origin_list->is_list());
 
     // The unpartitioned cookie set for google.com.au should be associated with
     // the eTLD+1, and thus won't have an origin entry as other origin entries
-    // exist for the unpartitioned storage.
-    EXPECT_EQ(2U, origin_list->GetList().size());
+    // exist for the unpartitioned storage. The partitioned cookie for
+    // google.com.au, partitioned by google.com.au should have also created an
+    // entry.
+    EXPECT_EQ(3U, origin_list->GetList().size());
 
     const base::Value& partitioned_origin_one_info = origin_list->GetList()[0];
     ASSERT_TRUE(partitioned_origin_one_info.is_dict());
 
-    EXPECT_EQ("https://www.another-example.com/",
+    EXPECT_EQ("https://google.com.au/",
               partitioned_origin_one_info.FindKey("origin")->GetString());
     EXPECT_EQ(0,
               partitioned_origin_one_info.FindKey("engagement")->GetDouble());
@@ -987,7 +994,7 @@
         partitioned_origin_one_info.FindKey("isPartitioned")->GetBool());
 
     const base::Value& partitioned_origin_two_info = origin_list->GetList()[1];
-    EXPECT_EQ("https://www.example.com/",
+    EXPECT_EQ("https://www.another-example.com/",
               partitioned_origin_two_info.FindKey("origin")->GetString());
     EXPECT_EQ(0,
               partitioned_origin_two_info.FindKey("engagement")->GetDouble());
@@ -996,6 +1003,20 @@
               partitioned_origin_two_info.FindKey("numCookies")->GetDouble());
     EXPECT_TRUE(
         partitioned_origin_two_info.FindKey("isPartitioned")->GetBool());
+
+    const base::Value& partitioned_origin_three_info =
+        origin_list->GetList()[2];
+    ASSERT_TRUE(partitioned_origin_three_info.is_dict());
+
+    EXPECT_EQ("https://www.example.com/",
+              partitioned_origin_three_info.FindKey("origin")->GetString());
+    EXPECT_EQ(0,
+              partitioned_origin_three_info.FindKey("engagement")->GetDouble());
+    EXPECT_EQ(0, partitioned_origin_three_info.FindKey("usage")->GetDouble());
+    EXPECT_EQ(1,
+              partitioned_origin_three_info.FindKey("numCookies")->GetDouble());
+    EXPECT_TRUE(
+        partitioned_origin_three_info.FindKey("isPartitioned")->GetBool());
   }
 }
 
@@ -1922,10 +1943,10 @@
   HostContentSettingsMap* map =
       HostContentSettingsMapFactory::GetForProfile(profile());
   content_settings::SettingInfo info;
-  std::unique_ptr<base::Value> value = map->GetWebsiteSetting(
+  base::Value value = base::Value::FromUniquePtrValue(map->GetWebsiteSetting(
       kWebUIOrigins[0].GetURL(), kWebUIOrigins[0].GetURL(),
-      content_settings_type, &info);
-  EXPECT_EQ(CONTENT_SETTING_ALLOW, value->GetInt());
+      content_settings_type, &info));
+  EXPECT_EQ(CONTENT_SETTING_ALLOW, value.GetInt());
   EXPECT_EQ(content_settings::SETTING_SOURCE_ALLOWLIST, info.source);
 
   // Register an ordinary website permission.
@@ -2444,7 +2465,7 @@
 TEST_F(SiteSettingsHandlerTest, HandleClearEtldPlus1DataAndCookies) {
   SetUpCookiesTreeModel();
 
-  EXPECT_EQ(27, handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
+  EXPECT_EQ(28, handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
 
   auto verify_site_group = [](const base::Value& site_group,
                               std::string expected_etld_plus1) {
@@ -2485,7 +2506,7 @@
     EXPECT_TRUE(cookie->IsPartitioned());
   }
 
-  EXPECT_EQ(18, handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
+  EXPECT_EQ(19, handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
 
   storage_and_cookie_list = GetOnStorageFetchedSentListView();
   EXPECT_EQ(2U, storage_and_cookie_list.size());
@@ -2497,7 +2518,7 @@
   handler()->HandleClearEtldPlus1DataAndCookies(
       &base::Value::AsListValue(args));
 
-  EXPECT_EQ(10, handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
+  EXPECT_EQ(11, handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
 
   storage_and_cookie_list = GetOnStorageFetchedSentListView();
   EXPECT_EQ(1U, storage_and_cookie_list.size());
@@ -2534,7 +2555,7 @@
 TEST_F(SiteSettingsHandlerTest, HandleClearUnpartitionedUsage) {
   SetUpCookiesTreeModel();
 
-  EXPECT_EQ(27, handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
+  EXPECT_EQ(28, handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
 
   base::Value args(base::Value::Type::LIST);
   args.Append("https://www.example.com/");
@@ -2556,6 +2577,23 @@
     EXPECT_EQ("www.example.com", cookie->Domain());
     EXPECT_TRUE(cookie->IsPartitioned());
   }
+
+  // Partitioned storage, even when keyed on the cookie domain site, should
+  // not be cleared.
+  args = base::Value(base::Value::Type::LIST);
+  args.Append("https://google.com.au/");
+  handler()->HandleClearUnpartitionedUsage(&base::Value::AsListValue(args));
+
+  remaining_host_nodes = GetHostNodes(GURL("https://google.com.au"));
+
+  // A single partitioned cookie should remain.
+  ASSERT_EQ(1u, remaining_host_nodes.size());
+  ASSERT_EQ(1u, remaining_host_nodes[0]->children().size());
+  const auto& cookies_node = remaining_host_nodes[0]->children()[0];
+  ASSERT_EQ(1u, cookies_node->children().size());
+  const auto& cookie_node = cookies_node->children()[0];
+  const auto& cookie = cookie_node->GetDetailedInfo().cookie;
+  EXPECT_TRUE(cookie->IsPartitioned());
 }
 
 TEST_F(SiteSettingsHandlerTest, ClearUnpartitionedClearsHints) {
@@ -2608,7 +2646,7 @@
   // Confirm that removing unpartitioned storage correctly removes the
   // appropriate nodes.
   SetUpCookiesTreeModel();
-  EXPECT_EQ(27, handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
+  EXPECT_EQ(28, handler()->cookies_tree_model_->GetRoot()->GetTotalNodeCount());
 
   base::Value args(base::Value::Type::LIST);
   args.Append("https://www.example.com/");
diff --git a/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.cc b/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.cc
index eb9e2ca..30aedae 100644
--- a/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.cc
+++ b/chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.cc
@@ -206,7 +206,7 @@
     action = CLOSE;
   }
 
-  NotifyModalSigninClosed();
+  NotifyModalDialogClosed();
 
   if (callback_)
     std::move(callback_).Run(action);
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip.mojom b/chrome/browser/ui/webui/tab_strip/tab_strip.mojom
index 6b0226ae..0afaf79 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip.mojom
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip.mojom
@@ -136,6 +136,9 @@
 
   // Report time spent between getTabs() and tabs are created.
   ReportTabCreationDuration(uint32 tab_count, uint32 duration_ms);
+
+  // Switch to a specific tab.
+  ActivateTab(int32 tab_id);
 };
 
 // WebUI-side handler for requests from the browser.
@@ -190,4 +193,7 @@
 
   // Called when a context menu is requested to show.
   ShowContextMenu();
+
+  // Called when the browser theme changed.
+  ThemeChanged();
 };
\ No newline at end of file
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc
index 91698e9e..e35e5a38 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -34,7 +35,9 @@
 #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_embedder.h"
 #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_metrics.h"
 #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_util.h"
+#include "chrome/browser/ui/webui/theme_source.h"
 #include "chrome/browser/ui/webui/util/image_util.h"
+#include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/tab_groups/tab_group_color.h"
 #include "components/tab_groups/tab_group_id.h"
@@ -190,6 +193,12 @@
   DCHECK(embedder_);
   web_ui_->GetWebContents()->SetDelegate(this);
   browser_->tab_strip_model()->AddObserver(this);
+
+  // Listen for theme installation.
+  ThemeServiceFactory::GetForProfile(browser_->profile())->AddObserver(this);
+
+  // Or native theme change.
+  theme_observation_.Observe(webui::GetNativeTheme(web_ui_->GetWebContents()));
 }
 
 void TabStripPageHandler::NotifyLayoutChanged() {
@@ -467,6 +476,10 @@
   return false;
 }
 
+bool TabStripPageHandler::IsPrivileged() {
+  return true;
+}
+
 void TabStripPageHandler::OnLongPressTimer() {
   page_->LongPress();
 }
@@ -897,3 +910,31 @@
       embedder_->GetColor(
           ThemeProperties::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_ACTIVE));
 }
+
+void TabStripPageHandler::ActivateTab(int32_t tab_id) {
+  TabStripModel* tab_strip_model = browser_->tab_strip_model();
+  for (int index = 0; index < tab_strip_model->count(); ++index) {
+    content::WebContents* contents = tab_strip_model->GetWebContentsAt(index);
+    if (extensions::ExtensionTabUtil::GetTabId(contents) == tab_id) {
+      tab_strip_model->ActivateTabAt(index);
+    }
+  }
+}
+
+void TabStripPageHandler::OnThemeChanged() {
+  page_->ThemeChanged();
+}
+
+void TabStripPageHandler::OnNativeThemeUpdated(
+    ui::NativeTheme* observed_theme) {
+  // There are two types of theme update. a) The observed theme change. e.g.
+  // switch between light/dark mode. b) A different theme is enabled. e.g.
+  // switch between GTK and classic theme on Linux. Reset observer in case b).
+  ui::NativeTheme* current_theme =
+      webui::GetNativeTheme(web_ui_->GetWebContents());
+  if (observed_theme != current_theme) {
+    theme_observation_.Reset();
+    theme_observation_.Observe(current_theme);
+  }
+  page_->ThemeChanged();
+}
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h
index 51394bfe..9f664e08 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h
@@ -7,7 +7,9 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
+#include "base/scoped_observation.h"
 #include "base/timer/timer.h"
+#include "chrome/browser/themes/theme_service_observer.h"
 #include "chrome/browser/ui/tabs/tab_change_type.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
@@ -18,13 +20,17 @@
 #include "content/public/browser/web_contents_delegate.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "ui/native_theme/native_theme.h"
+#include "ui/native_theme/native_theme_observer.h"
 
 class Browser;
 class TabStripUIEmbedder;
 
 class TabStripPageHandler : public tab_strip::mojom::PageHandler,
                             public TabStripModelObserver,
-                            public content::WebContentsDelegate {
+                            public content::WebContentsDelegate,
+                            public ThemeServiceObserver,
+                            public ui::NativeThemeObserver {
  public:
   TabStripPageHandler(
       mojo::PendingReceiver<tab_strip::mojom::PageHandler> receiver,
@@ -64,6 +70,7 @@
   bool CanDragEnter(content::WebContents* source,
                     const content::DropData& data,
                     blink::DragOperationsMask operations_allowed) override;
+  bool IsPrivileged() override;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(TabStripPageHandlerTest, CloseTab);
@@ -77,6 +84,7 @@
   FRIEND_TEST_ALL_PREFIXES(TabStripPageHandlerTest, MoveTabAcrossWindows);
   FRIEND_TEST_ALL_PREFIXES(TabStripPageHandlerTest,
                            RemoveTabIfInvalidContextMenu);
+  FRIEND_TEST_ALL_PREFIXES(TabStripPageHandlerTest, SwitchTab);
   FRIEND_TEST_ALL_PREFIXES(TabStripPageHandlerTest, UngroupTab);
 
   void OnLongPressTimer();
@@ -91,6 +99,12 @@
                                   base::TimeDelta duration);
   gfx::ImageSkia ThemeFavicon(const gfx::ImageSkia& source);
 
+  // ThemeServiceObserver implementation.
+  void OnThemeChanged() override;
+
+  // ui::NativeThemeObserver:
+  void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
+
   // tab_strip::mojom::PageHandler:
   void GetTabs(GetTabsCallback callback) override;
   void GetGroupVisualData(GetGroupVisualDataCallback callback) override;
@@ -118,6 +132,7 @@
                                      uint32_t duration_ms) override;
   void ReportTabCreationDuration(uint32_t tab_count,
                                  uint32_t duration_ms) override;
+  void ActivateTab(int32_t tab_id) override;
 
   mojo::Receiver<tab_strip::mojom::PageHandler> receiver_;
   mojo::Remote<tab_strip::mojom::Page> page_;
@@ -149,6 +164,9 @@
   // interrupted (eg by a scroll start gesture).
   std::unique_ptr<base::RetainingOneShotTimer> long_press_timer_;
 
+  base::ScopedObservation<ui::NativeTheme, ui::NativeThemeObserver>
+      theme_observation_{this};
+
   base::WeakPtrFactory<TabStripPageHandler> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler_unittest.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler_unittest.cc
index 99a2ab9..bf2b65f 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler_unittest.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui.h"
 #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_embedder.h"
 #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_layout.h"
+#include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/tab_groups/tab_group_color.h"
@@ -109,6 +110,7 @@
   MOCK_METHOD2(TabThumbnailUpdated,
                void(int32_t tab_id, const std::string& data_uri));
   MOCK_METHOD0(ShowContextMenu, void());
+  MOCK_METHOD0(ThemeChanged, void());
 };
 
 }  // namespace
@@ -511,6 +513,22 @@
                          browser()->tab_strip_model()->GetWebContentsAt(2))));
 }
 
+TEST_F(TabStripPageHandlerTest, SwitchTab) {
+  AddTab(browser(), GURL("http://foo"));
+  AddTab(browser(), GURL("http://foo"));
+  AddTab(browser(), GURL("http://foo"));
+
+  web_ui()->ClearTrackedCalls();
+  TabStripModel* tab_strip_model = browser()->tab_strip_model();
+  ASSERT_EQ(tab_strip_model->GetActiveWebContents(),
+            tab_strip_model->GetWebContentsAt(0));
+  int tab_id = extensions::ExtensionTabUtil::GetTabId(
+      browser()->tab_strip_model()->GetWebContentsAt(1));
+  handler()->ActivateTab(tab_id);
+  ASSERT_EQ(tab_strip_model->GetActiveWebContents(),
+            tab_strip_model->GetWebContentsAt(1));
+}
+
 TEST_F(TabStripPageHandlerTest, UngroupTab) {
   // Add a tab inside of a group.
   AddTab(browser(), GURL("http://foo"));
@@ -614,3 +632,9 @@
   // Close all tabs before destructing.
   new_browser.get()->tab_strip_model()->CloseAllTabs();
 }
+
+TEST_F(TabStripPageHandlerTest, OnThemeChanged) {
+  webui::GetNativeTheme(web_ui()->GetWebContents())
+      ->NotifyOnNativeThemeUpdated();
+  EXPECT_CALL(page_, ThemeChanged());
+}
\ No newline at end of file
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
index d17d7e60..49ccecf 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h"
 #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_embedder.h"
 #include "chrome/browser/ui/webui/tab_strip/tab_strip_ui_layout.h"
-#include "chrome/browser/ui/webui/theme_handler.h"
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/generated_resources.h"
@@ -91,9 +90,6 @@
   content::URLDataSource::Add(
       profile, std::make_unique<FaviconSource>(
                    profile, chrome::FaviconUrlFormat::kFavicon2));
-
-  // TODO(crbug.com/1234500): Migrate to mojo as well.
-  web_ui->AddMessageHandler(std::make_unique<ThemeHandler>());
 }
 
 TabStripUI::~TabStripUI() = default;
diff --git a/chrome/browser/ui/webui/webui_js_error/webui_js_error_ui.cc b/chrome/browser/ui/webui/webui_js_error/webui_js_error_ui.cc
index 541ed1b4..6ebc069 100644
--- a/chrome/browser/ui/webui/webui_js_error/webui_js_error_ui.cc
+++ b/chrome/browser/ui/webui/webui_js_error/webui_js_error_ui.cc
@@ -16,20 +16,10 @@
 #include "chrome/grit/webui_js_error_resources.h"
 #include "chrome/grit/webui_js_error_resources_map.h"
 #include "content/public/browser/web_ui_data_source.h"
-#include "content/public/common/content_features.h"
 
 WebUIJsErrorUI::WebUIJsErrorUI(content::WebUI* web_ui)
     : content::WebUIController(web_ui) {
-#if !defined(OS_WIN) && !defined(OS_FUCHSIA)
-  VLOG(3) << std::boolalpha << "chrome://webuijserror loading. "
-          << "Experiment state: send javascript errors is "
-          << base::FeatureList::IsEnabled(
-                 features::kSendWebUIJavaScriptErrorReports)
-          << " and send to prod is "
-          << features::kWebUIJavaScriptErrorReportsSendToProductionParam.Get();
-#else
   VLOG(3) << "chrome://webuijserror loading.";
-#endif
 
   content::WebUIDataSource* source =
       content::WebUIDataSource::Create(chrome::kChromeUIWebUIJsErrorHost);
diff --git a/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc b/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
index 6bdeac0..ab53c9f 100644
--- a/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
+++ b/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
@@ -86,7 +86,8 @@
   if (blink::PageZoomValuesEqual(level, host_zoom_map_->GetDefaultZoomLevel()))
     return;
 
-  DictionaryPrefUpdate update(pref_service_, prefs::kPartitionDefaultZoomLevel);
+  DictionaryPrefUpdateDeprecated update(pref_service_,
+                                        prefs::kPartitionDefaultZoomLevel);
   update->SetDoubleKey(partition_key_, level);
   // For unregistered paths, OnDefaultZoomLevelChanged won't be called, so
   // set this manually.
@@ -120,8 +121,8 @@
   if (change.mode != content::HostZoomMap::ZOOM_CHANGED_FOR_HOST)
     return;
   double level = change.zoom_level;
-  DictionaryPrefUpdate update(pref_service_,
-                              prefs::kPartitionPerHostZoomLevels);
+  DictionaryPrefUpdateDeprecated update(pref_service_,
+                                        prefs::kPartitionPerHostZoomLevels);
   base::DictionaryValue* host_zoom_dictionaries = update.Get();
   DCHECK(host_zoom_dictionaries);
 
@@ -204,8 +205,8 @@
   // Sanitize prefs to remove entries that match the default zoom level and/or
   // have an empty host.
   {
-    DictionaryPrefUpdate update(pref_service_,
-                                prefs::kPartitionPerHostZoomLevels);
+    DictionaryPrefUpdateDeprecated update(pref_service_,
+                                          prefs::kPartitionPerHostZoomLevels);
     base::DictionaryValue* host_zoom_dictionaries = update.Get();
     base::DictionaryValue* partition_dictionary = nullptr;
     host_zoom_dictionaries->GetDictionary(partition_key_,
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
index 0286712..30ccb75 100644
--- a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
+++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
@@ -395,7 +395,11 @@
 
   std::unique_ptr<apps::App> app = apps::AppPublisher::MakeApp(
       apps::ConvertMojomAppTypToAppType(app_type()), web_app->app_id(),
-      readiness, web_app->name());
+      readiness, web_app->name(),
+      apps::ConvertMojomInstallReasonToInstallReason(
+          GetHighestPriorityInstallReason(web_app)),
+      apps::ConvertMojomInstallSourceToInstallSource(
+          GetInstallSource(profile()->GetPrefs(), web_app->app_id())));
 
   app->description = web_app->description();
 
@@ -404,6 +408,11 @@
 
   app->icon_key =
       std::move(*icon_key_factory_.CreateIconKey(GetIconEffects(web_app)));
+
+  // For system web apps (only), the install source is |kSystem|.
+  DCHECK_EQ(web_app->IsSystemApp(),
+            app->install_reason == apps::InstallReason::kSystem);
+
   return app;
 }
 #endif
diff --git a/chrome/browser/web_applications/app_service/web_apps.cc b/chrome/browser/web_applications/app_service/web_apps.cc
index 1f150a1..6c82830 100644
--- a/chrome/browser/web_applications/app_service/web_apps.cc
+++ b/chrome/browser/web_applications/app_service/web_apps.cc
@@ -328,6 +328,11 @@
   subscribers_.Add(std::move(subscriber));
 }
 
+void WebApps::SetWindowMode(const std::string& app_id,
+                            apps::mojom::WindowMode window_mode) {
+  publisher_helper().SetWindowMode(app_id, window_mode);
+}
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 void WebApps::Uninstall(const std::string& app_id,
                         apps::mojom::UninstallSource uninstall_source,
@@ -514,10 +519,6 @@
   publisher_helper().ExecuteContextMenuCommand(app_id, shortcut_id, display_id);
 }
 
-void WebApps::SetWindowMode(const std::string& app_id,
-                            apps::mojom::WindowMode window_mode) {
-  publisher_helper().SetWindowMode(app_id, window_mode);
-}
 #endif
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/app_service/web_apps.h b/chrome/browser/web_applications/app_service/web_apps.h
index 5a559fdf..f63e2c5 100644
--- a/chrome/browser/web_applications/app_service/web_apps.h
+++ b/chrome/browser/web_applications/app_service/web_apps.h
@@ -134,6 +134,8 @@
   void InitWebApps();
   void StartPublishingWebApps(
       mojo::PendingRemote<apps::mojom::Subscriber> subscriber_remote);
+  void SetWindowMode(const std::string& app_id,
+                     apps::mojom::WindowMode window_mode) override;
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // apps::mojom::Publisher overrides.
@@ -153,8 +155,6 @@
                                  int command_id,
                                  const std::string& shortcut_id,
                                  int64_t display_id) override;
-  void SetWindowMode(const std::string& app_id,
-                     apps::mojom::WindowMode window_mode) override;
 
   void GetAppShortcutMenuModel(const std::string& app_id,
                                apps::mojom::MenuItemsPtr menu_items,
diff --git a/chrome/browser/web_applications/daily_metrics_helper.cc b/chrome/browser/web_applications/daily_metrics_helper.cc
index bbaab87..aa21c568 100644
--- a/chrome/browser/web_applications/daily_metrics_helper.cc
+++ b/chrome/browser/web_applications/daily_metrics_helper.cc
@@ -175,7 +175,7 @@
       prefs->GetDictionary(prefs::kWebAppsDailyMetrics);
   if (!urls_to_features)
     return;
-  DictionaryPrefUpdate update(prefs, prefs::kWebAppsDailyMetrics);
+  DictionaryPrefUpdateDeprecated update(prefs, prefs::kWebAppsDailyMetrics);
   update->DictClear();
 }
 
@@ -198,7 +198,7 @@
   }
 
   std::unique_ptr<DictionaryValue> record_dict = RecordToDict(record);
-  DictionaryPrefUpdate update(prefs, prefs::kWebAppsDailyMetrics);
+  DictionaryPrefUpdateDeprecated update(prefs, prefs::kWebAppsDailyMetrics);
 
   update->SetKey(url, std::move(*record_dict));
 }
diff --git a/chrome/browser/web_applications/extensions/externally_installed_web_app_prefs_unittest.cc b/chrome/browser/web_applications/extensions/externally_installed_web_app_prefs_unittest.cc
index 1b6a984..0e76af6 100644
--- a/chrome/browser/web_applications/extensions/externally_installed_web_app_prefs_unittest.cc
+++ b/chrome/browser/web_applications/extensions/externally_installed_web_app_prefs_unittest.cc
@@ -193,8 +193,8 @@
 
 TEST_F(ExternallyInstalledWebAppPrefsTest, OldPrefFormat) {
   // Set up the old format for this pref {url -> app_id}.
-  DictionaryPrefUpdate update(profile()->GetPrefs(),
-                              prefs::kWebAppsExtensionIDs);
+  DictionaryPrefUpdateDeprecated update(profile()->GetPrefs(),
+                                        prefs::kWebAppsExtensionIDs);
   update->SetKey("https://example.com", base::Value("add_id_string"));
   // This should not crash on invalid pref data.
   EXPECT_FALSE(ExternallyInstalledWebAppPrefs(profile()->GetPrefs())
diff --git a/chrome/browser/web_applications/externally_installed_web_app_prefs.cc b/chrome/browser/web_applications/externally_installed_web_app_prefs.cc
index af7c04a..d8467cbf 100644
--- a/chrome/browser/web_applications/externally_installed_web_app_prefs.cc
+++ b/chrome/browser/web_applications/externally_installed_web_app_prefs.cc
@@ -94,7 +94,8 @@
 // TODO(crbug.com/1236159): Can be removed after M99.
 void ExternallyInstalledWebAppPrefs::RemoveTerminalPWA(
     PrefService* pref_service) {
-  DictionaryPrefUpdate update(pref_service, prefs::kWebAppsExtensionIDs);
+  DictionaryPrefUpdateDeprecated update(pref_service,
+                                        prefs::kWebAppsExtensionIDs);
   update->RemoveKey("chrome-untrusted://terminal/html/pwa.html");
 }
 
@@ -164,7 +165,8 @@
   dict.SetKey(kExtensionId, base::Value(app_id));
   dict.SetKey(kInstallSource, base::Value(static_cast<int>(install_source)));
 
-  DictionaryPrefUpdate update(pref_service_, prefs::kWebAppsExtensionIDs);
+  DictionaryPrefUpdateDeprecated update(pref_service_,
+                                        prefs::kWebAppsExtensionIDs);
   update->SetKey(url.spec(), std::move(dict));
 }
 
@@ -215,7 +217,8 @@
 
   DCHECK(pref_service_->GetDictionary(prefs::kWebAppsExtensionIDs)
              ->FindKey(url.spec()));
-  DictionaryPrefUpdate update(pref_service_, prefs::kWebAppsExtensionIDs);
+  DictionaryPrefUpdateDeprecated update(pref_service_,
+                                        prefs::kWebAppsExtensionIDs);
   base::Value* map = update.Get();
 
   auto* app_entry = map->FindKey(url.spec());
diff --git a/chrome/browser/web_applications/install_bounce_metric.cc b/chrome/browser/web_applications/install_bounce_metric.cc
index 6556e83d..02f5ba6 100644
--- a/chrome/browser/web_applications/install_bounce_metric.cc
+++ b/chrome/browser/web_applications/install_bounce_metric.cc
@@ -87,7 +87,8 @@
   dict.SetKey(kInstallSource,
               base::Value(static_cast<int>(install_metrics.source)));
 
-  DictionaryPrefUpdate update(pref_service, prefs::kWebAppInstallMetrics);
+  DictionaryPrefUpdateDeprecated update(pref_service,
+                                        prefs::kWebAppInstallMetrics);
   update->SetKey(app_id, std::move(dict));
 }
 
diff --git a/chrome/browser/web_applications/os_integration_manager.cc b/chrome/browser/web_applications/os_integration_manager.cc
index 7991b01..75fe625 100644
--- a/chrome/browser/web_applications/os_integration_manager.cc
+++ b/chrome/browser/web_applications/os_integration_manager.cc
@@ -327,7 +327,7 @@
 }
 
 const apps::FileHandlers* OsIntegrationManager::GetEnabledFileHandlers(
-    const AppId& app_id) {
+    const AppId& app_id) const {
   DCHECK(file_handler_manager_);
   return file_handler_manager_->GetEnabledFileHandlers(app_id);
 }
diff --git a/chrome/browser/web_applications/os_integration_manager.h b/chrome/browser/web_applications/os_integration_manager.h
index 0ceec322..86d8600 100644
--- a/chrome/browser/web_applications/os_integration_manager.h
+++ b/chrome/browser/web_applications/os_integration_manager.h
@@ -152,7 +152,7 @@
 
   // Proxy calls for WebAppFileHandlerManager.
   bool IsFileHandlingAPIAvailable(const AppId& app_id);
-  const apps::FileHandlers* GetEnabledFileHandlers(const AppId& app_id);
+  const apps::FileHandlers* GetEnabledFileHandlers(const AppId& app_id) const;
   const absl::optional<GURL> GetMatchingFileHandlerURL(
       const AppId& app_id,
       const std::vector<base::FilePath>& launch_files);
diff --git a/chrome/browser/web_applications/preinstalled_web_app_utils.cc b/chrome/browser/web_applications/preinstalled_web_app_utils.cc
index 921499a..da12b9e 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_utils.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_utils.cc
@@ -170,7 +170,7 @@
 constexpr char kDisableIfTouchScreenWithStylusNotSupported[] =
     "disable_if_touchscreen_with_stylus_not_supported";
 
-void EnsureContains(ListPrefUpdate& update, base::StringPiece value) {
+void EnsureContains(ListPrefUpdateDeprecated& update, base::StringPiece value) {
   for (const base::Value& item : update->GetList()) {
     if (item.is_string() && item.GetString() == value)
       return;
@@ -602,8 +602,8 @@
 void MarkAppAsMigratedToWebApp(Profile* profile,
                                const std::string& app_id,
                                bool was_migrated) {
-  ListPrefUpdate update(profile->GetPrefs(),
-                        webapps::kWebAppsMigratedPreinstalledApps);
+  ListPrefUpdateDeprecated update(profile->GetPrefs(),
+                                  webapps::kWebAppsMigratedPreinstalledApps);
   if (was_migrated)
     EnsureContains(update, app_id);
   else
@@ -627,8 +627,8 @@
 void SetMigrationRun(Profile* profile,
                      base::StringPiece feature_name,
                      bool was_migrated) {
-  ListPrefUpdate update(profile->GetPrefs(),
-                        prefs::kWebAppsDidMigrateDefaultChromeApps);
+  ListPrefUpdateDeprecated update(profile->GetPrefs(),
+                                  prefs::kWebAppsDidMigrateDefaultChromeApps);
   if (was_migrated)
     EnsureContains(update, feature_name);
   else
@@ -654,8 +654,8 @@
                                       const std::string& app_id) {
   if (WasPreinstalledAppUninstalled(profile, app_id))
     return;
-  ListPrefUpdate update(profile->GetPrefs(),
-                        prefs::kWebAppsUninstalledDefaultChromeApps);
+  ListPrefUpdateDeprecated update(profile->GetPrefs(),
+                                  prefs::kWebAppsUninstalledDefaultChromeApps);
   EnsureContains(update, app_id);
 }
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_browsertest.cc b/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_browsertest.cc
index d084a30..a2007e7c 100644
--- a/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_browsertest.cc
+++ b/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_browsertest.cc
@@ -1428,8 +1428,9 @@
   ASSERT_FALSE(
       GetManager().GetAppIdForSystemApp(SystemAppType::SETTINGS).has_value());
   {
-    ListPrefUpdate update(TestingBrowserProcess::GetGlobal()->local_state(),
-                          policy::policy_prefs::kSystemFeaturesDisableList);
+    ListPrefUpdateDeprecated update(
+        TestingBrowserProcess::GetGlobal()->local_state(),
+        policy::policy_prefs::kSystemFeaturesDisableList);
     base::ListValue* list = update.Get();
     list->Append(policy::SystemFeature::kOsSettings);
   }
@@ -1444,8 +1445,9 @@
               GetAppIconKey(*settings_id)->icon_effects);
 
   {
-    ListPrefUpdate update(TestingBrowserProcess::GetGlobal()->local_state(),
-                          policy::policy_prefs::kSystemFeaturesDisableList);
+    ListPrefUpdateDeprecated update(
+        TestingBrowserProcess::GetGlobal()->local_state(),
+        policy::policy_prefs::kSystemFeaturesDisableList);
     base::ListValue* list = update.Get();
     list->ClearList();
   }
@@ -1466,8 +1468,9 @@
   EXPECT_EQ(apps::mojom::Readiness::kReady, GetAppReadiness(*settings_id));
 
   {
-    ListPrefUpdate update(TestingBrowserProcess::GetGlobal()->local_state(),
-                          policy::policy_prefs::kSystemFeaturesDisableList);
+    ListPrefUpdateDeprecated update(
+        TestingBrowserProcess::GetGlobal()->local_state(),
+        policy::policy_prefs::kSystemFeaturesDisableList);
     base::ListValue* list = update.Get();
     list->Append(policy::SystemFeature::kOsSettings);
   }
@@ -1480,8 +1483,9 @@
               GetAppIconKey(*settings_id)->icon_effects);
 
   {
-    ListPrefUpdate update(TestingBrowserProcess::GetGlobal()->local_state(),
-                          policy::policy_prefs::kSystemFeaturesDisableList);
+    ListPrefUpdateDeprecated update(
+        TestingBrowserProcess::GetGlobal()->local_state(),
+        policy::policy_prefs::kSystemFeaturesDisableList);
     base::ListValue* list = update.Get();
     list->ClearList();
   }
diff --git a/chrome/browser/web_applications/test/fake_web_app_file_handler_manager.cc b/chrome/browser/web_applications/test/fake_web_app_file_handler_manager.cc
index 2a3447d..c19be307 100644
--- a/chrome/browser/web_applications/test/fake_web_app_file_handler_manager.cc
+++ b/chrome/browser/web_applications/test/fake_web_app_file_handler_manager.cc
@@ -15,11 +15,11 @@
 FakeWebAppFileHandlerManager::~FakeWebAppFileHandlerManager() = default;
 
 const apps::FileHandlers* FakeWebAppFileHandlerManager::GetAllFileHandlers(
-    const AppId& app_id) {
+    const AppId& app_id) const {
   if (!base::Contains(file_handlers_, app_id))
     return nullptr;
 
-  return &file_handlers_[app_id];
+  return &file_handlers_.at(app_id);
 }
 
 void FakeWebAppFileHandlerManager::InstallFileHandler(const AppId& app_id,
diff --git a/chrome/browser/web_applications/test/fake_web_app_file_handler_manager.h b/chrome/browser/web_applications/test/fake_web_app_file_handler_manager.h
index fb1d1ee4..19cc8c5 100644
--- a/chrome/browser/web_applications/test/fake_web_app_file_handler_manager.h
+++ b/chrome/browser/web_applications/test/fake_web_app_file_handler_manager.h
@@ -26,7 +26,8 @@
       delete;
   ~FakeWebAppFileHandlerManager() override;
 
-  const apps::FileHandlers* GetAllFileHandlers(const AppId& app_id) override;
+  const apps::FileHandlers* GetAllFileHandlers(
+      const AppId& app_id) const override;
 
   using AcceptMap = std::map<std::string, base::flat_set<std::string>>;
   // Installs a file handler for |app_id| with the action url |handler|,
diff --git a/chrome/browser/web_applications/url_handler_prefs.cc b/chrome/browser/web_applications/url_handler_prefs.cc
index 140d4e0..1c6f8e8 100644
--- a/chrome/browser/web_applications/url_handler_prefs.cc
+++ b/chrome/browser/web_applications/url_handler_prefs.cc
@@ -581,7 +581,8 @@
   DCHECK(choice != UrlHandlerSavedChoice::kInBrowser ||
          (app_id == nullptr && profile_path == nullptr));
 
-  DictionaryPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
+  DictionaryPrefUpdateDeprecated update(local_state,
+                                        prefs::kWebAppsUrlHandlerInfo);
   base::Value* const pref_value = update.Get();
   if (!pref_value || !pref_value->is_dict())
     return;
@@ -713,7 +714,8 @@
   if (profile_path.empty() || url_handlers.empty())
     return;
 
-  DictionaryPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
+  DictionaryPrefUpdateDeprecated update(local_state,
+                                        prefs::kWebAppsUrlHandlerInfo);
   base::Value* const pref_value = update.Get();
   if (!pref_value || !pref_value->is_dict())
     return;
@@ -758,7 +760,8 @@
                   const base::FilePath& profile_path,
                   apps::UrlHandlers new_url_handlers,
                   const base::Time& time) {
-  DictionaryPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
+  DictionaryPrefUpdateDeprecated update(local_state,
+                                        prefs::kWebAppsUrlHandlerInfo);
   base::Value* const pref_value = update.Get();
   if (!pref_value || !pref_value->is_dict())
     return;
@@ -851,7 +854,8 @@
   if (app_id.empty() || profile_path.empty())
     return;
 
-  DictionaryPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
+  DictionaryPrefUpdateDeprecated update(local_state,
+                                        prefs::kWebAppsUrlHandlerInfo);
   base::Value* const pref_value = update.Get();
   if (!pref_value || !pref_value->is_dict())
     return;
@@ -864,7 +868,8 @@
   if (profile_path.empty())
     return;
 
-  DictionaryPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
+  DictionaryPrefUpdateDeprecated update(local_state,
+                                        prefs::kWebAppsUrlHandlerInfo);
   base::Value* const pref_value = update.Get();
   if (!pref_value || !pref_value->is_dict())
     return;
@@ -898,7 +903,8 @@
 }
 
 void Clear(PrefService* local_state) {
-  DictionaryPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
+  DictionaryPrefUpdateDeprecated update(local_state,
+                                        prefs::kWebAppsUrlHandlerInfo);
   base::Value* const pref_value = update.Get();
   pref_value->DictClear();
 }
@@ -942,7 +948,8 @@
                       bool has_origin_wildcard,
                       const std::string& url_path,
                       const base::Time& time) {
-  DictionaryPrefUpdate update(local_state, prefs::kWebAppsUrlHandlerInfo);
+  DictionaryPrefUpdateDeprecated update(local_state,
+                                        prefs::kWebAppsUrlHandlerInfo);
   base::Value* const pref_value = update.Get();
   if (!pref_value || !pref_value->is_dict())
     return;
diff --git a/chrome/browser/web_applications/web_app_file_handler_manager.cc b/chrome/browser/web_applications/web_app_file_handler_manager.cc
index ca8d68f..c938986 100644
--- a/chrome/browser/web_applications/web_app_file_handler_manager.cc
+++ b/chrome/browser/web_applications/web_app_file_handler_manager.cc
@@ -134,7 +134,7 @@
 }
 
 const apps::FileHandlers* WebAppFileHandlerManager::GetEnabledFileHandlers(
-    const AppId& app_id) {
+    const AppId& app_id) const {
   if (ShouldOsIntegrationBeEnabled(app_id) &&
       IsFileHandlingAPIAvailable(app_id) &&
       !GetRegistrar()->IsAppFileHandlerPermissionBlocked(app_id)) {
@@ -144,7 +144,8 @@
   return nullptr;
 }
 
-bool WebAppFileHandlerManager::IsFileHandlingAPIAvailable(const AppId& app_id) {
+bool WebAppFileHandlerManager::IsFileHandlingAPIAvailable(
+    const AppId& app_id) const {
   if (base::FeatureList::IsEnabled(blink::features::kFileHandlingAPI))
     return true;
 
@@ -186,7 +187,7 @@
 }
 
 const apps::FileHandlers* WebAppFileHandlerManager::GetAllFileHandlers(
-    const AppId& app_id) {
+    const AppId& app_id) const {
   const WebApp* web_app = GetRegistrar()->GetAppById(app_id);
   return web_app && !web_app->file_handlers().empty()
              ? &web_app->file_handlers()
diff --git a/chrome/browser/web_applications/web_app_file_handler_manager.h b/chrome/browser/web_applications/web_app_file_handler_manager.h
index 3fe9af9..45d22ea8 100644
--- a/chrome/browser/web_applications/web_app_file_handler_manager.h
+++ b/chrome/browser/web_applications/web_app_file_handler_manager.h
@@ -73,12 +73,12 @@
   // Gets all enabled file handlers for |app_id|. |nullptr| if the app has no
   // enabled file handlers. Note: The lifetime of the file handlers are tied to
   // the app they belong to.
-  const apps::FileHandlers* GetEnabledFileHandlers(const AppId& app_id);
+  const apps::FileHandlers* GetEnabledFileHandlers(const AppId& app_id) const;
 
   // Determines whether file handling is allowed for |app_id|. This is true if
   // the app has a valid origin trial token for the file handling API or if the
   // FileHandlingAPI flag is enabled.
-  bool IsFileHandlingAPIAvailable(const AppId& app_id);
+  bool IsFileHandlingAPIAvailable(const AppId& app_id) const;
 
   // Returns true when the system supports file type association icons.
   static bool IconsEnabled();
@@ -88,7 +88,8 @@
   // handlers or if app_id was uninstalled.
   // Note: The lifetime of the file handlers are tied to the app they belong to.
   // `virtual` for testing.
-  virtual const apps::FileHandlers* GetAllFileHandlers(const AppId& app_id);
+  virtual const apps::FileHandlers* GetAllFileHandlers(
+      const AppId& app_id) const;
 
  private:
   // Removes file handlers whose origin trials have expired (assuming
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index d748f67..b4bbba29 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -200,6 +200,11 @@
   return *os_integration_manager_;
 }
 
+const OsIntegrationManager& WebAppProvider::os_integration_manager() const {
+  CheckIsConnected();
+  return *os_integration_manager_;
+}
+
 void WebAppProvider::Shutdown() {
   ui_manager_->Shutdown();
   externally_managed_app_manager_->Shutdown();
diff --git a/chrome/browser/web_applications/web_app_provider.h b/chrome/browser/web_applications/web_app_provider.h
index cb826777..b7289e7 100644
--- a/chrome/browser/web_applications/web_app_provider.h
+++ b/chrome/browser/web_applications/web_app_provider.h
@@ -128,6 +128,7 @@
 
   // Manage all OS hooks that need to be deployed during Web Apps install
   OsIntegrationManager& os_integration_manager();
+  const OsIntegrationManager& os_integration_manager() const;
 
   // KeyedService:
   void Shutdown() override;
diff --git a/chrome/browser/webauthn/cablev2_devices.cc b/chrome/browser/webauthn/cablev2_devices.cc
index 9584859..ae982706 100644
--- a/chrome/browser/webauthn/cablev2_devices.cc
+++ b/chrome/browser/webauthn/cablev2_devices.cc
@@ -365,7 +365,7 @@
   // For Incognito/Guest profiles, pairings will only last for the duration of
   // that session. While an argument could be made that it's safe to persist
   // such pairing for longer, this seems like the safe option initially.
-  ListPrefUpdate update(pref_service, kWebAuthnCablePairingsPrefName);
+  ListPrefUpdateDeprecated update(pref_service, kWebAuthnCablePairingsPrefName);
 
   // Find any existing entries with the same public key and replace them. The
   // handshake protocol requires the phone to prove possession of the public
@@ -402,7 +402,7 @@
 void DeletePairingByPublicKey(
     PrefService* pref_service,
     std::array<uint8_t, device::kP256X962Length> public_key) {
-  ListPrefUpdate update(pref_service, kWebAuthnCablePairingsPrefName);
+  ListPrefUpdateDeprecated update(pref_service, kWebAuthnCablePairingsPrefName);
   DeletePairingByPublicKey(update.Get(), base::Base64Encode(public_key));
 }
 
@@ -414,7 +414,7 @@
   const std::string name = FindUniqueName(new_name, existing_names);
   const std::string public_key_base64 = base::Base64Encode(public_key);
 
-  ListPrefUpdate update(pref_service, kWebAuthnCablePairingsPrefName);
+  ListPrefUpdateDeprecated update(pref_service, kWebAuthnCablePairingsPrefName);
   base::Value::ListView list = update.Get()->GetList();
 
   for (base::Value& value : list) {
diff --git a/chrome/browser/webauthn/cablev2_devices_unittest.cc b/chrome/browser/webauthn/cablev2_devices_unittest.cc
index ab5ccd2..ec1046b 100644
--- a/chrome/browser/webauthn/cablev2_devices_unittest.cc
+++ b/chrome/browser/webauthn/cablev2_devices_unittest.cc
@@ -163,11 +163,12 @@
 }
 
 class CableV2DevicesProfileTest : public testing::Test {
+  // The `ScopedFeatureList` must be created before initializing any threads.
+  base::test::ScopedFeatureList scoped_feature_list_{
+      device::kWebAuthPhoneSupport};
   // A `BrowserTaskEnvironment` needs to be in-scope in order to create a
   // `TestingProfile`.
   content::BrowserTaskEnvironment task_environment_;
-  base::test::ScopedFeatureList scoped_feature_list_{
-      device::kWebAuthPhoneSupport};
 };
 
 TEST_F(CableV2DevicesProfileTest, InitiallyEmpty) {
diff --git a/chrome/browser/win/conflicts/incompatible_applications_updater.cc b/chrome/browser/win/conflicts/incompatible_applications_updater.cc
index c760f93..d94c9e4c 100644
--- a/chrome/browser/win/conflicts/incompatible_applications_updater.cc
+++ b/chrome/browser/win/conflicts/incompatible_applications_updater.cc
@@ -135,13 +135,13 @@
 // |state_application_names|.
 void RemoveStaleApplications(
     const std::vector<std::string>& stale_application_names) {
-  // Early exit because DictionaryPrefUpdate will write to the pref even if it
-  // doesn't contain a value.
+  // Early exit because DictionaryPrefUpdateDeprecated will write to the pref
+  // even if it doesn't contain a value.
   if (stale_application_names.empty())
     return;
 
-  DictionaryPrefUpdate update(g_browser_process->local_state(),
-                              prefs::kIncompatibleApplications);
+  DictionaryPrefUpdateDeprecated update(g_browser_process->local_state(),
+                                        prefs::kIncompatibleApplications);
   base::Value* existing_applications = update.Get();
 
   for (const auto& application_name : stale_application_names) {
@@ -209,8 +209,8 @@
   base::Value new_applications = ConvertToDictionary(incompatible_applications);
 
   // Update the existing dictionary.
-  DictionaryPrefUpdate update(g_browser_process->local_state(),
-                              prefs::kIncompatibleApplications);
+  DictionaryPrefUpdateDeprecated update(g_browser_process->local_state(),
+                                        prefs::kIncompatibleApplications);
   base::Value* existing_applications = update.Get();
   for (auto&& element : new_applications.DictItems()) {
     existing_applications->SetKey(std::move(element.first),
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index dba2f4c..6669820 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1641837549-d0239f085154d7cc9f77850079ba2f6daf29ab31.profdata
+chrome-linux-main-1641923982-b421bd00007351f3d3d347fadbedd0d211eadc8c.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 3c115f0..6f16506 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1641837549-99cb88add958b46efe7c3d53cd9d661085911744.profdata
+chrome-mac-main-1641923982-2abdb6ca3337708a0d5c9e656a9973060620435c.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index be4531f..246acbaa 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1641837549-361f98ecb6281f2e7bebc4bd82add6a2f74c5cd0.profdata
+chrome-win32-main-1641934172-6d2a8f926ae50b5d54a7c3db91c1a2f86b151807.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 0ccc3f96..f27f8bbe 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1641837549-7817fc87157570aa369eb47c2891f1931937f6d3.profdata
+chrome-win64-main-1641934172-034527ed13da2733050871a9ff6ab3c9f05b3e2c.profdata
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 7f441d95d..ee5b680 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -635,6 +635,7 @@
       "chrome_paths_lacros.h",
     ]
     deps += [ "//chromeos/crosapi/cpp" ]
+    public_deps += [ "//chromeos/crosapi/mojom" ]
   } else if (is_linux || is_chromeos) {
     sources += [ "chrome_paths_linux.cc" ]
   }
diff --git a/chrome/common/chrome_paths_lacros.cc b/chrome/common/chrome_paths_lacros.cc
index 6f761811..1545a49e 100644
--- a/chrome/common/chrome_paths_lacros.cc
+++ b/chrome/common/chrome_paths_lacros.cc
@@ -11,6 +11,7 @@
 #include "base/system/sys_info.h"
 #include "chrome/common/chrome_paths.h"
 #include "chromeos/crosapi/cpp/crosapi_constants.h"
+#include "chromeos/crosapi/mojom/crosapi.mojom.h"
 
 namespace chrome {
 namespace {
@@ -20,6 +21,9 @@
   base::FilePath downloads_dir;
   // |drivefs| is empty if Drive is not enabled in Ash.
   base::FilePath drivefs;
+  base::FilePath removable_media_dir;
+  base::FilePath android_files_dir;
+  base::FilePath linux_files_dir;
 };
 
 DefaultPaths& GetDefaultPaths() {
@@ -31,7 +35,10 @@
 
 void SetLacrosDefaultPaths(const base::FilePath& documents_dir,
                            const base::FilePath& downloads_dir,
-                           const base::FilePath& drivefs) {
+                           const base::FilePath& drivefs,
+                           const base::FilePath& removable_media_dir,
+                           const base::FilePath& android_files_dir,
+                           const base::FilePath& linux_files_dir) {
   DCHECK(!documents_dir.empty());
   DCHECK(documents_dir.IsAbsolute());
   GetDefaultPaths().documents_dir = documents_dir;
@@ -41,6 +48,34 @@
   GetDefaultPaths().downloads_dir = downloads_dir;
 
   GetDefaultPaths().drivefs = drivefs;
+  GetDefaultPaths().removable_media_dir = removable_media_dir;
+  GetDefaultPaths().android_files_dir = android_files_dir;
+  GetDefaultPaths().linux_files_dir = linux_files_dir;
+}
+
+void SetLacrosDefaultPathsFromInitParams(
+    const crosapi::mojom::BrowserInitParams* init_params) {
+  CHECK(init_params);
+  // default_paths may be null on browser_tests and individual components may be
+  // empty due to version skew between ash and lacros.
+  if (init_params->default_paths) {
+    base::FilePath drivefs_dir;
+    if (init_params->default_paths->drivefs.has_value())
+      drivefs_dir = init_params->default_paths->drivefs.value();
+    base::FilePath removable_media_dir;
+    if (init_params->default_paths->removable_media.has_value())
+      removable_media_dir = init_params->default_paths->removable_media.value();
+    base::FilePath android_files_dir;
+    if (init_params->default_paths->android_files.has_value())
+      android_files_dir = init_params->default_paths->android_files.value();
+    base::FilePath linux_files_dir;
+    if (init_params->default_paths->linux_files.has_value())
+      linux_files_dir = init_params->default_paths->linux_files.value();
+    chrome::SetLacrosDefaultPaths(init_params->default_paths->documents,
+                                  init_params->default_paths->downloads,
+                                  drivefs_dir, removable_media_dir,
+                                  android_files_dir, linux_files_dir);
+  }
 }
 
 void SetDriveFsMountPointPath(const base::FilePath& drivefs) {
@@ -114,4 +149,25 @@
   return true;
 }
 
+bool GetRemovableMediaPath(base::FilePath* result) {
+  if (GetDefaultPaths().removable_media_dir.empty())
+    return false;
+  *result = GetDefaultPaths().removable_media_dir;
+  return true;
+}
+
+bool GetAndroidFilesPath(base::FilePath* result) {
+  if (GetDefaultPaths().android_files_dir.empty())
+    return false;
+  *result = GetDefaultPaths().android_files_dir;
+  return true;
+}
+
+bool GetLinuxFilesPath(base::FilePath* result) {
+  if (GetDefaultPaths().linux_files_dir.empty())
+    return false;
+  *result = GetDefaultPaths().linux_files_dir;
+  return true;
+}
+
 }  // namespace chrome
diff --git a/chrome/common/chrome_paths_lacros.h b/chrome/common/chrome_paths_lacros.h
index 5a6f786..87f6ce3f 100644
--- a/chrome/common/chrome_paths_lacros.h
+++ b/chrome/common/chrome_paths_lacros.h
@@ -5,18 +5,28 @@
 #ifndef CHROME_COMMON_CHROME_PATHS_LACROS_H_
 #define CHROME_COMMON_CHROME_PATHS_LACROS_H_
 
+#include "chromeos/crosapi/mojom/crosapi.mojom.h"
+
 namespace base {
 class FilePath;
 }  // namespace base
 
 namespace chrome {
 
-// Sets the default paths for user documents, downloads and the mount point for
-// Drive. The paths are sent by ash-chrome and are set early in lacros-chrome
-// startup.
+// Sets the default paths for locations that store user controlled content
+// including documents, downloads, DriveFS, removable media, ARC storage
+// and Crostini's home directory. The paths are sent by ash-chrome and
+// are set early in lacros-chrome startup.
 void SetLacrosDefaultPaths(const base::FilePath& documents_dir,
                            const base::FilePath& downloads_dir,
-                           const base::FilePath& drivefs);
+                           const base::FilePath& drivefs,
+                           const base::FilePath& removable_media_dir,
+                           const base::FilePath& android_files_dir,
+                           const base::FilePath& linux_files_dir);
+
+// Sets the default paths from BrowserInitParams received from ash on startup.
+void SetLacrosDefaultPathsFromInitParams(
+    const crosapi::mojom::BrowserInitParams* init_params);
 
 // The drive fs mount point path is sent by ash-chrome, `drivefs` may be empty
 // in case drive is disabled in Ash. `SetDriveFsMountPointPath()` is triggered
@@ -25,6 +35,11 @@
 // Returns false if Drive is not enabled in Ash.
 bool GetDriveFsMountPointPath(base::FilePath* result);
 
+// These paths are sent by ash-chrome at Lacros startup. These return false if
+// the value was not sent (eg. due to API version skew).
+bool GetRemovableMediaPath(base::FilePath* result);
+bool GetAndroidFilesPath(base::FilePath* result);
+bool GetLinuxFilesPath(base::FilePath* result);
 }  // namespace chrome
 
 #endif  // CHROME_COMMON_CHROME_PATHS_LACROS_H_
diff --git a/chrome/common/extensions/chrome_extensions_client.cc b/chrome/common/extensions/chrome_extensions_client.cc
index 7e3471b..c2d5ce0 100644
--- a/chrome/common/extensions/chrome_extensions_client.cc
+++ b/chrome/common/extensions/chrome_extensions_client.cc
@@ -164,7 +164,7 @@
   return webstore_update_url_;
 }
 
-bool ChromeExtensionsClient::IsBlacklistUpdateURL(const GURL& url) const {
+bool ChromeExtensionsClient::IsBlocklistUpdateURL(const GURL& url) const {
   // The extension blocklist URL is returned from the update service and
   // therefore not determined by Chromium. If the location of the blocklist file
   // ever changes, we need to update this function. A DCHECK in the
diff --git a/chrome/common/extensions/chrome_extensions_client.h b/chrome/common/extensions/chrome_extensions_client.h
index e483d36..62081e3b 100644
--- a/chrome/common/extensions/chrome_extensions_client.h
+++ b/chrome/common/extensions/chrome_extensions_client.h
@@ -42,7 +42,7 @@
   bool IsScriptableURL(const GURL& url, std::string* error) const override;
   const GURL& GetWebstoreBaseURL() const override;
   const GURL& GetWebstoreUpdateURL() const override;
-  bool IsBlacklistUpdateURL(const GURL& url) const override;
+  bool IsBlocklistUpdateURL(const GURL& url) const override;
   std::set<base::FilePath> GetBrowserImagePaths(
       const Extension* extension) override;
   void AddOriginAccessPermissions(
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 0efc8a7..93f57d5 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -91,27 +91,6 @@
     "plugins/pdf_plugin_placeholder.h",
     "plugins/plugin_uma.cc",
     "plugins/plugin_uma.h",
-    "subresource_redirect/login_robots_compression_metrics.cc",
-    "subresource_redirect/login_robots_compression_metrics.h",
-    "subresource_redirect/login_robots_decider_agent.cc",
-    "subresource_redirect/login_robots_decider_agent.h",
-    "subresource_redirect/public_image_hints_decider_agent.cc",
-    "subresource_redirect/public_image_hints_decider_agent.h",
-    "subresource_redirect/public_resource_decider.h",
-    "subresource_redirect/public_resource_decider_agent.cc",
-    "subresource_redirect/public_resource_decider_agent.h",
-    "subresource_redirect/robots_rules_parser.cc",
-    "subresource_redirect/robots_rules_parser.h",
-    "subresource_redirect/robots_rules_parser_cache.cc",
-    "subresource_redirect/robots_rules_parser_cache.h",
-    "subresource_redirect/src_video_redirect_url_loader_throttle.cc",
-    "subresource_redirect/src_video_redirect_url_loader_throttle.h",
-    "subresource_redirect/subresource_redirect_params.cc",
-    "subresource_redirect/subresource_redirect_params.h",
-    "subresource_redirect/subresource_redirect_url_loader_throttle.cc",
-    "subresource_redirect/subresource_redirect_url_loader_throttle.h",
-    "subresource_redirect/subresource_redirect_util.cc",
-    "subresource_redirect/subresource_redirect_util.h",
     "sync_encryption_keys_extension.cc",
     "sync_encryption_keys_extension.h",
     "url_loader_throttle_provider_impl.cc",
@@ -187,8 +166,6 @@
     "//components/spellcheck:buildflags",
     "//components/subresource_filter/content/renderer",
     "//components/subresource_filter/core/common",
-    "//components/subresource_redirect/common",
-    "//components/subresource_redirect/proto",
     "//components/sync/driver",
     "//components/translate/content/renderer",
     "//components/translate/core/common",
@@ -467,21 +444,3 @@
     }
   }
 }
-
-fuzzer_test("robots_rules_parser_fuzzer") {
-  sources = [
-    "subresource_redirect/robots_rules_parser.cc",
-    "subresource_redirect/robots_rules_parser.h",
-    "subresource_redirect/robots_rules_parser_fuzzer.cc",
-    "subresource_redirect/subresource_redirect_params.cc",
-    "subresource_redirect/subresource_redirect_params.h",
-  ]
-  deps = [
-    "//base",
-    "//base/test:test_support",
-    "//chrome/common:constants",
-    "//components/subresource_redirect/proto",
-    "//third_party/blink/public/common",
-    "//third_party/icu/fuzzers:fuzzer_support",
-  ]
-}
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 6a29a52..2879bf7 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -55,9 +55,6 @@
 #include "chrome/renderer/plugins/non_loadable_plugin_placeholder.h"
 #include "chrome/renderer/plugins/pdf_plugin_placeholder.h"
 #include "chrome/renderer/plugins/plugin_uma.h"
-#include "chrome/renderer/subresource_redirect/login_robots_decider_agent.h"
-#include "chrome/renderer/subresource_redirect/public_image_hints_decider_agent.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_params.h"
 #include "chrome/renderer/sync_encryption_keys_extension.h"
 #include "chrome/renderer/url_loader_throttle_provider_impl.h"
 #include "chrome/renderer/v8_unwinder.h"
@@ -99,7 +96,6 @@
 #include "components/subresource_filter/content/renderer/subresource_filter_agent.h"
 #include "components/subresource_filter/content/renderer/unverified_ruleset_dealer.h"
 #include "components/subresource_filter/core/common/common_features.h"
-#include "components/subresource_redirect/common/subresource_redirect_features.h"
 #include "components/sync/engine/sync_engine_switches.h"
 #include "components/translate/content/renderer/per_frame_translate_agent.h"
 #include "components/translate/core/common/translate_util.h"
@@ -683,13 +679,6 @@
     subresource_filter_agent->Initialize();
   }
 
-  if (subresource_redirect::ShouldEnablePublicImageHintsBasedCompression()) {
-    new subresource_redirect::PublicImageHintsDeciderAgent(
-        associated_interfaces, render_frame);
-  } else if (subresource_redirect::ShouldEnableRobotsRulesFetching()) {
-    new subresource_redirect::LoginRobotsDeciderAgent(associated_interfaces,
-                                                      render_frame);
-  }
   if (translate::IsSubFrameTranslationEnabled()) {
     new translate::PerFrameTranslateAgent(
         render_frame, ISOLATED_WORLD_ID_TRANSLATE, associated_interfaces);
diff --git a/chrome/renderer/resources/extensions/tab_capture_custom_bindings.js b/chrome/renderer/resources/extensions/tab_capture_custom_bindings.js
index f6594be..bf4287f1 100644
--- a/chrome/renderer/resources/extensions/tab_capture_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/tab_capture_custom_bindings.js
@@ -5,8 +5,6 @@
 // Custom binding for the Tab Capture API.
 
 apiBridge.registerCustomHook(function(bindingsAPI, extensionId) {
-  var apiFunctions = bindingsAPI.apiFunctions;
-
   function proxyToGetUserMedia(callback, response) {
     if (!callback)
       return;
@@ -18,28 +16,25 @@
       return;
     }
 
-    // Convenience function for processing webkitGetUserMedia() error objects to
+    // Convenience function for processing getUserMedia() error objects to
     // provide runtime.lastError messages for the tab capture API.
-    function getErrorMessage(error, fallbackMessage) {
+    const getErrorMessage = (error, fallbackMessage) => {
       if (!error || (typeof error.message !== 'string'))
         return fallbackMessage;
-      return error.message.replace(/(navigator\.)?(webkit)?GetUserMedia/gi,
+      return error.message.replace('navigator.mediaDevices.getUserMedia',
                                    'tabCapture.capture');
-    }
+    };
 
-    var options = {};
+    let constraints = {};
     if (response.audioConstraints)
-      options.audio = response.audioConstraints;
+      constraints.audio = response.audioConstraints;
     if (response.videoConstraints)
-      options.video = response.videoConstraints;
+      constraints.video = response.videoConstraints;
     try {
-      navigator.webkitGetUserMedia(
-          options,
-          function onSuccess(media_stream) {
-            callback(media_stream);
-          },
-          function onError(error) {
-            bindingUtil.runCallbackWithLastError(
+      navigator.mediaDevices.getUserMedia(constraints)
+          .then(callback)
+          .catch(error => {
+              bindingUtil.runCallbackWithLastError(
                 getErrorMessage(error, "Failed to start MediaStream."),
                 $Function.bind(callback, null, null));
           });
@@ -50,5 +45,5 @@
     }
   }
 
-  apiFunctions.setCustomCallback('capture', proxyToGetUserMedia);
+  bindingsAPI.apiFunctions.setCustomCallback('capture', proxyToGetUserMedia);
 });
diff --git a/chrome/renderer/subresource_redirect/DEPS b/chrome/renderer/subresource_redirect/DEPS
deleted file mode 100644
index 53ade89..0000000
--- a/chrome/renderer/subresource_redirect/DEPS
+++ /dev/null
@@ -1,21 +0,0 @@
-include_rules = [
-  "+components/base32",
-  "+services/metrics/public/cpp",
-  "+components/subresource_redirect",
-  "+components/subresource_redirect/proto/robots_rules.pb.h",
-  "+services/network/test/test_utils.h",
-  "+third_party/icu/fuzzers/fuzzer_utils.h",
-]
-
-# These are browser tests that access both code from the browser and the
-# renderer.
-specific_include_rules = {
-  "subresource_redirect_renderer_browsertest\.cc": [
-    "+chrome/browser/data_reduction_proxy",
-    "+chrome/browser/login_detection",
-    "+chrome/browser/profiles",
-    "+chrome/browser/subresource_redirect",
-    "+chrome/browser/ui",
-    "+components/subresource_redirect",
-  ],
-}
diff --git a/chrome/renderer/subresource_redirect/DIR_METADATA b/chrome/renderer/subresource_redirect/DIR_METADATA
deleted file mode 100644
index ce14958..0000000
--- a/chrome/renderer/subresource_redirect/DIR_METADATA
+++ /dev/null
@@ -1,3 +0,0 @@
-monorail: {
-  component: "Internals>Network>DataProxy"
-}
diff --git a/chrome/renderer/subresource_redirect/OWNERS b/chrome/renderer/subresource_redirect/OWNERS
deleted file mode 100644
index 2783dea..0000000
--- a/chrome/renderer/subresource_redirect/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://components/data_reduction_proxy/OWNERS
diff --git a/chrome/renderer/subresource_redirect/login_robots_compression_metrics.cc b/chrome/renderer/subresource_redirect/login_robots_compression_metrics.cc
deleted file mode 100644
index 5cdf8a9..0000000
--- a/chrome/renderer/subresource_redirect/login_robots_compression_metrics.cc
+++ /dev/null
@@ -1,77 +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/renderer/subresource_redirect/login_robots_compression_metrics.h"
-
-#include "content/public/renderer/render_thread.h"
-#include "services/metrics/public/cpp/metrics_utils.h"
-#include "services/metrics/public/cpp/mojo_ukm_recorder.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
-
-namespace subresource_redirect {
-
-LoginRobotsCompressionMetrics::LoginRobotsCompressionMetrics(
-    ukm::SourceId ukm_source_id,
-    const base::TimeTicks& navigation_start_time)
-    : ukm_source_id_(ukm_source_id),
-      navigation_start_time_(navigation_start_time) {}
-
-void LoginRobotsCompressionMetrics::NotifyRequestStart() {
-  request_start_time_ = base::TimeTicks::Now();
-}
-
-void LoginRobotsCompressionMetrics::NotifyRequestSent() {
-  request_sent_time_ = base::TimeTicks::Now();
-}
-
-void LoginRobotsCompressionMetrics::RecordMetricsOnLoadFinished(
-    SubresourceRedirectResult redirect_result,
-    size_t content_length,
-    absl::optional<size_t> ofcl) {
-  base::TimeTicks response_received_time = base::TimeTicks::Now();
-
-  ukm::builders::PublicImageCompressionImageLoad
-      public_image_compression_image_load(ukm_source_id_);
-  public_image_compression_image_load.SetRedirectResult(
-      static_cast<int64_t>(redirect_result));
-  if (!navigation_start_time_.is_null()) {
-    if (!request_start_time_.is_null()) {
-      public_image_compression_image_load.SetNavigationToRequestStart(
-          base::TimeDelta(request_start_time_ - navigation_start_time_)
-              .InMilliseconds());
-    }
-    if (!request_sent_time_.is_null()) {
-      public_image_compression_image_load.SetNavigationToRequestSent(
-          base::TimeDelta(request_sent_time_ - navigation_start_time_)
-              .InMilliseconds());
-    }
-    public_image_compression_image_load.SetNavigationToResponseReceived(
-        base::TimeDelta(response_received_time - navigation_start_time_)
-            .InMilliseconds());
-  }
-  if (!request_start_time_.is_null() && !request_sent_time_.is_null()) {
-    public_image_compression_image_load.SetRobotsRulesFetchLatency(
-        base::TimeDelta(request_sent_time_ - request_start_time_)
-            .InMilliseconds());
-  }
-
-  if (ofcl) {
-    public_image_compression_image_load.SetOriginalBytes(
-        ukm::GetExponentialBucketMin(*ofcl, 1.3));
-    public_image_compression_image_load.SetCompressionPercentage(
-        static_cast<int64_t>(
-            100 - ((content_length / static_cast<float>(*ofcl)) * 100)));
-  } else {
-    public_image_compression_image_load.SetOriginalBytes(
-        ukm::GetExponentialBucketMin(content_length, 1.3));
-  }
-  mojo::PendingRemote<ukm::mojom::UkmRecorderInterface> recorder;
-  content::RenderThread::Get()->BindHostReceiver(
-      recorder.InitWithNewPipeAndPassReceiver());
-  std::unique_ptr<ukm::MojoUkmRecorder> ukm_recorder =
-      std::make_unique<ukm::MojoUkmRecorder>(std::move(recorder));
-  public_image_compression_image_load.Record(ukm_recorder.get());
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/login_robots_compression_metrics.h b/chrome/renderer/subresource_redirect/login_robots_compression_metrics.h
deleted file mode 100644
index a74ec80..0000000
--- a/chrome/renderer/subresource_redirect/login_robots_compression_metrics.h
+++ /dev/null
@@ -1,57 +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_RENDERER_SUBRESOURCE_REDIRECT_LOGIN_ROBOTS_COMPRESSION_METRICS_H_
-#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_LOGIN_ROBOTS_COMPRESSION_METRICS_H_
-
-#include <cstdint>
-#include "base/time/time.h"
-#include "components/subresource_redirect/common/subresource_redirect_result.h"
-#include "services/metrics/public/cpp/ukm_source_id.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-namespace subresource_redirect {
-
-// Holds the subresource redirect compression data use and timing metrics for
-// one resource.
-class LoginRobotsCompressionMetrics {
- public:
-  LoginRobotsCompressionMetrics(ukm::SourceId ukm_source_id,
-                                const base::TimeTicks& navigation_start_time);
-
-  // Notifies the subresource request was started.
-  void NotifyRequestStart();
-
-  // Notifies the subresource request fetch was sent to the origin server or
-  // compression server.
-  void NotifyRequestSent();
-
-  // Records metrics when resource load is complete. |content_length| is the
-  // network response bytes, and |ofcl| is populated for compressed response as
-  // the original full content length of the response sent by compression
-  // server.
-  void RecordMetricsOnLoadFinished(SubresourceRedirectResult redirect_result,
-                                   size_t content_length,
-                                   absl::optional<size_t> ofcl);
-
- private:
-  ukm::SourceId ukm_source_id_;
-
-  // The start time of current navigation.
-  base::TimeTicks navigation_start_time_;
-
-  // The start time of the subresource request. This is the time robots rules
-  // fetch triggered.
-  base::TimeTicks request_start_time_;
-
-  // The time the subresource request was sent to the origin server (when
-  // compression allowed) or the compression server (when compression is
-  // possible). This is the time robots rules have been retrieved from network
-  // or cache and the rules have been applied to the subresource.
-  base::TimeTicks request_sent_time_;
-};
-
-}  // namespace subresource_redirect
-
-#endif  // CHROME_RENDERER_SUBRESOURCE_REDIRECT_LOGIN_ROBOTS_COMPRESSION_METRICS_H_
diff --git a/chrome/renderer/subresource_redirect/login_robots_decider_agent.cc b/chrome/renderer/subresource_redirect/login_robots_decider_agent.cc
deleted file mode 100644
index 2f62e9e..0000000
--- a/chrome/renderer/subresource_redirect/login_robots_decider_agent.cc
+++ /dev/null
@@ -1,192 +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.
-
-#include "chrome/renderer/subresource_redirect/login_robots_decider_agent.h"
-
-#include "base/metrics/field_trial_params.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/strings/strcat.h"
-#include "chrome/renderer/subresource_redirect/robots_rules_parser.h"
-#include "chrome/renderer/subresource_redirect/robots_rules_parser_cache.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_params.h"
-#include "components/subresource_redirect/common/subresource_redirect_features.h"
-#include "components/subresource_redirect/common/subresource_redirect_result.h"
-#include "content/public/renderer/render_frame.h"
-
-namespace subresource_redirect {
-
-namespace {
-
-// Converts the RobotsRulesParser::CheckResult enum to SubresourceRedirectResult
-// enum.
-SubresourceRedirectResult ConvertToRedirectResult(
-    RobotsRulesParser::CheckResult check_result) {
-  switch (check_result) {
-    case RobotsRulesParser::CheckResult::kAllowed:
-      if (ShouldEnableLoginRobotsCheckedImageCompression() &&
-          !ShouldCompressRedirectSubresource()) {
-        return SubresourceRedirectResult::kIneligibleCompressionDisabled;
-      }
-      return SubresourceRedirectResult::kRedirectable;
-    case RobotsRulesParser::CheckResult::kDisallowed:
-    case RobotsRulesParser::CheckResult::kInvalidated:
-    case RobotsRulesParser::CheckResult::kEntryMissing:
-      return SubresourceRedirectResult::kIneligibleRobotsDisallowed;
-    case RobotsRulesParser::CheckResult::kTimedout:
-    case RobotsRulesParser::CheckResult::kDisallowedAfterTimeout:
-      return SubresourceRedirectResult::kIneligibleRobotsTimeout;
-  }
-}
-
-void RecordRedirectResultMetric(SubresourceRedirectResult redirect_result) {
-  base::UmaHistogramEnumeration(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      redirect_result);
-}
-
-}  // namespace
-
-LoginRobotsDeciderAgent::LoginRobotsDeciderAgent(
-    blink::AssociatedInterfaceRegistry* associated_interfaces,
-    content::RenderFrame* render_frame)
-    : PublicResourceDeciderAgent(associated_interfaces, render_frame) {
-  DCHECK(ShouldEnableRobotsRulesFetching());
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-}
-
-LoginRobotsDeciderAgent::~LoginRobotsDeciderAgent() = default;
-
-absl::optional<SubresourceRedirectResult>
-LoginRobotsDeciderAgent::ShouldRedirectSubresource(
-    const GURL& url,
-    ShouldRedirectDecisionCallback callback) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(url.SchemeIsHTTPOrHTTPS());
-  DCHECK(url.is_valid());
-  num_should_redirect_checks_++;
-
-  if (redirect_result_ != SubresourceRedirectResult::kRedirectable) {
-    RecordRedirectResultMetric(redirect_result_);
-    return redirect_result_;
-  }
-
-  if (num_should_redirect_checks_ <=
-      GetFirstKDisableSubresourceRedirectLimit()) {
-    DCHECK_LE(0UL, GetFirstKDisableSubresourceRedirectLimit());
-    RecordRedirectResultMetric(
-        SubresourceRedirectResult::kIneligibleFirstKDisableSubresourceRedirect);
-    return SubresourceRedirectResult::
-        kIneligibleFirstKDisableSubresourceRedirect;
-  }
-  CreateAndFetchRobotsRules(
-      url::Origin::Create(url),
-      num_should_redirect_checks_ <= GetFirstKSubresourceLimit()
-          ? GetRobotsRulesReceiveFirstKSubresourceTimeout()
-          : GetRobotsRulesReceiveTimeout());
-
-  absl::optional<RobotsRulesParser::CheckResult> result =
-      RobotsRulesParserCache::Get().CheckRobotsRules(
-          routing_id(), url,
-          base::BindOnce(
-              &LoginRobotsDeciderAgent::OnShouldRedirectSubresourceResult,
-              weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-  if (result) {
-    SubresourceRedirectResult redirect_result =
-        ConvertToRedirectResult(*result);
-    RecordRedirectResultMetric(redirect_result);
-    return redirect_result;
-  }
-
-  return absl::nullopt;
-}
-
-void LoginRobotsDeciderAgent::OnShouldRedirectSubresourceResult(
-    LoginRobotsDeciderAgent::ShouldRedirectDecisionCallback callback,
-    RobotsRulesParser::CheckResult check_result) {
-  // Verify if the navigation is still allowed to redirect.
-  if (redirect_result_ != SubresourceRedirectResult::kRedirectable) {
-    RecordRedirectResultMetric(redirect_result_);
-    std::move(callback).Run(redirect_result_);
-    return;
-  }
-  SubresourceRedirectResult redirect_result =
-      ConvertToRedirectResult(check_result);
-  RecordRedirectResultMetric(redirect_result);
-  std::move(callback).Run(redirect_result);
-}
-
-void LoginRobotsDeciderAgent::ReadyToCommitNavigation(
-    blink::WebDocumentLoader* document_loader) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  PublicResourceDeciderAgent::ReadyToCommitNavigation(document_loader);
-  if (is_pending_navigation_loggged_in_) {
-    redirect_result_ = *is_pending_navigation_loggged_in_
-                           ? SubresourceRedirectResult::kIneligibleLoginDetected
-                           : SubresourceRedirectResult::kRedirectable;
-    // Clear the logged-in state so it won't be reused for subsequent
-    // navigations.
-    is_pending_navigation_loggged_in_ = absl::nullopt;
-  } else {
-    // Logged-in state was not sent for the current navigation.
-    redirect_result_ = SubresourceRedirectResult::kUnknown;
-  }
-  num_should_redirect_checks_ = 0;
-  // Invalidate the previous requests that were started by previous navigation,
-  // for the current frame.
-  RobotsRulesParserCache::Get().InvalidatePendingRequests(routing_id());
-}
-
-void LoginRobotsDeciderAgent::SetLoggedInState(bool is_logged_in) {
-  // Logged-in state is sent when a new navigation is about to commit in the
-  // browser process. Save this state until the navigation is committed in the
-  // renderer process.
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(!is_pending_navigation_loggged_in_);
-  is_pending_navigation_loggged_in_ = is_logged_in;
-}
-
-void LoginRobotsDeciderAgent::RecordMetricsOnLoadFinished(
-    const GURL& url,
-    int64_t content_length,
-    SubresourceRedirectResult redirect_result) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  // TODO(crbug.com/1148980): Record coverage metrics
-}
-
-void LoginRobotsDeciderAgent::SetCompressPublicImagesHints(
-    mojom::CompressPublicImagesHintsPtr images_hints) {
-  // This mojo from browser process should not be called for robots rules based
-  // subresource compression on non logged-in pages.
-  DCHECK(ShouldEnableRobotsRulesFetching());
-  NOTREACHED();
-}
-
-void LoginRobotsDeciderAgent::NotifyIneligibleBlinkDisallowedSubresource() {
-  num_should_redirect_checks_++;
-}
-
-void LoginRobotsDeciderAgent::CreateAndFetchRobotsRules(
-    const url::Origin& origin,
-    const base::TimeDelta& rules_receive_timeout) {
-  DCHECK(!origin.opaque());
-  RobotsRulesParserCache& robots_rules_parser_cache =
-      RobotsRulesParserCache::Get();
-  if (!robots_rules_parser_cache.DoRobotsRulesParserExist(origin)) {
-    // Create the robots rules parser and start the fetch as well.
-    robots_rules_parser_cache.CreateRobotsRulesParser(origin,
-                                                      rules_receive_timeout);
-    GetSubresourceRedirectServiceRemote()->GetRobotsRules(
-        origin, base::BindOnce(&RobotsRulesParserCache::UpdateRobotsRules,
-                               robots_rules_parser_cache.GetWeakPtr(), origin));
-  }
-}
-
-void LoginRobotsDeciderAgent::PreloadSubresourceOptimizationsForOrigins(
-    const std::vector<blink::WebSecurityOrigin>& origins) {
-  for (const auto& origin : origins) {
-    CreateAndFetchRobotsRules(origin, GetRobotsRulesReceiveTimeout());
-  }
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/login_robots_decider_agent.h b/chrome/renderer/subresource_redirect/login_robots_decider_agent.h
deleted file mode 100644
index ca89663..0000000
--- a/chrome/renderer/subresource_redirect/login_robots_decider_agent.h
+++ /dev/null
@@ -1,96 +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 CHROME_RENDERER_SUBRESOURCE_REDIRECT_LOGIN_ROBOTS_DECIDER_AGENT_H_
-#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_LOGIN_ROBOTS_DECIDER_AGENT_H_
-
-#include "base/memory/weak_ptr.h"
-#include "base/threading/thread_checker.h"
-#include "chrome/renderer/subresource_redirect/public_resource_decider_agent.h"
-#include "chrome/renderer/subresource_redirect/robots_rules_parser.h"
-#include "url/gurl.h"
-#include "url/origin.h"
-
-namespace subresource_redirect {
-
-// The decider agent implementation that allows subresource redirect compression
-// based on robots rules on non-logged-in pages. Currently only handles
-// mainframes.
-// TODO(crbug.com/1149853): Add the logged-in checks.
-class LoginRobotsDeciderAgent : public PublicResourceDeciderAgent {
- public:
-  LoginRobotsDeciderAgent(
-      blink::AssociatedInterfaceRegistry* associated_interfaces,
-      content::RenderFrame* render_frame);
-  ~LoginRobotsDeciderAgent() override;
-
-  LoginRobotsDeciderAgent(const LoginRobotsDeciderAgent&) = delete;
-  LoginRobotsDeciderAgent& operator=(const LoginRobotsDeciderAgent&) = delete;
-
- private:
-  friend class SubresourceRedirectLoginRobotsDeciderAgentTest;
-  friend class SubresourceRedirectLoginRobotsURLLoaderThrottleTest;
-
-  // content::RenderFrameObserver:
-  void ReadyToCommitNavigation(
-      blink::WebDocumentLoader* document_loader) override;
-  void PreloadSubresourceOptimizationsForOrigins(
-      const std::vector<blink::WebSecurityOrigin>& origins) override;
-
-  // mojom::SubresourceRedirectHintsReceiver:
-  void SetCompressPublicImagesHints(
-      mojom::CompressPublicImagesHintsPtr images_hints) override;
-  void SetLoggedInState(bool is_logged_in) override;
-
-  // PublicResourceDeciderAgent:
-  absl::optional<SubresourceRedirectResult> ShouldRedirectSubresource(
-      const GURL& url,
-      ShouldRedirectDecisionCallback callback) override;
-  void RecordMetricsOnLoadFinished(
-      const GURL& url,
-      int64_t content_length,
-      SubresourceRedirectResult redirect_result) override;
-  void NotifyIneligibleBlinkDisallowedSubresource() override;
-
-  // Callback invoked when should redirect check result is available.
-  void OnShouldRedirectSubresourceResult(
-      ShouldRedirectDecisionCallback callback,
-      RobotsRulesParser::CheckResult check_result);
-
-  bool IsMainFrame() const;
-
-  // Creates and starts the fetch of robots rules for |origin| if the rules are
-  // not available. |rules_receive_timeout| is the timeout value for receiving
-  // the fetched rules.
-  void CreateAndFetchRobotsRules(const url::Origin& origin,
-                                 const base::TimeDelta& rules_receive_timeout);
-
-  // Current state of the redirect compression that should be used for the
-  // current navigation.
-  SubresourceRedirectResult redirect_result_ =
-      SubresourceRedirectResult::kUnknown;
-
-  // Tracks the count of subresource redirect allowed checks that happened for
-  // the current navigation. This is used in having a different robots rules
-  // fetch timeout for the first k subresources.
-  size_t num_should_redirect_checks_ = 0;
-
-  // Saves whether the upcoming navigation is logged-in. This is updated via the
-  // SetLoggedInState() mojo which is sent just before the navigation is
-  // committed in the browser process, and used in ReadyToCommitNavigation()
-  // when the navigation is committed in the renderer process. Value of
-  // absl::nullopt means logged-in state hasn't arrived from the browser. This
-  // value should be reset after each navigation commit, so that it won't get
-  // accidentally reused for subsequent navigations.
-  absl::optional<bool> is_pending_navigation_loggged_in_;
-
-  THREAD_CHECKER(thread_checker_);
-
-  // Used to get a weak pointer to |this|.
-  base::WeakPtrFactory<LoginRobotsDeciderAgent> weak_ptr_factory_{this};
-};
-
-}  // namespace subresource_redirect
-
-#endif  // CHROME_RENDERER_SUBRESOURCE_REDIRECT_LOGIN_ROBOTS_DECIDER_AGENT_H_
diff --git a/chrome/renderer/subresource_redirect/login_robots_decider_agent_browsertest.cc b/chrome/renderer/subresource_redirect/login_robots_decider_agent_browsertest.cc
deleted file mode 100644
index 61516153..0000000
--- a/chrome/renderer/subresource_redirect/login_robots_decider_agent_browsertest.cc
+++ /dev/null
@@ -1,252 +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.
-
-#include "base/memory/weak_ptr.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/renderer/subresource_redirect/login_robots_decider_agent.h"
-#include "chrome/renderer/subresource_redirect/robots_rules_parser_cache.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_util.h"
-#include "chrome/test/base/chrome_render_view_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/subresource_redirect/subresource_redirect_test_util.h"
-#include "content/public/renderer/render_frame.h"
-#include "content/public/renderer/render_view.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/mojom/fetch_api.mojom-shared.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
-#include "third_party/blink/public/platform/web_network_state_notifier.h"
-#include "third_party/blink/public/platform/web_url.h"
-#include "third_party/blink/public/platform/web_url_request.h"
-
-namespace subresource_redirect {
-
-// Helper class that exposes a callback for the decider to call, and maintains
-// the redirect result for verification.
-class RedirectResultReceiver {
- public:
-  PublicResourceDecider::ShouldRedirectDecisionCallback GetCallback() {
-    return base::BindOnce(
-        &RedirectResultReceiver::OnShouldRedirectDecisionCallback,
-        weak_ptr_factory_.GetWeakPtr());
-  }
-
-  SubresourceRedirectResult subresource_redirect_result() const {
-    return redirect_result_;
-  }
-
-  bool did_receive_result() const { return did_receive_result_; }
-
- private:
-  void OnShouldRedirectDecisionCallback(
-      SubresourceRedirectResult redirect_result) {
-    EXPECT_FALSE(did_receive_result_);
-    did_receive_result_ = true;
-    redirect_result_ = redirect_result;
-  }
-
-  SubresourceRedirectResult redirect_result_;
-  bool did_receive_result_ = false;
-  base::WeakPtrFactory<RedirectResultReceiver> weak_ptr_factory_{this};
-};
-
-class SubresourceRedirectLoginRobotsDeciderAgentTest
-    : public ChromeRenderViewTest {
- public:
-  void SetUpRobotsRules(const std::string& origin_str,
-                        const std::vector<RobotsRule>& patterns) {
-    const auto origin = url::Origin::Create(GURL(origin_str));
-    RobotsRulesParserCache& robots_rules_parser_cache =
-        RobotsRulesParserCache::Get();
-    if (!robots_rules_parser_cache.DoRobotsRulesParserExist(origin)) {
-      robots_rules_parser_cache.CreateRobotsRulesParser(origin,
-                                                        base::Seconds(2));
-    }
-    EXPECT_TRUE(robots_rules_parser_cache.DoRobotsRulesParserExist(origin));
-    robots_rules_parser_cache.UpdateRobotsRules(
-        origin, GetRobotsRulesProtoString(patterns));
-  }
-
-  bool ShouldRedirectSubresource(const std::string& url) {
-    RedirectResultReceiver result_receiver;
-    auto immediate_result =
-        login_robots_decider_agent_->ShouldRedirectSubresource(
-            GURL(url), result_receiver.GetCallback());
-    if (immediate_result) {
-      // When the reult was sent immediately, callback should not be invoked.
-      EXPECT_FALSE(result_receiver.did_receive_result());
-      return *immediate_result == SubresourceRedirectResult::kRedirectable;
-    }
-    task_environment_.FastForwardBy(base::Seconds(10));
-    return result_receiver.did_receive_result() &&
-           result_receiver.subresource_redirect_result() ==
-               SubresourceRedirectResult::kRedirectable;
-  }
-
-  void SetLoggedInState(bool is_logged_in) {
-    login_robots_decider_agent_->SetLoggedInState(is_logged_in);
-  }
-
-  void ReadyToCommitNavigation() {
-    login_robots_decider_agent_->ReadyToCommitNavigation(nullptr);
-  }
-
-  SubresourceRedirectResult decider_agent_redirect_result() {
-    return login_robots_decider_agent_->redirect_result_;
-  }
-
-  absl::optional<bool> is_pending_navigation_loggged_in() {
-    return login_robots_decider_agent_->is_pending_navigation_loggged_in_;
-  }
-
- protected:
-  void SetUp() override {
-    ChromeRenderViewTest::SetUp();
-    scoped_feature_list_.InitWithFeaturesAndParameters(
-        {{blink::features::kSubresourceRedirect,
-          {{"enable_login_robots_based_compression", "true"},
-           {"enable_public_image_hints_based_compression", "false"}}}},
-        {});
-    login_robots_decider_agent_ = new LoginRobotsDeciderAgent(
-        &associated_interfaces_, GetMainRenderFrame());
-  }
-
-  LoginRobotsDeciderAgent* login_robots_decider_agent_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-TEST_F(SubresourceRedirectLoginRobotsDeciderAgentTest,
-       TestAllowDisallowSingleOrigin) {
-  SetLoggedInState(false);
-  ReadyToCommitNavigation();
-  EXPECT_EQ(SubresourceRedirectResult::kRedirectable,
-            decider_agent_redirect_result());
-  EXPECT_FALSE(is_pending_navigation_loggged_in().has_value());
-
-  SetUpRobotsRules("https://foo.com", {{kRuleTypeAllow, "/public"},
-                                       {kRuleTypeDisallow, "/private"}});
-  EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/public.jpg"));
-  EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/private.jpg"));
-
-  EXPECT_FALSE(ShouldRedirectSubresource("https://m.foo.com/public.jpg"));
-  EXPECT_FALSE(ShouldRedirectSubresource("https://www.foo.com/public.jpg"));
-  EXPECT_FALSE(ShouldRedirectSubresource("https://bar.com/public.jpg"));
-}
-
-TEST_F(SubresourceRedirectLoginRobotsDeciderAgentTest,
-       TestHTTPRulesAreSeparate) {
-  SetLoggedInState(false);
-  ReadyToCommitNavigation();
-  EXPECT_EQ(SubresourceRedirectResult::kRedirectable,
-            decider_agent_redirect_result());
-  EXPECT_FALSE(is_pending_navigation_loggged_in().has_value());
-
-  SetUpRobotsRules("https://foo.com", {{kRuleTypeAllow, "/public"},
-                                       {kRuleTypeDisallow, "/private"}});
-  EXPECT_FALSE(ShouldRedirectSubresource("http://foo.com/public.jpg"));
-
-  SetUpRobotsRules("http://foo.com", {{kRuleTypeAllow, "/public"},
-                                      {kRuleTypeDisallow, "/private"}});
-  EXPECT_TRUE(ShouldRedirectSubresource("http://foo.com/public.jpg"));
-
-  EXPECT_FALSE(ShouldRedirectSubresource("http://m.foo.com/public.jpg"));
-  EXPECT_FALSE(ShouldRedirectSubresource("http://www.foo.com/public.jpg"));
-}
-
-TEST_F(SubresourceRedirectLoginRobotsDeciderAgentTest, TestURLWithArguments) {
-  SetLoggedInState(false);
-  ReadyToCommitNavigation();
-  EXPECT_EQ(SubresourceRedirectResult::kRedirectable,
-            decider_agent_redirect_result());
-  EXPECT_FALSE(is_pending_navigation_loggged_in().has_value());
-
-  SetUpRobotsRules("https://foo.com",
-                   {{kRuleTypeAllow, "/*.jpg$"},
-                    {kRuleTypeDisallow, "/*.png?*arg_disallowed"},
-                    {kRuleTypeAllow, "/*.png"},
-                    {kRuleTypeDisallow, "/*.gif?*arg_disallowed"},
-                    {kRuleTypeAllow, "/*.gif"},
-                    {kRuleTypeDisallow, "/"}});
-  EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/allowed.jpg"));
-  EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/allowed.png"));
-  EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/allowed.gif"));
-  EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/disallowed.jpg?arg"));
-  EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/allowed.png?arg"));
-  EXPECT_TRUE(
-      ShouldRedirectSubresource("https://foo.com/allowed.png?arg_allowed"));
-  EXPECT_TRUE(ShouldRedirectSubresource(
-      "https://foo.com/allowed.png?arg_allowed&arg_alllowed2"));
-  EXPECT_FALSE(ShouldRedirectSubresource(
-      "https://foo.com/disallowed.png?arg_disallowed"));
-  EXPECT_FALSE(ShouldRedirectSubresource(
-      "https://foo.com/disallowed.png?arg_disallowed&arg_disallowed2"));
-}
-
-TEST_F(SubresourceRedirectLoginRobotsDeciderAgentTest,
-       TestRulesAreCaseSensitive) {
-  SetLoggedInState(false);
-  ReadyToCommitNavigation();
-  EXPECT_EQ(SubresourceRedirectResult::kRedirectable,
-            decider_agent_redirect_result());
-  EXPECT_FALSE(is_pending_navigation_loggged_in().has_value());
-
-  SetUpRobotsRules("https://foo.com", {{kRuleTypeAllow, "/allowed"},
-                                       {kRuleTypeAllow, "/CamelCase"},
-                                       {kRuleTypeAllow, "/CAPITALIZE"},
-                                       {kRuleTypeDisallow, "/"}});
-  EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/allowed.jpg"));
-  EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/CamelCase.jpg"));
-  EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/CAPITALIZE.jpg"));
-  EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/Allowed.jpg"));
-  EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/camelcase.jpg"));
-  EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/capitalize.jpg"));
-}
-
-TEST_F(SubresourceRedirectLoginRobotsDeciderAgentTest,
-       TestDisabledWhenLoggedIn) {
-  SetLoggedInState(true);
-  ReadyToCommitNavigation();
-  EXPECT_EQ(SubresourceRedirectResult::kIneligibleLoginDetected,
-            decider_agent_redirect_result());
-  EXPECT_FALSE(is_pending_navigation_loggged_in().has_value());
-
-  SetUpRobotsRules("https://foo.com", {{kRuleTypeAllow, "/public"},
-                                       {kRuleTypeDisallow, "/private"}});
-  EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/public.jpg"));
-  EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/private.jpg"));
-}
-
-// Test that when logged-in state is sent after the navigation commit, it does
-// not take effect.
-TEST_F(SubresourceRedirectLoginRobotsDeciderAgentTest,
-       TestLoggedinStateIgnoredWhenSentOutOfOrder) {
-  SetUpRobotsRules("https://foo.com", {{kRuleTypeAllow, "/public"},
-                                       {kRuleTypeDisallow, "/private"}});
-  ReadyToCommitNavigation();
-  SetLoggedInState(false);
-  EXPECT_EQ(SubresourceRedirectResult::kUnknown,
-            decider_agent_redirect_result());
-  EXPECT_FALSE(*is_pending_navigation_loggged_in());
-  EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/public.jpg"));
-}
-
-TEST_F(SubresourceRedirectLoginRobotsDeciderAgentTest,
-       TestClearedOnNavigation) {
-  SetLoggedInState(false);
-  ReadyToCommitNavigation();
-  EXPECT_EQ(SubresourceRedirectResult::kRedirectable,
-            decider_agent_redirect_result());
-  EXPECT_FALSE(is_pending_navigation_loggged_in().has_value());
-
-  // When a navigation starts the state should be cleared.
-  ReadyToCommitNavigation();
-  EXPECT_EQ(SubresourceRedirectResult::kUnknown,
-            decider_agent_redirect_result());
-  EXPECT_FALSE(is_pending_navigation_loggged_in().has_value());
-  EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/public.jpg"));
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/login_robots_url_loader_throttle_browsertest.cc b/chrome/renderer/subresource_redirect/login_robots_url_loader_throttle_browsertest.cc
deleted file mode 100644
index 7a37f3d..0000000
--- a/chrome/renderer/subresource_redirect/login_robots_url_loader_throttle_browsertest.cc
+++ /dev/null
@@ -1,439 +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.
-
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/renderer/subresource_redirect/login_robots_decider_agent.h"
-#include "chrome/renderer/subresource_redirect/robots_rules_parser_cache.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_util.h"
-#include "chrome/test/base/chrome_render_view_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/subresource_redirect/subresource_redirect_test_util.h"
-#include "content/public/renderer/render_frame.h"
-#include "content/public/renderer/render_view.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/mojom/fetch_api.mojom-shared.h"
-#include "services/network/public/mojom/url_response_head.mojom.h"
-#include "services/network/test/test_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
-#include "third_party/blink/public/platform/web_network_state_notifier.h"
-#include "third_party/blink/public/platform/web_url.h"
-#include "third_party/blink/public/platform/web_url_request.h"
-
-namespace subresource_redirect {
-
-// Possible deferral states for a subresource after sending the
-// WillStartRequest.
-enum WillStartRequestDeferralState {
-  kRedirected,
-  kNotRedirected,
-  kDeferred,
-};
-
-// Holds the url loader throttle and the delegate together.
-class LoginRobotsDeciderInfo : public blink::URLLoaderThrottle::Delegate {
- public:
-  LoginRobotsDeciderInfo(
-      const std::string url,
-      std::unique_ptr<SubresourceRedirectURLLoaderThrottle> throttle)
-      : url_(url), throttle_(std::move(throttle)) {
-    throttle_->set_delegate(this);
-  }
-
-  // Sends WillStartRequest and verifies the deferral state.
-  void SendStartRequestAndVerifyDeferral(
-      WillStartRequestDeferralState expected_deferral) {
-    network::ResourceRequest request;
-    request.url = url_;
-    request.destination = network::mojom::RequestDestination::kImage;
-    request.previews_state = blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON;
-    bool defer = true;
-
-    throttle_->WillStartRequest(&request, &defer);
-    switch (expected_deferral) {
-      case WillStartRequestDeferralState::kDeferred:
-        EXPECT_TRUE(defer);
-        EXPECT_EQ(GetSubresourceURLForURL(url_), request.url);
-        break;
-      case WillStartRequestDeferralState::kRedirected:
-        EXPECT_FALSE(defer);
-        EXPECT_EQ(GetSubresourceURLForURL(url_), request.url);
-        break;
-      case WillStartRequestDeferralState::kNotRedirected:
-        EXPECT_FALSE(defer);
-        EXPECT_EQ(url_, request.url);
-        break;
-      default:
-        NOTREACHED();
-    }
-  }
-
-  // blink::URLLoaderThrottle::Delegate
-  void Resume() override {
-    EXPECT_FALSE(did_resume_);
-    did_resume_ = true;
-  }
-  void RestartWithURLResetAndFlags(int load_flags) override {
-    EXPECT_FALSE(did_restart_with_url_reset_and_flags_);
-    did_restart_with_url_reset_and_flags_ = true;
-  }
-  void CancelWithError(int error_code,
-                       base::StringPiece custom_reason = "") override {
-    NOTIMPLEMENTED();
-  }
-
-  void VerifyWillProcessResponse() {
-    network::mojom::URLResponseHeadPtr head =
-        network::CreateURLResponseHead(net::HTTP_OK);
-    head->headers->SetHeader("Content-Length", "1024");
-    bool defer = false;
-    throttle_->WillProcessResponse(GURL("https://foo.com/img.jpg"), head.get(),
-                                   &defer);
-    EXPECT_FALSE(defer);
-  }
-
-  bool did_resume() const { return did_resume_; }
-  bool did_restart_with_url_reset_and_flags() const {
-    return did_restart_with_url_reset_and_flags_;
-  }
-
- private:
-  GURL url_;
-  std::unique_ptr<SubresourceRedirectURLLoaderThrottle> throttle_;
-
-  // The state of delegate callbacks
-  bool did_resume_ = false;
-  bool did_restart_with_url_reset_and_flags_ = false;
-};
-
-class SubresourceRedirectLoginRobotsURLLoaderThrottleTest
-    : public ChromeRenderViewTest {
- public:
-  void DisableSubresourceRedirectFeature() {
-    scoped_feature_list_.Reset();
-    scoped_feature_list_.InitAndDisableFeature(
-        blink::features::kSubresourceRedirect);
-  }
-
-  void SetUpRobotsRules(const std::string& origin_str,
-                        const std::vector<RobotsRule>& patterns) {
-    const auto origin = url::Origin::Create(GURL(origin_str));
-    RobotsRulesParserCache& robots_rules_parser_cache =
-        RobotsRulesParserCache::Get();
-    if (!robots_rules_parser_cache.DoRobotsRulesParserExist(origin)) {
-      robots_rules_parser_cache.CreateRobotsRulesParser(origin,
-                                                        base::Seconds(2));
-    }
-    EXPECT_TRUE(robots_rules_parser_cache.DoRobotsRulesParserExist(origin));
-    robots_rules_parser_cache.UpdateRobotsRules(
-        origin, GetRobotsRulesProtoString(patterns));
-  }
-
-  std::unique_ptr<SubresourceRedirectURLLoaderThrottle>
-  CreateLoginRobotsDecider(
-      const std::string& url,
-      network::mojom::RequestDestination request_destination,
-      int previews_state) {
-    blink::WebURLRequest request;
-    request.SetUrl(GURL(url));
-    request.SetPreviewsState(previews_state);
-    request.SetRequestDestination(request_destination);
-    auto throttle = SubresourceRedirectURLLoaderThrottle::MaybeCreateThrottle(
-        request, GetMainRenderFrame()->GetRoutingID());
-    EXPECT_TRUE(throttle.get());
-    return throttle;
-  }
-
-  std::unique_ptr<LoginRobotsDeciderInfo> CreateURLLoaderThrottleInfo(
-      const std::string& url) {
-    return std::make_unique<LoginRobotsDeciderInfo>(
-        url, CreateLoginRobotsDecider(
-                 url, network::mojom::RequestDestination::kImage,
-                 blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON));
-  }
-
-  void SetLoggedInState(bool is_logged_in) {
-    login_robots_decider_agent_->SetLoggedInState(is_logged_in);
-    login_robots_decider_agent_->ReadyToCommitNavigation(nullptr);
-  }
-
- protected:
-  void SetUp() override {
-    ChromeRenderViewTest::SetUp();
-    scoped_feature_list_.InitWithFeaturesAndParameters(
-        {{blink::features::kSubresourceRedirect,
-          {{"enable_subresource_server_redirect", "true"},
-           {"enable_login_robots_based_compression", "true"},
-           {"enable_public_image_hints_based_compression", "false"}}}},
-        {});
-    login_robots_decider_agent_ = new LoginRobotsDeciderAgent(
-        &associated_interfaces_, GetMainRenderFrame());
-  }
-
- protected:
-  LoginRobotsDeciderAgent* login_robots_decider_agent_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-  base::HistogramTester histogram_tester_;
-};
-
-TEST_F(SubresourceRedirectLoginRobotsURLLoaderThrottleTest,
-       TestMaybeCreateThrottle) {
-  struct TestCase {
-    bool data_saver_enabled;
-    bool is_subresource_redirect_feature_enabled;
-    network::mojom::RequestDestination destination;
-    int previews_state;
-    const std::string url;
-    bool expected_is_throttle_created;
-  };
-
-  const TestCase kTestCases[]{
-      {true, true, network::mojom::RequestDestination::kImage,
-       blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-       "https://www.test.com/test.jpg", true},
-
-      // Failure cases
-      {false, true, network::mojom::RequestDestination::kImage,
-       blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-       "https://www.test.com/test.jpg", false},
-      {true, false, network::mojom::RequestDestination::kImage,
-       blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-       "https://www.test.com/test.jpg", false},
-      {true, true, network::mojom::RequestDestination::kScript,
-       blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-       "https://www.test.com/test.jpg", false},
-      {true, true, network::mojom::RequestDestination::kImage,
-       blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-       "http://www.test.com/test.jpg", false},
-      {true, true, network::mojom::RequestDestination::kImage,
-       blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-       "http://www.test.com/test.jpg", false},
-      {true, true, network::mojom::RequestDestination::kImage,
-       blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON, "mailto:foo@bar.com",
-       false},
-      {true, true, network::mojom::RequestDestination::kImage,
-       blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-       "data:image/png;base64,"
-       "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/"
-       "w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",
-       false},
-  };
-
-  for (const TestCase& test_case : kTestCases) {
-    blink::WebNetworkStateNotifier::SetSaveDataEnabled(
-        test_case.data_saver_enabled);
-    if (!test_case.is_subresource_redirect_feature_enabled) {
-      DisableSubresourceRedirectFeature();
-    }
-
-    blink::WebURLRequest request;
-    request.SetPreviewsState(test_case.previews_state);
-    request.SetUrl(GURL(test_case.url));
-    request.SetRequestDestination(test_case.destination);
-    EXPECT_EQ(test_case.expected_is_throttle_created,
-              SubresourceRedirectURLLoaderThrottle::MaybeCreateThrottle(
-                  request, GetMainRenderFrame()->GetRoutingID()) != nullptr);
-  }
-}
-
-TEST_F(SubresourceRedirectLoginRobotsURLLoaderThrottleTest,
-       TestGetSubresourceURL) {
-  struct TestCase {
-    int previews_state;
-    bool is_logged_in;
-    std::string original_url;
-    GURL redirected_subresource_url;  // Empty URL means there will be no
-                                      // redirect.
-  };
-
-  const TestCase kTestCases[]{
-      {
-          blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-          false,
-          "https://www.test.com/public_img.jpg",
-          GetSubresourceURLForURL(GURL("https://www.test.com/public_img.jpg")),
-      },
-      {
-          blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-          false,
-          "https://www.test.com/public_img.jpg#anchor",
-          GetSubresourceURLForURL(
-              GURL("https://www.test.com/public_img.jpg#anchor")),
-      },
-      {
-          blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-          false,
-          "https://www.test.com/public_img.jpg?public_arg1=bar&public_arg2",
-          GetSubresourceURLForURL(
-              GURL("https://www.test.com/"
-                   "public_img.jpg?public_arg1=bar&public_arg2")),
-      },
-      // Private images will not be redirected.
-      {
-          blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-          false,
-          "https://www.test.com/private_img.jpg",
-          GURL(),
-      },
-      {
-          blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-          false,
-          "https://www.test.com/public_img.jpg&private_arg1=foo",
-          GURL(),
-      },
-      // No redirection when logged-in
-      {
-          blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-          true,
-          "https://www.test.com/public_img.jpg",
-          GURL(),
-      },
-  };
-  blink::WebNetworkStateNotifier::SetSaveDataEnabled(true);
-
-  SetUpRobotsRules("https://www.test.com", {{kRuleTypeDisallow, "*private"},
-                                            {kRuleTypeAllow, "/public"},
-                                            {kRuleTypeDisallow, ""}});
-
-  for (const TestCase& test_case : kTestCases) {
-    SetLoggedInState(test_case.is_logged_in);
-    auto throttle = CreateLoginRobotsDecider(
-        test_case.original_url, network::mojom::RequestDestination::kImage,
-        test_case.previews_state);
-    network::ResourceRequest request;
-    request.url = GURL(test_case.original_url);
-    request.destination = network::mojom::RequestDestination::kImage;
-    request.previews_state = test_case.previews_state;
-    bool defer = true;
-    throttle->WillStartRequest(&request, &defer);
-
-    EXPECT_FALSE(defer);
-    if (!test_case.redirected_subresource_url.is_empty()) {
-      EXPECT_EQ(request.url, test_case.redirected_subresource_url);
-    } else {
-      EXPECT_EQ(request.url, test_case.original_url);
-    }
-  }
-}
-
-// Tests the cases when robots rules are already sent, before throttles are
-// created.
-TEST_F(SubresourceRedirectLoginRobotsURLLoaderThrottleTest,
-       TestRobotsRulesSentBeforeThrottle) {
-  blink::WebNetworkStateNotifier::SetSaveDataEnabled(true);
-  SetLoggedInState(false);
-
-  SetUpRobotsRules("https://www.test.com",
-                   {{kRuleTypeAllow, "/public"}, {kRuleTypeDisallow, ""}});
-
-  auto throttle_info1 =
-      CreateURLLoaderThrottleInfo("https://www.test.com/public.jpg");
-  auto throttle_info2 =
-      CreateURLLoaderThrottleInfo("https://www.test.com/private.jpg");
-
-  throttle_info1->SendStartRequestAndVerifyDeferral(
-      WillStartRequestDeferralState::kRedirected);
-  throttle_info2->SendStartRequestAndVerifyDeferral(
-      WillStartRequestDeferralState::kNotRedirected);
-  EXPECT_FALSE(throttle_info1->did_resume());
-  EXPECT_FALSE(throttle_info2->did_resume());
-  EXPECT_FALSE(throttle_info1->did_restart_with_url_reset_and_flags());
-  EXPECT_FALSE(throttle_info2->did_restart_with_url_reset_and_flags());
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 2);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kRedirectable, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleRobotsDisallowed, 1);
-}
-
-// Tests the cases when robots rules are sent, after throttles are
-// created.
-TEST_F(SubresourceRedirectLoginRobotsURLLoaderThrottleTest,
-       TestRobotsRulesSentAfterThrottle) {
-  blink::WebNetworkStateNotifier::SetSaveDataEnabled(true);
-  SetLoggedInState(false);
-
-  auto throttle_info1 =
-      CreateURLLoaderThrottleInfo("https://www.test.com/public.jpg");
-  auto throttle_info2 =
-      CreateURLLoaderThrottleInfo("https://www.test.com/private.jpg");
-
-  // Both requests will be deferred until rules are received.
-  throttle_info1->SendStartRequestAndVerifyDeferral(
-      WillStartRequestDeferralState::kDeferred);
-  throttle_info2->SendStartRequestAndVerifyDeferral(
-      WillStartRequestDeferralState::kDeferred);
-  EXPECT_FALSE(throttle_info1->did_resume());
-  EXPECT_FALSE(throttle_info2->did_resume());
-  EXPECT_FALSE(throttle_info1->did_restart_with_url_reset_and_flags());
-  EXPECT_FALSE(throttle_info2->did_restart_with_url_reset_and_flags());
-
-  SetUpRobotsRules("https://www.test.com",
-                   {{kRuleTypeAllow, "/public"}, {kRuleTypeDisallow, ""}});
-  // The public resource should resume loading, with the already modified URL.
-  EXPECT_TRUE(throttle_info1->did_resume());
-  EXPECT_FALSE(throttle_info1->did_restart_with_url_reset_and_flags());
-
-  // The private resource should restart and resume loading with the original
-  // URL.
-  EXPECT_TRUE(throttle_info2->did_restart_with_url_reset_and_flags());
-  EXPECT_TRUE(throttle_info2->did_resume());
-
-  throttle_info1->VerifyWillProcessResponse();
-  throttle_info2->VerifyWillProcessResponse();
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 2);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kRedirectable, 1);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleRobotsDisallowed, 1);
-}
-
-// Tests the cases when robots rules retrieval timesout.
-TEST_F(SubresourceRedirectLoginRobotsURLLoaderThrottleTest,
-       TestRobotsRulesTimeout) {
-  blink::WebNetworkStateNotifier::SetSaveDataEnabled(true);
-  SetLoggedInState(false);
-
-  auto throttle_info1 =
-      CreateURLLoaderThrottleInfo("https://www.test.com/public.jpg");
-  auto throttle_info2 =
-      CreateURLLoaderThrottleInfo("https://www.test.com/private.jpg");
-
-  // Both requests will be deferred until rule retrieval times out.
-  throttle_info1->SendStartRequestAndVerifyDeferral(
-      WillStartRequestDeferralState::kDeferred);
-  throttle_info2->SendStartRequestAndVerifyDeferral(
-      WillStartRequestDeferralState::kDeferred);
-  EXPECT_FALSE(throttle_info1->did_resume());
-  EXPECT_FALSE(throttle_info2->did_resume());
-  EXPECT_FALSE(throttle_info1->did_restart_with_url_reset_and_flags());
-  EXPECT_FALSE(throttle_info2->did_restart_with_url_reset_and_flags());
-
-  task_environment_.FastForwardBy(base::Seconds(10));
-
-  // Both resources should restart and resume loading with the original URL.
-  EXPECT_TRUE(throttle_info1->did_restart_with_url_reset_and_flags());
-  EXPECT_TRUE(throttle_info1->did_resume());
-  EXPECT_TRUE(throttle_info2->did_restart_with_url_reset_and_flags());
-  EXPECT_TRUE(throttle_info2->did_resume());
-
-  throttle_info1->VerifyWillProcessResponse();
-  throttle_info2->VerifyWillProcessResponse();
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 2);
-  histogram_tester_.ExpectBucketCount(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleRobotsTimeout, 2);
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/public_image_hints_decider_agent.cc b/chrome/renderer/subresource_redirect/public_image_hints_decider_agent.cc
deleted file mode 100644
index 0c0f07f..0000000
--- a/chrome/renderer/subresource_redirect/public_image_hints_decider_agent.cc
+++ /dev/null
@@ -1,218 +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.
-
-#include "chrome/renderer/subresource_redirect/public_image_hints_decider_agent.h"
-
-#include "base/metrics/field_trial_params.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_params.h"
-#include "components/subresource_redirect/common/subresource_redirect_features.h"
-#include "content/public/renderer/render_frame.h"
-#include "content/public/renderer/render_thread.h"
-#include "services/metrics/public/cpp/metrics_utils.h"
-#include "services/metrics/public/cpp/mojo_ukm_recorder.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
-#include "third_party/blink/public/web/web_document.h"
-#include "third_party/blink/public/web/web_local_frame.h"
-
-namespace subresource_redirect {
-
-namespace {
-
-// Returns the url spec with username, password, ref fragment stripped to be
-// useful for public URL decision making.
-std::string GetURLForPublicDecision(const GURL& url) {
-  GURL::Replacements rep;
-  rep.ClearRef();
-  rep.ClearPassword();
-  rep.ClearUsername();
-  return url.ReplaceComponents(rep).spec();
-}
-
-}  // namespace
-
-PublicImageHintsDeciderAgent::PublicImageHintsDeciderAgent(
-    blink::AssociatedInterfaceRegistry* associated_interfaces,
-    content::RenderFrame* render_frame)
-    : PublicResourceDeciderAgent(associated_interfaces, render_frame) {
-  DCHECK(ShouldEnablePublicImageHintsBasedCompression());
-}
-
-PublicImageHintsDeciderAgent::~PublicImageHintsDeciderAgent() = default;
-
-bool PublicImageHintsDeciderAgent::IsMainFrame() const {
-  return render_frame()->IsMainFrame();
-}
-
-void PublicImageHintsDeciderAgent::DidStartNavigation(
-    const GURL& url,
-    absl::optional<blink::WebNavigationType> navigation_type) {
-  if (!IsMainFrame())
-    return;
-  // Clear the hints when a navigation starts, so that hints from previous
-  // navigation do not apply in case the same renderframe is reused.
-  public_image_urls_ = absl::nullopt;
-}
-
-void PublicImageHintsDeciderAgent::ReadyToCommitNavigation(
-    blink::WebDocumentLoader* document_loader) {
-  PublicResourceDeciderAgent::ReadyToCommitNavigation(document_loader);
-  if (!IsMainFrame())
-    return;
-  // Its ok to use base::Unretained(this) here since the timer object is owned
-  // by |this|, and the timer and its callback will get deleted when |this| is
-  // destroyed.
-  hint_receive_timeout_timer_.Start(
-      FROM_HERE, base::Seconds(GetHintsReceiveTimeout()),
-      base::BindOnce(&PublicImageHintsDeciderAgent::OnHintsReceiveTimeout,
-                     base::Unretained(this)));
-}
-
-void PublicImageHintsDeciderAgent::OnDestruct() {
-  delete this;
-}
-
-void PublicImageHintsDeciderAgent::SetCompressPublicImagesHints(
-    mojom::CompressPublicImagesHintsPtr images_hints) {
-  if (!IsMainFrame())
-    return;
-  DCHECK(!public_image_urls_);
-  public_image_urls_ = images_hints->image_urls;
-  hint_receive_timeout_timer_.Stop();
-  RecordImageHintsUnavailableMetrics();
-}
-
-absl::optional<SubresourceRedirectResult>
-PublicImageHintsDeciderAgent::ShouldRedirectSubresource(
-    const GURL& url,
-    ShouldRedirectDecisionCallback callback) {
-  if (!IsMainFrame())
-    return SubresourceRedirectResult::kIneligibleSubframeResource;
-  if (!public_image_urls_)
-    return SubresourceRedirectResult::kIneligibleImageHintsUnavailable;
-
-  if (public_image_urls_->find(GetURLForPublicDecision(url)) !=
-      public_image_urls_->end()) {
-    if (!ShouldCompressRedirectSubresource())
-      return SubresourceRedirectResult::kIneligibleCompressionDisabled;
-    return SubresourceRedirectResult::kRedirectable;
-  }
-
-  return SubresourceRedirectResult::kIneligibleMissingInImageHints;
-}
-
-void PublicImageHintsDeciderAgent::RecordMetricsOnLoadFinished(
-    const GURL& url,
-    int64_t content_length,
-    SubresourceRedirectResult redirect_result) {
-  if (redirect_result ==
-      SubresourceRedirectResult::kIneligibleImageHintsUnavailable) {
-    unavailable_image_hints_urls_.insert(
-        std::make_pair(GetURLForPublicDecision(url), content_length));
-    return;
-  }
-  RecordMetrics(content_length, redirect_result);
-}
-
-void PublicImageHintsDeciderAgent::ClearImageHints() {
-  if (public_image_urls_)
-    public_image_urls_->clear();
-}
-
-void PublicImageHintsDeciderAgent::RecordMetrics(
-    int64_t content_length,
-    SubresourceRedirectResult redirect_result) const {
-  // TODO(1156757): Reduce the number of ukm records, by aggregating the
-  // image bytes per SubresourceRedirectResult and then recording once every k
-  // seconds, or k images.
-  if (!render_frame() || !render_frame()->GetWebFrame())
-    return;
-
-  ukm::builders::PublicImageCompressionDataUse
-      public_image_compression_data_use(
-          render_frame()->GetWebFrame()->GetDocument().GetUkmSourceId());
-  content_length = ukm::GetExponentialBucketMin(content_length, 1.3);
-
-  switch (redirect_result) {
-    case SubresourceRedirectResult::kRedirectable:
-      public_image_compression_data_use.SetCompressibleImageBytes(
-          content_length);
-      break;
-    case SubresourceRedirectResult::kIneligibleImageHintsUnavailable:
-      public_image_compression_data_use.SetIneligibleImageHintsUnavailableBytes(
-          content_length);
-      break;
-    case SubresourceRedirectResult::
-        kIneligibleImageHintsUnavailableButRedirectable:
-      public_image_compression_data_use
-          .SetIneligibleImageHintsUnavailableButCompressibleBytes(
-              content_length);
-      break;
-    case SubresourceRedirectResult::
-        kIneligibleImageHintsUnavailableAndMissingInHints:
-      public_image_compression_data_use
-          .SetIneligibleImageHintsUnavailableAndMissingInHintsBytes(
-              content_length);
-      break;
-    case SubresourceRedirectResult::kIneligibleMissingInImageHints:
-      public_image_compression_data_use.SetIneligibleMissingInImageHintsBytes(
-          content_length);
-      break;
-    case SubresourceRedirectResult::kUnknown:
-    case SubresourceRedirectResult::kIneligibleRedirectFailed:
-    case SubresourceRedirectResult::kIneligibleBlinkDisallowed:
-    case SubresourceRedirectResult::kIneligibleSubframeResource:
-    case SubresourceRedirectResult::kIneligibleCompressionDisabled:
-      public_image_compression_data_use.SetIneligibleOtherImageBytes(
-          content_length);
-      break;
-    case SubresourceRedirectResult::kIneligibleRobotsDisallowed:
-    case SubresourceRedirectResult::kIneligibleRobotsTimeout:
-    case SubresourceRedirectResult::kIneligibleLoginDetected:
-    case SubresourceRedirectResult::kIneligibleFirstKDisableSubresourceRedirect:
-      NOTREACHED();
-  }
-  mojo::PendingRemote<ukm::mojom::UkmRecorderInterface> recorder;
-  content::RenderThread::Get()->BindHostReceiver(
-      recorder.InitWithNewPipeAndPassReceiver());
-  std::unique_ptr<ukm::MojoUkmRecorder> ukm_recorder =
-      std::make_unique<ukm::MojoUkmRecorder>(std::move(recorder));
-  public_image_compression_data_use.Record(ukm_recorder.get());
-}
-
-void PublicImageHintsDeciderAgent::OnHintsReceiveTimeout() {
-  RecordImageHintsUnavailableMetrics();
-}
-
-void PublicImageHintsDeciderAgent::RecordImageHintsUnavailableMetrics() {
-  for (const auto& resource : unavailable_image_hints_urls_) {
-    auto redirect_result =
-        SubresourceRedirectResult::kIneligibleImageHintsUnavailable;
-    if (public_image_urls_) {
-      if (public_image_urls_->find(resource.first) !=
-          public_image_urls_->end()) {
-        redirect_result = SubresourceRedirectResult::
-            kIneligibleImageHintsUnavailableButRedirectable;
-      } else {
-        redirect_result = SubresourceRedirectResult::
-            kIneligibleImageHintsUnavailableAndMissingInHints;
-      }
-    }
-    RecordMetrics(resource.second, redirect_result);
-  }
-  unavailable_image_hints_urls_.clear();
-}
-
-void PublicImageHintsDeciderAgent::NotifyCompressedResourceFetchFailed(
-    base::TimeDelta retry_after) {
-  PublicResourceDeciderAgent::NotifyCompressedResourceFetchFailed(retry_after);
-  ClearImageHints();
-}
-
-void PublicImageHintsDeciderAgent::SetLoggedInState(bool is_logged_in) {
-  // This mojo from browser process should not be called for public image hints
-  // based compression.
-  NOTIMPLEMENTED();
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/public_image_hints_decider_agent.h b/chrome/renderer/subresource_redirect/public_image_hints_decider_agent.h
deleted file mode 100644
index 72f1156..0000000
--- a/chrome/renderer/subresource_redirect/public_image_hints_decider_agent.h
+++ /dev/null
@@ -1,94 +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 CHROME_RENDERER_SUBRESOURCE_REDIRECT_PUBLIC_IMAGE_HINTS_DECIDER_AGENT_H_
-#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_PUBLIC_IMAGE_HINTS_DECIDER_AGENT_H_
-
-#include "base/containers/flat_set.h"
-#include "base/timer/timer.h"
-#include "chrome/renderer/subresource_redirect/public_resource_decider_agent.h"
-#include "components/subresource_redirect/common/subresource_redirect_result.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "url/gurl.h"
-
-namespace subresource_redirect {
-
-// The decider agent implementation that allows image compression based on
-// public image hints received. Only redirects public images for mainframes.
-class PublicImageHintsDeciderAgent : public PublicResourceDeciderAgent {
- public:
-  PublicImageHintsDeciderAgent(
-      blink::AssociatedInterfaceRegistry* associated_interfaces,
-      content::RenderFrame* render_frame);
-  ~PublicImageHintsDeciderAgent() override;
-
-  PublicImageHintsDeciderAgent(const PublicImageHintsDeciderAgent&) = delete;
-  PublicImageHintsDeciderAgent& operator=(const PublicImageHintsDeciderAgent&) =
-      delete;
-
- private:
-  friend class SubresourceRedirectPublicImageHintsDeciderAgentTest;
-
-  // content::RenderFrameObserver:
-  void DidStartNavigation(
-      const GURL& url,
-      absl::optional<blink::WebNavigationType> navigation_type) override;
-  void ReadyToCommitNavigation(
-      blink::WebDocumentLoader* document_loader) override;
-  void OnDestruct() override;
-
-  // mojom::SubresourceRedirectHintsReceiver:
-  void SetCompressPublicImagesHints(
-      mojom::CompressPublicImagesHintsPtr images_hints) override;
-  void SetLoggedInState(bool is_logged_in) override;
-
-  // PublicResourceDeciderAgent:
-  absl::optional<SubresourceRedirectResult> ShouldRedirectSubresource(
-      const GURL& url,
-      ShouldRedirectDecisionCallback callback) override;
-  void RecordMetricsOnLoadFinished(
-      const GURL& url,
-      int64_t content_length,
-      SubresourceRedirectResult redirect_result) override;
-  void NotifyCompressedResourceFetchFailed(
-      base::TimeDelta retry_after) override;
-
-  bool IsMainFrame() const;
-
-  // Clears the image hint urls.
-  void ClearImageHints();
-
-  // Called when the hint receive timer expires to flush the metrics for
-  // resources that no hint has been received and clears
-  // |unavailable_image_hints_urls_|.
-  void OnHintsReceiveTimeout();
-
-  // Records the breakdown of bytes to UKM metrics.
-  void RecordMetrics(int64_t content_length,
-                     SubresourceRedirectResult redirect_result) const;
-
-  // Record the breakdown of bytes for unavailable image hints, whether the
-  // hints fetch timed out, the image was not in the delayed hints, or the
-  // image was in the delayed hints.
-  void RecordImageHintsUnavailableMetrics();
-
-  // The raw spec of image urls that are determined public, received from image
-  // hints. Will be absl::nullopt after the navigation starts and until the
-  // hints have been received.
-  absl::optional<base::flat_set<std::string>> public_image_urls_;
-
-  // To trigger the timeout for the hints to be received from the time
-  // navigation starts.
-  base::OneShotTimer hint_receive_timeout_timer_;
-
-  // The urls and resource size of images that were not redirectable due to
-  // image hints were not unavailable at the time of image fetch. This list is
-  // kept until hints are received or hints retrieval times out. This is used
-  // for metrics purposes.
-  base::flat_set<std::pair<std::string, int64_t>> unavailable_image_hints_urls_;
-};
-
-}  // namespace subresource_redirect
-
-#endif  // CHROME_RENDERER_SUBRESOURCE_REDIRECT_PUBLIC_IMAGE_HINTS_DECIDER_AGENT_H_
diff --git a/chrome/renderer/subresource_redirect/public_image_hints_decider_agent_browsertest.cc b/chrome/renderer/subresource_redirect/public_image_hints_decider_agent_browsertest.cc
deleted file mode 100644
index 0fd5a50..0000000
--- a/chrome/renderer/subresource_redirect/public_image_hints_decider_agent_browsertest.cc
+++ /dev/null
@@ -1,254 +0,0 @@
-// 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.
-
-#include "chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h"
-
-#include "base/test/scoped_feature_list.h"
-#include "chrome/renderer/subresource_redirect/public_image_hints_decider_agent.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_util.h"
-#include "chrome/test/base/chrome_render_view_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "content/public/renderer/render_frame.h"
-#include "content/public/renderer/render_view.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/mojom/fetch_api.mojom-shared.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
-#include "third_party/blink/public/platform/web_network_state_notifier.h"
-#include "third_party/blink/public/platform/web_url.h"
-#include "third_party/blink/public/platform/web_url_request.h"
-
-namespace subresource_redirect {
-
-int kRenderFrameID = 1;
-
-class SubresourceRedirectPublicImageHintsDeciderAgentTest
-    : public ChromeRenderViewTest {
- public:
-  void DisableSubresourceRedirectFeature() {
-    scoped_feature_list_.Reset();
-    scoped_feature_list_.InitAndDisableFeature(
-        blink::features::kSubresourceRedirect);
-  }
-
-  void SetCompressPublicImagesHints(
-      const std::vector<std::string>& public_image_urls) {
-    public_image_hints_decider_agent_->SetCompressPublicImagesHints(
-        mojom::CompressPublicImagesHints::New(public_image_urls));
-  }
-
-  std::unique_ptr<SubresourceRedirectURLLoaderThrottle>
-  CreateSubresourceRedirectURLLoaderThrottle(
-      const GURL& url,
-      network::mojom::RequestDestination request_destination,
-      int previews_state) {
-    blink::WebURLRequest request;
-    request.SetUrl(url);
-    request.SetPreviewsState(previews_state);
-    request.SetRequestDestination(request_destination);
-    DCHECK(SubresourceRedirectURLLoaderThrottle::MaybeCreateThrottle(
-               request, GetMainRenderFrame()->GetRoutingID())
-               .get() != nullptr);
-
-    return std::make_unique<SubresourceRedirectURLLoaderThrottle>(
-        GetMainRenderFrame()->GetRoutingID(),
-        previews_state & blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON);
-  }
-
-  void VerifyRedirectResult(SubresourceRedirectURLLoaderThrottle* throttle,
-                            SubresourceRedirectResult redirect_result) {
-    EXPECT_EQ(throttle->redirect_result_, redirect_result);
-  }
-
-  void VerifyRedirectState(SubresourceRedirectURLLoaderThrottle* throttle,
-                           PublicResourceDeciderRedirectState redirect_state) {
-    EXPECT_EQ(throttle->redirect_state_, redirect_state);
-  }
-
- protected:
-  void SetUp() override {
-    ChromeRenderViewTest::SetUp();
-    scoped_feature_list_.InitWithFeaturesAndParameters(
-        {{blink::features::kSubresourceRedirect,
-          {{"enable_subresource_server_redirect", "true"}}}},
-        {});
-    public_image_hints_decider_agent_ =
-        std::make_unique<PublicImageHintsDeciderAgent>(&associated_interfaces_,
-                                                       GetMainRenderFrame());
-  }
-
-  void TearDown() override {
-    public_image_hints_decider_agent_.reset();
-    ChromeRenderViewTest::TearDown();
-  }
-
- private:
-  std::unique_ptr<PublicImageHintsDeciderAgent>
-      public_image_hints_decider_agent_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-TEST_F(SubresourceRedirectPublicImageHintsDeciderAgentTest,
-       TestMaybeCreateThrottle) {
-  struct TestCase {
-    bool data_saver_enabled;
-    bool is_subresource_redirect_feature_enabled;
-    network::mojom::RequestDestination destination;
-    int previews_state;
-    const std::string url;
-    bool expected_is_throttle_created;
-  };
-
-  const TestCase kTestCases[]{
-      {true, true, network::mojom::RequestDestination::kImage,
-       blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-       "https://www.test.com/test.jpg", true},
-
-      // Failure cases
-      {false, true, network::mojom::RequestDestination::kImage,
-       blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-       "https://www.test.com/test.jpg", false},
-      {true, false, network::mojom::RequestDestination::kImage,
-       blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-       "https://www.test.com/test.jpg", false},
-      {true, true, network::mojom::RequestDestination::kScript,
-       blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-       "https://www.test.com/test.jpg", false},
-      {true, true, network::mojom::RequestDestination::kImage,
-       blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-       "http://www.test.com/test.jpg", false},
-  };
-
-  for (const TestCase& test_case : kTestCases) {
-    blink::WebNetworkStateNotifier::SetSaveDataEnabled(
-        test_case.data_saver_enabled);
-    if (!test_case.is_subresource_redirect_feature_enabled) {
-      DisableSubresourceRedirectFeature();
-    }
-
-    blink::WebURLRequest request;
-    request.SetPreviewsState(test_case.previews_state);
-    request.SetUrl(GURL(test_case.url));
-    request.SetRequestDestination(test_case.destination);
-    auto throttle = SubresourceRedirectURLLoaderThrottle::MaybeCreateThrottle(
-        request, kRenderFrameID);
-    EXPECT_EQ(test_case.expected_is_throttle_created, throttle != nullptr);
-    if (throttle)
-      VerifyRedirectResult(throttle.get(),
-                           SubresourceRedirectResult::kRedirectable);
-  }
-}
-
-TEST_F(SubresourceRedirectPublicImageHintsDeciderAgentTest,
-       TestGetSubresourceURL) {
-  struct TestCase {
-    int previews_state;
-    GURL original_url;
-    GURL redirected_subresource_url;  // Empty URL indicates no redirect.
-    SubresourceRedirectResult expected_redirect_result;
-    PublicResourceDeciderRedirectState expected_redirect_state;
-  };
-
-  const TestCase kTestCases[]{
-      {
-          blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-          GURL("https://www.test.com/public_img.jpg"),
-          GetSubresourceURLForURL(GURL("https://www.test.com/public_img.jpg")),
-          SubresourceRedirectResult::kRedirectable,
-          PublicResourceDeciderRedirectState::kRedirectAttempted,
-      },
-      {
-          blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-          GURL("https://www.test.com/public_img.jpg#anchor"),
-          GetSubresourceURLForURL(
-              GURL("https://www.test.com/public_img.jpg#anchor")),
-          SubresourceRedirectResult::kRedirectable,
-          PublicResourceDeciderRedirectState::kRedirectAttempted,
-      },
-      {
-          blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-          GURL("https://www.test.com/"
-               "public_img.jpg?public_arg1=bar&public_arg2"),
-          GetSubresourceURLForURL(
-              GURL("https://www.test.com/"
-                   "public_img.jpg?public_arg1=bar&public_arg2")),
-          SubresourceRedirectResult::kRedirectable,
-          PublicResourceDeciderRedirectState::kRedirectAttempted,
-      },
-      // Private images will not be redirected.
-      {
-          blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-          GURL("https://www.test.com/private_img.jpg"),
-          GURL(),
-          SubresourceRedirectResult::kIneligibleMissingInImageHints,
-          PublicResourceDeciderRedirectState::kRedirectNotAllowedByDecider,
-      },
-      {
-          blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON,
-          GURL("https://www.test.com/public_img.jpg&private_arg1=foo"),
-          GURL(),
-          SubresourceRedirectResult::kIneligibleMissingInImageHints,
-          PublicResourceDeciderRedirectState::kRedirectNotAllowedByDecider,
-      },
-      // Image disallowed by blink will not be redirected.
-      {
-          blink::PreviewsTypes::PREVIEWS_UNSPECIFIED,
-          GURL("https://www.test.com/public_img.jpg"),
-          GURL(),
-          SubresourceRedirectResult::kIneligibleBlinkDisallowed,
-          PublicResourceDeciderRedirectState::kNone,
-      },
-  };
-  blink::WebNetworkStateNotifier::SetSaveDataEnabled(true);
-
-  SetCompressPublicImagesHints(
-      {"https://www.test.com/public_img.jpg",
-       "https://www.test.com/public_img.jpg#anchor",
-       "https://www.test.com/public_img.jpg?public_arg1=bar&public_arg2"});
-
-  for (const TestCase& test_case : kTestCases) {
-    auto throttle = CreateSubresourceRedirectURLLoaderThrottle(
-        test_case.original_url, network::mojom::RequestDestination::kImage,
-        test_case.previews_state);
-    network::ResourceRequest request;
-    request.url = test_case.original_url;
-    request.destination = network::mojom::RequestDestination::kImage;
-    request.previews_state = test_case.previews_state;
-    bool defer = false;
-    throttle->WillStartRequest(&request, &defer);
-
-    EXPECT_FALSE(defer);
-    if (!test_case.redirected_subresource_url.is_empty()) {
-      EXPECT_EQ(request.url, test_case.redirected_subresource_url);
-    } else {
-      EXPECT_EQ(request.url, test_case.original_url);
-    }
-    VerifyRedirectResult(throttle.get(), test_case.expected_redirect_result);
-    VerifyRedirectState(throttle.get(), test_case.expected_redirect_state);
-  }
-}
-
-TEST_F(SubresourceRedirectPublicImageHintsDeciderAgentTest,
-       DeferOverridenToFalse) {
-  blink::WebNetworkStateNotifier::SetSaveDataEnabled(true);
-
-  SetCompressPublicImagesHints({"https://www.test.com/test.jpg"});
-
-  auto throttle = CreateSubresourceRedirectURLLoaderThrottle(
-      GURL("https://www.test.com/test.jpg"),
-      network::mojom::RequestDestination::kImage,
-      blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON);
-  network::ResourceRequest request;
-  request.url = GURL("https://www.test.com/test.jpg");
-  request.destination = network::mojom::RequestDestination::kImage;
-  request.previews_state = blink::PreviewsTypes::SUBRESOURCE_REDIRECT_ON;
-  bool defer = true;
-
-  throttle->WillStartRequest(&request, &defer);
-  EXPECT_FALSE(defer);
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/public_resource_decider.h b/chrome/renderer/subresource_redirect/public_resource_decider.h
deleted file mode 100644
index 1561888..0000000
--- a/chrome/renderer/subresource_redirect/public_resource_decider.h
+++ /dev/null
@@ -1,76 +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 CHROME_RENDERER_SUBRESOURCE_REDIRECT_PUBLIC_RESOURCE_DECIDER_H_
-#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_PUBLIC_RESOURCE_DECIDER_H_
-
-#include "base/bind.h"
-#include "base/time/time.h"
-#include "components/subresource_redirect/common/subresource_redirect_result.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "url/gurl.h"
-
-namespace subresource_redirect {
-
-// Different states the public resource decider redirection can be in.
-enum class PublicResourceDeciderRedirectState {
-  kNone = 0,
-
-  // The redirect decision is pending from the underlying decider.
-  kRedirectDecisionPending = 1,
-
-  // Redirect was disallowed by the underlying decider e.g., robots rules
-  // decider.
-  kRedirectNotAllowedByDecider = 2,
-
-  // The subresource request was redirected to attempt to compress it.
-  kRedirectAttempted = 3,
-
-  // Failed due to http response codes, net errors, and the subresource was
-  // fetched from original origin.
-  kRedirectFailed = 4,
-
-  // The subresource request was allowed to be redirect compressed.
-  kRedirectAllowed = 5,
-
-  kMax = kRedirectAllowed,
-};
-
-// Interface for the decider agent classes that decide whether a resource is
-// considered public and eligible for redirection for compression. Also allows
-// coverage metrics to be recorded for the resource load.
-class PublicResourceDecider {
- public:
-  using ShouldRedirectDecisionCallback =
-      base::OnceCallback<void(SubresourceRedirectResult)>;
-
-  // Determine whether the subresource url should be redirected. When the
-  // determination can be made immediately, the decision should be returned.
-  // Otherwise absl::nullopt should be returned and the callback should be
-  // invoked with the decision asynchronously.
-  virtual absl::optional<SubresourceRedirectResult> ShouldRedirectSubresource(
-      const GURL& url,
-      ShouldRedirectDecisionCallback callback) = 0;
-
-  // Notifies the decider that the subresource load finished.
-  virtual void RecordMetricsOnLoadFinished(
-      const GURL& url,
-      int64_t content_length,
-      SubresourceRedirectResult redirect_result) = 0;
-
-  // Notifies that compressed resource fetch had failed. |retry_after| indicates
-  // the duration returned by the compression server, until which subsequent
-  // fetches to compression server should be blocked.
-  virtual void NotifyCompressedResourceFetchFailed(
-      base::TimeDelta retry_after) = 0;
-
-  // Returns the start time of the current navigation.
-  virtual base::TimeTicks GetNavigationStartTime() const = 0;
-
-  virtual void NotifyIneligibleBlinkDisallowedSubresource() {}
-};
-
-}  // namespace subresource_redirect
-
-#endif  // CHROME_RENDERER_SUBRESOURCE_REDIRECT_PUBLIC_RESOURCE_DECIDER_H_
diff --git a/chrome/renderer/subresource_redirect/public_resource_decider_agent.cc b/chrome/renderer/subresource_redirect/public_resource_decider_agent.cc
deleted file mode 100644
index e285ec28..0000000
--- a/chrome/renderer/subresource_redirect/public_resource_decider_agent.cc
+++ /dev/null
@@ -1,61 +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.
-
-#include "chrome/renderer/subresource_redirect/public_resource_decider_agent.h"
-
-#include "content/public/renderer/render_frame.h"
-
-namespace subresource_redirect {
-
-PublicResourceDeciderAgent::PublicResourceDeciderAgent(
-    blink::AssociatedInterfaceRegistry* associated_interfaces,
-    content::RenderFrame* render_frame)
-    : content::RenderFrameObserver(render_frame),
-      content::RenderFrameObserverTracker<PublicResourceDeciderAgent>(
-          render_frame) {
-  DCHECK(render_frame);
-  // base::Unretained is safe here because |this| is created for the RenderFrame
-  // and never destroyed.
-  associated_interfaces->AddInterface(base::BindRepeating(
-      &PublicResourceDeciderAgent::BindHintsReceiver, base::Unretained(this)));
-}
-
-PublicResourceDeciderAgent::~PublicResourceDeciderAgent() = default;
-
-void PublicResourceDeciderAgent::ReadyToCommitNavigation(
-    blink::WebDocumentLoader* document_loader) {
-  navigation_start_ = base::TimeTicks::Now();
-}
-
-base::TimeTicks PublicResourceDeciderAgent::GetNavigationStartTime() const {
-  return navigation_start_;
-}
-
-void PublicResourceDeciderAgent::OnDestruct() {
-  delete this;
-}
-
-mojo::AssociatedRemote<subresource_redirect::mojom::SubresourceRedirectService>&
-PublicResourceDeciderAgent::GetSubresourceRedirectServiceRemote() {
-  if (!subresource_redirect_service_remote_) {
-    render_frame()->GetRemoteAssociatedInterfaces()->GetInterface(
-        &subresource_redirect_service_remote_);
-  }
-  return subresource_redirect_service_remote_;
-}
-
-void PublicResourceDeciderAgent::NotifyCompressedResourceFetchFailed(
-    base::TimeDelta retry_after) {
-  GetSubresourceRedirectServiceRemote()->NotifyCompressedImageFetchFailed(
-      retry_after);
-}
-
-void PublicResourceDeciderAgent::BindHintsReceiver(
-    mojo::PendingAssociatedReceiver<mojom::SubresourceRedirectHintsReceiver>
-        receiver) {
-  subresource_redirect_hints_receiver_.reset();
-  subresource_redirect_hints_receiver_.Bind(std::move(receiver));
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/public_resource_decider_agent.h b/chrome/renderer/subresource_redirect/public_resource_decider_agent.h
deleted file mode 100644
index ba8221f..0000000
--- a/chrome/renderer/subresource_redirect/public_resource_decider_agent.h
+++ /dev/null
@@ -1,75 +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 CHROME_RENDERER_SUBRESOURCE_REDIRECT_PUBLIC_RESOURCE_DECIDER_AGENT_H_
-#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_PUBLIC_RESOURCE_DECIDER_AGENT_H_
-
-#include "base/bind.h"
-#include "chrome/common/subresource_redirect_service.mojom.h"
-#include "chrome/renderer/subresource_redirect/public_resource_decider.h"
-#include "components/subresource_redirect/common/subresource_redirect_result.h"
-#include "content/public/renderer/render_frame_observer.h"
-#include "content/public/renderer/render_frame_observer_tracker.h"
-#include "mojo/public/cpp/bindings/associated_receiver.h"
-#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
-#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
-#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
-#include "url/gurl.h"
-
-namespace content {
-class RenderFrame;
-}  // namespace content
-
-namespace subresource_redirect {
-
-// Base class for the decider agent classes that decide whether a resource is
-// considered public and eligible for redirection for compression. Also allows
-// coverage metrics to be recorded for the resource load. This class is also the
-// point of contact for browser mojo.
-class PublicResourceDeciderAgent
-    : public content::RenderFrameObserver,
-      public mojom::SubresourceRedirectHintsReceiver,
-      public content::RenderFrameObserverTracker<PublicResourceDeciderAgent>,
-      public PublicResourceDecider {
- public:
-  PublicResourceDeciderAgent(
-      blink::AssociatedInterfaceRegistry* associated_interfaces,
-      content::RenderFrame* render_frame);
-  ~PublicResourceDeciderAgent() override;
-
-  void NotifyCompressedResourceFetchFailed(
-      base::TimeDelta retry_after) override;
-  base::TimeTicks GetNavigationStartTime() const override;
-
-  mojo::AssociatedRemote<
-      subresource_redirect::mojom::SubresourceRedirectService>&
-  GetSubresourceRedirectServiceRemote();
-
-  // content::RenderFrameObserver:
-  void ReadyToCommitNavigation(
-      blink::WebDocumentLoader* document_loader) override;
-
- private:
-  // content::RenderFrameObserver:
-  void OnDestruct() override;
-
-  // Binds the mojo hints receiver pipe.
-  void BindHintsReceiver(
-      mojo::PendingAssociatedReceiver<mojom::SubresourceRedirectHintsReceiver>
-          receiver);
-
-  // Maintains the time the current navigation started.
-  base::TimeTicks navigation_start_;
-
-  mojo::AssociatedReceiver<mojom::SubresourceRedirectHintsReceiver>
-      subresource_redirect_hints_receiver_{this};
-
-  mojo::AssociatedRemote<
-      subresource_redirect::mojom::SubresourceRedirectService>
-      subresource_redirect_service_remote_;
-};
-
-}  // namespace subresource_redirect
-
-#endif  // CHROME_RENDERER_SUBRESOURCE_REDIRECT_PUBLIC_RESOURCE_DECIDER_AGENT_H_
diff --git a/chrome/renderer/subresource_redirect/robots_rules_parser.cc b/chrome/renderer/subresource_redirect/robots_rules_parser.cc
deleted file mode 100644
index 4dfeb88b..0000000
--- a/chrome/renderer/subresource_redirect/robots_rules_parser.cc
+++ /dev/null
@@ -1,201 +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.
-
-#include "chrome/renderer/subresource_redirect/robots_rules_parser.h"
-
-#include "base/callback.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/pattern.h"
-#include "base/strings/strcat.h"
-#include "base/strings/string_util.h"
-#include "base/time/time.h"
-#include "base/timer/elapsed_timer.h"
-#include "components/subresource_redirect/proto/robots_rules.pb.h"
-
-namespace subresource_redirect {
-
-namespace {
-
-// Converts the given robots rule pattern to a pattern compatible with
-// |base::MatchPattern|.
-//
-// Robots rule patterns have slightly different semantics than the pattern
-// inputs for |base::MatchPattern|. They support '*', which matches zero or more
-// of any character, and '$', which matches the end of the input string. On the
-// other hand, |base::MatchPattern| supports '*' and '?', zero or one of any
-// character, but not '$'.
-//
-// Both patterns are anchored at the beginning of the input string, but
-// |base::MatchPattern| is also implicitly anchored to the end of the string.
-// That is, the pattern must match the whole string in order to match.
-//
-// We can convert the given |robots_rule| to one that is compatible with
-// |base::MatchPattern| by taking care of the optionally-present '$' character
-// at the end of the line and backslash-escaping any '?' characters, since they
-// should be interpreted literally.
-std::string ConvertRobotsRuleToGlob(const std::string& robots_rule) {
-  if (robots_rule.empty())
-    return "*";
-  std::string glob(robots_rule);
-  // Any '\' characters that appear in |robots_rule| are meant as literals. To
-  // prevent |base::MatchPattern| from interpreting bare '\' as an escape
-  // character, we replace each bare backslash with two backslashes.
-  base::ReplaceSubstringsAfterOffset(&glob, 0, "\\", "\\\\");
-  // |base::MatchPattern| treats '?' as a special symbol, but robots rule
-  // patterns do not. Escape each occurrence with a backslash.
-  base::ReplaceSubstringsAfterOffset(&glob, 0, "?", "\\?");
-  // |base::MatchPattern| implicitly anchors to the end of the string, but
-  // |robots rule patterns require an explicit trailing '$'.
-  if (glob.back() == '$')
-    glob.pop_back();
-  else
-    glob.push_back('*');
-  return glob;
-}
-
-void RecordRobotsRulesReceiveResultHistogram(
-    RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult result) {
-  UMA_HISTOGRAM_ENUMERATION(
-      "SubresourceRedirect.RobotRulesDecider.ReceiveResult", result);
-}
-
-void RecordRobotsRulesApplyDurationHistogram(base::TimeDelta duration) {
-  UMA_HISTOGRAM_TIMES("SubresourceRedirect.RobotRulesDecider.ApplyDuration",
-                      duration);
-}
-
-}  // namespace
-
-bool RobotsRulesParser::RobotsRule::Match(const std::string& path) const {
-  return base::MatchPattern(path, glob_);
-}
-
-RobotsRulesParser::RobotsRulesParser(
-    const base::TimeDelta& rules_receive_timeout) {
-  // Using base::Unretained(this) is safe here, since the timer
-  // |rules_receive_timeout_timer_| is owned by |this| and destroyed before
-  // |this|.
-  rules_receive_timeout_timer_.Start(
-      FROM_HERE, rules_receive_timeout,
-      base::BindOnce(&RobotsRulesParser::OnRulesReceiveTimeout,
-                     base::Unretained(this)));
-  rules_receive_state_ = RulesReceiveState::kTimerRunning;
-}
-
-RobotsRulesParser::~RobotsRulesParser() {
-  // Consider this as a timeout
-  if (rules_receive_timeout_timer_.IsRunning())
-    rules_receive_timeout_timer_.FireNow();
-}
-
-void RobotsRulesParser::UpdateRobotsRules(
-    const absl::optional<std::string>& rules) {
-  robots_rules_.clear();
-  rules_receive_timeout_timer_.Stop();
-
-  proto::RobotsRules robots_rules;
-  bool is_parse_success = rules && robots_rules.ParseFromString(*rules);
-  RecordRobotsRulesReceiveResultHistogram(
-      is_parse_success
-          ? SubresourceRedirectRobotsRulesReceiveResult::kSuccess
-          : SubresourceRedirectRobotsRulesReceiveResult::kParseError);
-  rules_receive_state_ = is_parse_success ? RulesReceiveState::kSuccess
-                                          : RulesReceiveState::kParseFailed;
-  if (is_parse_success) {
-    robots_rules_.reserve(robots_rules.image_ordered_rules().size());
-    std::set<std::string> allowed_pattern_set;
-    std::set<std::string> disallowed_pattern_set;
-    for (const auto& rule : robots_rules.image_ordered_rules()) {
-      if (rule.has_allowed_pattern()) {
-        const std::string& pattern = rule.allowed_pattern();
-        if (allowed_pattern_set.insert(pattern).second) {
-          robots_rules_.emplace_back(true, ConvertRobotsRuleToGlob(pattern));
-        }
-      } else if (rule.has_disallowed_pattern()) {
-        const std::string& pattern = rule.disallowed_pattern();
-        if (disallowed_pattern_set.insert(pattern).second) {
-          robots_rules_.emplace_back(false, ConvertRobotsRuleToGlob(pattern));
-        }
-      }
-    }
-    UMA_HISTOGRAM_COUNTS_1000("SubresourceRedirect.RobotRulesDecider.Count",
-                              robots_rules_.size());
-  }
-
-  // Respond to the pending requests, even if robots proto parse failed.
-  for (auto& requests : pending_check_requests_) {
-    for (auto& request : requests.second) {
-      std::move(request.first).Run(CheckRobotsRulesImmediate(request.second));
-    }
-  }
-  pending_check_requests_.clear();
-}
-
-absl::optional<RobotsRulesParser::CheckResult>
-RobotsRulesParser::CheckRobotsRules(int routing_id,
-                                    const GURL& url,
-                                    CheckResultCallback callback) {
-  DCHECK(url.is_valid());
-  std::string path_with_query = url.path();
-  if (url.has_query())
-    base::StrAppend(&path_with_query, {"?", url.query()});
-  if (rules_receive_state_ == RulesReceiveState::kTimerRunning) {
-    DCHECK(rules_receive_timeout_timer_.IsRunning());
-    auto it = pending_check_requests_.insert(std::make_pair(
-        routing_id,
-        std::vector<std::pair<CheckResultCallback, std::string>>()));
-    it.first->second.emplace_back(
-        std::make_pair(std::move(callback), path_with_query));
-    return absl::nullopt;
-  }
-  return CheckRobotsRulesImmediate(path_with_query);
-}
-
-RobotsRulesParser::CheckResult RobotsRulesParser::CheckRobotsRulesImmediate(
-    const std::string& url_path) const {
-  if (rules_receive_state_ == RulesReceiveState::kParseFailed)
-    return CheckResult::kDisallowed;
-  if (rules_receive_state_ == RulesReceiveState::kTimeout)
-    return CheckResult::kDisallowedAfterTimeout;
-  DCHECK_EQ(rules_receive_state_, RulesReceiveState::kSuccess);
-
-  base::ElapsedTimer rules_apply_timer;
-  for (const auto& rule : robots_rules_) {
-    if (rule.Match(url_path)) {
-      RecordRobotsRulesApplyDurationHistogram(rules_apply_timer.Elapsed());
-      return rule.is_allow_rule_ ? CheckResult::kAllowed
-                                 : CheckResult::kDisallowed;
-    }
-  }
-  RecordRobotsRulesApplyDurationHistogram(rules_apply_timer.Elapsed());
-
-  // Treat as allowed when none of the allow/disallow rules match.
-  return CheckResult::kAllowed;
-}
-
-void RobotsRulesParser::OnRulesReceiveTimeout() {
-  DCHECK(!rules_receive_timeout_timer_.IsRunning());
-  rules_receive_state_ = RulesReceiveState::kTimeout;
-  for (auto& requests : pending_check_requests_) {
-    for (auto& request : requests.second) {
-      std::move(request.first).Run(CheckResult::kTimedout);
-    }
-  }
-  pending_check_requests_.clear();
-  RecordRobotsRulesReceiveResultHistogram(
-      SubresourceRedirectRobotsRulesReceiveResult::kTimeout);
-}
-
-void RobotsRulesParser::InvalidatePendingRequests(int routing_id) {
-  auto it = pending_check_requests_.find(routing_id);
-  if (it == pending_check_requests_.end())
-    return;
-  for (auto& request : it->second) {
-    std::move(request.first).Run(CheckResult::kInvalidated);
-  }
-  pending_check_requests_.erase(it);
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/robots_rules_parser.h b/chrome/renderer/subresource_redirect/robots_rules_parser.h
deleted file mode 100644
index 322d86a..0000000
--- a/chrome/renderer/subresource_redirect/robots_rules_parser.h
+++ /dev/null
@@ -1,142 +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 CHROME_RENDERER_SUBRESOURCE_REDIRECT_ROBOTS_RULES_PARSER_H_
-#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_ROBOTS_RULES_PARSER_H_
-
-#include <map>
-#include <vector>
-
-#include "base/callback_forward.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "url/gurl.h"
-
-namespace subresource_redirect {
-
-// Holds the robots rules for a singe origin, and enables checking whether an
-// url path is allowed or disallowed.  This check result is returned immediately
-// if available, or delivered via callback asynchronously (when rules are not
-// yet available). Also supports a timeout to receive the robots rules after
-// which it will be treated as a full disallow. This class also tracks the
-// routing_id of the rules check requests, and that is used to invalidate the
-// previous check requests when a new navigation starts in the render frame.
-class RobotsRulesParser {
- public:
-  // The final result of robots rule retrieval.
-  // This should be kept in sync with
-  // SubresourceRedirectRobotsRulesReceiveResult in enums.xml.
-  enum SubresourceRedirectRobotsRulesReceiveResult {
-    kSuccess,     // Received and parsed successfully
-    kTimeout,     // Timeout in waiting for rules
-    kParseError,  // Parsing the received binary proto
-    kMaxValue = kParseError
-  };
-
-  enum CheckResult {
-    kAllowed,                 // The resource URL passed the robots rules check
-    kDisallowed,              // The resource URL failed the robots rules check
-    kTimedout,                // Timeout in retrieving the robots rules
-    kDisallowedAfterTimeout,  // Timeout got triggered already, and the resource
-                              // was disallowed
-    kInvalidated,   // The result check was invalidated, before robots rules are
-                    // received or timeout triggered.
-    kEntryMissing,  // The robots rules parser entry for the origin was missing
-                    // in the cache.
-  };
-
-  enum class RulesReceiveState {
-    // Rules are not received yet, and rules receive timer is still running.
-    // This is the default startup state, and is a non-terminal state.
-    kTimerRunning,
-
-    // Rules are not received, and rules retrieval timeout happened.
-    kTimeout,
-
-    // Rules were received but parsing failed.
-    kParseFailed,
-
-    // Rules were received and are parsed successfully.
-    kSuccess,
-  };
-
-  // Callback to notify the check robot rules result.
-  using CheckResultCallback = base::OnceCallback<void(CheckResult)>;
-
-  // |rules_receive_timeout| is the timeout that should be used for receiving
-  // the rules.
-  explicit RobotsRulesParser(const base::TimeDelta& rules_receive_timeout);
-
-  ~RobotsRulesParser();
-
-  RobotsRulesParser(const RobotsRulesParser&) = delete;
-  RobotsRulesParser& operator=(const RobotsRulesParser&) = delete;
-
-  // Update the robots rules. This causes any pending check requests to be
-  // processed immediately and called with the result.
-  void UpdateRobotsRules(const absl::optional<std::string>& rules);
-
-  // Check whether the URL is allowed or disallowed by robots rules. When the
-  // determination can be made immediately, the decision should be returned.
-  // Otherwise, absl::nullopt should be returned and |callback| will be added to
-  // |pending_check_requests_| and called when a decision can be made, like when
-  // rules are retrieved, a rule fetch times out, etc.
-  //
-  // |routing_id| is the render frame ID for which this URL is requested for.
-  //
-  // The robots rules check will use the |url| path and query parameters, but
-  // the origin, ref fragment, etc are immaterial. Note that the |url| must be
-  // valid.
-  absl::optional<CheckResult> CheckRobotsRules(int routing_id,
-                                               const GURL& url,
-                                               CheckResultCallback callback);
-
-  // Invalidate and cancel the pending requests that were added for
-  // |routing_id|.
-  void InvalidatePendingRequests(int routing_id);
-
- private:
-  friend class SubresourceRedirectRobotsRulesParserTest;
-
-  // Contains one robots.txt rule.
-  struct RobotsRule {
-    RobotsRule(bool is_allow_rule, std::string glob)
-        : is_allow_rule_(is_allow_rule), glob_(std::move(glob)) {}
-
-    bool Match(const std::string& path) const;
-
-    const bool is_allow_rule_;
-    const std::string glob_;
-  };
-
-  // Returns the immediate result of whether the URL path is allowed or
-  // disallowed by robots rules. Should be called only when rules retrieval
-  // state is in a terminal state, i.e., rules receive timer is not running.
-  CheckResult CheckRobotsRulesImmediate(const std::string& url_path) const;
-
-  // Called on rules receive timeout. All pending checks for robots rules are
-  // notified that the timeout expired and the requests known to |this| are
-  // cleared.
-  void OnRulesReceiveTimeout();
-
-  // Current state of the rules retrieval.
-  RulesReceiveState rules_receive_state_;
-
-  // Ordered list of robots rules from longest to shortest.
-  std::vector<RobotsRule> robots_rules_;
-
-  // Contains the requests that are pending for robots rules to be received,
-  // keyed by routing ID. Key is the rouging ID and the value holds the URL path
-  // and the callback.
-  std::map<int, std::vector<std::pair<CheckResultCallback, std::string>>>
-      pending_check_requests_;
-
-  // To trigger the timeout for the robots rules to be received.
-  base::OneShotTimer rules_receive_timeout_timer_;
-};
-
-}  // namespace subresource_redirect
-
-#endif  // CHROME_RENDERER_SUBRESOURCE_REDIRECT_ROBOTS_RULES_PARSER_H_
diff --git a/chrome/renderer/subresource_redirect/robots_rules_parser_cache.cc b/chrome/renderer/subresource_redirect/robots_rules_parser_cache.cc
deleted file mode 100644
index 7326a1f2..0000000
--- a/chrome/renderer/subresource_redirect/robots_rules_parser_cache.cc
+++ /dev/null
@@ -1,64 +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.
-
-#include "chrome/renderer/subresource_redirect/robots_rules_parser_cache.h"
-
-#include "base/check.h"
-#include "base/no_destructor.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_params.h"
-
-namespace subresource_redirect {
-
-// static
-RobotsRulesParserCache& RobotsRulesParserCache::Get() {
-  static base::NoDestructor<RobotsRulesParserCache> instance;
-  return *instance;
-}
-
-RobotsRulesParserCache::RobotsRulesParserCache()
-    : parsers_cache_(MaxRobotsRulesParsersCacheSize()) {}
-
-RobotsRulesParserCache::~RobotsRulesParserCache() = default;
-
-bool RobotsRulesParserCache::DoRobotsRulesParserExist(
-    const url::Origin& origin) {
-  return parsers_cache_.Get(origin) != parsers_cache_.end();
-}
-
-void RobotsRulesParserCache::CreateRobotsRulesParser(
-    const url::Origin& origin,
-    const base::TimeDelta& rules_receive_timeout) {
-  parsers_cache_.Put(
-      origin, std::make_unique<RobotsRulesParser>(rules_receive_timeout));
-}
-
-void RobotsRulesParserCache::UpdateRobotsRules(
-    const url::Origin& origin,
-    const absl::optional<std::string>& rules) {
-  // Update the rules when cache has an entry for the origin. It may be missing
-  // due to cache eviction.
-  auto it = parsers_cache_.Get(origin);
-  if (it != parsers_cache_.end())
-    it->second->UpdateRobotsRules(rules);
-}
-
-absl::optional<RobotsRulesParser::CheckResult>
-RobotsRulesParserCache::CheckRobotsRules(
-    int routing_id,
-    const GURL& url,
-    RobotsRulesParser::CheckResultCallback callback) {
-  DCHECK(url.is_valid());
-  auto it = parsers_cache_.Get(url::Origin::Create(url));
-  if (it == parsers_cache_.end()) {
-    return RobotsRulesParser::CheckResult::kEntryMissing;
-  }
-  return it->second->CheckRobotsRules(routing_id, url, std::move(callback));
-}
-
-void RobotsRulesParserCache::InvalidatePendingRequests(int routing_id) {
-  for (auto& entry : parsers_cache_)
-    entry.second->InvalidatePendingRequests(routing_id);
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/robots_rules_parser_cache.h b/chrome/renderer/subresource_redirect/robots_rules_parser_cache.h
deleted file mode 100644
index 0a420f97..0000000
--- a/chrome/renderer/subresource_redirect/robots_rules_parser_cache.h
+++ /dev/null
@@ -1,74 +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 CHROME_RENDERER_SUBRESOURCE_REDIRECT_ROBOTS_RULES_PARSER_CACHE_H_
-#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_ROBOTS_RULES_PARSER_CACHE_H_
-
-#include "base/containers/lru_cache.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/renderer/subresource_redirect/robots_rules_parser.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "url/gurl.h"
-#include "url/origin.h"
-
-namespace subresource_redirect {
-
-// The store of robots rules parsers keyed by origin
-class RobotsRulesParserCache {
- public:
-  // Returns the robots rules parser cache singleton that is shared across the
-  // RenderFrames in the renderer.
-  static RobotsRulesParserCache& Get();
-
-  RobotsRulesParserCache();
-  ~RobotsRulesParserCache();
-
-  RobotsRulesParserCache(const RobotsRulesParserCache&) = delete;
-  RobotsRulesParserCache& operator=(const RobotsRulesParserCache&) = delete;
-
-  // Returns if robots rules parser is available for |origin|.
-  bool DoRobotsRulesParserExist(const url::Origin& origin);
-
-  // Creates the robots rules parser for |origin| with |rules_receive_timeout|
-  // as the timeout value for receiving rules.
-  void CreateRobotsRulesParser(const url::Origin& origin,
-                               const base::TimeDelta& rules_receive_timeout);
-
-  // Update the robots |rules| to the parser for the |origin|. This update only
-  // happens when the cache already has an entry for |origin|.
-  void UpdateRobotsRules(const url::Origin& origin,
-                         const absl::optional<std::string>& rules);
-
-  // Returns the result of checking whether resource |url| is allowed by robots
-  // rules parser for the url origin. When the determination can be made
-  // immediately, the decision should be returned. Otherwise absl::nullopt
-  // should be returned and the |callback| will be invoked when the decision was
-  // made. Note that |url| must be valid.
-  absl::optional<RobotsRulesParser::CheckResult> CheckRobotsRules(
-      int routing_id,
-      const GURL& url,
-      RobotsRulesParser::CheckResultCallback callback);
-
-  // Invalidate and cancel the pending requests for the robots rules parser.
-  void InvalidatePendingRequests(int routing_id);
-
-  base::WeakPtr<RobotsRulesParserCache> GetWeakPtr() const {
-    return weak_ptr_factory_.GetWeakPtr();
-  }
-
- private:
-  friend class SubresourceRedirectLoginRobotsDeciderAgentTest;
-  friend class SubresourceRedirectLoginRobotsURLLoaderThrottleTest;
-
-  // The underlying cache of robots rules parsers.
-  base::LRUCache<url::Origin, std::unique_ptr<RobotsRulesParser>>
-      parsers_cache_;
-
-  // Used to get a weak pointer to |this|.
-  base::WeakPtrFactory<RobotsRulesParserCache> weak_ptr_factory_{this};
-};
-
-}  // namespace subresource_redirect
-
-#endif  // CHROME_RENDERER_SUBRESOURCE_REDIRECT_ROBOTS_RULES_PARSER_CACHE_H_
diff --git a/chrome/renderer/subresource_redirect/robots_rules_parser_cache_browsertest.cc b/chrome/renderer/subresource_redirect/robots_rules_parser_cache_browsertest.cc
deleted file mode 100644
index a179ae40..0000000
--- a/chrome/renderer/subresource_redirect/robots_rules_parser_cache_browsertest.cc
+++ /dev/null
@@ -1,264 +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 "base/memory/weak_ptr.h"
-#include "base/strings/strcat.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/task_environment.h"
-#include "base/time/time.h"
-#include "chrome/common/subresource_redirect_service.mojom.h"
-#include "chrome/renderer/subresource_redirect/robots_rules_parser.h"
-#include "chrome/renderer/subresource_redirect/robots_rules_parser_cache.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_params.h"
-#include "chrome/test/base/chrome_render_view_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/subresource_redirect/proto/robots_rules.pb.h"
-#include "components/subresource_redirect/subresource_redirect_test_util.h"
-#include "content/public/renderer/render_frame.h"
-#include "content/public/renderer/render_view.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-namespace subresource_redirect {
-
-constexpr char kTestOrigin[] = "https://test.com";
-
-const int kRenderFrameID = 1;
-
-constexpr base::TimeDelta kFetchTimeout = base::Seconds(1);
-
-// Provides a callback to send to robots rules parser, and to check the state of
-// the callback and its result.
-class CheckResultReceiver {
- public:
-  void OnCheckRobotsRulesResult(RobotsRulesParser::CheckResult check_result) {
-    EXPECT_FALSE(did_receive_result_);
-    did_receive_result_ = true;
-    check_result_ = check_result;
-  }
-  RobotsRulesParser::CheckResultCallback GetCallback() {
-    return base::BindOnce(&CheckResultReceiver::OnCheckRobotsRulesResult,
-                          weak_ptr_factory_.GetWeakPtr());
-  }
-  RobotsRulesParser::CheckResult check_result() const { return check_result_; }
-  bool did_receive_result() const { return did_receive_result_; }
-
- private:
-  RobotsRulesParser::CheckResult check_result_;
-  bool did_receive_result_ = false;
-  base::WeakPtrFactory<CheckResultReceiver> weak_ptr_factory_{this};
-};
-
-class SubresourceRedirectRobotsRulesParserCacheTest
-    : public ChromeRenderViewTest {
- public:
-  // Verify robots rules check result is received synchronously with the
-  // expected result.
-  void CheckRobotsRules(const GURL& url,
-                        RobotsRulesParser::CheckResult expected_result,
-                        int render_frame_id = kRenderFrameID) {
-    CheckResultReceiver result_receiver;
-    auto result = robots_rules_parser_cache_.CheckRobotsRules(
-        render_frame_id, url, result_receiver.GetCallback());
-    EXPECT_FALSE(result_receiver.did_receive_result());
-    EXPECT_EQ(expected_result, result);
-  }
-
-  // Verify robots rules check result is received asynchronously, and returns
-  // the receiver that can be used to check the result.
-  std::unique_ptr<CheckResultReceiver> CheckRobotsRulesAsync(
-      const GURL& url,
-      int render_frame_id = kRenderFrameID) {
-    auto result_receiver = std::make_unique<CheckResultReceiver>();
-    EXPECT_FALSE(robots_rules_parser_cache_.CheckRobotsRules(
-        render_frame_id, url, result_receiver->GetCallback()));
-    return result_receiver;
-  }
-
- protected:
-  base::HistogramTester histogram_tester_;
-  RobotsRulesParserCache robots_rules_parser_cache_;
-};
-
-TEST_F(SubresourceRedirectRobotsRulesParserCacheTest, AllowAndDisallowRules) {
-  const url::Origin origin = url::Origin::Create(GURL(kTestOrigin));
-  EXPECT_FALSE(robots_rules_parser_cache_.DoRobotsRulesParserExist(origin));
-  robots_rules_parser_cache_.CreateRobotsRulesParser(origin, kFetchTimeout);
-  EXPECT_TRUE(robots_rules_parser_cache_.DoRobotsRulesParserExist(origin));
-  robots_rules_parser_cache_.UpdateRobotsRules(
-      origin, GetRobotsRulesProtoString(
-                  {{kRuleTypeAllow, "/foo"}, {kRuleTypeDisallow, "/bar"}}));
-
-  CheckRobotsRules(GURL(base::StrCat({kTestOrigin, "/foo.jpg"})),
-                   RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules(GURL(base::StrCat({kTestOrigin, "/bar.jpg"})),
-                   RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules(GURL(kTestOrigin), RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules(GURL(base::StrCat({kTestOrigin, "/"})),
-                   RobotsRulesParser::CheckResult::kAllowed);
-}
-
-TEST_F(SubresourceRedirectRobotsRulesParserCacheTest,
-       AsyncAllowAndDisallowRules) {
-  const url::Origin origin = url::Origin::Create(GURL(kTestOrigin));
-  EXPECT_FALSE(robots_rules_parser_cache_.DoRobotsRulesParserExist(origin));
-  robots_rules_parser_cache_.CreateRobotsRulesParser(origin, kFetchTimeout);
-  EXPECT_TRUE(robots_rules_parser_cache_.DoRobotsRulesParserExist(origin));
-
-  // Async check for robots rules.
-  auto receiver1 =
-      CheckRobotsRulesAsync(GURL(base::StrCat({kTestOrigin, "/foo.jpg"})));
-  auto receiver2 =
-      CheckRobotsRulesAsync(GURL(base::StrCat({kTestOrigin, "/bar.jpg"})));
-  auto receiver3 = CheckRobotsRulesAsync(GURL(kTestOrigin));
-  auto receiver4 =
-      CheckRobotsRulesAsync(GURL(base::StrCat({kTestOrigin, "/"})));
-  EXPECT_FALSE(receiver1->did_receive_result());
-  EXPECT_FALSE(receiver2->did_receive_result());
-  EXPECT_FALSE(receiver3->did_receive_result());
-  EXPECT_FALSE(receiver4->did_receive_result());
-
-  // Send the rules now.
-  robots_rules_parser_cache_.UpdateRobotsRules(
-      origin, GetRobotsRulesProtoString(
-                  {{kRuleTypeAllow, "/foo"}, {kRuleTypeDisallow, "/bar"}}));
-
-  EXPECT_TRUE(receiver1->did_receive_result());
-  EXPECT_TRUE(receiver2->did_receive_result());
-  EXPECT_TRUE(receiver3->did_receive_result());
-  EXPECT_TRUE(receiver4->did_receive_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kAllowed,
-            receiver1->check_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kDisallowed,
-            receiver2->check_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kAllowed,
-            receiver3->check_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kAllowed,
-            receiver4->check_result());
-
-  // Sync check for robots rules
-  CheckRobotsRules(GURL(base::StrCat({kTestOrigin, "/foo.jpg"})),
-                   RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules(GURL(base::StrCat({kTestOrigin, "/bar.jpg"})),
-                   RobotsRulesParser::CheckResult::kDisallowed);
-}
-
-TEST_F(SubresourceRedirectRobotsRulesParserCacheTest, RulesFetchTimeout) {
-  const url::Origin origin = url::Origin::Create(GURL(kTestOrigin));
-  EXPECT_FALSE(robots_rules_parser_cache_.DoRobotsRulesParserExist(origin));
-  robots_rules_parser_cache_.CreateRobotsRulesParser(origin, kFetchTimeout);
-  EXPECT_TRUE(robots_rules_parser_cache_.DoRobotsRulesParserExist(origin));
-
-  // Async check for robots rules.
-  auto receiver1 =
-      CheckRobotsRulesAsync(GURL(base::StrCat({kTestOrigin, "/foo.jpg"})));
-  auto receiver2 =
-      CheckRobotsRulesAsync(GURL(base::StrCat({kTestOrigin, "/bar.jpg"})));
-  auto receiver3 = CheckRobotsRulesAsync(GURL(kTestOrigin));
-  auto receiver4 =
-      CheckRobotsRulesAsync(GURL(base::StrCat({kTestOrigin, "/"})));
-  EXPECT_FALSE(receiver1->did_receive_result());
-  EXPECT_FALSE(receiver2->did_receive_result());
-  EXPECT_FALSE(receiver3->did_receive_result());
-  EXPECT_FALSE(receiver4->did_receive_result());
-
-  // Let rule fetch timeout.
-  task_environment_.FastForwardBy(base::Seconds(10));
-
-  EXPECT_TRUE(receiver1->did_receive_result());
-  EXPECT_TRUE(receiver2->did_receive_result());
-  EXPECT_TRUE(receiver3->did_receive_result());
-  EXPECT_TRUE(receiver4->did_receive_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kTimedout,
-            receiver1->check_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kTimedout,
-            receiver2->check_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kTimedout,
-            receiver3->check_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kTimedout,
-            receiver4->check_result());
-
-  // Sync check for robots rules
-  CheckRobotsRules(GURL(base::StrCat({kTestOrigin, "/foo.jpg"})),
-                   RobotsRulesParser::CheckResult::kDisallowedAfterTimeout);
-  CheckRobotsRules(GURL(base::StrCat({kTestOrigin, "/bar.jpg"})),
-                   RobotsRulesParser::CheckResult::kDisallowedAfterTimeout);
-}
-
-TEST_F(SubresourceRedirectRobotsRulesParserCacheTest,
-       InvalidatePendingRequests) {
-  const url::Origin origin = url::Origin::Create(GURL(kTestOrigin));
-  EXPECT_FALSE(robots_rules_parser_cache_.DoRobotsRulesParserExist(origin));
-  robots_rules_parser_cache_.CreateRobotsRulesParser(origin, kFetchTimeout);
-  EXPECT_TRUE(robots_rules_parser_cache_.DoRobotsRulesParserExist(origin));
-
-  auto receiver1 = CheckRobotsRulesAsync(
-      GURL(base::StrCat({kTestOrigin, "/foo.jpg"})), 1 /* routing_id */);
-  auto receiver2 = CheckRobotsRulesAsync(
-      GURL(base::StrCat({kTestOrigin, "/bar.jpg"})), 2 /* routing_id */);
-
-  EXPECT_FALSE(receiver1->did_receive_result());
-  EXPECT_FALSE(receiver2->did_receive_result());
-
-  robots_rules_parser_cache_.InvalidatePendingRequests(1);
-  EXPECT_TRUE(receiver1->did_receive_result());
-  EXPECT_FALSE(receiver2->did_receive_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kInvalidated,
-            receiver1->check_result());
-
-  robots_rules_parser_cache_.InvalidatePendingRequests(2);
-  EXPECT_TRUE(receiver2->did_receive_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kInvalidated,
-            receiver2->check_result());
-}
-
-TEST_F(SubresourceRedirectRobotsRulesParserCacheTest,
-       CheckRulesBeforeCreating) {
-  const url::Origin origin = url::Origin::Create(GURL(kTestOrigin));
-  EXPECT_FALSE(robots_rules_parser_cache_.DoRobotsRulesParserExist(origin));
-
-  // Check for rules before creating the parser.
-  CheckRobotsRules(GURL(base::StrCat({kTestOrigin, "/foo.jpg"})),
-                   RobotsRulesParser::CheckResult::kEntryMissing);
-
-  // UpdateRobotsRules should have no effect.
-  robots_rules_parser_cache_.UpdateRobotsRules(
-      origin, GetRobotsRulesProtoString(
-                  {{kRuleTypeAllow, "/foo"}, {kRuleTypeDisallow, "/bar"}}));
-  CheckRobotsRules(GURL(base::StrCat({kTestOrigin, "/foo.jpg"})),
-                   RobotsRulesParser::CheckResult::kEntryMissing);
-  CheckRobotsRules(GURL(base::StrCat({kTestOrigin, "/bar.jpg"})),
-                   RobotsRulesParser::CheckResult::kEntryMissing);
-
-  // Now, create the parser.
-  robots_rules_parser_cache_.CreateRobotsRulesParser(origin, kFetchTimeout);
-  EXPECT_TRUE(robots_rules_parser_cache_.DoRobotsRulesParserExist(origin));
-
-  auto receiver1 = CheckRobotsRulesAsync(
-      GURL(base::StrCat({kTestOrigin, "/foo.jpg"})), 1 /* routing_id */);
-  auto receiver2 = CheckRobotsRulesAsync(
-      GURL(base::StrCat({kTestOrigin, "/bar.jpg"})), 2 /* routing_id */);
-
-  EXPECT_FALSE(receiver1->did_receive_result());
-  EXPECT_FALSE(receiver2->did_receive_result());
-
-  // Update robots rules now.
-  robots_rules_parser_cache_.UpdateRobotsRules(
-      origin, GetRobotsRulesProtoString(
-                  {{kRuleTypeAllow, "/foo"}, {kRuleTypeDisallow, "/bar"}}));
-  EXPECT_TRUE(receiver1->did_receive_result());
-  EXPECT_TRUE(receiver2->did_receive_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kAllowed,
-            receiver1->check_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kDisallowed,
-            receiver2->check_result());
-
-  // Sync check for robots rules
-  CheckRobotsRules(GURL(base::StrCat({kTestOrigin, "/foo.jpg"})),
-                   RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules(GURL(base::StrCat({kTestOrigin, "/bar.jpg"})),
-                   RobotsRulesParser::CheckResult::kDisallowed);
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/robots_rules_parser_fuzzer.cc b/chrome/renderer/subresource_redirect/robots_rules_parser_fuzzer.cc
deleted file mode 100644
index fa71be70..0000000
--- a/chrome/renderer/subresource_redirect/robots_rules_parser_fuzzer.cc
+++ /dev/null
@@ -1,63 +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 <fuzzer/FuzzedDataProvider.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include "base/command_line.h"
-#include "base/run_loop.h"
-#include "base/test/task_environment.h"
-#include "base/test/test_timeouts.h"
-#include "base/time/time.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/icu/fuzzers/fuzzer_utils.h"
-#include "url/gurl.h"
-
-#include "chrome/renderer/subresource_redirect/robots_rules_parser.h"
-
-namespace subresource_redirect {
-
-namespace {
-class Environment {
- public:
-  Environment() {
-    base::CommandLine::Init(0, nullptr);
-    TestTimeouts::Initialize();
-    task_env_.emplace();  // Construct after calling TestTimeouts::Initialize().
-  }
-
- private:
-  IcuEnvironment icu;
-  absl::optional<base::test::SingleThreadTaskEnvironment> task_env_;
-};
-
-}  // namespace
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  static Environment env;
-
-  FuzzedDataProvider provider(data, size);
-  std::string rules = provider.ConsumeRandomLengthString();
-  const GURL url(provider.ConsumeRandomLengthString());
-  if (!url.is_valid())  // Required by RobotsRulesParser::CheckRobotsRules.
-    return 0;
-
-  base::RunLoop run_loop;
-
-  RobotsRulesParser parser(base::Seconds(1));
-  parser.UpdateRobotsRules({std::move(rules)});
-  parser.CheckRobotsRules(0, url,
-                          base::BindOnce(
-                              [](base::RepeatingClosure run_loop_quit,
-                                 RobotsRulesParser::CheckResult result) {
-                                std::move(run_loop_quit).Run();
-                              },
-                              run_loop.QuitClosure()));
-
-  run_loop.RunUntilIdle();
-  return 0;
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/robots_rules_parser_unittest.cc b/chrome/renderer/subresource_redirect/robots_rules_parser_unittest.cc
deleted file mode 100644
index 5ca0fdd..0000000
--- a/chrome/renderer/subresource_redirect/robots_rules_parser_unittest.cc
+++ /dev/null
@@ -1,476 +0,0 @@
-// 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.
-
-#include <memory>
-
-#include "base/memory/weak_ptr.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/task_environment.h"
-#include "base/time/time.h"
-#include "chrome/renderer/subresource_redirect/robots_rules_parser.h"
-#include "components/subresource_redirect/proto/robots_rules.pb.h"
-#include "components/subresource_redirect/subresource_redirect_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/features.h"
-
-namespace subresource_redirect {
-
-constexpr char kTestOrigin[] = "https://test.com";
-
-class CheckResultReceiver {
- public:
-  void OnCheckRobotsRulesResult(RobotsRulesParser::CheckResult check_result) {
-    EXPECT_FALSE(did_receive_result_);
-    did_receive_result_ = true;
-    check_result_ = check_result;
-  }
-  RobotsRulesParser::CheckResultCallback GetCallback() {
-    return base::BindOnce(&CheckResultReceiver::OnCheckRobotsRulesResult,
-                          weak_ptr_factory_.GetWeakPtr());
-  }
-  RobotsRulesParser::CheckResult check_result() const { return check_result_; }
-  bool did_receive_result() const { return did_receive_result_; }
-
- private:
-  RobotsRulesParser::CheckResult check_result_;
-  bool did_receive_result_ = false;
-  base::WeakPtrFactory<CheckResultReceiver> weak_ptr_factory_{this};
-};
-
-const int kRenderFrameID = 1;
-
-class SubresourceRedirectRobotsRulesParserTest : public testing::Test {
- public:
-  SubresourceRedirectRobotsRulesParserTest()
-      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
-        robots_rules_parser_(base::Seconds(1)) {}
-  void SetUp() override {
-    scoped_feature_list_.InitWithFeaturesAndParameters(
-        {{blink::features::kSubresourceRedirect, {{}}}}, {});
-  }
-
-  void SetUpRobotsRules(const std::vector<RobotsRule>& patterns) {
-    robots_rules_parser_.UpdateRobotsRules(GetRobotsRulesProtoString(patterns));
-    VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kSuccess);
-  }
-
-  // Verify robots rules check result is received synchronously with the
-  // expected result.
-  void CheckRobotsRules(const std::string& url_path_with_query,
-                        RobotsRulesParser::CheckResult expected_result,
-                        int render_frame_id = kRenderFrameID) {
-    CheckResultReceiver result_receiver;
-    auto result = robots_rules_parser_.CheckRobotsRules(
-        render_frame_id, GURL(kTestOrigin + url_path_with_query),
-        result_receiver.GetCallback());
-    EXPECT_FALSE(result_receiver.did_receive_result());
-    EXPECT_EQ(expected_result, result);
-  }
-
-  // Verify robots rules check result is received asynchronously, and returns
-  // the receiver that can be used to check the result.
-  std::unique_ptr<CheckResultReceiver> CheckRobotsRulesAsync(
-      const std::string& url_path_with_query,
-      int render_frame_id = kRenderFrameID) {
-    auto result_receiver = std::make_unique<CheckResultReceiver>();
-    EXPECT_FALSE(robots_rules_parser_.CheckRobotsRules(
-        render_frame_id, GURL(kTestOrigin + url_path_with_query),
-        result_receiver->GetCallback()));
-    return result_receiver;
-  }
-
-  void VerifyRulesReceiveState(
-      RobotsRulesParser::RulesReceiveState expected_rules_receive_state) {
-    EXPECT_EQ(robots_rules_parser_.rules_receive_state_,
-              expected_rules_receive_state);
-  }
-
-  void VerifyRobotsRulesReceiveResultHistogram(
-      RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult result) {
-    histogram_tester().ExpectUniqueSample(
-        "SubresourceRedirect.RobotRulesDecider.ReceiveResult", result, 1);
-  }
-
-  void VerifyReceivedRobotsRulesCountHistogram(size_t count) {
-    histogram_tester().ExpectUniqueSample(
-        "SubresourceRedirect.RobotRulesDecider.Count", count, 1);
-  }
-
-  void VerifyTotalRobotsRulesApplyHistograms(size_t total) {
-    histogram_tester().ExpectTotalCount(
-        "SubresourceRedirect.RobotRulesDecider.ApplyDuration", total);
-  }
-
-  const base::HistogramTester& histogram_tester() { return histogram_tester_; }
-
- protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
-  base::test::TaskEnvironment task_environment_;
-  base::HistogramTester histogram_tester_;
-  RobotsRulesParser robots_rules_parser_;
-};
-
-TEST_F(SubresourceRedirectRobotsRulesParserTest,
-       InvalidProtoParseErrorDisallowsAllPaths) {
-  VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kTimerRunning);
-  robots_rules_parser_.UpdateRobotsRules("INVALID PROTO");
-  VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kParseFailed);
-  VerifyRobotsRulesReceiveResultHistogram(
-      RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::
-          kParseError);
-  histogram_tester().ExpectTotalCount(
-      "SubresourceRedirect.RobotRulesDecider.Count", 0);
-
-  // All url paths should be disallowed.
-  CheckRobotsRules("", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/foo.jpg", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/foo/bar.jpg", RobotsRulesParser::CheckResult::kDisallowed);
-  VerifyTotalRobotsRulesApplyHistograms(0);
-}
-
-TEST_F(SubresourceRedirectRobotsRulesParserTest, EmptyRulesAllowsAllPaths) {
-  VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kTimerRunning);
-  SetUpRobotsRules({});
-  VerifyRobotsRulesReceiveResultHistogram(
-      RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kSuccess);
-  VerifyReceivedRobotsRulesCountHistogram(0);
-
-  // All url paths should be allowed.
-  CheckRobotsRules("", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/foo.jpg", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/foo/bar.jpg", RobotsRulesParser::CheckResult::kAllowed);
-  VerifyTotalRobotsRulesApplyHistograms(4);
-}
-
-TEST_F(SubresourceRedirectRobotsRulesParserTest,
-       RulesReceiveTimeoutDisallowsAllPaths) {
-  // Let the rule fetch timeout.
-  VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kTimerRunning);
-  task_environment_.FastForwardBy(base::Seconds(10));
-  VerifyRobotsRulesReceiveResultHistogram(
-      RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kTimeout);
-  VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kTimeout);
-
-  // All url paths should be disallowed due to timeout.
-  CheckRobotsRules("", RobotsRulesParser::CheckResult::kDisallowedAfterTimeout);
-  CheckRobotsRules("/",
-                   RobotsRulesParser::CheckResult::kDisallowedAfterTimeout);
-  CheckRobotsRules("/foo.jpg",
-                   RobotsRulesParser::CheckResult::kDisallowedAfterTimeout);
-  CheckRobotsRules("/foo/bar.jpg",
-                   RobotsRulesParser::CheckResult::kDisallowedAfterTimeout);
-  VerifyTotalRobotsRulesApplyHistograms(0);
-}
-
-TEST_F(SubresourceRedirectRobotsRulesParserTest,
-       CheckResultCallbackAfterRulesReceived) {
-  auto receiver1 = CheckRobotsRulesAsync("/foo.jpg");
-  auto receiver2 = CheckRobotsRulesAsync("/bar");
-  EXPECT_FALSE(receiver1->did_receive_result());
-  EXPECT_FALSE(receiver2->did_receive_result());
-  VerifyTotalRobotsRulesApplyHistograms(0);
-
-  // Once the rules are received the callback should get called with the result.
-  VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kTimerRunning);
-  SetUpRobotsRules({{kRuleTypeAllow, "/foo"}, {kRuleTypeDisallow, "/"}});
-  VerifyRobotsRulesReceiveResultHistogram(
-      RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kSuccess);
-  VerifyReceivedRobotsRulesCountHistogram(2);
-
-  EXPECT_TRUE(receiver1->did_receive_result());
-  EXPECT_TRUE(receiver2->did_receive_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kAllowed,
-            receiver1->check_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kDisallowed,
-            receiver2->check_result());
-  VerifyTotalRobotsRulesApplyHistograms(2);
-
-  CheckRobotsRules("/foo.png", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/bar", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/baz", RobotsRulesParser::CheckResult::kDisallowed);
-  VerifyTotalRobotsRulesApplyHistograms(5);
-}
-
-// Verify if the callback is called before the decider gets destroyed.
-TEST_F(SubresourceRedirectRobotsRulesParserTest,
-       VerifyCallbackCalledBeforeDeciderDestroy) {
-  auto robots_rules_parser =
-      std::make_unique<RobotsRulesParser>(base::Seconds(1));
-  auto receiver1 = std::make_unique<CheckResultReceiver>();
-  auto receiver2 = std::make_unique<CheckResultReceiver>();
-
-  robots_rules_parser->CheckRobotsRules(kRenderFrameID,
-                                        GURL("https://test.com/foo.jpg"),
-                                        receiver1->GetCallback());
-  robots_rules_parser->CheckRobotsRules(
-      kRenderFrameID, GURL("https://test.com/bar"), receiver2->GetCallback());
-  EXPECT_FALSE(receiver1->did_receive_result());
-  EXPECT_FALSE(receiver2->did_receive_result());
-  VerifyTotalRobotsRulesApplyHistograms(0);
-
-  robots_rules_parser->UpdateRobotsRules(GetRobotsRulesProtoString(
-      {{kRuleTypeAllow, "/foo"}, {kRuleTypeDisallow, "/"}}));
-
-  // Destroying the decider should trigger the callbacks.
-  robots_rules_parser.reset();
-
-  EXPECT_TRUE(receiver1->did_receive_result());
-  EXPECT_TRUE(receiver2->did_receive_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kAllowed,
-            receiver1->check_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kDisallowed,
-            receiver2->check_result());
-  VerifyTotalRobotsRulesApplyHistograms(2);
-}
-
-TEST_F(SubresourceRedirectRobotsRulesParserTest,
-       CheckResultCallbackAfterRulesReceiveTimeout) {
-  auto receiver1 = CheckRobotsRulesAsync("/foo.jpg");
-  auto receiver2 = CheckRobotsRulesAsync("/bar");
-  EXPECT_FALSE(receiver1->did_receive_result());
-  EXPECT_FALSE(receiver2->did_receive_result());
-  VerifyTotalRobotsRulesApplyHistograms(0);
-
-  // Once the rule fetch timesout,the callback should get called with the
-  // result.
-  task_environment_.FastForwardBy(base::Seconds(10));
-  VerifyRobotsRulesReceiveResultHistogram(
-      RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kTimeout);
-  VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kTimeout);
-
-  EXPECT_TRUE(receiver1->did_receive_result());
-  EXPECT_TRUE(receiver2->did_receive_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kTimedout,
-            receiver1->check_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kTimedout,
-            receiver2->check_result());
-  VerifyTotalRobotsRulesApplyHistograms(0);
-
-  SetUpRobotsRules({{kRuleTypeAllow, "/foo"}, {kRuleTypeDisallow, "/"}});
-
-  CheckRobotsRules("/foo.png", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/bar", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/baz", RobotsRulesParser::CheckResult::kDisallowed);
-  VerifyTotalRobotsRulesApplyHistograms(3);
-}
-
-TEST_F(SubresourceRedirectRobotsRulesParserTest, FullAllowRule) {
-  SetUpRobotsRules({{kRuleTypeAllow, "*"}});
-  VerifyRobotsRulesReceiveResultHistogram(
-      RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kSuccess);
-  VerifyReceivedRobotsRulesCountHistogram(1);
-
-  // All url paths should be allowed.
-  CheckRobotsRules("", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/foo.jpg", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/foo/bar.jpg", RobotsRulesParser::CheckResult::kAllowed);
-  VerifyTotalRobotsRulesApplyHistograms(4);
-}
-
-TEST_F(SubresourceRedirectRobotsRulesParserTest, FullDisallowRule) {
-  SetUpRobotsRules({{kRuleTypeDisallow, "*"}});
-  VerifyRobotsRulesReceiveResultHistogram(
-      RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kSuccess);
-  VerifyReceivedRobotsRulesCountHistogram(1);
-
-  // All url paths should be disallowed.
-  CheckRobotsRules("", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/foo.jpg", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/foo/bar.jpg", RobotsRulesParser::CheckResult::kDisallowed);
-  VerifyTotalRobotsRulesApplyHistograms(4);
-}
-
-TEST_F(SubresourceRedirectRobotsRulesParserTest, TwoAllowRules) {
-  SetUpRobotsRules({{kRuleTypeAllow, "/foo"},
-                    {kRuleTypeAllow, "/bar$"},
-                    {kRuleTypeDisallow, "*"}});
-  VerifyRobotsRulesReceiveResultHistogram(
-      RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kSuccess);
-  VerifyReceivedRobotsRulesCountHistogram(3);
-
-  CheckRobotsRules("", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/foo.jpg", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/foo/baz.jpg", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/bar", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/bar.jpg", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/baz", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("foo", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("bar", RobotsRulesParser::CheckResult::kDisallowed);
-  VerifyTotalRobotsRulesApplyHistograms(9);
-}
-
-// When the URL path matches multiple allow and disallow rules, whichever rule
-// that comes first in the list should take precedence.
-TEST_F(SubresourceRedirectRobotsRulesParserTest,
-       FirstAllowRuleMatchOverridesLaterDisallowRule) {
-  SetUpRobotsRules({{kRuleTypeAllow, "/foo"}, {kRuleTypeDisallow, "/foo"}});
-  VerifyRobotsRulesReceiveResultHistogram(
-      RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kSuccess);
-  VerifyReceivedRobotsRulesCountHistogram(2);
-
-  CheckRobotsRules("/foo", RobotsRulesParser::CheckResult::kAllowed);
-
-  SetUpRobotsRules({{kRuleTypeDisallow, "/foo"}, {kRuleTypeAllow, "/foo"}});
-  CheckRobotsRules("/foo", RobotsRulesParser::CheckResult::kDisallowed);
-
-  SetUpRobotsRules({{kRuleTypeAllow, "/foo.jpg"}, {kRuleTypeDisallow, "/foo"}});
-  CheckRobotsRules("/foo.jpg", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/foo", RobotsRulesParser::CheckResult::kDisallowed);
-
-  SetUpRobotsRules({{kRuleTypeDisallow, "/foo.jpg"}, {kRuleTypeAllow, "/foo"}});
-  CheckRobotsRules("/foo.jpg", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/foo", RobotsRulesParser::CheckResult::kAllowed);
-
-  VerifyTotalRobotsRulesApplyHistograms(6);
-}
-
-TEST_F(SubresourceRedirectRobotsRulesParserTest, TestURLWithArguments) {
-  SetUpRobotsRules({{kRuleTypeAllow, "/*.jpg$"},
-                    {kRuleTypeDisallow, "/*.png?*arg_disallowed"},
-                    {kRuleTypeAllow, "/*.png"},
-                    {kRuleTypeDisallow, "/*.gif?*arg_disallowed"},
-                    {kRuleTypeAllow, "/*.gif"},
-                    {kRuleTypeDisallow, "/"}});
-  VerifyRobotsRulesReceiveResultHistogram(
-      RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kSuccess);
-  VerifyReceivedRobotsRulesCountHistogram(6);
-
-  CheckRobotsRules("/allowed.jpg", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/allowed.png", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/allowed.gif", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/disallowed.jpg?arg",
-                   RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/allowed.png?arg",
-                   RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/allowed.png?arg_allowed",
-                   RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/allowed.png?arg_allowed&arg_allowed2",
-                   RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/allowed.png?arg_disallowed",
-                   RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/allowed.png?arg_disallowed&arg_disallowed2",
-                   RobotsRulesParser::CheckResult::kDisallowed);
-
-  VerifyTotalRobotsRulesApplyHistograms(9);
-}
-
-TEST_F(SubresourceRedirectRobotsRulesParserTest, TestRulesAreCaseSensitive) {
-  SetUpRobotsRules({{kRuleTypeAllow, "/allowed"},
-                    {kRuleTypeAllow, "/CamelCase"},
-                    {kRuleTypeAllow, "/CAPITALIZE"},
-                    {kRuleTypeDisallow, "/"}});
-  VerifyReceivedRobotsRulesCountHistogram(4);
-
-  CheckRobotsRules("/allowed.jpg", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/CamelCase.jpg", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/CAPITALIZE.jpg", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/Allowed.jpg", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/camelcase.jpg",
-                   RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/capitalize.jpg",
-                   RobotsRulesParser::CheckResult::kDisallowed);
-
-  VerifyTotalRobotsRulesApplyHistograms(6);
-}
-
-TEST_F(SubresourceRedirectRobotsRulesParserTest,
-       TestRulesHandleSpecialGlobSymbols) {
-  SetUpRobotsRules({
-      {kRuleTypeAllow, "/allo?ed"},
-      {kRuleTypeAllow, "/foo"},
-      {kRuleTypeAllow, "/bar$"},
-      {kRuleTypeAllow, "/boo$$"},
-      {kRuleTypeAllow, "/baz*"},
-      {kRuleTypeAllow, "/bop*$"},
-      {kRuleTypeAllow, "*startwithstar"},
-      {kRuleTypeAllow, "*anchoredtoend$"},
-      {kRuleTypeDisallow, "/"},
-  });
-  VerifyReceivedRobotsRulesCountHistogram(9);
-
-  // The '?' character is *not* treated as a special globbing symbol.
-  CheckRobotsRules("/allo?ed.jpg", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/allowed.jpg", RobotsRulesParser::CheckResult::kDisallowed);
-
-  // A pattern without a final '$' is not anchored to the end of the string.
-  CheckRobotsRules("/foo", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/foo.jpg", RobotsRulesParser::CheckResult::kAllowed);
-
-  // A pattern with a final '$' is anchored to the end of the string.
-  CheckRobotsRules("/bar", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/bar.jpg", RobotsRulesParser::CheckResult::kDisallowed);
-
-  // When a pattern ends with two '$' characters, the first '$' is a literal,
-  // and the second matches the end of the string.
-  CheckRobotsRules("/boo$", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/boo$$", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/boo", RobotsRulesParser::CheckResult::kDisallowed);
-  CheckRobotsRules("/boo$.jpg", RobotsRulesParser::CheckResult::kDisallowed);
-
-  // A trailing '*' has no effect, and it is compatible with '$'.
-  CheckRobotsRules("/baz", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/bazfoo", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/bop", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/bopfoo", RobotsRulesParser::CheckResult::kAllowed);
-
-  // Patterns with leading '*' are not anchored to the beginning of the string.
-  CheckRobotsRules("/startwithstar", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/ZZZstartwithstar",
-                   RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/ZZZstartwithstarbar",
-                   RobotsRulesParser::CheckResult::kAllowed);
-
-  // Patterns with leading '*' correctly interact with trailing '$'.
-  CheckRobotsRules("/ZZZanchoredtoend",
-                   RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/ZZZanchoredtoendZZZ",
-                   RobotsRulesParser::CheckResult::kDisallowed);
-
-  VerifyTotalRobotsRulesApplyHistograms(19);
-}
-
-TEST_F(SubresourceRedirectRobotsRulesParserTest,
-       TestInvalidatePendingRequests) {
-  auto receiver1 = CheckRobotsRulesAsync("/allowed.jpg", 1 /*render_frame_id*/);
-  auto receiver2 =
-      CheckRobotsRulesAsync("/disallowed.jpg", 1 /*render_frame_id*/);
-  auto receiver3 = CheckRobotsRulesAsync("/allowed.jpg", 2 /*render_frame_id*/);
-  auto receiver4 =
-      CheckRobotsRulesAsync("/disallowed.jpg", 2 /*render_frame_id*/);
-
-  // Invalidate should cancel requests for that render frame ID.
-  robots_rules_parser_.InvalidatePendingRequests(1 /*render_frame_id*/);
-  EXPECT_TRUE(receiver1->did_receive_result());
-  EXPECT_TRUE(receiver2->did_receive_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kInvalidated,
-            receiver1->check_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kInvalidated,
-            receiver2->check_result());
-  EXPECT_FALSE(receiver3->did_receive_result());
-  EXPECT_FALSE(receiver4->did_receive_result());
-  VerifyTotalRobotsRulesApplyHistograms(0);
-
-  // When robots rules are retrieved, the
-  SetUpRobotsRules({{kRuleTypeAllow, "/allow*"}, {kRuleTypeDisallow, "/*"}});
-  EXPECT_TRUE(receiver3->did_receive_result());
-  EXPECT_TRUE(receiver4->did_receive_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kAllowed,
-            receiver3->check_result());
-  EXPECT_EQ(RobotsRulesParser::CheckResult::kDisallowed,
-            receiver4->check_result());
-  VerifyTotalRobotsRulesApplyHistograms(2);
-
-  CheckRobotsRules("/allowed.jpg", RobotsRulesParser::CheckResult::kAllowed);
-  CheckRobotsRules("/disallowed.jpg",
-                   RobotsRulesParser::CheckResult::kDisallowed);
-  VerifyTotalRobotsRulesApplyHistograms(4);
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/src_video_redirect_url_loader_throttle.cc b/chrome/renderer/subresource_redirect/src_video_redirect_url_loader_throttle.cc
deleted file mode 100644
index 5f5e0e6d..0000000
--- a/chrome/renderer/subresource_redirect/src_video_redirect_url_loader_throttle.cc
+++ /dev/null
@@ -1,191 +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/renderer/subresource_redirect/src_video_redirect_url_loader_throttle.h"
-
-#include "base/metrics/histogram_macros_local.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_util.h"
-#include "components/subresource_redirect/common/subresource_redirect_features.h"
-#include "content/public/renderer/render_frame.h"
-#include "content/public/renderer/render_thread.h"
-#include "net/http/http_status_code.h"
-#include "services/metrics/public/cpp/metrics_utils.h"
-#include "services/metrics/public/cpp/mojo_ukm_recorder.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/mojom/url_response_head.mojom.h"
-#include "third_party/blink/public/platform/web_network_state_notifier.h"
-#include "third_party/blink/public/web/web_document.h"
-#include "third_party/blink/public/web/web_local_frame.h"
-
-namespace subresource_redirect {
-
-namespace {
-
-// Returns the decider for the render frame
-PublicResourceDeciderAgent* GetPublicResourceDeciderAgent(int render_frame_id) {
-  return PublicResourceDeciderAgent::Get(
-      content::RenderFrame::FromRoutingID(render_frame_id));
-}
-
-// Returns the full content length of the response, either from the range, or
-// content-length response headers, or the total body length.
-absl::optional<uint64_t> GetFullContentLength(
-    const network::mojom::URLResponseHead& response_head) {
-  if (response_head.headers->response_code() == net::HTTP_PARTIAL_CONTENT) {
-    // Parse the full length from range response.
-    int64_t first_byte, last_byte, total_length;
-    if (response_head.headers->GetContentRangeFor206(&first_byte, &last_byte,
-                                                     &total_length)) {
-      return total_length;
-    }
-  } else if ((response_head.headers->response_code() >= 200 &&
-              response_head.headers->response_code() <= 299)) {
-    if (response_head.content_length > 0)
-      return static_cast<uint64_t>(response_head.content_length);
-    if (response_head.encoded_body_length > 0)
-      return static_cast<uint64_t>(response_head.encoded_body_length);
-  }
-  return absl::nullopt;
-}
-
-}  // namespace
-
-// static
-std::unique_ptr<SrcVideoRedirectURLLoaderThrottle>
-SrcVideoRedirectURLLoaderThrottle::MaybeCreateThrottle(
-    const blink::WebURLRequest& request,
-    int render_frame_id) {
-  if (!ShouldRecordLoginRobotsCheckedSrcVideoMetrics())
-    return nullptr;
-  if (!blink::WebNetworkStateNotifier::SaveDataEnabled())
-    return nullptr;
-  if (request.GetRequestDestination() !=
-      network::mojom::RequestDestination::kVideo)
-    return nullptr;
-  if (!request.Url().ProtocolIs(url::kHttpsScheme) &&
-      !request.Url().ProtocolIs(url::kHttpScheme)) {
-    return nullptr;
-  }
-  if (!(request.GetPreviewsState() &
-        blink::PreviewsTypes::kSrcVideoRedirectOn)) {
-    return nullptr;
-  }
-
-  return base::WrapUnique<SrcVideoRedirectURLLoaderThrottle>(
-      new SrcVideoRedirectURLLoaderThrottle(render_frame_id));
-}
-
-SrcVideoRedirectURLLoaderThrottle::SrcVideoRedirectURLLoaderThrottle(
-    int render_frame_id)
-    : render_frame_id_(render_frame_id) {
-  DCHECK(ShouldRecordLoginRobotsCheckedSrcVideoMetrics());
-  redirect_result_ = SubresourceRedirectResult::kRedirectable;
-}
-
-SrcVideoRedirectURLLoaderThrottle::~SrcVideoRedirectURLLoaderThrottle() =
-    default;
-
-void SrcVideoRedirectURLLoaderThrottle::WillStartRequest(
-    network::ResourceRequest* request,
-    bool* defer) {
-  DCHECK_EQ(request->destination, network::mojom::RequestDestination::kVideo);
-  DCHECK(request->url.SchemeIs(url::kHttpsScheme) ||
-         request->url.SchemeIs(url::kHttpScheme));
-  DCHECK_EQ(redirect_result_, SubresourceRedirectResult::kRedirectable);
-  DCHECK_EQ(redirect_state_, PublicResourceDeciderRedirectState::kNone);
-
-  // Do not redirect if its already a litepage subresource.
-  if (IsCompressionServerOrigin(request->url))
-    return;
-
-  auto* public_resource_decider_agent =
-      GetPublicResourceDeciderAgent(render_frame_id_);
-  if (!public_resource_decider_agent)
-    return;
-
-  auto redirect_result =
-      public_resource_decider_agent->ShouldRedirectSubresource(
-          request->url,
-          base::BindOnce(
-              &SrcVideoRedirectURLLoaderThrottle::NotifyRedirectDeciderDecision,
-              weak_ptr_factory_.GetWeakPtr()));
-  if (!redirect_result) {
-    // Decision cannot be made yet. Wait for the NotifyRedirectDeciderDecision
-    // callback.
-    redirect_state_ =
-        PublicResourceDeciderRedirectState::kRedirectDecisionPending;
-    return;
-  }
-
-  // The decider decision has been made.
-  redirect_result_ = *redirect_result;
-  redirect_state_ =
-      redirect_result_ == SubresourceRedirectResult::kRedirectable
-          ? PublicResourceDeciderRedirectState::kRedirectAllowed
-          : PublicResourceDeciderRedirectState::kRedirectNotAllowedByDecider;
-}
-
-void SrcVideoRedirectURLLoaderThrottle::NotifyRedirectDeciderDecision(
-    SubresourceRedirectResult redirect_result) {
-  DCHECK_EQ(PublicResourceDeciderRedirectState::kRedirectDecisionPending,
-            redirect_state_);
-  redirect_result_ = redirect_result;
-  redirect_state_ =
-      redirect_result_ == SubresourceRedirectResult::kRedirectable
-          ? PublicResourceDeciderRedirectState::kRedirectAllowed
-          : PublicResourceDeciderRedirectState::kRedirectNotAllowedByDecider;
-}
-
-void SrcVideoRedirectURLLoaderThrottle::WillProcessResponse(
-    const GURL& response_url,
-    network::mojom::URLResponseHead* response_head,
-    bool* defer) {
-  if (!response_url.is_valid())
-    return;
-  if (response_head->was_fetched_via_cache)
-    return;
-
-  content::RenderFrame* render_frame =
-      content::RenderFrame::FromRoutingID(render_frame_id_);
-  if (!render_frame || !render_frame->GetWebFrame())
-    return;
-
-  if (redirect_state_ ==
-      PublicResourceDeciderRedirectState::kRedirectDecisionPending) {
-    // When the robots rules is not retrieved yet, treat that as a timeout.
-    redirect_result_ = SubresourceRedirectResult::kIneligibleRobotsTimeout;
-  }
-  auto full_content_length = GetFullContentLength(*response_head);
-
-  ukm::builders::SubresourceRedirect_PublicSrcVideoCompression
-      src_video_compression(
-          render_frame->GetWebFrame()->GetDocument().GetUkmSourceId());
-  src_video_compression.SetSubresourceRedirectResult(
-      static_cast<int64_t>(redirect_result_));
-  src_video_compression.SetFullContentLength(
-      ukm::GetExponentialBucketMin(full_content_length.value_or(0), 1.3));
-
-  mojo::PendingRemote<ukm::mojom::UkmRecorderInterface> recorder;
-  content::RenderThread::Get()->BindHostReceiver(
-      recorder.InitWithNewPipeAndPassReceiver());
-  std::unique_ptr<ukm::MojoUkmRecorder> ukm_recorder =
-      std::make_unique<ukm::MojoUkmRecorder>(std::move(recorder));
-  src_video_compression.Record(ukm_recorder.get());
-
-  LOCAL_HISTOGRAM_ENUMERATION(
-      "SubresourceRedirect.SrcVideo.SubresourceRedirectResult",
-      redirect_result_);
-  if (redirect_result_ == SubresourceRedirectResult::kRedirectable) {
-    DCHECK_EQ(PublicResourceDeciderRedirectState::kRedirectAllowed,
-              redirect_state_);
-    LOCAL_HISTOGRAM_COUNTS_1000000(
-        "SubresourceRedirect.SrcVideo.CompressibleFullContentBytes",
-        full_content_length.value_or(0));
-  }
-}
-
-void SrcVideoRedirectURLLoaderThrottle::DetachFromCurrentSequence() {}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/src_video_redirect_url_loader_throttle.h b/chrome/renderer/subresource_redirect/src_video_redirect_url_loader_throttle.h
deleted file mode 100644
index b1d182e9d..0000000
--- a/chrome/renderer/subresource_redirect/src_video_redirect_url_loader_throttle.h
+++ /dev/null
@@ -1,68 +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_RENDERER_SUBRESOURCE_REDIRECT_SRC_VIDEO_REDIRECT_URL_LOADER_THROTTLE_H_
-#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_SRC_VIDEO_REDIRECT_URL_LOADER_THROTTLE_H_
-
-#include "base/memory/weak_ptr.h"
-#include "chrome/renderer/subresource_redirect/public_resource_decider_agent.h"
-#include "content/public/renderer/render_frame.h"
-#include "third_party/blink/public/common/loader/url_loader_throttle.h"
-
-namespace blink {
-class WebURLRequest;
-}  // namespace blink
-
-namespace subresource_redirect {
-
-// This class records coverage information for src videos. Whether the video is
-// eligible for compression, the reasons for ineligibility, and the total bytes
-// of the video are recorded.
-class SrcVideoRedirectURLLoaderThrottle : public blink::URLLoaderThrottle {
- public:
-  static std::unique_ptr<SrcVideoRedirectURLLoaderThrottle> MaybeCreateThrottle(
-      const blink::WebURLRequest& request,
-      int render_frame_id);
-
-  explicit SrcVideoRedirectURLLoaderThrottle(int render_frame_id);
-  ~SrcVideoRedirectURLLoaderThrottle() override;
-
-  SrcVideoRedirectURLLoaderThrottle(const SrcVideoRedirectURLLoaderThrottle&) =
-      delete;
-  SrcVideoRedirectURLLoaderThrottle& operator=(
-      const SrcVideoRedirectURLLoaderThrottle&) = delete;
-
-  // blink::URLLoaderThrottle:
-  void WillStartRequest(network::ResourceRequest* request,
-                        bool* defer) override;
-  void WillProcessResponse(const GURL& response_url,
-                           network::mojom::URLResponseHead* response_head,
-                           bool* defer) override;
-  // Overridden to do nothing as the default implementation is NOT_REACHED()
-  void DetachFromCurrentSequence() override;
-
- private:
-  // Callback to notify the decision of the decider.
-  void NotifyRedirectDeciderDecision(SubresourceRedirectResult redirect_result);
-
-  // Render frame id to get the hints agent of the render frame.
-  const int render_frame_id_;
-
-  // The current state of redirect.
-  PublicResourceDeciderRedirectState redirect_state_ =
-      PublicResourceDeciderRedirectState::kNone;
-
-  // Whether the subresource can be redirected or not and what was the reason if
-  // its not eligible.
-  SubresourceRedirectResult redirect_result_ =
-      SubresourceRedirectResult::kUnknown;
-
-  // Used to get a weak pointer to |this|.
-  base::WeakPtrFactory<SrcVideoRedirectURLLoaderThrottle> weak_ptr_factory_{
-      this};
-};
-
-}  // namespace subresource_redirect
-
-#endif  // CHROME_RENDERER_SUBRESOURCE_REDIRECT_SRC_VIDEO_REDIRECT_URL_LOADER_THROTTLE_H_
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_params.cc b/chrome/renderer/subresource_redirect/subresource_redirect_params.cc
deleted file mode 100644
index 608eba5..0000000
--- a/chrome/renderer/subresource_redirect/subresource_redirect_params.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-// 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.
-
-#include "chrome/renderer/subresource_redirect/subresource_redirect_params.h"
-
-#include "base/command_line.h"
-#include "base/metrics/field_trial_params.h"
-#include "chrome/common/chrome_features.h"
-#include "third_party/blink/public/common/features.h"
-#include "url/gurl.h"
-
-namespace subresource_redirect {
-
-namespace {
-
-// Default timeout for the hints to be received from the time navigation starts.
-const int64_t kHintsReceiveDefaultTimeoutSeconds = 5;
-
-}  // namespace
-
-base::TimeDelta GetCompressionRedirectTimeout() {
-  return base::Milliseconds(base::GetFieldTrialParamByFeatureAsInt(
-      blink::features::kSubresourceRedirect, "subresource_redirect_timeout",
-      5000));
-}
-
-int64_t GetHintsReceiveTimeout() {
-  return base::GetFieldTrialParamByFeatureAsInt(
-      blink::features::kSubresourceRedirect, "hints_receive_timeout",
-      kHintsReceiveDefaultTimeoutSeconds);
-}
-
-base::TimeDelta GetRobotsRulesReceiveTimeout() {
-  if (base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect)) {
-    return base::Milliseconds(base::GetFieldTrialParamByFeatureAsInt(
-        blink::features::kSubresourceRedirect,
-        "robots_rules_receive_timeout_ms", 2000));
-  }
-  return base::Milliseconds(base::GetFieldTrialParamByFeatureAsInt(
-      blink::features::kSubresourceRedirectSrcVideo,
-      "robots_rules_receive_timeout_ms", 2000));
-}
-
-size_t GetFirstKSubresourceLimit() {
-  return base::GetFieldTrialParamByFeatureAsInt(
-      blink::features::kSubresourceRedirect, "first_k_subresource_limit", 0);
-}
-
-base::TimeDelta GetRobotsRulesReceiveFirstKSubresourceTimeout() {
-  if (base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect)) {
-    return base::Milliseconds(base::GetFieldTrialParamByFeatureAsInt(
-        blink::features::kSubresourceRedirect,
-        "robots_rules_receive_first_k_timeout_ms", 2000));
-  }
-  return base::Milliseconds(base::GetFieldTrialParamByFeatureAsInt(
-      blink::features::kSubresourceRedirectSrcVideo,
-      "robots_rules_receive_first_k_timeout_ms", 2000));
-}
-
-size_t GetFirstKDisableSubresourceRedirectLimit() {
-  return base::GetFieldTrialParamByFeatureAsInt(
-      blink::features::kSubresourceRedirect,
-      "first_k_disable_subresource_redirect_limit", 0);
-}
-
-int MaxRobotsRulesParsersCacheSize() {
-  return base::GetFieldTrialParamByFeatureAsInt(
-      blink::features::kSubresourceRedirect,
-      "max_robots_rules_parsers_cache_size", 20);
-}
-
-bool ShouldRecordLoginRobotsUkmMetrics() {
-  return base::GetFieldTrialParamByFeatureAsBool(
-      blink::features::kSubresourceRedirect, "record_login_robots_ukm_metrics",
-      true);
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_params.h b/chrome/renderer/subresource_redirect/subresource_redirect_params.h
deleted file mode 100644
index 707021cb..0000000
--- a/chrome/renderer/subresource_redirect/subresource_redirect_params.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// 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.
-
-#ifndef CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_PARAMS_H_
-#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_PARAMS_H_
-
-#include "base/time/time.h"
-#include "url/origin.h"
-
-namespace subresource_redirect {
-
-// Returns the timeout for the compressed subresource redirect, after which the
-// subresource should be fetched directly from the origin.
-base::TimeDelta GetCompressionRedirectTimeout();
-
-// Returns the public image hinte receive timeout value from field trial.
-int64_t GetHintsReceiveTimeout();
-
-// Returns the timeout to wait for the robots rules to be received, after which
-// the subresource should be fetched directly from the origin.
-base::TimeDelta GetRobotsRulesReceiveTimeout();
-
-// Returns the count of subresources for which first k timeout limit should be
-// applied.
-size_t GetFirstKSubresourceLimit();
-
-// Returns the timeout for first k subresouces, to wait for the robots rules to
-// be received, after which the subresource should be fetched directly from the
-// origin.
-base::TimeDelta GetRobotsRulesReceiveFirstKSubresourceTimeout();
-
-// Returns the count of first k subresources for which redirect compression
-// should be disabled. Value of 0 means this first k subresource disabling
-// feature is turned off.
-size_t GetFirstKDisableSubresourceRedirectLimit();
-
-// The maximum number of robots rules parsers the renderer should cache locally
-// for reuse by the renderframes in the renderer process.
-int MaxRobotsRulesParsersCacheSize();
-
-// Returns whether image compression ukm metrics should be recorded.
-bool ShouldRecordLoginRobotsUkmMetrics();
-
-}  // namespace subresource_redirect
-
-#endif  // CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_PARAMS_H_
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_renderer_browsertest.cc b/chrome/renderer/subresource_redirect/subresource_redirect_renderer_browsertest.cc
deleted file mode 100644
index fa5a378..0000000
--- a/chrome/renderer/subresource_redirect/subresource_redirect_renderer_browsertest.cc
+++ /dev/null
@@ -1,181 +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 "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
-#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
-#include "chrome/browser/login_detection/login_detection_type.h"
-#include "chrome/browser/login_detection/login_detection_util.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/subresource_redirect/https_image_compression_infobar_decider.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/subresource_redirect/common/subresource_redirect_result.h"
-#include "components/subresource_redirect/subresource_redirect_browser_test_util.h"
-#include "components/subresource_redirect/subresource_redirect_test_util.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "net/http/http_status_code.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "third_party/blink/public/common/features.h"
-
-namespace subresource_redirect {
-
-// Test class that sets up logged-in sites from field trial.
-class SubresourceRedirectLoggedInSitesBrowserTest
-    : public InProcessBrowserTest {
- public:
-  SubresourceRedirectLoggedInSitesBrowserTest()
-      : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitchASCII("host-rules", "MAP * 127.0.0.1");
-    command_line->AppendSwitch("enable-spdy-proxy-auth");
-
-    // Disable infobar shown check to actually compress the pages.
-    command_line->AppendSwitch("override-https-image-compression-infobar");
-  }
-
-  void SetUp() override {
-    ASSERT_TRUE(robots_rules_server_.Start());
-    ASSERT_TRUE(image_compression_server_.Start());
-    https_test_server_.ServeFilesFromSourceDirectory("chrome/test/data");
-    ASSERT_TRUE(https_test_server_.Start());
-
-    std::vector<base::test::ScopedFeatureList::FeatureAndParams>
-        enabled_features;
-    base::FieldTrialParams params, login_detection_params;
-    params["enable_public_image_hints_based_compression"] = "false";
-    params["enable_login_robots_based_compression"] = "true";
-    params["lite_page_robots_origin"] = robots_rules_server_.GetURL();
-    params["lite_page_subresource_origin"] = image_compression_server_.GetURL();
-    // This rules fetch timeout is chosen such that the tests would have
-    // enough time to fetch the rules without causing a timeout.
-    params["robots_rules_receive_timeout"] = "1000";
-    enabled_features.emplace_back(blink::features::kSubresourceRedirect,
-                                  params);
-
-    login_detection_params["logged_in_sites"] = "https://loggedin.com";
-    enabled_features.emplace_back(login_detection::kLoginDetection,
-                                  login_detection_params);
-
-    scoped_feature_list_.InitWithFeaturesAndParameters(enabled_features, {});
-    InProcessBrowserTest::SetUp();
-  }
-
-  GURL GetHttpsTestURL(const std::string& path) const {
-    return https_test_server_.GetURL("test_https_server.com", path);
-  }
-
-  void NavigateAndWaitForLoad(Browser* browser, const GURL& url) {
-    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser, url));
-    EXPECT_EQ(true, EvalJs(browser->tab_strip_model()->GetActiveWebContents(),
-                           "checkImage()"));
-    FetchHistogramsFromChildProcesses();
-  }
-
-  bool RunScriptExtractBool(const std::string& script,
-                            content::WebContents* web_contents = nullptr) {
-    if (!web_contents)
-      web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-    return EvalJs(web_contents, script).ExtractBool();
-  }
-
- protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
-  // Simulates the LitePages servers that return the robots rules and compress
-  // images.
-  RobotsRulesTestServer robots_rules_server_;
-  ImageCompressionTestServer image_compression_server_;
-  net::EmbeddedTestServer https_test_server_;
-
-  base::HistogramTester histogram_tester_;
-};
-
-// TODO(crbug.com/1187754): Enable the test after fixing the flake.
-// Verify that when image load gets canceled due to subsequent page load, the
-// subresource redirect for the image is canceled as well.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectLoggedInSitesBrowserTest,
-                       DISABLED_TestCancelBeforeImageLoad) {
-  robots_rules_server_.set_failure_mode(
-      RobotsRulesTestServer::FailureMode::kTimeout);
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeAllow, ""}});
-
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), GetHttpsTestURL("/load_image/image.html")));
-
-  // Wait for the image request to start and its robots rules to be requested.
-  while (robots_rules_server_.received_requests().empty()) {
-    base::RunLoop().RunUntilIdle();
-  }
-
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), GetHttpsTestURL("/load_image/simple.html")));
-  FetchHistogramsFromChildProcesses();
-
-  RetryForHistogramUntilCountReached(
-      &histogram_tester_,
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleRobotsTimeout, 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-
-  robots_rules_server_.VerifyRequestedOrigins({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths({});
-}
-
-// TODO(crbug.com/1187754): Enable the test after fixing the flake.
-// Verify that when image load gets canceled due to subsequent navigation to a
-// logged-in page, the subresource redirect for the image is disabled as well.
-IN_PROC_BROWSER_TEST_F(SubresourceRedirectLoggedInSitesBrowserTest,
-                       DISABLED_TestCancelBeforeImageLoadForLoggedInSite) {
-  robots_rules_server_.set_failure_mode(
-      RobotsRulesTestServer::FailureMode::kTimeout);
-  robots_rules_server_.AddRobotsRules(GetHttpsTestURL("/"),
-                                      {{kRuleTypeAllow, ""}});
-
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), GetHttpsTestURL("/load_image/image.html")));
-
-  // Wait for the image request to start and its robots rules to be requested.
-  while (robots_rules_server_.received_requests().empty()) {
-    base::RunLoop().RunUntilIdle();
-  }
-
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(),
-      https_test_server_.GetURL("loggedin.com", "/load_image/simple.html")));
-  FetchHistogramsFromChildProcesses();
-  histogram_tester_.ExpectBucketCount(
-      "Login.PageLoad.DetectionType",
-      login_detection::LoginDetectionType::kFieldTrialLoggedInSite, 1);
-
-  RetryForHistogramUntilCountReached(
-      &histogram_tester_,
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult", 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult",
-      SubresourceRedirectResult::kIneligibleRobotsTimeout, 1);
-  histogram_tester_.ExpectUniqueSample(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      net::HTTP_TEMPORARY_REDIRECT, 1);
-  histogram_tester_.ExpectTotalCount(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", 0);
-
-  robots_rules_server_.VerifyRequestedOrigins({GetHttpsTestURL("/").spec()});
-  image_compression_server_.VerifyRequestedImagePaths({});
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.cc b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.cc
deleted file mode 100644
index 153022f..0000000
--- a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.cc
+++ /dev/null
@@ -1,406 +0,0 @@
-// 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.
-
-#include "chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h"
-
-#include "base/memory/ptr_util.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/stl_util.h"
-#include "base/time/time.h"
-#include "chrome/renderer/subresource_redirect/login_robots_decider_agent.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_params.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_util.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
-#include "components/subresource_redirect/common/subresource_redirect_features.h"
-#include "components/subresource_redirect/common/subresource_redirect_result.h"
-#include "content/public/renderer/render_frame.h"
-#include "net/base/escape.h"
-#include "net/base/load_flags.h"
-#include "net/http/http_status_code.h"
-#include "net/http/http_util.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/mojom/fetch_api.mojom-shared.h"
-#include "services/network/public/mojom/url_response_head.mojom.h"
-#include "third_party/blink/public/common/loader/previews_state.h"
-#include "third_party/blink/public/platform/web_network_state_notifier.h"
-#include "third_party/blink/public/platform/web_url.h"
-#include "third_party/blink/public/platform/web_url_request.h"
-#include "third_party/blink/public/web/web_document.h"
-#include "third_party/blink/public/web/web_local_frame.h"
-
-namespace subresource_redirect {
-
-namespace {
-
-// Returns the decider for the render frame
-PublicResourceDeciderAgent* GetPublicResourceDeciderAgent(int render_frame_id) {
-  return PublicResourceDeciderAgent::Get(
-      content::RenderFrame::FromRoutingID(render_frame_id));
-}
-
-// Records the per image load metrics.
-void RecordMetricsOnLoadFinished(
-    LoginRobotsCompressionMetrics* login_robots_compression_metrics,
-    SubresourceRedirectResult redirect_result,
-    uint64_t content_length,
-    absl::optional<float> ofcl) {
-  if (login_robots_compression_metrics) {
-    login_robots_compression_metrics->RecordMetricsOnLoadFinished(
-        redirect_result, content_length, ofcl);
-  }
-}
-
-// Returns whether the redirect state is in some terminal state.
-bool IsTerminalRedirectState(
-    PublicResourceDeciderRedirectState redirect_state) {
-  switch (redirect_state) {
-    case PublicResourceDeciderRedirectState::kNone:
-    case PublicResourceDeciderRedirectState::kRedirectAttempted:
-    case PublicResourceDeciderRedirectState::kRedirectNotAllowedByDecider:
-    case PublicResourceDeciderRedirectState::kRedirectFailed:
-    case PublicResourceDeciderRedirectState::kRedirectAllowed:
-      // This enum is not used in this file.
-      return true;
-    case PublicResourceDeciderRedirectState::kRedirectDecisionPending:
-      return false;
-  }
-}
-
-}  // namespace
-
-// static
-std::unique_ptr<SubresourceRedirectURLLoaderThrottle>
-SubresourceRedirectURLLoaderThrottle::MaybeCreateThrottle(
-    const blink::WebURLRequest& request,
-    int render_frame_id) {
-  if (!ShouldEnablePublicImageHintsBasedCompression() &&
-      !ShouldEnableLoginRobotsCheckedImageCompression()) {
-    return nullptr;
-  }
-  if (request.GetRequestDestination() ==
-          network::mojom::RequestDestination::kImage &&
-      request.Url().ProtocolIs(url::kHttpsScheme) &&
-      blink::WebNetworkStateNotifier::SaveDataEnabled() &&
-      request.GetRequestContext() !=
-          blink::mojom::RequestContextType::FAVICON) {
-    return base::WrapUnique<SubresourceRedirectURLLoaderThrottle>(
-        new SubresourceRedirectURLLoaderThrottle(
-            render_frame_id, request.GetPreviewsState() &
-                                 blink::PreviewsTypes::kSubresourceRedirectOn));
-  }
-  return nullptr;
-}
-
-SubresourceRedirectURLLoaderThrottle::SubresourceRedirectURLLoaderThrottle(
-    int render_frame_id,
-    bool allowed_to_redirect)
-    : render_frame_id_(render_frame_id) {
-  DCHECK(ShouldEnablePublicImageHintsBasedCompression() ||
-         ShouldEnableLoginRobotsCheckedImageCompression());
-  redirect_result_ =
-      allowed_to_redirect
-          ? SubresourceRedirectResult::kRedirectable
-          : SubresourceRedirectResult::kIneligibleBlinkDisallowed;
-  if (!ShouldRecordLoginRobotsUkmMetrics())
-    return;
-  if (!ShouldEnableLoginRobotsCheckedImageCompression())
-    return;
-  content::RenderFrame* render_frame =
-      content::RenderFrame::FromRoutingID(render_frame_id);
-  if (!render_frame || !render_frame->GetWebFrame())
-    return;
-  auto* public_resource_decider_agent =
-      GetPublicResourceDeciderAgent(render_frame_id);
-  if (!public_resource_decider_agent)
-    return;
-  login_robots_compression_metrics_ = LoginRobotsCompressionMetrics(
-      render_frame->GetWebFrame()->GetDocument().GetUkmSourceId(),
-      public_resource_decider_agent->GetNavigationStartTime());
-}
-
-SubresourceRedirectURLLoaderThrottle::~SubresourceRedirectURLLoaderThrottle() =
-    default;
-
-void SubresourceRedirectURLLoaderThrottle::WillStartRequest(
-    network::ResourceRequest* request,
-    bool* defer) {
-  DCHECK_EQ(request->destination, network::mojom::RequestDestination::kImage);
-  DCHECK(request->url.SchemeIs(url::kHttpsScheme));
-
-  if (redirect_result_ ==
-      SubresourceRedirectResult::kIneligibleBlinkDisallowed) {
-    if (auto* public_resource_decider_agent =
-            GetPublicResourceDeciderAgent(render_frame_id_)) {
-      public_resource_decider_agent
-          ->NotifyIneligibleBlinkDisallowedSubresource();
-    }
-    if (login_robots_compression_metrics_) {
-      login_robots_compression_metrics_->NotifyRequestStart();
-      login_robots_compression_metrics_->NotifyRequestSent();
-    }
-    return;
-  }
-
-  if (redirect_result_ != SubresourceRedirectResult::kRedirectable)
-    return;
-
-  // Do not redirect if its already a litepage subresource.
-  if (IsCompressionServerOrigin(request->url))
-    return;
-
-  if (login_robots_compression_metrics_)
-    login_robots_compression_metrics_->NotifyRequestStart();
-
-  auto* public_resource_decider_agent =
-      GetPublicResourceDeciderAgent(render_frame_id_);
-  if (!public_resource_decider_agent)
-    return;
-
-  auto redirect_result =
-      public_resource_decider_agent->ShouldRedirectSubresource(
-          request->url, base::BindOnce(&SubresourceRedirectURLLoaderThrottle::
-                                           NotifyRedirectDeciderDecision,
-                                       weak_ptr_factory_.GetWeakPtr()));
-  if (!redirect_result) {
-    // Decision cannot be made yet. Defer the subresource and change the URL to
-    // compression server URL. The NotifyRedirectDeciderDecision callback will
-    // continue with compression or disable compression by resetting to original
-    // URL.
-    redirect_state_ =
-        PublicResourceDeciderRedirectState::kRedirectDecisionPending;
-    *defer = true;
-    request->url = GetSubresourceURLForURL(request->url);
-    return;
-  }
-
-  // The decider decision has been made.
-  if (login_robots_compression_metrics_)
-    login_robots_compression_metrics_->NotifyRequestSent();
-  *defer = false;
-  redirect_result_ = *redirect_result;
-  if (redirect_result_ != SubresourceRedirectResult::kRedirectable) {
-    redirect_state_ =
-        PublicResourceDeciderRedirectState::kRedirectNotAllowedByDecider;
-    return;
-  }
-
-  // Redirect is allowed.
-  redirect_state_ = PublicResourceDeciderRedirectState::kRedirectAttempted;
-  request->url = GetSubresourceURLForURL(request->url);
-  StartRedirectTimeoutTimer();
-}
-
-const char*
-SubresourceRedirectURLLoaderThrottle::NameForLoggingWillStartRequest() {
-  return "SubresourceRedirectThrottle";
-}
-
-void SubresourceRedirectURLLoaderThrottle::NotifyRedirectDeciderDecision(
-    SubresourceRedirectResult redirect_result) {
-  DCHECK_EQ(PublicResourceDeciderRedirectState::kRedirectDecisionPending,
-            redirect_state_);
-  redirect_result_ = redirect_result;
-  if (login_robots_compression_metrics_)
-    login_robots_compression_metrics_->NotifyRequestSent();
-
-  if (redirect_result_ != SubresourceRedirectResult::kRedirectable) {
-    // Restart the fetch to the original URL.
-    redirect_state_ =
-        PublicResourceDeciderRedirectState::kRedirectNotAllowedByDecider;
-    delegate_->RestartWithURLResetAndFlags(net::LOAD_NORMAL);
-    delegate_->Resume();
-    return;
-  }
-
-  // Redirect is allowed.
-  redirect_state_ = PublicResourceDeciderRedirectState::kRedirectAttempted;
-  delegate_->Resume();
-  StartRedirectTimeoutTimer();
-}
-
-void SubresourceRedirectURLLoaderThrottle::WillRedirectRequest(
-    net::RedirectInfo* redirect_info,
-    const network::mojom::URLResponseHead& response_head,
-    bool* defer,
-    std::vector<std::string>* to_be_removed_request_headers,
-    net::HttpRequestHeaders* modified_request_headers,
-    net::HttpRequestHeaders* modified_cors_exempt_request_headers) {
-  // Check if the redirect is in some terminal state.
-  DCHECK(IsTerminalRedirectState(redirect_state_));
-  if (redirect_state_ ==
-          PublicResourceDeciderRedirectState::kRedirectAttempted &&
-      redirect_timeout_timer_) {
-    redirect_timeout_timer_->Start(
-        FROM_HERE, GetCompressionRedirectTimeout(),
-        base::BindOnce(&SubresourceRedirectURLLoaderThrottle::OnRedirectTimeout,
-                       base::Unretained(this)));
-  }
-  UMA_HISTOGRAM_ENUMERATION(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      static_cast<net::HttpStatusCode>(response_head.headers->response_code()),
-      net::HTTP_VERSION_NOT_SUPPORTED);
-}
-
-void SubresourceRedirectURLLoaderThrottle::BeforeWillProcessResponse(
-    const GURL& response_url,
-    const network::mojom::URLResponseHead& response_head,
-    bool* defer) {
-  DCHECK(IsTerminalRedirectState(redirect_state_));
-  if (redirect_state_ != PublicResourceDeciderRedirectState::kRedirectAttempted)
-    return;
-  DCHECK(ShouldCompressRedirectSubresource());
-  // If response was not from the compression server, don't restart it.
-  if (!response_url.is_valid())
-    return;
-
-  // Log all response codes, from the compression server.
-  UMA_HISTOGRAM_ENUMERATION(
-      "SubresourceRedirect.CompressionAttempt.ResponseCode",
-      static_cast<net::HttpStatusCode>(response_head.headers->response_code()),
-      net::HTTP_VERSION_NOT_SUPPORTED);
-  redirect_timeout_timer_.reset();
-
-  // Do nothing with 2XX responses, as these requests were handled
-  // correctly by the compression server.
-  if ((response_head.headers->response_code() >= 200 &&
-       response_head.headers->response_code() <= 299) ||
-      response_head.headers->response_code() == 304) {
-    return;
-  }
-  redirect_result_ = SubresourceRedirectResult::kIneligibleRedirectFailed;
-
-  // 503 response code indicates loadshed from the compression server. Notify
-  // the browser process which will bypass subresource redirect for subsequent
-  // page loads. Retry-After response header may mention the bypass duration,
-  // otherwise the browser will choose a random duration.
-  if (response_head.headers->response_code() == 503) {
-    std::string retry_after_string;
-    base::TimeDelta retry_after;
-    if (response_head.headers->EnumerateHeader(nullptr, "Retry-After",
-                                               &retry_after_string)) {
-      net::HttpUtil::ParseRetryAfterHeader(retry_after_string,
-                                           base::Time::Now(), &retry_after);
-    }
-    if (auto* public_resource_decider_agent =
-            GetPublicResourceDeciderAgent(render_frame_id_)) {
-      public_resource_decider_agent->NotifyCompressedResourceFetchFailed(
-          retry_after);
-    }
-  }
-
-  // Non 2XX responses from the compression server need to have unaltered
-  // requests sent to the original resource.
-  redirect_state_ = PublicResourceDeciderRedirectState::kRedirectFailed;
-  delegate_->RestartWithURLResetAndFlags(net::LOAD_NORMAL);
-}
-
-void SubresourceRedirectURLLoaderThrottle::WillProcessResponse(
-    const GURL& response_url,
-    network::mojom::URLResponseHead* response_head,
-    bool* defer) {
-  DCHECK(IsTerminalRedirectState(redirect_state_));
-  // If response was not from the compression server, don't record any
-  // metrics.
-  if (!response_url.is_valid())
-    return;
-  if (response_head->was_fetched_via_cache)
-    return;
-  int64_t content_length = response_head->headers->GetContentLength();
-  if (content_length < 0)
-    return;
-
-  if (auto* public_resource_decider_agent =
-          GetPublicResourceDeciderAgent(render_frame_id_)) {
-    public_resource_decider_agent->RecordMetricsOnLoadFinished(
-        response_url, content_length, redirect_result_);
-  }
-
-  if (redirect_state_ !=
-      PublicResourceDeciderRedirectState::kRedirectAttempted) {
-    RecordMetricsOnLoadFinished(
-        base::OptionalOrNullptr(login_robots_compression_metrics_),
-        redirect_result_, content_length, absl::nullopt);
-    return;
-  }
-  DCHECK(ShouldCompressRedirectSubresource());
-
-  // Record that the server responded.
-  UMA_HISTOGRAM_BOOLEAN(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", true);
-
-  // If compression was unsuccessful don't try and record compression percent.
-  if (response_head->headers->response_code() != 200) {
-    RecordMetricsOnLoadFinished(
-        base::OptionalOrNullptr(login_robots_compression_metrics_),
-        redirect_result_, content_length, absl::nullopt);
-    return;
-  }
-
-  int64_t ofcl =
-      static_cast<float>(data_reduction_proxy::GetDataReductionProxyOFCL(
-          response_head->headers.get()));
-
-  // If |ofcl| is missing don't compute compression percent.
-  if (ofcl <= 0) {
-    RecordMetricsOnLoadFinished(
-        base::OptionalOrNullptr(login_robots_compression_metrics_),
-        redirect_result_, content_length, absl::nullopt);
-    return;
-  }
-
-  UMA_HISTOGRAM_PERCENTAGE(
-      "SubresourceRedirect.DidCompress.CompressionPercent",
-      static_cast<int>(100 -
-                       ((content_length / static_cast<float>(ofcl)) * 100)));
-
-  UMA_HISTOGRAM_COUNTS_1M("SubresourceRedirect.DidCompress.BytesSaved",
-                          ofcl - content_length);
-  RecordMetricsOnLoadFinished(
-      base::OptionalOrNullptr(login_robots_compression_metrics_),
-      redirect_result_, content_length, ofcl);
-}
-
-void SubresourceRedirectURLLoaderThrottle::WillOnCompleteWithError(
-    const network::URLLoaderCompletionStatus& status,
-    bool* defer) {
-  if (redirect_state_ != PublicResourceDeciderRedirectState::kRedirectAttempted)
-    return;
-  DCHECK(ShouldCompressRedirectSubresource());
-  redirect_result_ = SubresourceRedirectResult::kIneligibleRedirectFailed;
-
-  // If the server fails, restart the request to the original resource, and
-  // record it.
-  redirect_state_ = PublicResourceDeciderRedirectState::kRedirectFailed;
-  redirect_timeout_timer_.reset();
-  delegate_->RestartWithURLResetAndFlags(net::LOAD_NORMAL);
-  UMA_HISTOGRAM_BOOLEAN(
-      "SubresourceRedirect.CompressionAttempt.ServerResponded", false);
-}
-
-void SubresourceRedirectURLLoaderThrottle::StartRedirectTimeoutTimer() {
-  DCHECK(!redirect_timeout_timer_);
-  redirect_timeout_timer_ = std::make_unique<base::OneShotTimer>();
-  redirect_timeout_timer_->Start(
-      FROM_HERE, GetCompressionRedirectTimeout(),
-      base::BindOnce(&SubresourceRedirectURLLoaderThrottle::OnRedirectTimeout,
-                     base::Unretained(this)));
-}
-
-void SubresourceRedirectURLLoaderThrottle::OnRedirectTimeout() {
-  DCHECK_EQ(PublicResourceDeciderRedirectState::kRedirectAttempted,
-            redirect_state_);
-  redirect_state_ = PublicResourceDeciderRedirectState::kRedirectFailed;
-  delegate_->RestartWithURLResetAndFlagsNow(net::LOAD_NORMAL);
-  if (auto* public_resource_decider_agent =
-          GetPublicResourceDeciderAgent(render_frame_id_)) {
-    public_resource_decider_agent->NotifyCompressedResourceFetchFailed(
-        base::TimeDelta());
-  }
-  UMA_HISTOGRAM_BOOLEAN("SubresourceRedirect.CompressionFetchTimeout", true);
-}
-
-void SubresourceRedirectURLLoaderThrottle::DetachFromCurrentSequence() {}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h b/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h
deleted file mode 100644
index 1d9899d..0000000
--- a/chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h
+++ /dev/null
@@ -1,100 +0,0 @@
-// 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.
-
-#ifndef CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_URL_LOADER_THROTTLE_H_
-#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_URL_LOADER_THROTTLE_H_
-
-#include "base/memory/weak_ptr.h"
-#include "base/timer/timer.h"
-#include "chrome/renderer/subresource_redirect/login_robots_compression_metrics.h"
-#include "chrome/renderer/subresource_redirect/public_resource_decider_agent.h"
-#include "content/public/renderer/render_frame.h"
-#include "third_party/blink/public/common/loader/url_loader_throttle.h"
-
-namespace blink {
-class WebURLRequest;
-}  // namespace blink
-
-namespace subresource_redirect {
-
-// This class handles internal redirects for HTTPS public subresources
-// (currently only for images) compressed versions of subresources. When the
-// redirect fails/timesout the original image is fetched directly. Subclasses
-// should implement the decider logic if an URL should be compressed.
-class SubresourceRedirectURLLoaderThrottle : public blink::URLLoaderThrottle {
- public:
-  static std::unique_ptr<SubresourceRedirectURLLoaderThrottle>
-  MaybeCreateThrottle(const blink::WebURLRequest& request, int render_frame_id);
-
-  SubresourceRedirectURLLoaderThrottle(int render_frame_id,
-                                       bool allowed_to_redirect);
-  ~SubresourceRedirectURLLoaderThrottle() override;
-
-  SubresourceRedirectURLLoaderThrottle(
-      const SubresourceRedirectURLLoaderThrottle&) = delete;
-  SubresourceRedirectURLLoaderThrottle& operator=(
-      const SubresourceRedirectURLLoaderThrottle&) = delete;
-
-  // blink::URLLoaderThrottle:
-  void WillStartRequest(network::ResourceRequest* request,
-                        bool* defer) override;
-  const char* NameForLoggingWillStartRequest() override;
-  void WillRedirectRequest(
-      net::RedirectInfo* redirect_info,
-      const network::mojom::URLResponseHead& response_head,
-      bool* defer,
-      std::vector<std::string>* to_be_removed_request_headers,
-      net::HttpRequestHeaders* modified_request_headers,
-      net::HttpRequestHeaders* modified_cors_exempt_request_headers) override;
-  void BeforeWillProcessResponse(
-      const GURL& response_url,
-      const network::mojom::URLResponseHead& response_head,
-      bool* defer) override;
-  void WillProcessResponse(const GURL& response_url,
-                           network::mojom::URLResponseHead* response_head,
-                           bool* defer) override;
-  void WillOnCompleteWithError(const network::URLLoaderCompletionStatus& status,
-                               bool* defer) override;
-  // Overridden to do nothing as the default implementation is NOT_REACHED()
-  void DetachFromCurrentSequence() override;
-
- private:
-  friend class SubresourceRedirectPublicImageHintsDeciderAgentTest;
-
-  // Callback to notify the decision of decider subclasses.
-  void NotifyRedirectDeciderDecision(SubresourceRedirectResult);
-
-  // Start the timer for redirect fetch timeout.
-  void StartRedirectTimeoutTimer();
-
-  // Callback invoked when the redirect fetch times out.
-  void OnRedirectTimeout();
-
-  // Render frame id to get the hints agent of the render frame.
-  const int render_frame_id_;
-
-  // The current state of redirect.
-  PublicResourceDeciderRedirectState redirect_state_ =
-      PublicResourceDeciderRedirectState::kNone;
-
-  // Timer to detect whether the response from compression server has timed out.
-  std::unique_ptr<base::OneShotTimer> redirect_timeout_timer_;
-
-  // Whether the subresource can be redirected or not and what was the reason if
-  // its not eligible.
-  SubresourceRedirectResult redirect_result_ =
-      SubresourceRedirectResult::kUnknown;
-
-  // Used to record the image load and compression metrics.
-  absl::optional<LoginRobotsCompressionMetrics>
-      login_robots_compression_metrics_;
-
-  // Used to get a weak pointer to |this|.
-  base::WeakPtrFactory<SubresourceRedirectURLLoaderThrottle> weak_ptr_factory_{
-      this};
-};
-
-}  // namespace subresource_redirect
-
-#endif  // CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_URL_LOADER_THROTTLE_H_
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_util.cc b/chrome/renderer/subresource_redirect/subresource_redirect_util.cc
deleted file mode 100644
index 482e55a..0000000
--- a/chrome/renderer/subresource_redirect/subresource_redirect_util.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// 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.
-
-#include "chrome/renderer/subresource_redirect/subresource_redirect_util.h"
-
-#include "base/command_line.h"
-#include "base/strings/string_number_conversions.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_params.h"
-#include "components/base32/base32.h"
-#include "components/subresource_redirect/common/subresource_redirect_features.h"
-#include "crypto/sha2.h"
-#include "net/base/escape.h"
-#include "url/gurl.h"
-
-namespace subresource_redirect {
-
-GURL GetSubresourceURLForURL(const GURL& original_url) {
-  DCHECK(original_url.is_valid());
-
-  GURL compressed_url = GetSubresourceRedirectOrigin().GetURL();
-  std::string query_str =
-      "u=" + net::EscapeQueryParamValue(original_url.GetAsReferrer().spec(),
-                                        true /* use_plus */);
-  std::string ref_str = original_url.ref();
-
-  GURL::Replacements replacements;
-  replacements.SetPathStr("/i");
-  replacements.SetQueryStr(query_str);
-  if (!ref_str.empty())
-    replacements.SetRefStr(ref_str);
-
-  compressed_url = compressed_url.ReplaceComponents(replacements);
-  DCHECK(compressed_url.is_valid());
-  return compressed_url;
-}
-
-bool IsCompressionServerOrigin(const GURL& url) {
-  auto compression_server = GetSubresourceRedirectOrigin();
-  return url.DomainIs(compression_server.host()) &&
-         (url.EffectiveIntPort() == compression_server.port()) &&
-         (url.scheme() == compression_server.scheme());
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_util.h b/chrome/renderer/subresource_redirect/subresource_redirect_util.h
deleted file mode 100644
index 684686b..0000000
--- a/chrome/renderer/subresource_redirect/subresource_redirect_util.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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.
-
-#ifndef CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_UTIL_H_
-#define CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_UTIL_H_
-
-#include "url/gurl.h"
-
-namespace subresource_redirect {
-
-// Gets the new URL for the compressed version of the image resource to enable
-// internal redirects.
-GURL GetSubresourceURLForURL(const GURL& original_url);
-
-// Returns whether the url points to compression server origin.
-bool IsCompressionServerOrigin(const GURL& url);
-
-}  // namespace subresource_redirect
-
-#endif  // CHROME_RENDERER_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_UTIL_H_
diff --git a/chrome/renderer/subresource_redirect/subresource_redirect_util_unittest.cc b/chrome/renderer/subresource_redirect/subresource_redirect_util_unittest.cc
deleted file mode 100644
index d91a813..0000000
--- a/chrome/renderer/subresource_redirect/subresource_redirect_util_unittest.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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.
-
-#include "chrome/renderer/subresource_redirect/subresource_redirect_util.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace subresource_redirect {
-
-TEST(SubresourceRedirectURL, ProperlyChangesURL) {
-  EXPECT_EQ(GetSubresourceURLForURL(GURL("https://www.test.com/test.jpg")),
-            GURL("https://"
-                 "litepages.googlezip.net/"
-                 "i?u=https%3A%2F%2Fwww.test.com%2Ftest.jpg"));
-}
-
-TEST(SubresourceRedirectURL, ProperlyHandlesFragment) {
-  EXPECT_EQ(GetSubresourceURLForURL(GURL("https://www.test.com/test.jpg#test")),
-            GURL("https://"
-                 "litepages.googlezip.net/"
-                 "i?u=https%3A%2F%2Fwww.test.com%2Ftest.jpg#test"));
-}
-
-TEST(SubresourceRedirectURL, ProperlyHandlesSetPort) {
-  EXPECT_EQ(GetSubresourceURLForURL(GURL("https://www.test.com:4444/test.jpg")),
-            GURL("https://"
-                 "litepages.googlezip.net/i?u=https%3A%2F%2Fwww.test."
-                 "com%3A4444%2Ftest.jpg"));
-}
-
-TEST(SubresourceRedirectURL, ProperlyHandlesQueryParams) {
-  EXPECT_EQ(GetSubresourceURLForURL(
-                GURL("https://www.test.com/test.jpg?color=yellow")),
-            GURL("https://"
-                 "litepages.googlezip.net/"
-                 "i?u=https%3A%2F%2Fwww.test.com%2Ftest.jpg%3Fcolor%3Dyellow"));
-}
-
-TEST(SubresourceRedirectURL, ProperlyHandlesMultipleQueryParams) {
-  EXPECT_EQ(GetSubresourceURLForURL(
-                GURL("https://www.test.com/test.jpg?color=yellow&name=test")),
-            GURL("https://"
-                 "litepages.googlezip.net/"
-                 "i?u=https%3A%2F%2Fwww.test.com%2Ftest.jpg%3Fcolor%3Dyellow%"
-                 "26name%3Dtest"));
-}
-
-TEST(SubresourceRedirectURL, ProperlyHandlesQueryParamsWithFragments) {
-  EXPECT_EQ(GetSubresourceURLForURL(
-                GURL("https://www.test.com/test.jpg?color=yellow#test")),
-            GURL("https://"
-                 "litepages.googlezip.net/"
-                 "i?u=https%3A%2F%2Fwww.test.com%2Ftest.jpg%3Fcolor%3Dyellow"
-                 "#test"));
-}
-
-// Currently redirects are not supported for HTTP subresources, but there is
-// potential to add them in the future.
-TEST(SubresourceRedirectURL, ProperlyChangesHTTPURL) {
-  EXPECT_EQ(GetSubresourceURLForURL(GURL("http://www.test.com/test.jpg")),
-            GURL("https://"
-                 "litepages.googlezip.net/i?u=http%3A%2F%2Fwww.test.com%2Ftest."
-                 "jpg"));
-}
-
-}  // namespace subresource_redirect
diff --git a/chrome/renderer/url_loader_throttle_provider_impl.cc b/chrome/renderer/url_loader_throttle_provider_impl.cc
index 01bf8be..cf739b68 100644
--- a/chrome/renderer/url_loader_throttle_provider_impl.cc
+++ b/chrome/renderer/url_loader_throttle_provider_impl.cc
@@ -17,9 +17,6 @@
 #include "chrome/renderer/chrome_content_renderer_client.h"
 #include "chrome/renderer/chrome_render_frame_observer.h"
 #include "chrome/renderer/chrome_render_thread_observer.h"
-#include "chrome/renderer/subresource_redirect/src_video_redirect_url_loader_throttle.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_params.h"
-#include "chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h"
 #include "components/no_state_prefetch/renderer/no_state_prefetch_helper.h"
 #include "components/safe_browsing/content/renderer/renderer_url_loader_throttle.h"
 #include "components/safe_browsing/core/common/features.h"
@@ -195,17 +192,6 @@
           ->chromeos_listener()));
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-  auto subresource_redirect_throttle =
-      subresource_redirect::SubresourceRedirectURLLoaderThrottle::
-          MaybeCreateThrottle(request, render_frame_id);
-  if (subresource_redirect_throttle)
-    throttles.emplace_back(std::move(subresource_redirect_throttle));
-  auto src_video_redirect_throttle =
-      subresource_redirect::SrcVideoRedirectURLLoaderThrottle::
-          MaybeCreateThrottle(request, render_frame_id);
-  if (src_video_redirect_throttle)
-    throttles.emplace_back(std::move(src_video_redirect_throttle));
-
   return throttles;
 }
 
diff --git a/chrome/services/sharing/nearby/platform/BUILD.gn b/chrome/services/sharing/nearby/platform/BUILD.gn
index dd7f914..bef8d58 100644
--- a/chrome/services/sharing/nearby/platform/BUILD.gn
+++ b/chrome/services/sharing/nearby/platform/BUILD.gn
@@ -69,6 +69,7 @@
 
   deps = [
     "//ash/constants",
+    "//ash/services/nearby/public/cpp:tcp_server_socket_port",
     "//ash/services/nearby/public/mojom",
     "//base",
     "//chrome/services/sharing/webrtc",
@@ -97,12 +98,12 @@
     "condition_variable_unittest.cc",
     "count_down_latch_unittest.cc",
     "crypto_unittest.cc",
-    "fake_network_context.cc",
-    "fake_network_context.h",
     "fake_tcp_connected_socket.cc",
     "fake_tcp_connected_socket.h",
     "fake_tcp_server_socket.cc",
     "fake_tcp_server_socket.h",
+    "fake_tcp_socket_factory.cc",
+    "fake_tcp_socket_factory.h",
     "input_file_unittest.cc",
     "input_stream_impl_unittest.cc",
     "multi_thread_executor_unittest.cc",
@@ -119,11 +120,11 @@
 
   deps = [
     ":platform",
+    "//ash/services/nearby/public/cpp:tcp_server_socket_port",
     "//ash/services/nearby/public/mojom",
     "//base/test:test_support",
     "//chrome/services/sharing/nearby/test_support",
     "//mojo/public/cpp/bindings",
-    "//services/network:test_support",
     "//services/network/public/mojom",
     "//testing/gtest",
   ]
diff --git a/chrome/services/sharing/nearby/platform/DEPS b/chrome/services/sharing/nearby/platform/DEPS
index f31ee74..52861ce9 100644
--- a/chrome/services/sharing/nearby/platform/DEPS
+++ b/chrome/services/sharing/nearby/platform/DEPS
@@ -6,7 +6,6 @@
   '+ash/constants/ash_features.h',
   '+device/bluetooth/public',
   '+services/network/public',
-  '+services/network/test',
   '+third_party/abseil-cpp/absl/strings/string_view.h',
   '+third_party/abseil-cpp/absl/time/time.h',
   "+unicode/locid.h"
diff --git a/chrome/services/sharing/nearby/platform/fake_network_context.cc b/chrome/services/sharing/nearby/platform/fake_network_context.cc
deleted file mode 100644
index 8649a86..0000000
--- a/chrome/services/sharing/nearby/platform/fake_network_context.cc
+++ /dev/null
@@ -1,154 +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/services/sharing/nearby/platform/fake_network_context.h"
-
-#include "base/test/bind.h"
-#include "chrome/services/sharing/nearby/platform/fake_tcp_connected_socket.h"
-#include "chrome/services/sharing/nearby/platform/fake_tcp_server_socket.h"
-#include "mojo/public/cpp/bindings/self_owned_receiver.h"
-#include "mojo/public/cpp/system/data_pipe.h"
-#include "net/base/net_errors.h"
-
-namespace location {
-namespace nearby {
-namespace chrome {
-
-FakeNetworkContext::FakeNetworkContext(
-    const net::IPEndPoint& default_local_addr)
-    : default_local_addr_(default_local_addr) {}
-
-FakeNetworkContext::~FakeNetworkContext() = default;
-
-void FakeNetworkContext::SetCreateServerSocketCallExpectations(
-    size_t expected_num_create_server_socket_calls,
-    base::OnceClosure on_all_create_server_socket_calls_queued) {
-  expected_num_create_server_socket_calls_ =
-      expected_num_create_server_socket_calls;
-  if (expected_num_create_server_socket_calls == 0) {
-    std::move(on_all_create_server_socket_calls_queued).Run();
-  } else {
-    on_all_create_server_socket_calls_queued_ =
-        std::move(on_all_create_server_socket_calls_queued);
-  }
-}
-
-void FakeNetworkContext::SetCreateConnectedSocketCallExpectations(
-    size_t expected_num_create_connected_socket_calls,
-    base::OnceClosure on_all_create_connected_socket_calls_queued) {
-  expected_num_create_connected_socket_calls_ =
-      expected_num_create_connected_socket_calls;
-  if (expected_num_create_connected_socket_calls == 0) {
-    std::move(on_all_create_connected_socket_calls_queued).Run();
-  } else {
-    on_all_create_connected_socket_calls_queued_ =
-        std::move(on_all_create_connected_socket_calls_queued);
-  }
-}
-
-void FakeNetworkContext::FinishNextCreateServerSocket(int32_t result) {
-  DCHECK(!pending_create_server_socket_callbacks_.empty());
-  CreateCallback callback =
-      std::move(pending_create_server_socket_callbacks_.front());
-  pending_create_server_socket_callbacks_.pop_front();
-
-  std::move(callback).Run(result);
-}
-
-void FakeNetworkContext::FinishNextCreateConnectedSocket(int32_t result) {
-  DCHECK(!pending_create_connected_socket_callbacks_.empty());
-  CreateCallback callback =
-      std::move(pending_create_connected_socket_callbacks_.front());
-  pending_create_connected_socket_callbacks_.pop_front();
-
-  std::move(callback).Run(result);
-}
-
-void FakeNetworkContext::CreateTCPServerSocket(
-    const net::IPEndPoint& local_addr,
-    uint32_t backlog,
-    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
-    mojo::PendingReceiver<network::mojom::TCPServerSocket> socket,
-    CreateTCPServerSocketCallback callback) {
-  pending_create_server_socket_callbacks_.push_back(base::BindLambdaForTesting(
-      [local_addr, socket = std::move(socket),
-       callback = std::move(callback)](int32_t result) mutable {
-        if (result != net::OK) {
-          std::move(callback).Run(result, /*local_addr_out=*/absl::nullopt);
-          return;
-        }
-
-        mojo::MakeSelfOwnedReceiver(std::make_unique<FakeTcpServerSocket>(),
-                                    std::move(socket));
-
-        std::move(callback).Run(result, local_addr);
-      }));
-
-  DCHECK_GE(expected_num_create_server_socket_calls_,
-            pending_create_server_socket_callbacks_.size());
-
-  if (pending_create_server_socket_callbacks_.size() ==
-      expected_num_create_server_socket_calls_) {
-    DCHECK(on_all_create_server_socket_calls_queued_);
-    std::move(on_all_create_server_socket_calls_queued_).Run();
-  }
-}
-
-void FakeNetworkContext::CreateTCPConnectedSocket(
-    const absl::optional<net::IPEndPoint>& local_addr,
-    const net::AddressList& remote_addr_list,
-    network::mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options,
-    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
-    mojo::PendingReceiver<network::mojom::TCPConnectedSocket> socket,
-    mojo::PendingRemote<network::mojom::SocketObserver> observer,
-    CreateTCPConnectedSocketCallback callback) {
-  pending_create_connected_socket_callbacks_.push_back(
-      base::BindLambdaForTesting(
-          [local_addr = local_addr.value_or(default_local_addr_),
-           remote_addr = remote_addr_list[0], socket = std::move(socket),
-           callback = std::move(callback)](int32_t result) mutable {
-            if (result != net::OK) {
-              std::move(callback).Run(result, /*local_addr=*/absl::nullopt,
-                                      /*peer_addr=*/absl::nullopt,
-                                      mojo::ScopedDataPipeConsumerHandle(),
-                                      mojo::ScopedDataPipeProducerHandle());
-              return;
-            }
-
-            mojo::ScopedDataPipeProducerHandle receive_pipe_producer_handle;
-            mojo::ScopedDataPipeConsumerHandle receive_pipe_consumer_handle;
-            DCHECK_EQ(MOJO_RESULT_OK,
-                      mojo::CreateDataPipe(/*options=*/nullptr,
-                                           receive_pipe_producer_handle,
-                                           receive_pipe_consumer_handle));
-            mojo::ScopedDataPipeProducerHandle send_pipe_producer_handle;
-            mojo::ScopedDataPipeConsumerHandle send_pipe_consumer_handle;
-            DCHECK_EQ(MOJO_RESULT_OK,
-                      mojo::CreateDataPipe(/*options=*/nullptr,
-                                           send_pipe_producer_handle,
-                                           send_pipe_consumer_handle));
-            mojo::MakeSelfOwnedReceiver(
-                std::make_unique<FakeTcpConnectedSocket>(
-                    std::move(receive_pipe_producer_handle),
-                    std::move(send_pipe_consumer_handle)),
-                std::move(socket));
-
-            std::move(callback).Run(result, local_addr, remote_addr,
-                                    std::move(receive_pipe_consumer_handle),
-                                    std::move(send_pipe_producer_handle));
-          }));
-
-  DCHECK_GE(expected_num_create_connected_socket_calls_,
-            pending_create_connected_socket_callbacks_.size());
-
-  if (pending_create_connected_socket_callbacks_.size() ==
-      expected_num_create_connected_socket_calls_) {
-    DCHECK(on_all_create_connected_socket_calls_queued_);
-    std::move(on_all_create_connected_socket_calls_queued_).Run();
-  }
-}
-
-}  // namespace chrome
-}  // namespace nearby
-}  // namespace location
diff --git a/chrome/services/sharing/nearby/platform/fake_network_context.h b/chrome/services/sharing/nearby/platform/fake_network_context.h
deleted file mode 100644
index 2b6cf0e..0000000
--- a/chrome/services/sharing/nearby/platform/fake_network_context.h
+++ /dev/null
@@ -1,74 +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_SERVICES_SHARING_NEARBY_PLATFORM_FAKE_NETWORK_CONTEXT_H_
-#define CHROME_SERVICES_SHARING_NEARBY_PLATFORM_FAKE_NETWORK_CONTEXT_H_
-
-#include "base/callback.h"
-#include "base/containers/circular_deque.h"
-#include "net/base/ip_endpoint.h"
-#include "services/network/test/test_network_context.h"
-
-namespace location {
-namespace nearby {
-namespace chrome {
-
-// An implementation of NetworkContext used for unit tests. The user sets
-// expectations--via SetCreate{Server,Connected}SocketCallExpectations()--for
-// the number of CreateTCP{Server,Connected}Socket() calls that will be queued
-// up. The user is notified when all calls are queued. The user sequentially
-// processes the callbacks in the queue via
-// FinishNextCreate{Server,Connected}Socket(). On success,
-// FakeTcp{Server,Connected}Sockets are returned.
-class FakeNetworkContext : public network::TestNetworkContext {
- public:
-  explicit FakeNetworkContext(const net::IPEndPoint& default_local_addr);
-  ~FakeNetworkContext() override;
-  FakeNetworkContext(const FakeNetworkContext&) = delete;
-  FakeNetworkContext& operator=(const FakeNetworkContext&) = delete;
-
-  void SetCreateServerSocketCallExpectations(
-      size_t expected_num_create_server_socket_calls,
-      base::OnceClosure on_all_create_server_socket_calls_queued);
-  void SetCreateConnectedSocketCallExpectations(
-      size_t expected_num_create_connected_socket_calls,
-      base::OnceClosure on_all_create_connected_socket_calls_queued);
-
-  void FinishNextCreateServerSocket(int32_t result);
-  void FinishNextCreateConnectedSocket(int32_t result);
-
- private:
-  using CreateCallback = base::OnceCallback<void(int32_t result)>;
-
-  // network::TestNetworkContext:
-  void CreateTCPServerSocket(
-      const net::IPEndPoint& local_addr,
-      uint32_t backlog,
-      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
-      mojo::PendingReceiver<network::mojom::TCPServerSocket> socket,
-      CreateTCPServerSocketCallback callback) override;
-  void CreateTCPConnectedSocket(
-      const absl::optional<net::IPEndPoint>& local_addr,
-      const net::AddressList& remote_addr_list,
-      network::mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options,
-      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
-      mojo::PendingReceiver<network::mojom::TCPConnectedSocket> socket,
-      mojo::PendingRemote<network::mojom::SocketObserver> observer,
-      CreateTCPConnectedSocketCallback callback) override;
-
-  net::IPEndPoint default_local_addr_;
-  size_t expected_num_create_server_socket_calls_ = 0;
-  size_t expected_num_create_connected_socket_calls_ = 0;
-  base::OnceClosure on_all_create_server_socket_calls_queued_;
-  base::OnceClosure on_all_create_connected_socket_calls_queued_;
-  base::circular_deque<CreateCallback> pending_create_server_socket_callbacks_;
-  base::circular_deque<CreateCallback>
-      pending_create_connected_socket_callbacks_;
-};
-
-}  // namespace chrome
-}  // namespace nearby
-}  // namespace location
-
-#endif  // CHROME_SERVICES_SHARING_NEARBY_PLATFORM_FAKE_NETWORK_CONTEXT_H_
diff --git a/chrome/services/sharing/nearby/platform/fake_tcp_socket_factory.cc b/chrome/services/sharing/nearby/platform/fake_tcp_socket_factory.cc
new file mode 100644
index 0000000..8ef567e
--- /dev/null
+++ b/chrome/services/sharing/nearby/platform/fake_tcp_socket_factory.cc
@@ -0,0 +1,156 @@
+// 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/services/sharing/nearby/platform/fake_tcp_socket_factory.h"
+
+#include "base/test/bind.h"
+#include "chrome/services/sharing/nearby/platform/fake_tcp_connected_socket.h"
+#include "chrome/services/sharing/nearby/platform/fake_tcp_server_socket.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "net/base/net_errors.h"
+
+namespace location {
+namespace nearby {
+namespace chrome {
+
+FakeTcpSocketFactory::FakeTcpSocketFactory(
+    const net::IPEndPoint& default_local_addr)
+    : default_local_addr_(default_local_addr) {}
+
+FakeTcpSocketFactory::~FakeTcpSocketFactory() = default;
+
+void FakeTcpSocketFactory::SetCreateServerSocketCallExpectations(
+    size_t expected_num_create_server_socket_calls,
+    base::OnceClosure on_all_create_server_socket_calls_queued) {
+  expected_num_create_server_socket_calls_ =
+      expected_num_create_server_socket_calls;
+  if (expected_num_create_server_socket_calls == 0) {
+    std::move(on_all_create_server_socket_calls_queued).Run();
+  } else {
+    on_all_create_server_socket_calls_queued_ =
+        std::move(on_all_create_server_socket_calls_queued);
+  }
+}
+
+void FakeTcpSocketFactory::SetCreateConnectedSocketCallExpectations(
+    size_t expected_num_create_connected_socket_calls,
+    base::OnceClosure on_all_create_connected_socket_calls_queued) {
+  expected_num_create_connected_socket_calls_ =
+      expected_num_create_connected_socket_calls;
+  if (expected_num_create_connected_socket_calls == 0) {
+    std::move(on_all_create_connected_socket_calls_queued).Run();
+  } else {
+    on_all_create_connected_socket_calls_queued_ =
+        std::move(on_all_create_connected_socket_calls_queued);
+  }
+}
+
+void FakeTcpSocketFactory::FinishNextCreateServerSocket(int32_t result) {
+  DCHECK(!pending_create_server_socket_callbacks_.empty());
+  CreateCallback callback =
+      std::move(pending_create_server_socket_callbacks_.front());
+  pending_create_server_socket_callbacks_.pop_front();
+
+  std::move(callback).Run(result);
+}
+
+void FakeTcpSocketFactory::FinishNextCreateConnectedSocket(int32_t result) {
+  DCHECK(!pending_create_connected_socket_callbacks_.empty());
+  CreateCallback callback =
+      std::move(pending_create_connected_socket_callbacks_.front());
+  pending_create_connected_socket_callbacks_.pop_front();
+
+  std::move(callback).Run(result);
+}
+
+void FakeTcpSocketFactory::CreateTCPServerSocket(
+    const net::IPAddress& local_addr,
+    const ash::nearby::TcpServerSocketPort& port,
+    uint32_t backlog,
+    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+    mojo::PendingReceiver<network::mojom::TCPServerSocket> socket,
+    CreateTCPServerSocketCallback callback) {
+  pending_create_server_socket_callbacks_.push_back(base::BindLambdaForTesting(
+      [local_addr, port, socket = std::move(socket),
+       callback = std::move(callback)](int32_t result) mutable {
+        if (result != net::OK) {
+          std::move(callback).Run(result, /*local_addr_out=*/absl::nullopt);
+          return;
+        }
+
+        mojo::MakeSelfOwnedReceiver(std::make_unique<FakeTcpServerSocket>(),
+                                    std::move(socket));
+
+        std::move(callback).Run(result,
+                                net::IPEndPoint(local_addr, port.port()));
+      }));
+
+  DCHECK_GE(expected_num_create_server_socket_calls_,
+            pending_create_server_socket_callbacks_.size());
+
+  if (pending_create_server_socket_callbacks_.size() ==
+      expected_num_create_server_socket_calls_) {
+    DCHECK(on_all_create_server_socket_calls_queued_);
+    std::move(on_all_create_server_socket_calls_queued_).Run();
+  }
+}
+
+void FakeTcpSocketFactory::CreateTCPConnectedSocket(
+    const absl::optional<net::IPEndPoint>& local_addr,
+    const net::AddressList& remote_addr_list,
+    network::mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options,
+    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+    mojo::PendingReceiver<network::mojom::TCPConnectedSocket> socket,
+    mojo::PendingRemote<network::mojom::SocketObserver> observer,
+    CreateTCPConnectedSocketCallback callback) {
+  pending_create_connected_socket_callbacks_.push_back(
+      base::BindLambdaForTesting(
+          [local_addr = local_addr.value_or(default_local_addr_),
+           remote_addr = remote_addr_list[0], socket = std::move(socket),
+           callback = std::move(callback)](int32_t result) mutable {
+            if (result != net::OK) {
+              std::move(callback).Run(result, /*local_addr=*/absl::nullopt,
+                                      /*peer_addr=*/absl::nullopt,
+                                      mojo::ScopedDataPipeConsumerHandle(),
+                                      mojo::ScopedDataPipeProducerHandle());
+              return;
+            }
+
+            mojo::ScopedDataPipeProducerHandle receive_pipe_producer_handle;
+            mojo::ScopedDataPipeConsumerHandle receive_pipe_consumer_handle;
+            DCHECK_EQ(MOJO_RESULT_OK,
+                      mojo::CreateDataPipe(/*options=*/nullptr,
+                                           receive_pipe_producer_handle,
+                                           receive_pipe_consumer_handle));
+            mojo::ScopedDataPipeProducerHandle send_pipe_producer_handle;
+            mojo::ScopedDataPipeConsumerHandle send_pipe_consumer_handle;
+            DCHECK_EQ(MOJO_RESULT_OK,
+                      mojo::CreateDataPipe(/*options=*/nullptr,
+                                           send_pipe_producer_handle,
+                                           send_pipe_consumer_handle));
+            mojo::MakeSelfOwnedReceiver(
+                std::make_unique<FakeTcpConnectedSocket>(
+                    std::move(receive_pipe_producer_handle),
+                    std::move(send_pipe_consumer_handle)),
+                std::move(socket));
+
+            std::move(callback).Run(result, local_addr, remote_addr,
+                                    std::move(receive_pipe_consumer_handle),
+                                    std::move(send_pipe_producer_handle));
+          }));
+
+  DCHECK_GE(expected_num_create_connected_socket_calls_,
+            pending_create_connected_socket_callbacks_.size());
+
+  if (pending_create_connected_socket_callbacks_.size() ==
+      expected_num_create_connected_socket_calls_) {
+    DCHECK(on_all_create_connected_socket_calls_queued_);
+    std::move(on_all_create_connected_socket_calls_queued_).Run();
+  }
+}
+
+}  // namespace chrome
+}  // namespace nearby
+}  // namespace location
diff --git a/chrome/services/sharing/nearby/platform/fake_tcp_socket_factory.h b/chrome/services/sharing/nearby/platform/fake_tcp_socket_factory.h
new file mode 100644
index 0000000..56ce1f1
--- /dev/null
+++ b/chrome/services/sharing/nearby/platform/fake_tcp_socket_factory.h
@@ -0,0 +1,77 @@
+// 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_SERVICES_SHARING_NEARBY_PLATFORM_FAKE_TCP_SOCKET_FACTORY_H_
+#define CHROME_SERVICES_SHARING_NEARBY_PLATFORM_FAKE_TCP_SOCKET_FACTORY_H_
+
+#include "ash/services/nearby/public/cpp/tcp_server_socket_port.h"
+#include "ash/services/nearby/public/mojom/tcp_socket_factory.mojom.h"
+#include "base/callback.h"
+#include "base/containers/circular_deque.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
+
+namespace location {
+namespace nearby {
+namespace chrome {
+
+// An implementation of TcpSocketFactory used for unit tests. The user sets
+// expectations--via SetCreate{Server,Connected}SocketCallExpectations()--for
+// the number of CreateTCP{Server,Connected}Socket() calls that will be queued
+// up. The user is notified when all calls are queued. The user sequentially
+// processes the callbacks in the queue via
+// FinishNextCreate{Server,Connected}Socket(). On success,
+// FakeTcp{Server,Connected}Sockets are returned.
+class FakeTcpSocketFactory : public sharing::mojom::TcpSocketFactory {
+ public:
+  explicit FakeTcpSocketFactory(const net::IPEndPoint& default_local_addr);
+  ~FakeTcpSocketFactory() override;
+  FakeTcpSocketFactory(const FakeTcpSocketFactory&) = delete;
+  FakeTcpSocketFactory& operator=(const FakeTcpSocketFactory&) = delete;
+
+  void SetCreateServerSocketCallExpectations(
+      size_t expected_num_create_server_socket_calls,
+      base::OnceClosure on_all_create_server_socket_calls_queued);
+  void SetCreateConnectedSocketCallExpectations(
+      size_t expected_num_create_connected_socket_calls,
+      base::OnceClosure on_all_create_connected_socket_calls_queued);
+
+  void FinishNextCreateServerSocket(int32_t result);
+  void FinishNextCreateConnectedSocket(int32_t result);
+
+ private:
+  using CreateCallback = base::OnceCallback<void(int32_t result)>;
+
+  // sharing::mojom::TcpSocketFactory:
+  void CreateTCPServerSocket(
+      const net::IPAddress& local_addr,
+      const ash::nearby::TcpServerSocketPort& port,
+      uint32_t backlog,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+      mojo::PendingReceiver<network::mojom::TCPServerSocket> socket,
+      CreateTCPServerSocketCallback callback) override;
+  void CreateTCPConnectedSocket(
+      const absl::optional<net::IPEndPoint>& local_addr,
+      const net::AddressList& remote_addr_list,
+      network::mojom::TCPConnectedSocketOptionsPtr tcp_connected_socket_options,
+      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+      mojo::PendingReceiver<network::mojom::TCPConnectedSocket> socket,
+      mojo::PendingRemote<network::mojom::SocketObserver> observer,
+      CreateTCPConnectedSocketCallback callback) override;
+
+  net::IPEndPoint default_local_addr_;
+  size_t expected_num_create_server_socket_calls_ = 0;
+  size_t expected_num_create_connected_socket_calls_ = 0;
+  base::OnceClosure on_all_create_server_socket_calls_queued_;
+  base::OnceClosure on_all_create_connected_socket_calls_queued_;
+  base::circular_deque<CreateCallback> pending_create_server_socket_callbacks_;
+  base::circular_deque<CreateCallback>
+      pending_create_connected_socket_callbacks_;
+};
+
+}  // namespace chrome
+}  // namespace nearby
+}  // namespace location
+
+#endif  // CHROME_SERVICES_SHARING_NEARBY_PLATFORM_FAKE_TCP_SOCKET_FACTORY_H_
diff --git a/chrome/services/sharing/nearby/platform/wifi_lan_medium.cc b/chrome/services/sharing/nearby/platform/wifi_lan_medium.cc
index 6a4f76d..8e2219b 100644
--- a/chrome/services/sharing/nearby/platform/wifi_lan_medium.cc
+++ b/chrome/services/sharing/nearby/platform/wifi_lan_medium.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/services/sharing/nearby/platform/wifi_lan_medium.h"
 
+#include "ash/services/nearby/public/cpp/tcp_server_socket_port.h"
 #include "base/bind.h"
 #include "base/check.h"
 #include "base/logging.h"
@@ -62,11 +63,11 @@
 }  // namespace
 
 WifiLanMedium::WifiLanMedium(
-    const mojo::SharedRemote<network::mojom::NetworkContext>& network_context)
+    const mojo::SharedRemote<sharing::mojom::TcpSocketFactory>& socket_factory)
     : task_runner_(
           base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})),
-      network_context_(network_context) {
-  // NOTE: We do not set the disconnect handler for |network_context_| here. It
+      socket_factory_(socket_factory) {
+  // NOTE: We do not set the disconnect handler for |socket_factory_| here. It
   // is a fundamental dependency of the Nearby Connections process, which will
   // crash if any dependency disconnects.
 }
@@ -141,7 +142,7 @@
   mojo::PendingRemote<network::mojom::TCPConnectedSocket> tcp_connected_socket;
   mojo::PendingReceiver<network::mojom::TCPConnectedSocket> receiver =
       tcp_connected_socket.InitWithNewPipeAndPassReceiver();
-  network_context_->CreateTCPConnectedSocket(
+  socket_factory_->CreateTCPConnectedSocket(
       /*local_addr=*/absl::nullopt, address_list,
       /*tcp_connected_socket_options=*/nullptr,
       net::MutableNetworkTrafficAnnotationTag(kTrafficAnnotation),
@@ -228,18 +229,34 @@
 
   pending_listen_waitable_events_.insert(listen_waitable_event);
 
+  // TcpServerSocketPort enforces any necessary restrictions on port number
+  // ranges. If |port| is 0, choose a random port from the acceptable range.
+  absl::optional<ash::nearby::TcpServerSocketPort> tcp_port =
+      port == 0 ? absl::make_optional<ash::nearby::TcpServerSocketPort>(
+                      ash::nearby::TcpServerSocketPort::Random())
+                : ash::nearby::TcpServerSocketPort::FromInt(port);
+  if (!tcp_port) {
+    LOG(WARNING)
+        << "WifiLanMedium::" << __func__
+        << ": Failed to construct a TcpServerSocketPort from port number "
+        << port;
+    FinishListenAttempt(listen_waitable_event);
+    return;
+  }
+
   // TODO(https://crbug.com/1261238): Implement local IP address fetching. We
   // temporarily use this hardcoding for unit tests.
   net::IPAddress address(192, 168, 86, 75);
   OnLocalIpAddressFetched(server_socket_parameters, listen_waitable_event,
-                          net::IPEndPoint(address, port));
+                          address, *tcp_port);
 }
 
 void WifiLanMedium::OnLocalIpAddressFetched(
     absl::optional<WifiLanServerSocket::ServerSocketParameters>*
         server_socket_parameters,
     base::WaitableEvent* listen_waitable_event,
-    const net::IPEndPoint& local_end_point) {
+    const net::IPAddress& ip_address,
+    const ash::nearby::TcpServerSocketPort& port) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
 
   // TODO(https://crbug.com/1261238): Process fetched local IP address and log
@@ -247,13 +264,14 @@
 
   mojo::PendingRemote<network::mojom::TCPServerSocket> tcp_server_socket;
   auto receiver = tcp_server_socket.InitWithNewPipeAndPassReceiver();
-  network_context_->CreateTCPServerSocket(
-      local_end_point, kBacklog,
+  socket_factory_->CreateTCPServerSocket(
+      ip_address, port, kBacklog,
       net::MutableNetworkTrafficAnnotationTag(kTrafficAnnotation),
       std::move(receiver),
       base::BindOnce(&WifiLanMedium::OnTcpServerSocketCreated,
                      base::Unretained(this), server_socket_parameters,
-                     listen_waitable_event, std::move(tcp_server_socket)));
+                     listen_waitable_event, std::move(tcp_server_socket),
+                     ip_address, port));
 }
 
 void WifiLanMedium::OnTcpServerSocketCreated(
@@ -261,6 +279,8 @@
         server_socket_parameters,
     base::WaitableEvent* listen_waitable_event,
     mojo::PendingRemote<network::mojom::TCPServerSocket> tcp_server_socket,
+    const net::IPAddress& ip_address,
+    const ash::nearby::TcpServerSocketPort& port,
     int32_t result,
     const absl::optional<net::IPEndPoint>& local_addr) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
@@ -274,14 +294,27 @@
     return;
   }
 
-  // TODO(https://crbug.com/1261238): Open firewall hole.
   DCHECK(tcp_server_socket);
   DCHECK(local_addr);
   VLOG(1) << "WifiLanMedium::" << __func__
           << ": Created TCP server socket. local_addr="
           << local_addr->ToString();
+
+  // TODO(https://crbug.com/1261238): Log metric.
+  if (local_addr->address() != ip_address ||
+      local_addr->port() != port.port()) {
+    LOG(WARNING) << "WifiLanMedium::" << __func__
+                 << ": TCP server socket's IP:port disagrees with "
+                    "input values. in="
+                 << ip_address.ToString() << ":" << port.port()
+                 << ", out=" << local_addr->ToString();
+    FinishListenAttempt(listen_waitable_event);
+    return;
+  }
+
+  // TODO(https://crbug.com/1261238): Open firewall hole.
   OnFirewallHoleCreated(server_socket_parameters, listen_waitable_event,
-                        std::move(tcp_server_socket), local_addr);
+                        std::move(tcp_server_socket), *local_addr);
 }
 
 void WifiLanMedium::OnFirewallHoleCreated(
@@ -289,13 +322,13 @@
         server_socket_parameters,
     base::WaitableEvent* listen_waitable_event,
     mojo::PendingRemote<network::mojom::TCPServerSocket> tcp_server_socket,
-    const absl::optional<net::IPEndPoint>& local_addr) {
+    const net::IPEndPoint& local_addr) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
 
   // TODO(https://crbug.com/1261238): Process firewall hole, log metric, and add
   // firewall hole to server socket parameters.
 
-  *server_socket_parameters = {*local_addr, std::move(tcp_server_socket)};
+  *server_socket_parameters = {local_addr, std::move(tcp_server_socket)};
 
   FinishListenAttempt(listen_waitable_event);
 }
@@ -361,8 +394,8 @@
   // those already in the task queue.
   // TODO(https://crbug.com/1261238): Reset firewall hole factory.
   VLOG(1) << "WifiLanMedium::" << __func__
-          << ": Closing NetworkContext Remote.";
-  network_context_.reset();
+          << ": Closing TcpSocketFactory Remote.";
+  socket_factory_.reset();
 
   // Cancel all pending connect/listen calls. This is thread safe because all
   // changes to the pending-event sets are sequenced. Make a copy of the events
diff --git a/chrome/services/sharing/nearby/platform/wifi_lan_medium.h b/chrome/services/sharing/nearby/platform/wifi_lan_medium.h
index 253aac00..8f329718 100644
--- a/chrome/services/sharing/nearby/platform/wifi_lan_medium.h
+++ b/chrome/services/sharing/nearby/platform/wifi_lan_medium.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 
+#include "ash/services/nearby/public/mojom/tcp_socket_factory.mojom.h"
 #include "base/containers/flat_set.h"
 #include "base/memory/scoped_refptr.h"
 #include "chrome/services/sharing/nearby/platform/wifi_lan_server_socket.h"
@@ -16,11 +17,18 @@
 #include "mojo/public/cpp/bindings/shared_remote.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "net/base/address_list.h"
+#include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
-#include "services/network/public/mojom/network_context.mojom.h"
 #include "services/network/public/mojom/tcp_socket.mojom.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/nearby/src/cpp/platform/api/wifi_lan.h"
 
+namespace ash {
+namespace nearby {
+class TcpServerSocketPort;
+}  // namespace nearby
+}  // namespace ash
+
 namespace base {
 class SequencedTaskRunner;
 class WaitableEvent;
@@ -31,18 +39,18 @@
 namespace chrome {
 
 // An implementation of the abstract Nearby Connections's class
-// api::WifiLanMedium. The implementation uses the network services's
-// NetworkContext mojo interface to 1) connect to remote server sockets, and 2)
-// open local server sockets to listen for incoming connection requests from
-// remote devices. We block while 1) trying to connect, 2) creating a server
-// socket, and 3) cancelling pending tasks in the destructor. We guarantee
-// thread safety, and we guarantee that all blocking connection and listening
-// attempts return before destruction.
+// api::WifiLanMedium. The implementation uses the
+// sharing::mojom::TcpSocketFactory mojo interface to 1) connect to remote
+// server sockets, and 2) open local server sockets to listen for incoming
+// connection requests from remote devices. We block while 1) trying to connect,
+// 2) creating a server socket, and 3) cancelling pending tasks in the
+// destructor. We guarantee thread safety, and we guarantee that all blocking
+// connection and listening attempts return before destruction.
 class WifiLanMedium : public api::WifiLanMedium {
  public:
   explicit WifiLanMedium(
-      const mojo::SharedRemote<network::mojom::NetworkContext>&
-          network_context);
+      const mojo::SharedRemote<sharing::mojom::TcpSocketFactory>&
+          socket_factory);
   WifiLanMedium(const WifiLanMedium&) = delete;
   WifiLanMedium& operator=(const WifiLanMedium&) = delete;
   ~WifiLanMedium() override;
@@ -89,12 +97,15 @@
       absl::optional<WifiLanServerSocket::ServerSocketParameters>*
           server_socket_parameters,
       base::WaitableEvent* listen_waitable_event,
-      const net::IPEndPoint& local_end_point);
+      const net::IPAddress& ip_address,
+      const ash::nearby::TcpServerSocketPort& port);
   void OnTcpServerSocketCreated(
       absl::optional<WifiLanServerSocket::ServerSocketParameters>*
           server_socket_parameters,
       base::WaitableEvent* listen_waitable_event,
       mojo::PendingRemote<network::mojom::TCPServerSocket> tcp_server_socket,
+      const net::IPAddress& ip_address,
+      const ash::nearby::TcpServerSocketPort& port,
       int32_t result,
       const absl::optional<net::IPEndPoint>& local_addr);
   // TODO(https://crbug.com/1261238): Add firewall hole PendingRemote argument.
@@ -103,7 +114,7 @@
           server_socket_parameters,
       base::WaitableEvent* listen_waitable_event,
       mojo::PendingRemote<network::mojom::TCPServerSocket> tcp_server_socket,
-      const absl::optional<net::IPEndPoint>& local_addr);
+      const net::IPEndPoint& local_addr);
   /*==========================================================================*/
 
   /*==========================================================================*/
@@ -123,13 +134,13 @@
   void FinishConnectAttempt(base::WaitableEvent* event);
   void FinishListenAttempt(base::WaitableEvent* event);
 
-  // Resets the |network_context_| and finishes all pending connect/listen
+  // Resets the |tcp_socket_factory_| and finishes all pending connect/listen
   // attempts with null results.
   void Shutdown(base::WaitableEvent* shutdown_waitable_event);
 
   // TODO(https://crbug.com/1261238): Add firewall hole factory.
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
-  mojo::SharedRemote<network::mojom::NetworkContext> network_context_;
+  mojo::SharedRemote<sharing::mojom::TcpSocketFactory> socket_factory_;
 
   // Track all pending connect/listen tasks in case Close() is called while
   // waiting.
diff --git a/chrome/services/sharing/nearby/platform/wifi_lan_medium_unittest.cc b/chrome/services/sharing/nearby/platform/wifi_lan_medium_unittest.cc
index 43b402f..03de1a7f 100644
--- a/chrome/services/sharing/nearby/platform/wifi_lan_medium_unittest.cc
+++ b/chrome/services/sharing/nearby/platform/wifi_lan_medium_unittest.cc
@@ -4,14 +4,17 @@
 
 #include "chrome/services/sharing/nearby/platform/wifi_lan_medium.h"
 
+#include "ash/services/nearby/public/cpp/tcp_server_socket_port.h"
 #include "base/task/thread_pool.h"
 #include "base/test/task_environment.h"
-#include "chrome/services/sharing/nearby/platform/fake_network_context.h"
+#include "chrome/services/sharing/nearby/platform/fake_tcp_socket_factory.h"
 #include "chrome/services/sharing/nearby/platform/wifi_lan_server_socket.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "mojo/public/cpp/bindings/shared_remote.h"
 #include "net/base/net_errors.h"
+#include "services/network/public/mojom/tcp_socket.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace location {
 namespace nearby {
@@ -20,12 +23,12 @@
 namespace {
 
 const char kLocalIpString[] = "\xC0\xA8\x56\x4B";
-const int kLocalPort = 44444;
+const int kLocalPort = ash::nearby::TcpServerSocketPort::kMin;
 const net::IPEndPoint kLocalAddress(net::IPAddress(192, 168, 86, 75),
                                     kLocalPort);
 
 const char kRemoteIpString[] = "\xC0\xA8\x56\x3E";
-const int kRemotePort = 33333;
+const int kRemotePort = ash::nearby::TcpServerSocketPort::kMax;
 
 }  // namespace
 
@@ -37,35 +40,35 @@
   WifiLanMediumTest& operator=(const WifiLanMediumTest&) = delete;
 
   void SetUp() override {
-    auto fake_network_context = std::make_unique<FakeNetworkContext>(
+    auto fake_socket_factory = std::make_unique<FakeTcpSocketFactory>(
         /*default_local_addr=*/kLocalAddress);
-    fake_network_context_ = fake_network_context.get();
+    fake_socket_factory_ = fake_socket_factory.get();
     mojo::MakeSelfOwnedReceiver(
-        std::move(fake_network_context),
-        network_context_shared_remote_.BindNewPipeAndPassReceiver());
+        std::move(fake_socket_factory),
+        socket_factory_shared_remote_.BindNewPipeAndPassReceiver());
 
     wifi_lan_medium_ =
-        std::make_unique<WifiLanMedium>(network_context_shared_remote_);
+        std::make_unique<WifiLanMedium>(socket_factory_shared_remote_);
   }
 
   void TearDown() override { wifi_lan_medium_.reset(); }
 
   // Calls ConnectToService()/ListenForService() from |num_threads|, which will
   // each block until failure or the TCP connected/server socket is created.
-  // This method returns when |expected_num_calls_sent_to_network_context|
-  // NetworkContext::CreateTCPConnectedSocket()/CreateTCPServerSocket() calls
+  // This method returns when |expected_num_calls_sent_to_socket_factory|
+  // TcpSocketFactory::CreateTCPConnectedSocket()/CreateTCPServerSocket() calls
   // are queued up. When the ConnectToService()/ListenForService() calls finish
   // on all threads, |on_finished| is invoked.
   void CallConnectToServiceFromThreads(
       size_t num_threads,
-      size_t expected_num_calls_sent_to_network_context,
+      size_t expected_num_calls_sent_to_socket_factory,
       bool expected_success,
       base::OnceClosure on_connect_calls_finished) {
-    // The run loop quits when NetworkContext receives all of the expected
+    // The run loop quits when TcpSocketFactory receives all of the expected
     // CreateTCPConnectedSocket() calls.
     base::RunLoop run_loop;
-    fake_network_context_->SetCreateConnectedSocketCallExpectations(
-        expected_num_calls_sent_to_network_context,
+    fake_socket_factory_->SetCreateConnectedSocketCallExpectations(
+        expected_num_calls_sent_to_socket_factory,
         /*on_all_create_connected_socket_calls_queued=*/run_loop.QuitClosure());
 
     on_connect_calls_finished_ = std::move(on_connect_calls_finished);
@@ -81,14 +84,14 @@
 
   void CallListenForServiceFromThreads(
       size_t num_threads,
-      size_t expected_num_calls_sent_to_network_context,
+      size_t expected_num_calls_sent_to_socket_factory,
       bool expected_success,
       base::OnceClosure on_listen_calls_finished) {
-    // The run loop quits when NetworkContext receives all of the expected
+    // The run loop quits when TcpSocketFactory receives all of the expected
     // CreateTCPServerSocket() calls.
     base::RunLoop run_loop;
-    fake_network_context_->SetCreateServerSocketCallExpectations(
-        expected_num_calls_sent_to_network_context,
+    fake_socket_factory_->SetCreateServerSocketCallExpectations(
+        expected_num_calls_sent_to_socket_factory,
         /*on_all_create_server_socket_calls_queued=*/run_loop.QuitClosure());
 
     on_listen_calls_finished_ = std::move(on_listen_calls_finished);
@@ -137,9 +140,9 @@
   size_t num_running_listen_calls_ = 0;
   base::OnceClosure on_connect_calls_finished_;
   base::OnceClosure on_listen_calls_finished_;
-  FakeNetworkContext* fake_network_context_;
-  mojo::SharedRemote<network::mojom::NetworkContext>
-      network_context_shared_remote_;
+  FakeTcpSocketFactory* fake_socket_factory_;
+  mojo::SharedRemote<sharing::mojom::TcpSocketFactory>
+      socket_factory_shared_remote_;
   std::unique_ptr<WifiLanMedium> wifi_lan_medium_;
 };
 
@@ -150,10 +153,10 @@
   base::RunLoop run_loop;
   CallConnectToServiceFromThreads(
       /*num_threads=*/1u,
-      /*expected_num_calls_sent_to_network_context=*/1u,
+      /*expected_num_calls_sent_to_socket_factory=*/1u,
       /*expected_success=*/true,
       /*on_connect_calls_finished=*/run_loop.QuitClosure());
-  fake_network_context_->FinishNextCreateConnectedSocket(net::OK);
+  fake_socket_factory_->FinishNextCreateConnectedSocket(net::OK);
   run_loop.Run();
 }
 
@@ -162,11 +165,11 @@
   base::RunLoop run_loop;
   CallConnectToServiceFromThreads(
       kNumThreads,
-      /*expected_num_calls_sent_to_network_context=*/kNumThreads,
+      /*expected_num_calls_sent_to_socket_factory=*/kNumThreads,
       /*expected_success=*/true,
       /*on_connect_calls_finished=*/run_loop.QuitClosure());
   for (size_t thread = 0; thread < kNumThreads; ++thread) {
-    fake_network_context_->FinishNextCreateConnectedSocket(net::OK);
+    fake_socket_factory_->FinishNextCreateConnectedSocket(net::OK);
   }
   run_loop.Run();
 }
@@ -175,10 +178,10 @@
   base::RunLoop run_loop;
   CallConnectToServiceFromThreads(
       /*num_threads=*/1u,
-      /*expected_num_calls_sent_to_network_context=*/1u,
+      /*expected_num_calls_sent_to_socket_factory=*/1u,
       /*expected_success=*/false,
       /*on_connect_calls_finished=*/run_loop.QuitClosure());
-  fake_network_context_->FinishNextCreateConnectedSocket(net::ERR_FAILED);
+  fake_socket_factory_->FinishNextCreateConnectedSocket(net::ERR_FAILED);
   run_loop.Run();
 }
 
@@ -187,11 +190,11 @@
   base::RunLoop run_loop;
   CallConnectToServiceFromThreads(
       kNumThreads,
-      /*expected_num_calls_sent_to_network_context=*/kNumThreads,
+      /*expected_num_calls_sent_to_socket_factory=*/kNumThreads,
       /*expected_success=*/false,
       /*on_connect_calls_finished=*/run_loop.QuitClosure());
   for (size_t thread = 0; thread < kNumThreads; ++thread) {
-    fake_network_context_->FinishNextCreateConnectedSocket(net::ERR_FAILED);
+    fake_socket_factory_->FinishNextCreateConnectedSocket(net::ERR_FAILED);
   }
   run_loop.Run();
 }
@@ -201,7 +204,7 @@
   base::RunLoop run_loop;
   CallConnectToServiceFromThreads(
       kNumThreads,
-      /*expected_num_calls_sent_to_network_context=*/kNumThreads,
+      /*expected_num_calls_sent_to_socket_factory=*/kNumThreads,
       /*expected_success=*/false,
       /*on_connect_calls_finished=*/run_loop.QuitClosure());
 
@@ -220,10 +223,10 @@
   base::RunLoop run_loop;
   CallListenForServiceFromThreads(
       /*num_threads=*/1u,
-      /*expected_num_calls_sent_to_network_context=*/1u,
+      /*expected_num_calls_sent_to_socket_factory=*/1u,
       /*expected_success=*/true,
       /*on_listen_calls_finished=*/run_loop.QuitClosure());
-  fake_network_context_->FinishNextCreateServerSocket(net::OK);
+  fake_socket_factory_->FinishNextCreateServerSocket(net::OK);
   run_loop.Run();
 }
 
@@ -232,46 +235,52 @@
   base::RunLoop run_loop;
   CallListenForServiceFromThreads(
       kNumThreads,
-      /*expected_num_calls_sent_to_network_context=*/kNumThreads,
+      /*expected_num_calls_sent_to_socket_factory=*/kNumThreads,
       /*expected_success=*/true,
       /*on_listen_calls_finished=*/run_loop.QuitClosure());
   for (size_t thread = 0; thread < kNumThreads; ++thread) {
-    fake_network_context_->FinishNextCreateServerSocket(net::OK);
+    fake_socket_factory_->FinishNextCreateServerSocket(net::OK);
   }
   run_loop.Run();
 }
 
-TEST_F(WifiLanMediumTest, Listen_Failure) {
+TEST_F(WifiLanMediumTest, Listen_Failure_CreateTcpServerSocket) {
   base::RunLoop run_loop;
   CallListenForServiceFromThreads(
       /*num_threads=*/1u,
-      /*expected_num_calls_sent_to_network_context=*/1u,
+      /*expected_num_calls_sent_to_socket_factory=*/1u,
       /*expected_success=*/false,
       /*on_listen_calls_finished=*/run_loop.QuitClosure());
-  fake_network_context_->FinishNextCreateServerSocket(net::ERR_FAILED);
+  fake_socket_factory_->FinishNextCreateServerSocket(net::ERR_FAILED);
   run_loop.Run();
 }
 
-TEST_F(WifiLanMediumTest, Listen_Failure_ConcurrentCalls) {
+TEST_F(WifiLanMediumTest,
+       Listen_Failure_CreateTcpServerSocket_ConcurrentCalls) {
   const size_t kNumThreads = 3;
   base::RunLoop run_loop;
   CallListenForServiceFromThreads(
       kNumThreads,
-      /*expected_num_calls_sent_to_network_context=*/kNumThreads,
+      /*expected_num_calls_sent_to_socket_factory=*/kNumThreads,
       /*expected_success=*/false,
       /*on_listen_calls_finished=*/run_loop.QuitClosure());
   for (size_t thread = 0; thread < kNumThreads; ++thread) {
-    fake_network_context_->FinishNextCreateServerSocket(net::ERR_FAILED);
+    fake_socket_factory_->FinishNextCreateServerSocket(net::ERR_FAILED);
   }
   run_loop.Run();
 }
 
+TEST_F(WifiLanMediumTest, Listen_Failure_InvalidPort) {
+  base::ScopedAllowBaseSyncPrimitivesForTesting allow;
+  EXPECT_FALSE(wifi_lan_medium_->ListenForService(/*port=*/-1));
+}
+
 TEST_F(WifiLanMediumTest, Listen_DestroyWhileWaiting) {
   const size_t kNumThreads = 3;
   base::RunLoop run_loop;
   CallListenForServiceFromThreads(
       kNumThreads,
-      /*expected_num_calls_sent_to_network_context=*/kNumThreads,
+      /*expected_num_calls_sent_to_socket_factory=*/kNumThreads,
       /*expected_success=*/false,
       /*on_listen_calls_finished=*/run_loop.QuitClosure());
 
diff --git a/chrome/services/speech/audio_source_fetcher_impl.cc b/chrome/services/speech/audio_source_fetcher_impl.cc
index cb0d4823..7d3e892 100644
--- a/chrome/services/speech/audio_source_fetcher_impl.cc
+++ b/chrome/services/speech/audio_source_fetcher_impl.cc
@@ -87,6 +87,7 @@
   }
   send_audio_callback_.Reset();
   is_started_ = false;
+  speech_recognition_recognizer_->MarkDone();
 }
 
 void AudioSourceFetcherImpl::Capture(const media::AudioBus* audio_source,
diff --git a/chrome/services/speech/cros_speech_recognition_recognizer_impl.cc b/chrome/services/speech/cros_speech_recognition_recognizer_impl.cc
index 17afe69..f3d867b 100644
--- a/chrome/services/speech/cros_speech_recognition_recognizer_impl.cc
+++ b/chrome/services/speech/cros_speech_recognition_recognizer_impl.cc
@@ -68,10 +68,6 @@
           config_path),
       binary_path_(binary_path),
       languagepack_path_(config_path) {
-  recognition_event_callback_ = base::BindRepeating(
-      &CrosSpeechRecognitionRecognizerImpl::OnRecognitionEvent,
-      weak_factory_.GetWeakPtr());
-
   cros_soda_client_ = std::make_unique<soda::CrosSodaClient>();
 }
 
@@ -108,9 +104,22 @@
         options_->enable_formatting
             ? chromeos::machine_learning::mojom::OptionalBool::kTrue
             : chromeos::machine_learning::mojom::OptionalBool::kFalse;
-    cros_soda_client_->Reset(std::move(config), recognition_event_callback_);
+    cros_soda_client_->Reset(std::move(config), recognition_event_callback(),
+                             speech_recognition_stopped_callback());
   }
   cros_soda_client_->AddAudio(reinterpret_cast<char*>(buffer->data.data()),
                               buffer_size);
 }
+
+void CrosSpeechRecognitionRecognizerImpl::MarkDone() {
+  if (cros_soda_client_ == nullptr || !cros_soda_client_->IsInitialized()) {
+    LOG(DFATAL)
+        << "No soda client or soda client is not initialized, stopping.";
+    mojo::ReportBadMessage(kNoClientError);
+    return;
+  }
+
+  cros_soda_client_->MarkDone();
+}
+
 }  // namespace speech
diff --git a/chrome/services/speech/cros_speech_recognition_recognizer_impl.h b/chrome/services/speech/cros_speech_recognition_recognizer_impl.h
index 7df55f4..05f6830 100644
--- a/chrome/services/speech/cros_speech_recognition_recognizer_impl.h
+++ b/chrome/services/speech/cros_speech_recognition_recognizer_impl.h
@@ -46,19 +46,14 @@
       const base::FilePath& binary_path,
       const base::FilePath& config_path);
 
-  OnRecognitionEventCallback recognition_event_callback() const {
-    return recognition_event_callback_;
-  }
   // SpeechRecognitionRecognizerImpl:
   void SendAudioToSpeechRecognitionServiceInternal(
       media::mojom::AudioDataS16Ptr buffer) override;
 
+  void MarkDone() override;
+
  private:
   std::unique_ptr<soda::CrosSodaClient> cros_soda_client_;
-  // The callback that is eventually executed on a speech recognition event
-  // which passes the transcribed audio back to the caller via the speech
-  // recognition event client remote.
-  OnRecognitionEventCallback recognition_event_callback_;
 
   const base::FilePath binary_path_, languagepack_path_;
 
diff --git a/chrome/services/speech/soda/cros_soda_client.cc b/chrome/services/speech/soda/cros_soda_client.cc
index 988cdd08..1f17fc8 100644
--- a/chrome/services/speech/soda/cros_soda_client.cc
+++ b/chrome/services/speech/soda/cros_soda_client.cc
@@ -54,9 +54,15 @@
   soda_recognizer_->AddAudio(audio);
 }
 
+void CrosSodaClient::MarkDone() {
+  DCHECK(IsInitialized()) << "Can't mark as done before starting";
+  soda_recognizer_->MarkDone();
+}
+
 void CrosSodaClient::Reset(
     chromeos::machine_learning::mojom::SodaConfigPtr soda_config,
-    CrosSodaClient::TranscriptionResultCallback callback) {
+    CrosSodaClient::TranscriptionResultCallback transcription_callback,
+    CrosSodaClient::OnStopCallback stop_callback) {
   sample_rate_ = soda_config->sample_rate;
   channel_count_ = soda_config->channel_count;
   if (is_initialized_) {
@@ -79,13 +85,17 @@
             }
           }));
 
-  callback_ = callback;
+  transcription_callback_ = transcription_callback;
+  stop_callback_ = stop_callback;
+
   // Ensure this one is started.
   soda_recognizer_->Start();
 }
+
 void CrosSodaClient::OnStop() {
-  // Do nothing OnStop.
+  stop_callback_.Run();
 }
+
 void CrosSodaClient::OnStart() {
   // Do nothing OnStart.
 }
@@ -94,12 +104,14 @@
   if (event->is_final_result()) {
     auto& final_result = event->get_final_result();
     if (!final_result->final_hypotheses.empty())
-      callback_.Run(GetSpeechRecognitionResultFromFinalEvent(final_result));
+      transcription_callback_.Run(
+          GetSpeechRecognitionResultFromFinalEvent(final_result));
   } else if (event->is_partial_result()) {
     auto& partial_result = event->get_partial_result();
     if (!partial_result->partial_text.empty()) {
       const std::string partial_hyp = partial_result->partial_text.front();
-      callback_.Run(media::SpeechRecognitionResult(partial_hyp, false));
+      transcription_callback_.Run(
+          media::SpeechRecognitionResult(partial_hyp, false));
     }
   } else if (!event->is_endpointer_event() && !event->is_audio_event()) {
     LOG(ERROR) << "Some kind of other soda event, ignoring completely. Tag is '"
diff --git a/chrome/services/speech/soda/cros_soda_client.h b/chrome/services/speech/soda/cros_soda_client.h
index 1762628..61aa40c 100644
--- a/chrome/services/speech/soda/cros_soda_client.h
+++ b/chrome/services/speech/soda/cros_soda_client.h
@@ -25,10 +25,17 @@
   using TranscriptionResultCallback =
       base::RepeatingCallback<void(media::SpeechRecognitionResult event)>;
 
+  using OnStopCallback = base::RepeatingCallback<void()>;
+
   // Adds audio to this soda client. Only makes sense when initialized.
   // Eventually, asynchronous callbacks to the ::SodaClient overrides below are
   // executed.
   void AddAudio(const char* audio_buffer, int audio_buffer_size) const;
+
+  // Notifies the soda client to stop speech recognition after processing the
+  // audio it has received so far.
+  void MarkDone();
+
   // Checks if the sample rate / channels changed between calls.
   bool DidAudioPropertyChange(int sample_rate, int channel_count);
   bool IsInitialized() const { return is_initialized_; }
@@ -43,12 +50,17 @@
   // Reset this client with the provided configuration, and send recognition
   // callbacks of (text, is_final) to the given callback.
   void Reset(chromeos::machine_learning::mojom::SodaConfigPtr soda_config,
-             TranscriptionResultCallback callback);
+             TranscriptionResultCallback transcription_callback,
+             OnStopCallback stop_callback);
 
  private:
   // This callback is called with (media::mojom::SpeechRecognitionResult)
   // whenever soda responds appropriately.
-  TranscriptionResultCallback callback_;
+  TranscriptionResultCallback transcription_callback_;
+
+  // This callback is called with transcription stops.
+  OnStopCallback stop_callback_;
+
   bool is_initialized_ = false;
   int sample_rate_ = 0;
   int channel_count_ = 0;
diff --git a/chrome/services/speech/soda/soda_client.cc b/chrome/services/speech/soda/soda_client.cc
index cd620fa..cd5b1cba 100644
--- a/chrome/services/speech/soda/soda_client.cc
+++ b/chrome/services/speech/soda/soda_client.cc
@@ -24,6 +24,8 @@
           lib_.GetFunctionPointer("DeleteExtendedSodaAsync"))),
       add_audio_func_(reinterpret_cast<AddAudioFunction>(
           lib_.GetFunctionPointer("ExtendedAddAudio"))),
+      mark_done_func_(reinterpret_cast<MarkDoneFunction>(
+          lib_.GetFunctionPointer("ExtendedSodaMarkDone"))),
       soda_start_func_(reinterpret_cast<SodaStartFunction>(
           lib_.GetFunctionPointer("ExtendedSodaStart"))),
       is_initialized_(false),
@@ -39,12 +41,13 @@
   DCHECK(create_soda_func_);
   DCHECK(delete_soda_func_);
   DCHECK(add_audio_func_);
+  DCHECK(mark_done_func_);
   DCHECK(soda_start_func_);
 
   if (!lib_.is_valid()) {
     load_soda_result_ = LoadSodaResultValue::kBinaryInvalid;
   } else if (!(create_soda_func_ && delete_soda_func_ && add_audio_func_ &&
-               soda_start_func_)) {
+               soda_start_func_ && mark_done_func_)) {
     load_soda_result_ = LoadSodaResultValue::kFunctionPointerInvalid;
   } else {
     load_soda_result_ = LoadSodaResultValue::kSuccess;
@@ -88,6 +91,13 @@
   add_audio_func_(soda_async_handle_, audio_buffer, audio_buffer_size);
 }
 
+NO_SANITIZE("cfi-icall")
+void SodaClient::MarkDone() {
+  if (load_soda_result_ != LoadSodaResultValue::kSuccess)
+    return;
+  mark_done_func_(soda_async_handle_);
+}
+
 bool SodaClient::DidAudioPropertyChange(int sample_rate, int channel_count) {
   return sample_rate != sample_rate_ || channel_count != channel_count_;
 }
diff --git a/chrome/services/speech/soda/soda_client.h b/chrome/services/speech/soda/soda_client.h
index 1e6c937..73f02b3 100644
--- a/chrome/services/speech/soda/soda_client.h
+++ b/chrome/services/speech/soda/soda_client.h
@@ -37,6 +37,9 @@
   // Feeds raw audio to SODA in the form of a contiguous stream of characters.
   void AddAudio(const char* audio_buffer, int audio_buffer_size);
 
+  // Notifies the client to finish transcribing.
+  void MarkDone();
+
   // Checks whether the sample rate or channel count differs from the values
   // used to initialize the SODA instance.
   bool DidAudioPropertyChange(int sample_rate, int channel_count);
@@ -65,6 +68,9 @@
   typedef void (*AddAudioFunction)(void*, const char*, int);
   AddAudioFunction add_audio_func_;
 
+  typedef void (*MarkDoneFunction)(void*);
+  MarkDoneFunction mark_done_func_;
+
   typedef void (*SodaStartFunction)(void*);
   SodaStartFunction soda_start_func_;
 
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl.cc b/chrome/services/speech/speech_recognition_recognizer_impl.cc
index c6bbdde..db205242 100644
--- a/chrome/services/speech/speech_recognition_recognizer_impl.cc
+++ b/chrome/services/speech/speech_recognition_recognizer_impl.cc
@@ -90,6 +90,12 @@
              static_cast<media::mojom::ConfidenceLevel>(
                  event.confidence_level()));
   }
+
+  if (response.soda_type() == soda::chrome::SodaResponse::STOP) {
+    static_cast<SpeechRecognitionRecognizerImpl*>(callback_handle)
+        ->speech_recognition_stopped_callback()
+        .Run();
+  }
 }
 
 speech::soda::chrome::ExtendedSodaConfigMsg::RecognitionMode
@@ -157,6 +163,12 @@
   }
 }
 
+void SpeechRecognitionRecognizerImpl::OnRecognitionStoppedCallback() {
+  if (client_remote_.is_bound()) {
+    client_remote_->OnSpeechRecognitionStopped();
+  }
+}
+
 SpeechRecognitionRecognizerImpl::SpeechRecognitionRecognizerImpl(
     mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient> remote,
     base::WeakPtr<SpeechRecognitionServiceImpl> speech_recognition_service_impl,
@@ -173,6 +185,10 @@
       media::BindToCurrentLoop(base::BindRepeating(
           &SpeechRecognitionRecognizerImpl::OnLanguageIdentificationEvent,
           weak_factory_.GetWeakPtr()));
+  speech_recognition_stopped_callback_ =
+      media::BindToCurrentLoop(base::BindRepeating(
+          &SpeechRecognitionRecognizerImpl::OnRecognitionStoppedCallback,
+          weak_factory_.GetWeakPtr()));
 
   // Unretained is safe because |this| owns the mojo::Remote.
   client_remote_.set_disconnect_handler(
@@ -180,14 +196,14 @@
                      weak_factory_.GetWeakPtr()));
 
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
-    // On Chrome OS Ash, soda_client_ is not used, so don't try to create it
-    // here because it exists at a different location. Instead,
-    // CrosSpeechRecognitionRecognizerImpl has its own CrosSodaClient.
-    DCHECK(base::PathExists(binary_path));
-    soda_client_ = std::make_unique<::soda::SodaClient>(binary_path);
-    if (!soda_client_->BinaryLoadedSuccessfully()) {
-      OnSpeechRecognitionError();
-    }
+  // On Chrome OS Ash, soda_client_ is not used, so don't try to create it
+  // here because it exists at a different location. Instead,
+  // CrosSpeechRecognitionRecognizerImpl has its own CrosSodaClient.
+  DCHECK(base::PathExists(binary_path));
+  soda_client_ = std::make_unique<::soda::SodaClient>(binary_path);
+  if (!soda_client_->BinaryLoadedSuccessfully()) {
+    OnSpeechRecognitionError();
+  }
 #endif
 }
 
@@ -244,6 +260,10 @@
   }
 }
 
+void SpeechRecognitionRecognizerImpl::MarkDone() {
+  soda_client_->MarkDone();
+}
+
 void SpeechRecognitionRecognizerImpl::
     SendAudioToSpeechRecognitionServiceInternal(
         media::mojom::AudioDataS16Ptr buffer) {
@@ -257,15 +277,15 @@
     return;
   }
 
-    CHECK(soda_client_);
-    DCHECK(base::PathExists(config_path_));
-    if (!soda_client_->IsInitialized() ||
-        soda_client_->DidAudioPropertyChange(sample_rate_, channel_count_)) {
-      ResetSoda();
-    }
+  CHECK(soda_client_);
+  DCHECK(base::PathExists(config_path_));
+  if (!soda_client_->IsInitialized() ||
+      soda_client_->DidAudioPropertyChange(sample_rate_, channel_count_)) {
+    ResetSoda();
+  }
 
-    soda_client_->AddAudio(reinterpret_cast<char*>(buffer->data.data()),
-                           buffer_size);
+  soda_client_->AddAudio(reinterpret_cast<char*>(buffer->data.data()),
+                         buffer_size);
 }
 
 void SpeechRecognitionRecognizerImpl::OnLanguageChanged(
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl.h b/chrome/services/speech/speech_recognition_recognizer_impl.h
index 24a7b9f36..c3eea81 100644
--- a/chrome/services/speech/speech_recognition_recognizer_impl.h
+++ b/chrome/services/speech/speech_recognition_recognizer_impl.h
@@ -31,6 +31,8 @@
       const std::string& language,
       const media::mojom::ConfidenceLevel confidence_level)>;
 
+  using OnSpeechRecognitionStoppedCallback = base::RepeatingCallback<void()>;
+
   SpeechRecognitionRecognizerImpl(
       mojo::PendingRemote<media::mojom::SpeechRecognitionRecognizerClient>
           remote,
@@ -71,6 +73,11 @@
     return language_identification_event_callback_;
   }
 
+  OnSpeechRecognitionStoppedCallback speech_recognition_stopped_callback()
+      const {
+    return speech_recognition_stopped_callback_;
+  }
+
   // Convert the audio buffer into the appropriate format and feed the raw audio
   // into the speech recognition instance.
   void SendAudioToSpeechRecognitionService(
@@ -78,6 +85,8 @@
 
   void OnSpeechRecognitionError();
 
+  void MarkDone() override;
+
  protected:
   virtual void SendAudioToSpeechRecognitionServiceInternal(
       media::mojom::AudioDataS16Ptr buffer);
@@ -90,6 +99,8 @@
       const std::string& language,
       const media::mojom::ConfidenceLevel confidence_level);
 
+  void OnRecognitionStoppedCallback();
+
   media::mojom::SpeechRecognitionOptionsPtr options_;
 
  private:
@@ -121,6 +132,8 @@
 
   OnLanguageIdentificationEventCallback language_identification_event_callback_;
 
+  OnSpeechRecognitionStoppedCallback speech_recognition_stopped_callback_;
+
   base::FilePath config_path_;
   int sample_rate_ = 0;
   int channel_count_ = 0;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index cd3dcca..fbe1e3a 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -668,15 +668,7 @@
   }
 }
 
-if (!is_android) {
-  group("telemetry_gpu_integration_test") {
-    testonly = true
-    data_deps = [
-      "//content/test:telemetry_gpu_integration_test_support",
-      "//tools/perf/chrome_telemetry_build:telemetry_chrome_test",
-    ]
-  }
-} else {
+if (is_android) {
   template("telemetry_gpu_integration_test_android_template") {
     forward_variables_from(invoker, [ "telemetry_target_suffix" ])
     group(target_name) {
@@ -694,6 +686,34 @@
       telemetry_target_suffix = _target_suffix
     }
   }
+} else if (is_fuchsia) {
+  group("telemetry_gpu_integration_test_fuchsia") {
+    testonly = true
+    data = [
+      "//content/test/gpu/run_gpu_integration_test_fuchsia.py",
+      "//content/test/gpu/fuchsia_util.py",
+    ]
+    data_deps = [
+      "//content/test:telemetry_gpu_integration_test_support",
+      "//tools/perf/chrome_telemetry_build:telemetry_chrome_test",
+    ]
+    if (fuchsia_browser_type == "web_engine_shell") {
+      data_deps += [
+        "//fuchsia/engine:web_engine_shell",
+        "//fuchsia/engine:web_engine_with_webui_installer",
+      ]
+    } else if (fuchsia_browser_type == "chrome") {
+      data_deps += [ "//chrome/app:chrome_fuchsia" ]
+    }
+  }
+} else {
+  group("telemetry_gpu_integration_test") {
+    testonly = true
+    data_deps = [
+      "//content/test:telemetry_gpu_integration_test_support",
+      "//tools/perf/chrome_telemetry_build:telemetry_chrome_test",
+    ]
+  }
 }
 
 if (is_win) {
@@ -1281,9 +1301,6 @@
       "//components/subresource_filter/core/browser",
       "//components/subresource_filter/core/browser:test_support",
       "//components/subresource_filter/core/common:test_support",
-      "//components/subresource_redirect:test_support",
-      "//components/subresource_redirect/common",
-      "//components/subresource_redirect/proto",
       "//components/sync",
       "//components/sync:test_support_model",
       "//components/sync/test/fake_server",
@@ -1532,9 +1549,6 @@
       "../browser/data_saver/data_saver_browsertest.cc",
       "../browser/data_saver/data_saver_holdback_browsertest.cc",
       "../browser/data_saver/data_saver_webapis_browsertest.cc",
-      "../browser/data_saver/login_robots_src_video_browsertest.cc",
-      "../browser/data_saver/subresource_redirect_browsertest.cc",
-      "../browser/data_saver/subresource_redirect_login_robots_browsertest.cc",
       "../browser/data_use_measurement/chrome_data_use_measurement_browsertest.cc",
       "../browser/device_api/managed_configuration_api_browsertest.cc",
       "../browser/devtools/device/adb/adb_client_socket_browsertest.cc",
@@ -1716,6 +1730,7 @@
       "../browser/optimization_guide/browser_test_util.h",
       "../browser/optimization_guide/hints_fetcher_browsertest.cc",
       "../browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc",
+      "../browser/optimization_guide/page_content_annotations_service_browsertest.cc",
       "../browser/optimization_guide/page_text_observer_browsertest.cc",
       "../browser/optimization_guide/prediction/prediction_manager_browsertest.cc",
       "../browser/page_load_metrics/observers/ad_metrics/ad_density_intervention_browsertest.cc",
@@ -2106,11 +2121,6 @@
       "../renderer/chrome_content_renderer_client_browsertest.cc",
       "../renderer/chrome_content_settings_agent_delegate_browsertest.cc",
       "../renderer/chrome_render_frame_observer_browsertest.cc",
-      "../renderer/subresource_redirect/login_robots_decider_agent_browsertest.cc",
-      "../renderer/subresource_redirect/login_robots_url_loader_throttle_browsertest.cc",
-      "../renderer/subresource_redirect/public_image_hints_decider_agent_browsertest.cc",
-      "../renderer/subresource_redirect/robots_rules_parser_cache_browsertest.cc",
-      "../renderer/subresource_redirect/subresource_redirect_renderer_browsertest.cc",
       "../renderer/translate/per_frame_translate_agent_browsertest.cc",
       "../renderer/translate/translate_agent_browsertest.cc",
       "../renderer/translate/translate_script_browsertest.cc",
@@ -2141,11 +2151,6 @@
       "v8/wasm_trap_handler_browsertest.cc",
     ]
 
-    # crbug.com/1279884 Flaky on CrOS
-    if (!is_chromeos) {
-      sources += [ "../browser/optimization_guide/page_content_annotations_service_browsertest.cc" ]
-    }
-
     if (enable_reporting) {
       sources += [ "../browser/net/reporting_browsertest.cc" ]
     }
@@ -2161,6 +2166,7 @@
 
         # https://crbug.com/1252812 The intent picker (launch icon) actions
         # are not working on Lacros.
+        "../browser/ui/views/intent_picker_bubble_view_browsertest.cc",
         "../browser/ui/views/web_apps/web_app_integration_browsertest.cc",
 
         # Lacros does not seem to have any actual WebView-based UI to test.
@@ -2409,11 +2415,6 @@
       ]
     }
 
-    if (!is_chromeos) {
-      sources +=
-          [ "../browser/ui/views/intent_picker_bubble_view_browsertest.cc" ]
-    }
-
     if (include_js_tests) {
       deps += [
         "//chrome/browser/resources:browser_tests_js",
@@ -4550,7 +4551,6 @@
     "../browser/custom_handlers/test_protocol_handler_registry_delegate.cc",
     "../browser/custom_handlers/test_protocol_handler_registry_delegate.h",
     "../browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_unittest.cc",
-    "../browser/data_saver/subresource_redirect_login_robots_unittest.cc",
     "../browser/download/bubble/download_display_controller_unittest.cc",
     "../browser/download/chrome_download_manager_delegate_unittest.cc",
     "../browser/download/deferred_client_wrapper_unittest.cc",
@@ -4770,10 +4770,6 @@
     "../browser/security_events/security_event_recorder_impl_unittest.cc",
     "../browser/segmentation_platform/segmentation_platform_profile_observer_unittest.cc",
     "../browser/signin/e2e_tests/test_accounts_util_unittest.cc",
-    "../browser/subresource_redirect/https_image_compression_infobar_decider_unittest.cc",
-    "../browser/subresource_redirect/litepages_service_bypass_decider_unittest.cc",
-    "../browser/subresource_redirect/origin_robots_rules_unittest.cc",
-    "../browser/subresource_redirect/subresource_redirect_util_unit_test.cc",
     "../browser/ui/autofill/payments/offer_notification_helper_unittest.cc",
 
     # TODO(hashimoto): those tests should be componentized and moved to
@@ -4890,8 +4886,6 @@
     "../renderer/media/flash_embed_rewrite_unittest.cc",
     "../renderer/net/net_error_helper_core_unittest.cc",
     "../renderer/plugins/plugin_uma_unittest.cc",
-    "../renderer/subresource_redirect/robots_rules_parser_unittest.cc",
-    "../renderer/subresource_redirect/subresource_redirect_util_unittest.cc",
     "../renderer/v8_unwinder_unittest.cc",
     "../test/base/menu_model_test.cc",
     "../test/base/menu_model_test.h",
@@ -5282,6 +5276,8 @@
     "//components/browsing_data/core",
     "//components/captive_portal/content",
     "//components/captive_portal/core:buildflags",
+    "//components/cast_channel:cast_channel",
+    "//components/cast_channel:test_support",
     "//components/component_updater:test_support",
     "//components/content_settings/browser",
     "//components/content_settings/browser:test_support",
@@ -5426,8 +5422,6 @@
     "//components/strings",
     "//components/subresource_filter/core/browser",
     "//components/subresource_filter/core/browser:test_support",
-    "//components/subresource_redirect:test_support",
-    "//components/subresource_redirect/proto",
     "//components/sync:test_support",
     "//components/sync_bookmarks",
     "//components/sync_device_info:test_support",
@@ -6361,6 +6355,7 @@
         "//chromeos/dbus/attestation",
         "//chromeos/dbus/cicerone",
         "//chromeos/dbus/concierge",
+        "//chromeos/dbus/cros_disks",
         "//chromeos/dbus/debug_daemon",
         "//chromeos/dbus/hermes",
         "//chromeos/dbus/power",
@@ -6601,6 +6596,7 @@
       "../browser/nearby_sharing/payload_tracker_unittest.cc",
       "../browser/nearby_sharing/sharesheet/nearby_share_action_unittest.cc",
       "../browser/nearby_sharing/tachyon_ice_config_fetcher_unittest.cc",
+      "../browser/nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory_unittest.cc",
       "../browser/nearby_sharing/text_attachment_unittest.cc",
       "../browser/nearby_sharing/webrtc_request_builder_unittest.cc",
       "../browser/nearby_sharing/webrtc_signaling_messenger_unittest.cc",
@@ -6717,6 +6713,7 @@
       "//ash/public/cpp/assistant/test_support",
       "//ash/public/cpp/resources:ash_public_unscaled_resources",
       "//ash/resources/vector_icons",
+      "//ash/services/nearby/public/cpp:tcp_server_socket_port",
       "//ash/services/nearby/public/cpp:test_support",
       "//ash/strings",
       "//ash/webui/projector_app:test_support",
@@ -7812,8 +7809,8 @@
       "../browser/ui/views/tabs/tab_strip_unittest.cc",
       "../browser/ui/views/tabs/tab_unittest.cc",
       "../browser/ui/views/toolbar/chrome_labs_bubble_view_model_unittest.cc",
-      "../browser/ui/views/toolbar/chrome_labs_bubble_view_unittest.cc",
       "../browser/ui/views/toolbar/chrome_labs_button_unittest.cc",
+      "../browser/ui/views/toolbar/chrome_labs_unittest.cc",
       "../browser/ui/views/toolbar/reload_button_unittest.cc",
       "../browser/ui/views/toolbar/side_panel_toolbar_button_unittest.cc",
       "../browser/ui/views/toolbar/toolbar_action_view_unittest.cc",
diff --git a/chrome/test/base/chrome_test_suite.cc b/chrome/test/base/chrome_test_suite.cc
index 67b2f6a..4a52bf5 100644
--- a/chrome/test/base/chrome_test_suite.cc
+++ b/chrome/test/base/chrome_test_suite.cc
@@ -117,7 +117,10 @@
   base::FilePath temp_path = scoped_temp_dir_.GetPath();
   chrome::SetLacrosDefaultPaths(/*documents_dir=*/temp_path,
                                 /*downloads_dir=*/temp_path,
-                                /*drivefs=*/base::FilePath());
+                                /*drivefs=*/base::FilePath(),
+                                /*removable_media_dir*/ base::FilePath(),
+                                /*android_files_dir*/ base::FilePath(),
+                                /*linux_files_dir*/ base::FilePath());
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 }
 
diff --git a/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.cc b/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.cc
index e94495b..845d9fc 100644
--- a/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.cc
+++ b/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.cc
@@ -25,10 +25,7 @@
     : test_controller_ash_(std::make_unique<crosapi::TestControllerAsh>()) {}
 
 FakeAshTestChromeBrowserMainExtraParts::
-    ~FakeAshTestChromeBrowserMainExtraParts() {
-  crosapi::CrosapiManager::Get()->crosapi_ash()->SetTestControllerForTesting(
-      nullptr);
-}
+    ~FakeAshTestChromeBrowserMainExtraParts() = default;
 
 // Create a file so test_runner know ash is ready for testing.
 void AshIsReadyForTesting() {
@@ -70,4 +67,9 @@
   AshIsReadyForTesting();
 }
 
+void FakeAshTestChromeBrowserMainExtraParts::PostMainMessageLoopRun() {
+  crosapi::CrosapiManager::Get()->crosapi_ash()->SetTestControllerForTesting(
+      nullptr);
+}
+
 }  // namespace test
diff --git a/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.h b/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.h
index 9bfeac3..ab410960 100644
--- a/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.h
+++ b/chrome/test/base/chromeos/fake_ash_test_chrome_browser_main_extra_parts.h
@@ -27,6 +27,7 @@
 
   void PreBrowserStart() override;
   void PostBrowserStart() override;
+  void PostMainMessageLoopRun() override;
 
  private:
   std::unique_ptr<crosapi::TestControllerAsh> test_controller_ash_;
diff --git a/chrome/test/base/devtools_listener.cc b/chrome/test/base/devtools_listener.cc
index 6523ae1c..1692612 100644
--- a/chrome/test/base/devtools_listener.cc
+++ b/chrome/test/base/devtools_listener.cc
@@ -156,9 +156,9 @@
     base::DictionaryValue* entry = nullptr;
     CHECK(coverage_entries->GetDictionary(i, &entry));
 
-    std::string script_id;
-    CHECK(entry->GetString("scriptId", &script_id));
-    const auto it = script_id_map_.find(script_id);
+    std::string* script_id = entry->FindStringKey("scriptId");
+    CHECK(script_id);
+    const auto it = script_id_map_.find(*script_id);
     if (it == script_id_map_.end())
       continue;
 
@@ -194,14 +194,21 @@
                                     const base::FilePath& store) {
   for (size_t i = 0; i < script_.size(); ++i, value_.reset()) {
     std::string id;
-    CHECK(script_[i]->GetString("params.scriptId", &id));
-    CHECK(!id.empty());
+    {
+      std::string* id_ptr = script_[i]->FindStringPath("params.scriptId");
+      CHECK(id_ptr && !id_ptr->empty());
+      id = *id_ptr;
+    }
 
     std::string url;
-    if (!script_[i]->GetString("params.url", &url))
-      script_[i]->GetString("params.sourceURL", &url);
-    if (url.empty())
-      continue;
+    {
+      std::string* url_ptr = script_[i]->FindStringPath("params.url");
+      if (!url_ptr)
+        url_ptr = script_[i]->FindStringPath("params.sourceURL");
+      if (!url_ptr || url_ptr->empty())
+        continue;
+      url = *url_ptr;
+    }
 
     std::string get_script_source = base::StringPrintf(
         "{\"id\":50,\"method\":\"Debugger.getScriptSource\""
@@ -210,15 +217,23 @@
     SendCommandMessage(host, get_script_source);
     AwaitCommandResponse(50);
 
-    base::DictionaryValue* result = nullptr;
-    CHECK(value_->GetDictionary("result", &result));
     std::string text;
-    result->GetString("scriptSource", &text);
-    if (text.empty())
-      continue;
+    {
+      base::DictionaryValue* result = nullptr;
+      CHECK(value_->GetDictionary("result", &result));
+      std::string* text_ptr = result->FindStringKey("scriptSource");
+      if (!text_ptr || text_ptr->empty())
+        continue;
+      text = *text_ptr;
+    }
 
     std::string hash;
-    CHECK(script_[i]->GetString("params.hash", &hash));
+    {
+      std::string* hash_ptr = script_[i]->FindStringPath("params.hash");
+      CHECK(hash_ptr);
+      hash = *hash_ptr;
+    }
+
     if (script_id_map_.find(id) != script_id_map_.end())
       LOG(FATAL) << "Duplicate script by id " << url;
     script_id_map_[id] = hash;
diff --git a/chrome/test/chromedriver/chrome/frame_tracker.cc b/chrome/test/chromedriver/chrome/frame_tracker.cc
index d65745e..05cd274e 100644
--- a/chrome/test/chromedriver/chrome/frame_tracker.cc
+++ b/chrome/test/chromedriver/chrome/frame_tracker.cc
@@ -143,33 +143,38 @@
   } else if (method == "Runtime.executionContextsCleared") {
     frame_to_context_map_.clear();
   } else if (method == "Page.frameAttached") {
-    std::string frame_id;
-    if (!params.GetString("frameId", &frame_id))
+    if (const std::string* frame_id = params.FindStringKey("frameId")) {
+      attached_frames_.insert(*frame_id);
+    } else {
       return Status(kUnknownError,
                     "missing frameId in Page.frameAttached event");
-    attached_frames_.insert(frame_id);
+    }
   } else if (method == "Page.frameDetached") {
-    std::string frame_id;
-    if (!params.GetString("frameId", &frame_id))
+    if (const std::string* frame_id = params.FindStringKey("frameId")) {
+      attached_frames_.erase(*frame_id);
+    } else {
       return Status(kUnknownError,
                     "missing frameId in Page.frameDetached event");
-    attached_frames_.erase(frame_id);
+    }
   } else if (method == "Page.frameNavigated") {
     if (!params.FindPath("frame.parentId"))
       frame_to_context_map_.clear();
   } else if (method == "Target.attachedToTarget") {
-    std::string type, target_id, session_id;
-    if (!params.GetString("targetInfo.type", &type))
+    const std::string* type = params.FindStringPath("targetInfo.type");
+    if (!type)
       return Status(kUnknownError,
                     "missing target type in Target.attachedToTarget event");
-    if (type == "iframe") {
-      if (!params.GetString("targetInfo.targetId", &target_id))
+    if (*type == "iframe") {
+      const std::string* target_id =
+          params.FindStringPath("targetInfo.targetId");
+      if (!target_id)
         return Status(kUnknownError,
                       "missing target ID in Target.attachedToTarget event");
-      if (!params.GetString("sessionId", &session_id))
+      const std::string* session_id = params.FindStringKey("sessionId");
+      if (!session_id)
         return Status(kUnknownError,
                       "missing session ID in Target.attachedToTarget event");
-      if (frame_to_target_map_.count(target_id) > 0) {
+      if (frame_to_target_map_.count(*target_id) > 0) {
         // Since chrome 70 we are seeing multiple Target.attachedToTarget events
         // for the same target_id.  This is causing crashes because:
         // - replacing the value in frame_to_target_map_ is causing the
@@ -181,20 +186,20 @@
         // The fix is to not replace an pre-existing frame_to_target_map_ entry.
       } else {
         std::unique_ptr<WebViewImpl> child(
-            static_cast<WebViewImpl*>(web_view_)->CreateChild(session_id,
-                                                              target_id));
+            static_cast<WebViewImpl*>(web_view_)->CreateChild(*session_id,
+                                                              *target_id));
         WebViewImplHolder child_holder(child.get());
-        frame_to_target_map_[target_id] = std::move(child);
-        frame_to_target_map_[target_id]->SetUpDevTools();
+        frame_to_target_map_[*target_id] = std::move(child);
+        frame_to_target_map_[*target_id]->SetUpDevTools();
       }
     }
   } else if (method == "Target.detachedFromTarget") {
-    std::string target_id;
-    if (!params.GetString("targetId", &target_id))
+    const std::string* target_id = params.FindStringKey("targetId");
+    if (!target_id)
       // Some types of Target.detachedFromTarget events do not have targetId.
       // We are not interested in those types of targets.
       return Status(kOk);
-    auto target_iter = frame_to_target_map_.find(target_id);
+    auto target_iter = frame_to_target_map_.find(*target_id);
     if (target_iter == frame_to_target_map_.end())
       // There are some target types that we're not keeping track of, thus not
       // finding the target in frame_to_target_map_ is OK.
@@ -203,7 +208,7 @@
     if (target->IsLocked())
       target->SetDetached();
     else
-      frame_to_target_map_.erase(target_id);
+      frame_to_target_map_.erase(*target_id);
   }
   return Status(kOk);
 }
diff --git a/chrome/test/data/BUILD.gn b/chrome/test/data/BUILD.gn
index b554785..435dded 100644
--- a/chrome/test/data/BUILD.gn
+++ b/chrome/test/data/BUILD.gn
@@ -4,7 +4,6 @@
 
 import("//chrome/common/features.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
-import("//pdf/features.gni")
 import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/grit/grit_rule.gni")
 
@@ -51,9 +50,6 @@
     "cast:closure_compile",
   ]
 
-  if (enable_pdf) {
-    deps += [ "pdf:closure_compile" ]
-  }
   if (!is_android) {
     deps += [ "webui:closure_compile" ]
   }
diff --git a/chrome/test/data/banners/no-sw-with-colors.html b/chrome/test/data/banners/no-sw-with-colors.html
new file mode 100644
index 0000000..4f5b71fb
--- /dev/null
+++ b/chrome/test/data/banners/no-sw-with-colors.html
@@ -0,0 +1,10 @@
+<html>
+  <head>
+    <title>Manifest test app with colors and no service worker</title>
+    <link rel="manifest" href="no-sw-with-colors.json">
+    <meta id="no-sw-with-colors" name="no-sw-with-colors" content="yellow">
+  </head>
+  <body>
+    Manifest test app with colors and no service worker.
+  </body>
+</html>
diff --git a/chrome/test/data/banners/no-sw-with-colors.json b/chrome/test/data/banners/no-sw-with-colors.json
new file mode 100644
index 0000000..d5c648bc
--- /dev/null
+++ b/chrome/test/data/banners/no-sw-with-colors.json
@@ -0,0 +1,15 @@
+{
+    "name": "Manifest test app with colors and no service worker",
+    "icons": [
+      {
+        "src": "launcher-icon-4x.png",
+        "sizes": "192x192",
+        "type": "image/png"
+      }
+    ],
+    "start_url": "no-sw-with-colors.html",
+    "display": "fullscreen",
+    "background_color": "yellow",
+    "theme_color": "lime",
+    "orientation": "portrait"
+}
diff --git a/chrome/test/data/capability_delegation/payment_request_delegation.html b/chrome/test/data/capability_delegation/payment_request_delegation.html
index a37d7ae1..ad1701b 100644
--- a/chrome/test/data/capability_delegation/payment_request_delegation.html
+++ b/chrome/test/data/capability_delegation/payment_request_delegation.html
@@ -20,7 +20,7 @@
       let post_message_options = {};
       post_message_options["targetOrigin"] = "*";
       if (delegate) {
-          post_message_options["delegate"] = "paymentrequest";
+          post_message_options["delegate"] = "payment";
       }
       frames[0].postMessage("try", post_message_options);
       return promise;
diff --git a/chrome/test/data/pdf/BUILD.gn b/chrome/test/data/pdf/BUILD.gn
deleted file mode 100644
index e1b4085..0000000
--- a/chrome/test/data/pdf/BUILD.gn
+++ /dev/null
@@ -1,269 +0,0 @@
-# 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.
-
-import("//pdf/features.gni")
-import("//third_party/closure_compiler/compile_js.gni")
-
-js_type_check("closure_compile") {
-  is_polymer3 = true
-  closure_flags = default_closure_args + [
-                    "browser_resolver_prefix_replacements=\"chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/_test_resources/=../../chrome/test/data/\"",
-                    "browser_resolver_prefix_replacements=\"chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/=../../chrome/browser/resources/pdf/\"",
-                    "js_module_root=../../chrome/test/data/webui/",
-                    "js_module_root=./gen/chrome/test/data/webui/",
-                  ]
-  deps = [
-    #":annotations_feature_enabled_test",
-    ":basic_plugin_test",
-    ":basic_test",
-    ":beep_test",
-    ":bookmarks_test",
-    ":download_controls_test",
-    ":fullscreen_test",
-    ":gesture_detector_test",
-    ":layout_test",
-    ":material_elements_test",
-
-    #":metrics_test",
-    ":navigator_test",
-    ":nobeep_test",
-    ":page_change_test",
-    ":params_parser_test",
-    ":post_message_proxy_test",
-    ":printing_icon_test",
-
-    #":redirects_fail_test",
-    ":scroll_with_form_field_focused_test",
-    ":test_util",
-    ":title_test",
-
-    #":toolbar_manager_test",
-    #":touch_handling_test",
-    ":viewer_password_dialog_test",
-    ":viewer_pdf_sidenav_test",
-    ":viewer_properties_dialog_test",
-    ":viewer_thumbnail_bar_test",
-    ":viewer_thumbnail_test",
-    ":viewer_toolbar_test",
-    ":viewport_scroller_test",
-    ":viewport_test",
-    ":whitespace_title_test",
-
-    #":zoom_manager_test",
-  ]
-
-  if (enable_ink) {
-    deps += [ ":viewer_toolbar_dropdown_test" ]
-  }
-}
-
-js_library("basic_plugin_test") {
-  deps = [ "//chrome/browser/resources/pdf:pdf_viewer_wrapper" ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("basic_test") {
-  deps = [ "//chrome/browser/resources/pdf:pdf_viewer_wrapper" ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("beep_test") {
-  deps = [ "//chrome/browser/resources/pdf:pdf_viewer_wrapper" ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("bookmarks_test") {
-  deps = [
-    ":test_util",
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("gesture_detector_test") {
-  deps = [
-    ":test_util",
-    "//chrome/browser/resources/pdf:gesture_detector",
-    "//ui/webui/resources/js/cr:event_target.m",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("layout_test") {
-  deps = [ "//chrome/browser/resources/pdf:pdf_viewer_wrapper" ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("material_elements_test") {
-  deps = [
-    ":test_util",
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button.m",
-    "//ui/webui/resources/js:cr.m",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-if (enable_ink) {
-  js_library("viewer_toolbar_dropdown_test") {
-    deps = [ "//chrome/browser/resources/pdf:pdf_viewer_wrapper" ]
-    externs_list = [ "$externs_path/test.js" ]
-  }
-}
-
-js_library("download_controls_test") {
-  deps = [
-    "../webui:test_util",
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu",
-    "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button.m",
-    "//ui/webui/resources/js:util.m",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("fullscreen_test") {
-  deps = [
-    ":test_util",
-    "../webui:test_util",
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-    "//third_party/polymer/v3_0/components-chromium/iron-test-helpers:mock-interactions",
-    "//ui/webui/resources/js:cr.m",
-    "//ui/webui/resources/js:util.m",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("navigator_test") {
-  deps = [
-    ":test_util",
-    "../webui:test_browser_proxy",
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("nobeep_test") {
-  deps = [ "//chrome/browser/resources/pdf:pdf_viewer_wrapper" ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("page_change_test") {
-  deps = [
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-    "//third_party/polymer/v3_0/components-chromium/iron-test-helpers:mock-interactions",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("post_message_proxy_test") {
-  deps = [
-    "../webui:test_util",
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("printing_icon_test") {
-  deps = [ "//chrome/browser/resources/pdf:pdf_viewer_wrapper" ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("params_parser_test") {
-  deps = [ "//chrome/browser/resources/pdf:pdf_viewer_wrapper" ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("scroll_with_form_field_focused_test") {
-  deps = [
-    "../webui:test_util",
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("test_util") {
-  deps = [
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-  ]
-}
-
-js_library("title_test") {
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("viewer_password_dialog_test") {
-  deps = [
-    "../webui:test_util",
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-    "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
-    "//ui/webui/resources/cr_elements/cr_input:cr_input.m",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("viewer_pdf_sidenav_test") {
-  deps = [ "//chrome/browser/resources/pdf:pdf_viewer_wrapper" ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("viewer_properties_dialog_test") {
-  deps = [
-    "../webui:test_util",
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("viewer_thumbnail_bar_test") {
-  deps = [
-    "../webui:test_util",
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-    "//third_party/polymer/v3_0/components-chromium/iron-test-helpers:mock-interactions",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/js/cr/ui:focus_outline_manager.m",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("viewer_thumbnail_test") {
-  deps = [
-    "../webui:test_util",
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("viewer_toolbar_test") {
-  deps = [
-    "../webui:test_util",
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("viewport_scroller_test") {
-  deps = [
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-    "//ui/webui/resources/js:cr.m",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("viewport_test") {
-  deps = [
-    ":test_util",
-    "//chrome/browser/resources/pdf:pdf_viewer_wrapper",
-  ]
-  externs_list = [ "$externs_path/test.js" ]
-}
-
-js_library("whitespace_title_test") {
-  externs_list = [ "$externs_path/test.js" ]
-}
diff --git a/chrome/test/data/pdf/basic_plugin_test.js b/chrome/test/data/pdf/basic_plugin_test.js
index 58ec418..e282a09 100644
--- a/chrome/test/data/pdf/basic_plugin_test.js
+++ b/chrome/test/data/pdf/basic_plugin_test.js
@@ -18,8 +18,8 @@
     chrome.test.assertTrue(viewer.viewport.getZoom() <= 1);
 
     viewer.viewport.setZoom(1);
-    chrome.test.assertEq(826, viewer.viewport.contentSizeForTesting.width);
-    chrome.test.assertEq(1066, viewer.viewport.contentSizeForTesting.height);
+    chrome.test.assertEq(826, viewer.viewport.contentSize.width);
+    chrome.test.assertEq(1066, viewer.viewport.contentSize.height);
     chrome.test.succeed();
   },
 
diff --git a/chrome/test/data/pdf/download_controls_test.js b/chrome/test/data/pdf/download_controls_test.js
index f7d1aa5..7aa0d63 100644
--- a/chrome/test/data/pdf/download_controls_test.js
+++ b/chrome/test/data/pdf/download_controls_test.js
@@ -3,9 +3,8 @@
 // found in the LICENSE file.
 
 import {eventToPromise} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/_test_resources/webui/test_util.js';
-import {CrActionMenuElement, SaveRequestType, ViewerDownloadControlsElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {SaveRequestType, ViewerDownloadControlsElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import {listenOnce} from 'chrome://resources/js/util.m.js';
-import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 const tests = [
   /**
diff --git a/chrome/test/data/pdf/viewport_test.js b/chrome/test/data/pdf/viewport_test.js
index a414e67..c6a8d7b4 100644
--- a/chrome/test/data/pdf/viewport_test.js
+++ b/chrome/test/data/pdf/viewport_test.js
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import {FittingType, PAGE_SHADOW, Viewport} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {isMac} from 'chrome://resources/js/cr.m.js';
 
 import {createMockUnseasonedPdfPluginForTest, getZoomableViewport, MockDocumentDimensions, MockElement, MockSizer, MockUnseasonedPdfPluginElement, MockViewportChangedCallback} from './test_util.js';
 
@@ -49,6 +50,31 @@
 }
 
 const tests = [
+  function testScrollbarWidth() {
+    const viewport = getZoomableViewport(
+        new MockElement(100, 100, null), new MockSizer(), 43, 1);
+
+    chrome.test.assertEq(43, viewport.scrollbarWidth);
+    chrome.test.succeed();
+  },
+
+  function testOverlayScrollbarWidth_local() {
+    const viewport = getZoomableViewport(
+        new MockElement(100, 100, null), new MockSizer(), 43, 1);
+
+    chrome.test.assertEq(isMac ? 16 : 0, viewport.overlayScrollbarWidth);
+    chrome.test.succeed();
+  },
+
+  function testOverlayScrollbarWidth_remote() {
+    const viewport = getZoomableViewport(
+        new MockElement(100, 100, null), new MockSizer(), 43, 1);
+    viewport.setRemoteContent(createMockUnseasonedPdfPluginForTest());
+
+    chrome.test.assertEq(isMac ? 16 : 43, viewport.overlayScrollbarWidth);
+    chrome.test.succeed();
+  },
+
   function testDocumentNeedsScrollbars() {
     const viewport = getZoomableViewport(
         new MockElement(100, 100, null), new MockSizer(), 10, 1);
@@ -1343,9 +1369,9 @@
 
     const {width, height} = mockPlugin.findMessage('updateSize');
     chrome.test.assertEq(20, width);
-    chrome.test.assertEq(20, viewport.contentSizeForTesting.width);
+    chrome.test.assertEq(20, viewport.contentSize.width);
     chrome.test.assertEq(30, height);
-    chrome.test.assertEq(30, viewport.contentSizeForTesting.height);
+    chrome.test.assertEq(30, viewport.contentSize.height);
     chrome.test.succeed();
   },
 
diff --git a/chrome/test/data/prerender/onprerendering_check.html b/chrome/test/data/prerender/onprerendering_check.html
new file mode 100644
index 0000000..d3e9fb2
--- /dev/null
+++ b/chrome/test/data/prerender/onprerendering_check.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+// Records if 'onprerenderingchange' event is triggered at least once.
+let onprerenderingchange_observed = false;
+document.addEventListener('prerenderingchange', e => {
+  onprerenderingchange_observed = true;
+});
+</script>
+</head>
+</html>
diff --git a/chrome/test/data/webui/chromeos/firmware_update/fake_update_controller_test.js b/chrome/test/data/webui/chromeos/firmware_update/fake_update_controller_test.js
index 90fd655..62e7a077 100644
--- a/chrome/test/data/webui/chromeos/firmware_update/fake_update_controller_test.js
+++ b/chrome/test/data/webui/chromeos/firmware_update/fake_update_controller_test.js
@@ -4,9 +4,9 @@
 
 import {fakeInstallationProgress} from 'chrome://accessory-update/fake_data.js';
 import {FakeUpdateController} from 'chrome://accessory-update/fake_update_controller.js';
-import {UpdateProgressObserver} from 'chrome://accessory-update/firmware_update_types.js';
+import {UpdateProgressObserverRemote} from 'chrome://accessory-update/firmware_update_types.js';
 
-import {assertDeepEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 import {flushTasks} from '../../test_util.js';
 
 export function fakeUpdateControllerTest() {
@@ -31,32 +31,30 @@
   test('StartUpdate', async () => {
     const deviceId = '1';
     controller.setUpdateIntervalInMs(0);
+    controller.setDeviceIdForUpdateInProgress(deviceId);
     // Keep track of which observation we should get.
-    let observerCallCount = 0;
-    /** @type {!UpdateProgressObserver} */
-    const updateProgressObserverRemote = {
-      onProgressChanged: (installationProgress) => {
-        // Only expect 3 calls.
-        assertTrue(observerCallCount <= 2);
-        assertDeepEquals(
-            fakeInstallationProgress[observerCallCount++],
-            installationProgress);
-      }
-    };
+    let onStatusChangedCallCount = 0;
 
-    controller.startUpdate(deviceId, updateProgressObserverRemote);
-    await flushTasks();
-    return controller
-        .getStartUpdatePromiseForTesting()
-        // Use flushTasks to process the 3 fake installation progress
-        // observations.
-        .then(() => flushTasks())
-        .then(() => flushTasks())
-        .then(() => flushTasks())
-        // Trigger stops when update is completed.
-        .then(() => {
-          assertFalse(controller.isUpdateInProgress());
-          assertTrue(getCompletedFirmwareUpdates().has(deviceId));
+    const updateProgressObserverRemote =
+        /** @type {!UpdateProgressObserverRemote} */ ({
+          onStatusChanged: (update) => {
+            // Only expect 3 calls.
+            assertTrue(onStatusChangedCallCount <= 2);
+            assertEquals(
+                fakeInstallationProgress[onStatusChangedCallCount].percentage,
+                update.percentage);
+            assertEquals(
+                fakeInstallationProgress[onStatusChangedCallCount].state,
+                update.state);
+            onStatusChangedCallCount++;
+          },
         });
+
+    controller.addObserver(updateProgressObserverRemote);
+    controller.beginUpdate();
+    // Allow firmware update to complete.
+    await controller.getUpdateCompletedPromiseForTesting();
+    assertFalse(controller.isUpdateInProgress());
+    assertTrue(getCompletedFirmwareUpdates().has(deviceId));
   });
 }
diff --git a/chrome/test/data/webui/chromeos/firmware_update/firmware_update_test.js b/chrome/test/data/webui/chromeos/firmware_update/firmware_update_test.js
index 7f6c9a3..0b8b49b8 100644
--- a/chrome/test/data/webui/chromeos/firmware_update/firmware_update_test.js
+++ b/chrome/test/data/webui/chromeos/firmware_update/firmware_update_test.js
@@ -104,6 +104,8 @@
     await flushTasks();
     // Open dialog for first firmware update card.
     getUpdateCards()[0].shadowRoot.querySelector(`#updateButton`).click();
+    // Process |OnStateChanged| and |OnProgressChanged| calls.
+    await flushTasks();
     await flushTasks();
     assertTrue(getUpdateDialog().open);
   });
@@ -112,6 +114,8 @@
     await flushTasks();
     // Open dialog for firmware update.
     getUpdateCards()[1].shadowRoot.querySelector(`#updateButton`).click();
+    // Process |OnStateChanged| and |OnProgressChanged| calls.
+    await flushTasks();
     await flushTasks();
     assertEquals(DialogState.UPDATING, getDialogState());
     const fakeFirmwareUpdate = getFirmwareUpdateFromDialog();
@@ -120,6 +124,7 @@
         getUpdateDialogTitle().innerText.trim());
     // Allow firmware update to complete.
     await controller.getUpdateCompletedPromiseForTesting();
+    await flushTasks();
     assertEquals(DialogState.UPDATE_DONE, getDialogState());
     assertTrue(getUpdateDialog().open);
     assertEquals(
diff --git a/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn b/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn
index 1a90624..46fc303 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn
@@ -43,7 +43,9 @@
     "personalization_toast_element_test.ts",
     "test_personalization_store.ts",
     "test_theme_interface_provider.ts",
+    "test_user_interface_provider.ts",
     "test_wallpaper_interface_provider.ts",
+    "user_preview_element_test.ts",
     "user_subpage_element_test.ts",
     "wallpaper_collections_element_test.ts",
     "wallpaper_fullscreen_element_test.ts",
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_test.ts
index 0e47c38..f66db1d1 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_test.ts
@@ -14,6 +14,7 @@
 import {PersonalizationRouterTest} from './personalization_router_element_test.js';
 import {PersonalizationThemeTest} from './personalization_theme_element_test.js';
 import {PersonalizationToastTest} from './personalization_toast_element_test.js';
+import {UserPreviewTest} from './user_preview_element_test.js';
 import {UserSubpageTest} from './user_subpage_element_test.js';
 import {WallpaperCollectionsTest} from './wallpaper_collections_element_test.js';
 import {WallpaperFullscreenTest} from './wallpaper_fullscreen_element_test.js';
@@ -36,6 +37,7 @@
   PersonalizationRouterTest,
   PersonalizationThemeTest,
   PersonalizationToastTest,
+  UserPreviewTest,
   UserSubpageTest,
   WallpaperCollectionsTest,
   WallpaperFullscreenTest,
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_test_utils.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_test_utils.ts
index 8130f538..2e3122bb 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_test_utils.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_test_utils.ts
@@ -7,16 +7,19 @@
  * SWA.
  */
 
+import {IFrameApi} from 'chrome://personalization/trusted/iframe_api.js';
 import {emptyState, PersonalizationState} from 'chrome://personalization/trusted/personalization_state.js';
 import {setThemeProviderForTesting} from 'chrome://personalization/trusted/theme/theme_interface_provider.js';
+import {setUserProviderForTesting} from 'chrome://personalization/trusted/user/user_interface_provider.js';
 import {setWallpaperProviderForTesting} from 'chrome://personalization/trusted/wallpaper/wallpaper_interface_provider.js';
 import {flush, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
 import {assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 import {flushTasks} from 'chrome://webui-test/test_util.js';
 
 import {TestPersonalizationStore} from './test_personalization_store.js';
 import {TestThemeProvider} from './test_theme_interface_provider.js';
+import {TestUserProvider} from './test_user_interface_provider.js';
 import {TestWallpaperProvider} from './test_wallpaper_interface_provider.js';
 
 /**
@@ -60,10 +63,12 @@
   setWallpaperProviderForTesting(wallpaperProvider);
   const themeProvider = new TestThemeProvider();
   setThemeProviderForTesting(themeProvider);
+  const userProvider = new TestUserProvider();
+  setUserProviderForTesting(userProvider);
   const personalizationStore = new TestPersonalizationStore(initialState);
   personalizationStore.replaceSingleton();
   document.body.innerHTML = '';
-  return {themeProvider, wallpaperProvider, personalizationStore};
+  return {themeProvider, userProvider, wallpaperProvider, personalizationStore};
 }
 
 function getDebugString(w: any) {
@@ -84,3 +89,12 @@
       `Window objects are not identical: ${getDebugString(x)}, ${
           getDebugString(y)}`);
 }
+
+/**
+ * Helper function to setup a mock `IFrameApi` singleton.
+ */
+export function setupTestIFrameApi(): IFrameApi&TestBrowserProxy<IFrameApi> {
+  const testProxy = TestBrowserProxy.fromClass(IFrameApi);
+  IFrameApi.setInstance(testProxy);
+  return testProxy;
+}
diff --git a/chrome/test/data/webui/chromeos/personalization_app/test_user_interface_provider.ts b/chrome/test/data/webui/chromeos/personalization_app/test_user_interface_provider.ts
new file mode 100644
index 0000000..810cc1b
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/personalization_app/test_user_interface_provider.ts
@@ -0,0 +1,20 @@
+// Copyright 2022 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 {UserInfo, UserProviderInterface} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+
+export class TestUserProvider extends TestBrowserProxy implements
+    UserProviderInterface {
+  public info: UserInfo = {name: 'test name', email: 'test@email'};
+
+  constructor() {
+    super(['getUserInfo']);
+  }
+
+  async getUserInfo(): Promise<{userInfo: UserInfo}> {
+    this.methodCalled('getUserInfo');
+    return Promise.resolve({userInfo: this.info});
+  }
+}
diff --git a/chrome/test/data/webui/chromeos/personalization_app/user_preview_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/user_preview_element_test.ts
new file mode 100644
index 0000000..cb5d834e
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/personalization_app/user_preview_element_test.ts
@@ -0,0 +1,46 @@
+// Copyright 2022 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 {UserPreview} from 'chrome://personalization/trusted/user_preview_element.js';
+import {assertEquals} from 'chrome://webui-test/chai_assert.js';
+import {waitAfterNextRender} from 'chrome://webui-test/test_util.js';
+
+import {baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
+import {TestPersonalizationStore} from './test_personalization_store.js';
+import {TestUserProvider} from './test_user_interface_provider.js';
+
+export function UserPreviewTest() {
+  let userPreviewElement: UserPreview|null;
+  let personalizationStore: TestPersonalizationStore;
+  let userProvider: TestUserProvider;
+
+  setup(() => {
+    const mocks = baseSetup();
+    personalizationStore = mocks.personalizationStore;
+    userProvider = mocks.userProvider;
+  });
+
+  teardown(async () => {
+    await teardownElement(userPreviewElement);
+    userPreviewElement = null;
+  });
+
+  test('fetches user info on creation', async () => {
+    assertEquals(0, userProvider.getCallCount('getUserInfo'));
+    userPreviewElement = initElement(UserPreview);
+    await userProvider.whenCalled('getUserInfo');
+  });
+
+  test('displays user info when set', async () => {
+    personalizationStore.data.user.info = userProvider.info;
+    userPreviewElement = initElement(UserPreview);
+    await waitAfterNextRender(userPreviewElement!);
+    assertEquals(
+        userProvider.info.email,
+        userPreviewElement!.shadowRoot!.getElementById('email')!.innerText);
+    assertEquals(
+        userProvider.info.name,
+        userPreviewElement!.shadowRoot!.getElementById('name')!.innerText);
+  });
+}
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.ts
index bb3c089..2250647 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.ts
@@ -3,15 +3,13 @@
 // found in the LICENSE file.
 
 import {kMaximumGooglePhotosPreviews, kMaximumLocalImagePreviews} from 'chrome://personalization/common/constants.js';
-import {WallpaperCollection} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
+import {IFrameApi} from 'chrome://personalization/trusted/iframe_api.js';
 import {emptyState} from 'chrome://personalization/trusted/personalization_state.js';
-import {promisifyIframeFunctionsForTesting, WallpaperCollections} from 'chrome://personalization/trusted/wallpaper/wallpaper_collections_element.js';
-import {FilePath} from 'chrome://resources/mojo/mojo/public/mojom/base/file_path.mojom-webui.js';
-import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
+import {WallpaperCollections} from 'chrome://personalization/trusted/wallpaper/wallpaper_collections_element.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
-import {assertWindowObjectsEqual, baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
+import {assertWindowObjectsEqual, baseSetup, initElement, setupTestIFrameApi, teardownElement} from './personalization_app_test_utils.js';
 import {TestPersonalizationStore} from './test_personalization_store.js';
 import {TestWallpaperProvider} from './test_wallpaper_interface_provider.js';
 
@@ -34,8 +32,7 @@
   });
 
   test('sends wallpaper collections when loaded', async () => {
-    const {sendCollections: sendCollectionsPromise} =
-        promisifyIframeFunctionsForTesting();
+    const testProxy = setupTestIFrameApi();
     wallpaperCollectionsElement = initElement(WallpaperCollections);
 
     personalizationStore.data.wallpaper.loading = {
@@ -47,9 +44,8 @@
     personalizationStore.notifyObservers();
 
     // Wait for |sendCollections| to be called.
-    const [target, data] = await (
-        sendCollectionsPromise as
-        Promise<[Window, Array<WallpaperCollection>]>);
+    const [target, data] = await testProxy.whenCalled('sendCollections') as
+        Parameters<IFrameApi['sendCollections']>;
     await waitAfterNextRender(wallpaperCollectionsElement);
 
     const iframe =
@@ -61,8 +57,7 @@
   });
 
   test('sends Google Photos count when loaded', async () => {
-    const {sendGooglePhotosCount: sendGooglePhotosCountPromise} =
-        promisifyIframeFunctionsForTesting();
+    const testProxy = setupTestIFrameApi();
 
     wallpaperCollectionsElement = initElement(WallpaperCollections);
 
@@ -71,8 +66,9 @@
     personalizationStore.notifyObservers();
 
     // Wait for |sendGooglePhotosCount| to be called.
-    const [target, data] = await (
-        sendGooglePhotosCountPromise as Promise<[Window, number | null]>);
+    const [target, data] =
+        await testProxy.whenCalled('sendGooglePhotosCount') as
+        Parameters<IFrameApi['sendGooglePhotosCount']>;
     await waitAfterNextRender(wallpaperCollectionsElement);
 
     const iframe =
@@ -85,8 +81,7 @@
   });
 
   test('sends Google Photos photos when loaded', async () => {
-    const {sendGooglePhotosPhotos: sendGooglePhotosPhotosPromise} =
-        promisifyIframeFunctionsForTesting();
+    const testProxy = setupTestIFrameApi();
 
     wallpaperCollectionsElement = initElement(WallpaperCollections);
 
@@ -97,8 +92,9 @@
     personalizationStore.notifyObservers();
 
     // Wait for |sendGooglePhotosPhotos| to be called.
-    const [target, data] = await (
-        sendGooglePhotosPhotosPromise as Promise<[Window, Array<Url>| null]>);
+    const [target, data] =
+        await testProxy.whenCalled('sendGooglePhotosPhotos') as
+        Parameters<IFrameApi['sendGooglePhotosPhotos']>;
     await waitAfterNextRender(wallpaperCollectionsElement);
 
     const iframe =
@@ -125,25 +121,24 @@
 
     wallpaperCollectionsElement = initElement(WallpaperCollections);
     // Wait for initial load to complete.
-    await promisifyIframeFunctionsForTesting().sendImageCounts;
+    const testProxy = setupTestIFrameApi();
+    await testProxy.whenCalled('sendImageCounts');
 
-    let {sendImageCounts: sendImageCountsPromise} =
-        promisifyIframeFunctionsForTesting();
-
+    testProxy.resetResolver('sendImageCounts');
     personalizationStore.data.wallpaper.backdrop.images = {
       'id_0': [wallpaperProvider.images![0]]
     };
     personalizationStore.data.wallpaper.loading.images = {'id_0': false};
     personalizationStore.notifyObservers();
 
-    let counts = (await (
-        sendImageCountsPromise as
-        Promise<[Window, {[key: string]: number | null}]>))[1];
+    let counts =
+        (await testProxy.whenCalled('sendImageCounts') as
+         Parameters<IFrameApi['sendImageCounts']>)[1];
     assertDeepEquals({'id_0': 1}, counts);
 
     // Load two collections in at once, and simulate one failure.
-    sendImageCountsPromise =
-        promisifyIframeFunctionsForTesting().sendImageCounts;
+    testProxy.resetResolver('sendImageCounts');
+
     personalizationStore.data.wallpaper.backdrop.images = {
       'id_0': [wallpaperProvider.images![0]],
       'id_1': [wallpaperProvider.images![0], wallpaperProvider.images![1]],
@@ -160,16 +155,15 @@
     };
     personalizationStore.notifyObservers();
 
-    counts = (await (
-        sendImageCountsPromise as
-        Promise<[Window, {[key: string]: number | null}]>))[1];
+    counts =
+        (await testProxy.whenCalled('sendImageCounts') as
+         Parameters<IFrameApi['sendImageCounts']>)[1];
     assertDeepEquals(
         {'id_0': 1, 'id_1': 2, 'id_2': 0, 'id_3': null, 'id_4': 1}, counts);
   });
 
   test('sends local images when loaded', async () => {
-    const {sendLocalImages: sendLocalImagesPromise} =
-        promisifyIframeFunctionsForTesting();
+    const testProxy = setupTestIFrameApi();
 
     wallpaperCollectionsElement = initElement(WallpaperCollections);
 
@@ -185,8 +179,8 @@
     personalizationStore.notifyObservers();
 
     // Wait for |sendLocalImages| to be called.
-    const [target, data] =
-        await (sendLocalImagesPromise as Promise<[Window, FilePath[]]>);
+    const [target, data] = await testProxy.whenCalled('sendLocalImages') as
+        Parameters<IFrameApi['sendLocalImages']>;
     await waitAfterNextRender(wallpaperCollectionsElement);
 
     const iframe =
@@ -198,10 +192,7 @@
   });
 
   test('sends collections and local images when no internet', async () => {
-    const {
-      sendCollections: sendCollectionsPromise,
-      sendLocalImages: sendLocalImagesPromise
-    } = promisifyIframeFunctionsForTesting();
+    const testProxy = setupTestIFrameApi();
 
     wallpaperCollectionsElement = initElement(WallpaperCollections);
 
@@ -217,9 +208,8 @@
     personalizationStore.notifyObservers();
 
     // Wait for |sendCollections| to be called.
-    let [target, data] = await (
-        sendCollectionsPromise as
-        Promise<[Window, Array<WallpaperCollection>]>);
+    let [target, data] = await testProxy.whenCalled('sendCollections') as
+        Parameters<IFrameApi['sendCollections']>;
     await waitAfterNextRender(wallpaperCollectionsElement);
 
     const iframe =
@@ -231,7 +221,8 @@
 
     // Wait for |sendLocalImages| to be called.
     let [imageTarget, imageData] =
-        await (sendLocalImagesPromise as Promise<[Window, FilePath[]]>);
+        await testProxy.whenCalled('sendLocalImages') as
+        Parameters<IFrameApi['sendLocalImages']>;
     await waitAfterNextRender(wallpaperCollectionsElement);
 
     assertFalse(iframe!.hidden);
@@ -273,14 +264,12 @@
     // Actually run the reducers.
     personalizationStore.setReducersEnabled(true);
 
-    const {sendCollections: sendCollectionsPromise} =
-        promisifyIframeFunctionsForTesting();
+    const testProxy = setupTestIFrameApi();
 
     wallpaperCollectionsElement = initElement(WallpaperCollections);
 
-    const [_, collections] = await (
-        sendCollectionsPromise as
-        Promise<[Window, Array<WallpaperCollection>]>);
+    const [_, collections] = await testProxy.whenCalled('sendCollections') as
+        Parameters<IFrameApi['sendCollections']>;
     assertDeepEquals(wallpaperProvider.collections, collections);
 
     assertDeepEquals(
@@ -323,12 +312,11 @@
             wallpaperProvider.collections;
         personalizationStore.data.wallpaper.loading.collections = false;
 
-        const {sendLocalImages, sendLocalImageData} =
-            promisifyIframeFunctionsForTesting();
+        const testProxy = setupTestIFrameApi();
 
         wallpaperCollectionsElement = initElement(WallpaperCollections);
 
-        await sendLocalImages;
+        await testProxy.whenCalled('sendLocalImages');
 
         // No thumbnails loaded so none sent.
         assertFalse(wallpaperCollectionsElement['didSendLocalImageData_']);
@@ -364,8 +352,9 @@
 
         // 2 thumbnails have now loaded. 1 failed. But there are no more
         // remaining to try loading, should send local image data anyway.
-        const [_, sentData] = await (
-            sendLocalImageData as Promise<[Window, Record<string, string>]>);
+        const [_, sentData] =
+            await testProxy.whenCalled('sendLocalImageData') as
+            Parameters<IFrameApi['sendLocalImageData']>;
 
         assertTrue(wallpaperCollectionsElement['didSendLocalImageData_']);
         assertDeepEquals(
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts
index 937872a..60f799e 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ImageTile} from 'chrome://personalization/common/constants.js';
+import {IFrameApi} from 'chrome://personalization/trusted/iframe_api.js';
 import {WallpaperLayout, WallpaperType} from 'chrome://personalization/trusted/personalization_app.mojom-webui.js';
 import {PersonalizationRouter} from 'chrome://personalization/trusted/personalization_router_element.js';
-import {getDarkLightImageTiles, getRegularImageTiles, promisifyImagesIframeFunctionsForTesting, WallpaperImages} from 'chrome://personalization/trusted/wallpaper/wallpaper_images_element.js';
+import {getDarkLightImageTiles, getRegularImageTiles, WallpaperImages} from 'chrome://personalization/trusted/wallpaper/wallpaper_images_element.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {assertDeepEquals, assertEquals, assertFalse} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
-import {assertWindowObjectsEqual, baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
+import {assertWindowObjectsEqual, baseSetup, initElement, setupTestIFrameApi, teardownElement} from './personalization_app_test_utils.js';
 import {TestPersonalizationStore} from './test_personalization_store.js';
 import {TestWallpaperProvider} from './test_wallpaper_interface_provider.js';
 
@@ -32,8 +32,7 @@
   });
 
   test('send current wallpaper asset id', async () => {
-    let {sendCurrentWallpaperAssetId: sendCurrentWallpaperAssetIdPromise} =
-        promisifyImagesIframeFunctionsForTesting();
+    const testProxy = setupTestIFrameApi();
 
     // Set the current wallpaper as an online wallpaper.
     // The currentSelected asset id should be sent to iframe.
@@ -48,14 +47,14 @@
 
     // Wait for iframe to receive data.
     let [targetWindow, data] =
-        await sendCurrentWallpaperAssetIdPromise as [Window, bigint | null];
+        await testProxy.whenCalled('sendCurrentWallpaperAssetId') as
+        Parameters<IFrameApi['sendCurrentWallpaperAssetId']>;
 
     assertEquals(iframe.contentWindow, targetWindow);
     assertDeepEquals(
         BigInt(personalizationStore.data.wallpaper.currentSelected.key), data);
 
-    sendCurrentWallpaperAssetIdPromise =
-        promisifyImagesIframeFunctionsForTesting().sendCurrentWallpaperAssetId;
+    testProxy.resetResolver('sendCurrentWallpaperAssetId');
 
     // Set the current wallpaper as a daily refresh wallpaper.
     // The currentSelected asset id should be sent to iframe.
@@ -70,13 +69,13 @@
 
     // Wait for iframe to receive data.
     [targetWindow, data] =
-        await sendCurrentWallpaperAssetIdPromise as [Window, bigint | null];
+        await testProxy.whenCalled('sendCurrentWallpaperAssetId') as
+        Parameters<IFrameApi['sendCurrentWallpaperAssetId']>;
     assertEquals(iframe.contentWindow, targetWindow);
     assertDeepEquals(
         BigInt(personalizationStore.data.wallpaper.currentSelected.key), data);
 
-    sendCurrentWallpaperAssetIdPromise =
-        promisifyImagesIframeFunctionsForTesting().sendCurrentWallpaperAssetId;
+    testProxy.resetResolver('sendCurrentWallpaperAssetId');
 
     // Set the current wallpaper not as an online wallpaper.
     // No asset id is sent to iframe.
@@ -91,7 +90,8 @@
 
     // Wait for iframe to receive data.
     [targetWindow, data] =
-        await sendCurrentWallpaperAssetIdPromise as [Window, bigint | null];
+        await testProxy.whenCalled('sendCurrentWallpaperAssetId') as
+        Parameters<IFrameApi['sendCurrentWallpaperAssetId']>;
     assertEquals(iframe.contentWindow, targetWindow);
     assertEquals(undefined, data);
   });
@@ -112,8 +112,7 @@
     };
     personalizationStore.data.wallpaper.loading.collections = false;
 
-    let {sendImageTiles: sendImageTilesPromise} =
-        promisifyImagesIframeFunctionsForTesting();
+    const testProxy = setupTestIFrameApi();
     wallpaperImagesElement =
         initElement(WallpaperImages, {collectionId: 'id_0'});
 
@@ -121,8 +120,8 @@
                        'images-iframe') as HTMLIFrameElement;
 
     // Wait for iframe to receive data.
-    let [targetWindow, data] =
-        await sendImageTilesPromise as [Window, ImageTile[]];
+    let [targetWindow, data] = await testProxy.whenCalled('sendImageTiles') as
+        Parameters<IFrameApi['sendImageTiles']>;
     assertEquals(iframe.contentWindow, targetWindow);
     assertDeepEquals(
         getRegularImageTiles(
@@ -132,12 +131,12 @@
     await waitAfterNextRender(wallpaperImagesElement);
     assertFalse(iframe.hidden);
 
-    sendImageTilesPromise =
-        promisifyImagesIframeFunctionsForTesting().sendImageTiles;
+    testProxy.resetResolver('sendImageTiles');
     wallpaperImagesElement.collectionId = 'id_1';
 
     // Wait for iframe to receive new data.
-    [targetWindow, data] = await sendImageTilesPromise as [Window, ImageTile[]];
+    [targetWindow, data] = await testProxy.whenCalled('sendImageTiles') as
+        Parameters<IFrameApi['sendImageTiles']>;
 
     await waitAfterNextRender(wallpaperImagesElement);
 
@@ -167,8 +166,7 @@
     };
     personalizationStore.data.wallpaper.loading.collections = false;
 
-    let {sendImageTiles: sendImageTilesPromise} =
-        promisifyImagesIframeFunctionsForTesting();
+    const testProxy = setupTestIFrameApi();
     wallpaperImagesElement =
         initElement(WallpaperImages, {collectionId: 'id_0'});
 
@@ -176,8 +174,8 @@
                        'images-iframe') as HTMLIFrameElement;
 
     // Wait for iframe to receive data.
-    let [targetWindow, data] =
-        await sendImageTilesPromise as [Window, ImageTile[]];
+    let [targetWindow, data] = await testProxy.whenCalled('sendImageTiles') as
+        Parameters<IFrameApi['sendImageTiles']>;
     assertEquals(iframe.contentWindow, targetWindow);
     const tiles = getDarkLightImageTiles(
         false, personalizationStore.data.wallpaper.backdrop.images['id_0']);
@@ -192,12 +190,12 @@
     await waitAfterNextRender(wallpaperImagesElement);
     assertFalse(iframe.hidden);
 
-    sendImageTilesPromise =
-        promisifyImagesIframeFunctionsForTesting().sendImageTiles;
+    testProxy.resetResolver('sendImageTiles');
     wallpaperImagesElement.collectionId = 'id_1';
 
     // Wait for iframe to receive new data.
-    [targetWindow, data] = await sendImageTilesPromise as [Window, ImageTile[]];
+    [targetWindow, data] = await testProxy.whenCalled('sendImageTiles') as
+        Parameters<IFrameApi['sendImageTiles']>;
 
     await waitAfterNextRender(wallpaperImagesElement);
 
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/onboarding_select_components_page_test.js b/chrome/test/data/webui/chromeos/shimless_rma/onboarding_select_components_page_test.js
index 018e01e..9727034 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/onboarding_select_components_page_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/onboarding_select_components_page_test.js
@@ -166,4 +166,33 @@
     assertEquals(1, callCounter);
     assertDeepEquals(expectedResult, savedResult);
   });
+
+  test('SelectComponentsPageDisablesComponents', async () => {
+    await initializeComponentSelectPage(fakeComponentsForRepairStateTest);
+
+    const cameraComponent =
+        component.shadowRoot.querySelector('#componentCamera');
+    const touchpadComponent =
+        component.shadowRoot.querySelector('#componentTouchpad');
+    assertFalse(cameraComponent.disabled);
+    assertFalse(touchpadComponent.disabled);
+    component.allButtonsDisabled = true;
+    assertTrue(cameraComponent.disabled);
+    assertTrue(touchpadComponent.disabled);
+  });
+
+  test('SelectComponentsPageReworkLinkDisabled', async () => {
+    const resolver = new PromiseResolver();
+    await initializeComponentSelectPage(fakeComponentsForRepairStateTest);
+    let callCounter = 0;
+    service.reworkMainboard = () => {
+      callCounter++;
+      return resolver.promise;
+    };
+
+    component.allButtonsDisabled = true;
+    await clickReworkButton();
+
+    assertEquals(0, callCounter);
+  });
 }
diff --git a/chrome/test/data/webui/cr_components/chromeos/bluetooth/bluetooth_pairing_ui_test.js b/chrome/test/data/webui/cr_components/chromeos/bluetooth/bluetooth_pairing_ui_test.js
index d9e245c68..5452035 100644
--- a/chrome/test/data/webui/cr_components/chromeos/bluetooth/bluetooth_pairing_ui_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/bluetooth/bluetooth_pairing_ui_test.js
@@ -510,75 +510,77 @@
     await displayPinOrPasskey(PairingAuthType.DISPLAY_PASSKEY);
   });
 
-  test('Pairing a new device cancels old pairing', async function() {
-    await init();
-    let finishedPromise = eventToPromise('finished', bluetoothPairingUi);
-    const device = createDefaultBluetoothDevice(
-        /*id=*/ '1234321',
-        /*publicName=*/ 'BeatsX',
-        /*connectionState=*/
-        chromeos.bluetoothConfig.mojom.DeviceConnectionState.kConnected,
-        /*opt_nickname=*/ 'device 1',
-        /*opt_audioCapability=*/
-        mojom.AudioOutputCapability.kCapableOfAudioOutput,
-        /*opt_deviceType=*/ mojom.DeviceType.kMouse);
+  // TODO(b/213943745) Fix flaky test.
+  // test('Pairing a new device cancels old pairing', async function() {
+  //   await init();
+  //   let finishedPromise = eventToPromise('finished', bluetoothPairingUi);
+  //   const device = createDefaultBluetoothDevice(
+  //       /*id=*/ '1234321',
+  //       /*publicName=*/ 'BeatsX',
+  //       /*connectionState=*/
+  //       chromeos.bluetoothConfig.mojom.DeviceConnectionState.kConnected,
+  //       /*opt_nickname=*/ 'device 1',
+  //       /*opt_audioCapability=*/
+  //       mojom.AudioOutputCapability.kCapableOfAudioOutput,
+  //       /*opt_deviceType=*/ mojom.DeviceType.kMouse);
 
-    const device1 = createDefaultBluetoothDevice(
-        /*id=*/ '12345654321',
-        /*publicName=*/ 'Head phones',
-        /*connectionState=*/
-        chromeos.bluetoothConfig.mojom.DeviceConnectionState.kConnected,
-        /*opt_nickname=*/ 'device 2',
-        /*opt_audioCapability=*/
-        mojom.AudioOutputCapability.kCapableOfAudioOutput,
-        /*opt_deviceType=*/ mojom.DeviceType.kMouse);
+  //   const device1 = createDefaultBluetoothDevice(
+  //       /*id=*/ '12345654321',
+  //       /*publicName=*/ 'Head phones',
+  //       /*connectionState=*/
+  //       chromeos.bluetoothConfig.mojom.DeviceConnectionState.kConnected,
+  //       /*opt_nickname=*/ 'device 2',
+  //       /*opt_audioCapability=*/
+  //       mojom.AudioOutputCapability.kCapableOfAudioOutput,
+  //       /*opt_deviceType=*/ mojom.DeviceType.kMouse);
 
-    const device2 = createDefaultBluetoothDevice(
-        /*id=*/ '123454321',
-        /*publicName=*/ 'Speakers',
-        /*connectionState=*/
-        chromeos.bluetoothConfig.mojom.DeviceConnectionState.kConnected,
-        /*opt_nickname=*/ 'device 3',
-        /*opt_audioCapability=*/
-        mojom.AudioOutputCapability.kCapableOfAudioOutput,
-        /*opt_deviceType=*/ mojom.DeviceType.kMouse);
+  //   const device2 = createDefaultBluetoothDevice(
+  //       /*id=*/ '123454321',
+  //       /*publicName=*/ 'Speakers',
+  //       /*connectionState=*/
+  //       chromeos.bluetoothConfig.mojom.DeviceConnectionState.kConnected,
+  //       /*opt_nickname=*/ 'device 3',
+  //       /*opt_audioCapability=*/
+  //       mojom.AudioOutputCapability.kCapableOfAudioOutput,
+  //       /*opt_deviceType=*/ mojom.DeviceType.kMouse);
 
-    bluetoothConfig.appendToDiscoveredDeviceList(
-        [device.deviceProperties, device1.deviceProperties]);
-    await flushTasks();
-    let deviceHandler = bluetoothConfig.getLastCreatedPairingHandler();
+  //   bluetoothConfig.appendToDiscoveredDeviceList(
+  //       [device.deviceProperties, device1.deviceProperties]);
+  //   await flushTasks();
+  //   let deviceHandler = bluetoothConfig.getLastCreatedPairingHandler();
 
-    // Try pairing to first device.
-    await selectDevice(device.deviceProperties);
-    await waitAfterNextRender(bluetoothPairingUi);
+  //   // Try pairing to first device.
+  //   await selectDevice(device.deviceProperties);
+  //   await waitAfterNextRender(bluetoothPairingUi);
 
-    // Try pairing to second device, before first device has completed pairing.
-    await selectDevice(device1.deviceProperties);
-    await waitAfterNextRender(bluetoothPairingUi);
+  //   // Try pairing to second device, before first device has completed
+  //   pairing. await selectDevice(device1.deviceProperties); await
+  //   waitAfterNextRender(bluetoothPairingUi);
 
-    // Try pairing to third device, before first device has completed pairing.
-    await selectDevice(device2.deviceProperties);
-    await waitAfterNextRender(bluetoothPairingUi);
+  //   // Try pairing to third device, before first device has completed
+  //   pairing. await selectDevice(device2.deviceProperties); await
+  //   waitAfterNextRender(bluetoothPairingUi);
 
-    // Simulate device pairing cancellation.
-    deviceHandler.completePairDevice(/*success=*/ false);
-    await waitAfterNextRender(bluetoothPairingUi);
+  //   // Simulate device pairing cancellation.
+  //   deviceHandler.completePairDevice(/*success=*/ false);
+  //   await waitAfterNextRender(bluetoothPairingUi);
 
-    assertEquals(deviceHandler.getPairDeviceCalledCount(), 2);
+  //   assertEquals(deviceHandler.getPairDeviceCalledCount(), 2);
 
-    // Complete second device pairing.
-    deviceHandler.completePairDevice(/*success=*/ true);
-    await finishedPromise;
-  });
+  //   // Complete second device pairing.
+  //   deviceHandler.completePairDevice(/*success=*/ true);
+  //   await finishedPromise;
+  // });
 
-  test('Pair with a specific device by address, success', async function() {
-    await pairByDeviceAddress(/*address=*/ '123456');
+  // TODO(b/213943745) Fix flaky test.
+  // test('Pair with a specific device by address, success', async function() {
+  //   await pairByDeviceAddress(/*address=*/ '123456');
 
-    let finishedPromise = eventToPromise('finished', bluetoothPairingUi);
-    const deviceHandler = bluetoothConfig.getLastCreatedPairingHandler();
-    deviceHandler.completePairDevice(/*success=*/ true);
-    await finishedPromise;
-  });
+  //   let finishedPromise = eventToPromise('finished', bluetoothPairingUi);
+  //   const deviceHandler = bluetoothConfig.getLastCreatedPairingHandler();
+  //   deviceHandler.completePairDevice(/*success=*/ true);
+  //   await finishedPromise;
+  // });
 
   // TODO(b/210128630) Fix flaky test. Closure compiler complains about using
   // test.skip() here, see
@@ -624,33 +626,34 @@
   //       await finishedPromise;
   //     });
 
-  test('Pair with a specific device by address with auth', async function() {
-    await pairByDeviceAddress(/*address=*/ '123456');
+  // TODO(b/213943745) Fix flaky test.
+  // test('Pair with a specific device by address with auth', async function() {
+  //   await pairByDeviceAddress(/*address=*/ '123456');
 
-    const pairingCode = '123457';
-    let deviceHandler = bluetoothConfig.getLastCreatedPairingHandler();
-    deviceHandler.requireAuthentication(
-        PairingAuthType.CONFIRM_PASSKEY, pairingCode);
-    await flushTasks();
+  //   const pairingCode = '123457';
+  //   let deviceHandler = bluetoothConfig.getLastCreatedPairingHandler();
+  //   deviceHandler.requireAuthentication(
+  //       PairingAuthType.CONFIRM_PASSKEY, pairingCode);
+  //   await flushTasks();
 
-    // Confirmation code page should be shown.
-    assertTrue(!!getConfirmCodePage());
-    assertEquals(getConfirmCodePage().code, pairingCode);
+  //   // Confirmation code page should be shown.
+  //   assertTrue(!!getConfirmCodePage());
+  //   assertEquals(getConfirmCodePage().code, pairingCode);
 
-    // Simulate pressing 'Confirm'.
-    let event = new CustomEvent('confirm-code');
-    getConfirmCodePage().dispatchEvent(event);
-    await waitAfterNextRender(bluetoothPairingUi);
+  //   // Simulate pressing 'Confirm'.
+  //   let event = new CustomEvent('confirm-code');
+  //   getConfirmCodePage().dispatchEvent(event);
+  //   await waitAfterNextRender(bluetoothPairingUi);
 
-    // Spinner should be shown.
-    assertTrue(!!getSpinnerPage());
-    assertTrue(deviceHandler.getConfirmPasskeyResult());
+  //   // Spinner should be shown.
+  //   assertTrue(!!getSpinnerPage());
+  //   assertTrue(deviceHandler.getConfirmPasskeyResult());
 
-    // Finishing the pairing with success should fire the |finished| event.
-    let finishedPromise = eventToPromise('finished', bluetoothPairingUi);
-    deviceHandler.completePairDevice(/*success=*/ true);
-    await finishedPromise;
-  });
+  //   // Finishing the pairing with success should fire the |finished| event.
+  //   let finishedPromise = eventToPromise('finished', bluetoothPairingUi);
+  //   deviceHandler.completePairDevice(/*success=*/ true);
+  //   await finishedPromise;
+  // });
 
   test(
       'Cancel pairing with a specific device by address with auth',
@@ -774,101 +777,102 @@
     assertEquals(2, attemptFocusLastSelectedItemCallCount);
   });
 
-  test('Disable Bluetooth during pairing', async function() {
-    await init();
-    assertTrue(!!getDeviceSelectionPage());
-    assertTrue(getDeviceSelectionPage().isBluetoothEnabled);
+  // TODO(b/213943745) Fix flaky test.
+  // test('Disable Bluetooth during pairing', async function() {
+  //   await init();
+  //   assertTrue(!!getDeviceSelectionPage());
+  //   assertTrue(getDeviceSelectionPage().isBluetoothEnabled);
 
-    const deviceId = '123456';
-    const device = createDefaultBluetoothDevice(
-        deviceId,
-        /*publicName=*/ 'BeatsX',
-        /*connectionState=*/
-        chromeos.bluetoothConfig.mojom.DeviceConnectionState.kConnected,
-        /*opt_nickname=*/ 'device1',
-        /*opt_audioCapability=*/
-        mojom.AudioOutputCapability.kCapableOfAudioOutput,
-        /*opt_deviceType=*/ mojom.DeviceType.kMouse);
-    bluetoothConfig.appendToDiscoveredDeviceList([device.deviceProperties]);
-    await flushTasks();
+  //   const deviceId = '123456';
+  //   const device = createDefaultBluetoothDevice(
+  //       deviceId,
+  //       /*publicName=*/ 'BeatsX',
+  //       /*connectionState=*/
+  //       chromeos.bluetoothConfig.mojom.DeviceConnectionState.kConnected,
+  //       /*opt_nickname=*/ 'device1',
+  //       /*opt_audioCapability=*/
+  //       mojom.AudioOutputCapability.kCapableOfAudioOutput,
+  //       /*opt_deviceType=*/ mojom.DeviceType.kMouse);
+  //   bluetoothConfig.appendToDiscoveredDeviceList([device.deviceProperties]);
+  //   await flushTasks();
 
-    // Disable Bluetooth.
-    bluetoothConfig.setSystemState(
-        chromeos.bluetoothConfig.mojom.BluetoothSystemState.kDisabled);
-    await flushTasks();
+  //   // Disable Bluetooth.
+  //   bluetoothConfig.setSystemState(
+  //       chromeos.bluetoothConfig.mojom.BluetoothSystemState.kDisabled);
+  //   await flushTasks();
 
-    // This should propagate to the device selection page.
-    assertFalse(getDeviceSelectionPage().isBluetoothEnabled);
+  //   // This should propagate to the device selection page.
+  //   assertFalse(getDeviceSelectionPage().isBluetoothEnabled);
 
-    // Re-enable and select the device.
-    bluetoothConfig.setSystemState(
-        chromeos.bluetoothConfig.mojom.BluetoothSystemState.kEnabled);
-    await flushTasks();
-    await waitAfterNextRender(bluetoothPairingUi);
+  //   // Re-enable and select the device.
+  //   bluetoothConfig.setSystemState(
+  //       chromeos.bluetoothConfig.mojom.BluetoothSystemState.kEnabled);
+  //   await flushTasks();
+  //   await waitAfterNextRender(bluetoothPairingUi);
 
-    assertTrue(getDeviceSelectionPage().isBluetoothEnabled);
-    await selectDevice(device.deviceProperties);
-    await flushTasks();
+  //   assertTrue(getDeviceSelectionPage().isBluetoothEnabled);
+  //   await selectDevice(device.deviceProperties);
+  //   await flushTasks();
 
-    const pairingCode = '123456';
-    let deviceHandler = bluetoothConfig.getLastCreatedPairingHandler();
-    deviceHandler.requireAuthentication(
-        PairingAuthType.CONFIRM_PASSKEY, pairingCode);
-    await flushTasks();
+  //   const pairingCode = '123456';
+  //   let deviceHandler = bluetoothConfig.getLastCreatedPairingHandler();
+  //   deviceHandler.requireAuthentication(
+  //       PairingAuthType.CONFIRM_PASSKEY, pairingCode);
+  //   await flushTasks();
 
-    // Confirmation code page should be shown.
-    assertTrue(!!getConfirmCodePage());
-    assertEquals(getConfirmCodePage().code, pairingCode);
+  //   // Confirmation code page should be shown.
+  //   assertTrue(!!getConfirmCodePage());
+  //   assertEquals(getConfirmCodePage().code, pairingCode);
 
-    // Disable Bluetooth.
-    bluetoothConfig.setSystemState(
-        chromeos.bluetoothConfig.mojom.BluetoothSystemState.kDisabled);
-    await flushTasks();
+  //   // Disable Bluetooth.
+  //   bluetoothConfig.setSystemState(
+  //       chromeos.bluetoothConfig.mojom.BluetoothSystemState.kDisabled);
+  //   await flushTasks();
 
-    // We should be back to the device selection page again.
-    assertFalse(!!getConfirmCodePage());
-    assertTrue(!!getDeviceSelectionPage());
-    assertFalse(getDeviceSelectionPage().isBluetoothEnabled);
+  //   // We should be back to the device selection page again.
+  //   assertFalse(!!getConfirmCodePage());
+  //   assertTrue(!!getDeviceSelectionPage());
+  //   assertFalse(getDeviceSelectionPage().isBluetoothEnabled);
 
-    // Re-enable.
-    bluetoothConfig.setSystemState(
-        chromeos.bluetoothConfig.mojom.BluetoothSystemState.kEnabled);
-    await flushTasks();
-    await waitAfterNextRender(bluetoothPairingUi);
+  //   // Re-enable.
+  //   bluetoothConfig.setSystemState(
+  //       chromeos.bluetoothConfig.mojom.BluetoothSystemState.kEnabled);
+  //   await flushTasks();
+  //   await waitAfterNextRender(bluetoothPairingUi);
 
-    assertTrue(getDeviceSelectionPage().isBluetoothEnabled);
+  //   assertTrue(getDeviceSelectionPage().isBluetoothEnabled);
 
-    // Error text shouldn't be showing because this pairing failed due to
-    // Bluetooth disabling.
-    assertEquals(getDeviceSelectionPage().failedPairingDeviceId, '');
+  //   // Error text shouldn't be showing because this pairing failed due to
+  //   // Bluetooth disabling.
+  //   assertEquals(getDeviceSelectionPage().failedPairingDeviceId, '');
 
-    // Select the device.
-    await selectDevice(device.deviceProperties);
-    await flushTasks();
+  //   // Select the device.
+  //   await selectDevice(device.deviceProperties);
+  //   await flushTasks();
 
-    // Simulate pairing failing.
-    deviceHandler = bluetoothConfig.getLastCreatedPairingHandler();
-    deviceHandler.completePairDevice(/*success=*/ false);
-    await flushTasks();
-    await waitAfterNextRender(bluetoothPairingUi);
+  //   // Simulate pairing failing.
+  //   deviceHandler = bluetoothConfig.getLastCreatedPairingHandler();
+  //   deviceHandler.completePairDevice(/*success=*/ false);
+  //   await flushTasks();
+  //   await waitAfterNextRender(bluetoothPairingUi);
 
-    // Error text should be showing.
-    assertTrue(!!getDeviceSelectionPage());
-    assertEquals(getDeviceSelectionPage().failedPairingDeviceId, deviceId);
+  //   // Error text should be showing.
+  //   assertTrue(!!getDeviceSelectionPage());
+  //   assertEquals(getDeviceSelectionPage().failedPairingDeviceId, deviceId);
 
-    // Disable and re-enable.
-    bluetoothConfig.setSystemState(
-        chromeos.bluetoothConfig.mojom.BluetoothSystemState.kDisabled);
-    await flushTasks();
+  //   // Disable and re-enable.
+  //   bluetoothConfig.setSystemState(
+  //       chromeos.bluetoothConfig.mojom.BluetoothSystemState.kDisabled);
+  //   await flushTasks();
 
-    assertFalse(getDeviceSelectionPage().isBluetoothEnabled);
-    bluetoothConfig.setSystemState(
-        chromeos.bluetoothConfig.mojom.BluetoothSystemState.kEnabled);
-    await flushTasks();
+  //   assertFalse(getDeviceSelectionPage().isBluetoothEnabled);
+  //   bluetoothConfig.setSystemState(
+  //       chromeos.bluetoothConfig.mojom.BluetoothSystemState.kEnabled);
+  //   await flushTasks();
 
-    assertTrue(getDeviceSelectionPage().isBluetoothEnabled);
+  //   assertTrue(getDeviceSelectionPage().isBluetoothEnabled);
 
-    // Error text should no longer be showing.
-    assertEquals(getDeviceSelectionPage().failedPairingDeviceId, '');
-  });
+  //   // Error text should no longer be showing.
+  //   assertEquals(getDeviceSelectionPage().failedPairingDeviceId, '');
+  // });
 });
diff --git a/chrome/test/data/webui/cr_components/chromeos/bluetooth/fake_bluetooth_config.js b/chrome/test/data/webui/cr_components/chromeos/bluetooth/fake_bluetooth_config.js
index 65204de..3b9bfa9 100644
--- a/chrome/test/data/webui/cr_components/chromeos/bluetooth/fake_bluetooth_config.js
+++ b/chrome/test/data/webui/cr_components/chromeos/bluetooth/fake_bluetooth_config.js
@@ -5,6 +5,8 @@
 import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
 import 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js';
 import 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js';
+// cros_bluetooth_config.mojom-lite.js depends on url.mojom.Url.
+import 'chrome://resources/mojo/url/mojom/url.mojom-lite.js';
 // TODO(crbug.com/1010321): Use cros_bluetooth_config.mojom-webui.js instead
 // as non-module JS is deprecated.
 import 'chrome://resources/mojo/chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-lite.js';
diff --git a/chrome/test/data/webui/cr_components/chromeos/bluetooth/fake_bluetooth_discovery_delegate.js b/chrome/test/data/webui/cr_components/chromeos/bluetooth/fake_bluetooth_discovery_delegate.js
index e212b01a..222a2ede 100644
--- a/chrome/test/data/webui/cr_components/chromeos/bluetooth/fake_bluetooth_discovery_delegate.js
+++ b/chrome/test/data/webui/cr_components/chromeos/bluetooth/fake_bluetooth_discovery_delegate.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// cros_bluetooth_config.mojom-lite.js depends on url.mojom.Url.
+import 'chrome://resources/mojo/url/mojom/url.mojom-lite.js';
 // TODO(crbug.com/1010321): Use cros_bluetooth_config.mojom-webui.js instead
 // as non-module JS is deprecated.
 import 'chrome://resources/mojo/chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-lite.js';
diff --git a/chrome/test/data/webui/cr_components/chromeos/bluetooth/fake_device_pairing_handler.js b/chrome/test/data/webui/cr_components/chromeos/bluetooth/fake_device_pairing_handler.js
index 4ff6cbe..1ac090a 100644
--- a/chrome/test/data/webui/cr_components/chromeos/bluetooth/fake_device_pairing_handler.js
+++ b/chrome/test/data/webui/cr_components/chromeos/bluetooth/fake_device_pairing_handler.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// cros_bluetooth_config.mojom-lite.js depends on url.mojom.Url.
+import 'chrome://resources/mojo/url/mojom/url.mojom-lite.js';
 // TODO(crbug.com/1010321): Use cros_bluetooth_config.mojom-webui.js instead
 // as non-module JS is deprecated.
 import 'chrome://resources/mojo/chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-lite.js';
diff --git a/chrome/test/data/webui/cr_components/chromeos/network/network_config_test.js b/chrome/test/data/webui/cr_components/chromeos/network/network_config_test.js
index e67c67b..04c6ae3d 100644
--- a/chrome/test/data/webui/cr_components/chromeos/network/network_config_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/network/network_config_test.js
@@ -243,6 +243,19 @@
         assertEquals('0.0.0.0/0', peer.allowedIps);
       });
     });
+
+    test('Preshared key display and config value', function() {
+      return flushAsync().then(() => {
+        const configProperties = networkConfig.get('configProperties_');
+        assertEquals(
+            '(credential)',
+            configProperties.typeConfig.vpn.wireguard.peers[0].presharedKey);
+        const configToSet = networkConfig.getPropertiesToSet_();
+        assertEquals(
+            undefined,
+            configToSet.typeConfig.vpn.wireguard.peers[0].presharedKey);
+      });
+    });
   });
 
   suite('Share', function() {
diff --git a/chrome/test/data/webui/cr_components/chromeos/network/network_list_item_test.js b/chrome/test/data/webui/cr_components/chromeos/network/network_list_item_test.js
index f1b2ba0a..99cae0aa 100644
--- a/chrome/test/data/webui/cr_components/chromeos/network/network_list_item_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/network/network_list_item_test.js
@@ -600,6 +600,27 @@
         assertFalse(eventTriggered);
       });
 
+  test(
+      'Network item force disabled, no arrow and enter and click does not ' +
+          'fire events',
+      async () => {
+        init();
+
+        listItem.disableItem = true;
+        await flushAsync();
+
+        let arrow = listItem.$$('#subpageButton');
+        assertFalse(!!arrow);
+
+        listItem.$$('#divOuter').click();
+        await flushAsync();
+        assertFalse(eventTriggered);
+
+        enter();
+        await flushAsync();
+        assertFalse(eventTriggered);
+      });
+
   test('Show locked sublabel when cellular network is locked', async () => {
     init();
 
diff --git a/chrome/test/data/webui/new_tab_page/BUILD.gn b/chrome/test/data/webui/new_tab_page/BUILD.gn
index 0a0d1b1..8a5fc00 100644
--- a/chrome/test/data/webui/new_tab_page/BUILD.gn
+++ b/chrome/test/data/webui/new_tab_page/BUILD.gn
@@ -17,6 +17,15 @@
                     "js_module_root=" + rebase_path(
                             "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/new_tab_page/foo/",
                             root_build_dir),
+                    "js_module_root=" + rebase_path(
+                            "$root_gen_dir/mojom-webui/chrome/browser/cart",
+                            root_build_dir),
+                    "js_module_root=" + rebase_path(
+                            "$root_gen_dir/mojom-webui/chrome/browser/new_tab_page/modules/photos",
+                            root_build_dir),
+                    "js_module_root=" + rebase_path(
+                            "$root_gen_dir/mojom-webui/chrome/browser/new_tab_page/modules/drive",
+                            root_build_dir),
                   ]
   deps = [
     ":metrics_utils_test",
diff --git a/chrome/test/data/webui/new_tab_page/customize_modules_test.js b/chrome/test/data/webui/new_tab_page/customize_modules_test.js
index 10ea21557..44ddbd3 100644
--- a/chrome/test/data/webui/new_tab_page/customize_modules_test.js
+++ b/chrome/test/data/webui/new_tab_page/customize_modules_test.js
@@ -4,6 +4,7 @@
 
 import 'chrome://new-tab-page/lazy_load.js';
 
+import {CartHandlerRemote} from 'chrome://new-tab-page/chrome_cart.mojom-webui.js';
 import {$$, ChromeCartProxy, ModuleDescriptor, ModuleRegistry, NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {fakeMetricsPrivate, MetricsTracker} from 'chrome://test/new_tab_page/metrics_test_support.js';
@@ -67,8 +68,7 @@
     moduleRegistry = installMock(ModuleRegistry);
     metrics = fakeMetricsPrivate();
     loadTimeData.overrideValues({modulesVisibleManagedByPolicy: false});
-    cartHandler = installMock(
-        chromeCart.mojom.CartHandlerRemote, ChromeCartProxy.setHandler);
+    cartHandler = installMock(CartHandlerRemote, ChromeCartProxy.setHandler);
     cartHandler.setResultFor(
         'getDiscountEnabled', Promise.resolve({enabled: false}));
   });
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart/BUILD.gn b/chrome/test/data/webui/new_tab_page/modules/cart/BUILD.gn
index 0025f80..f33bb86 100644
--- a/chrome/test/data/webui/new_tab_page/modules/cart/BUILD.gn
+++ b/chrome/test/data/webui/new_tab_page/modules/cart/BUILD.gn
@@ -8,6 +8,7 @@
   deps = [
     "../..:metrics_test_support",
     "../..:test_support",
+    "//chrome/browser/cart:mojo_bindings_webui_js",
     "//chrome/browser/resources/new_tab_page",
     "//chrome/test/data/webui:chai_assert",
     "//chrome/test/data/webui:test_browser_proxy",
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart/module_test.js b/chrome/test/data/webui/new_tab_page/modules/cart/module_test.js
index e33666d..ab21ee3 100644
--- a/chrome/test/data/webui/new_tab_page/modules/cart/module_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/cart/module_test.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {CartHandlerRemote} from 'chrome://new-tab-page/chrome_cart.mojom-webui.js';
 import {$$, chromeCartDescriptor, ChromeCartProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
@@ -21,8 +22,7 @@
   setup(() => {
     document.body.innerHTML = '';
 
-    handler = installMock(
-        chromeCart.mojom.CartHandlerRemote, ChromeCartProxy.setHandler);
+    handler = installMock(CartHandlerRemote, ChromeCartProxy.setHandler);
     metrics = fakeMetricsPrivate();
     // Not show welcome surface by default.
     handler.setResultFor(
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart_v2/BUILD.gn b/chrome/test/data/webui/new_tab_page/modules/cart_v2/BUILD.gn
index 0025f80..f33bb86 100644
--- a/chrome/test/data/webui/new_tab_page/modules/cart_v2/BUILD.gn
+++ b/chrome/test/data/webui/new_tab_page/modules/cart_v2/BUILD.gn
@@ -8,6 +8,7 @@
   deps = [
     "../..:metrics_test_support",
     "../..:test_support",
+    "//chrome/browser/cart:mojo_bindings_webui_js",
     "//chrome/browser/resources/new_tab_page",
     "//chrome/test/data/webui:chai_assert",
     "//chrome/test/data/webui:test_browser_proxy",
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.js b/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.js
index bfe87da..2e57be4 100644
--- a/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {CartHandlerRemote} from 'chrome://new-tab-page/chrome_cart.mojom-webui.js';
 import {$$, ChromeCartProxy, chromeCartV2Descriptor, ModuleHeight} from 'chrome://new-tab-page/new_tab_page.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
@@ -21,8 +22,7 @@
   setup(() => {
     document.body.innerHTML = '';
 
-    handler = installMock(
-        chromeCart.mojom.CartHandlerRemote, ChromeCartProxy.setHandler);
+    handler = installMock(CartHandlerRemote, ChromeCartProxy.setHandler);
     metrics = fakeMetricsPrivate();
     // Not show welcome surface by default.
     handler.setResultFor(
diff --git a/chrome/test/data/webui/new_tab_page/modules/drive/BUILD.gn b/chrome/test/data/webui/new_tab_page/modules/drive/BUILD.gn
index 238a27e0..0015cc54 100644
--- a/chrome/test/data/webui/new_tab_page/modules/drive/BUILD.gn
+++ b/chrome/test/data/webui/new_tab_page/modules/drive/BUILD.gn
@@ -7,6 +7,7 @@
 js_library("module_test") {
   deps = [
     "../..:test_support",
+    "//chrome/browser/new_tab_page/modules/drive:mojo_bindings_webui_js",
     "//chrome/browser/resources/new_tab_page",
     "//chrome/test/data/webui:chai_assert",
     "//chrome/test/data/webui:test_browser_proxy",
diff --git a/chrome/test/data/webui/new_tab_page/modules/drive/module_test.js b/chrome/test/data/webui/new_tab_page/modules/drive/module_test.js
index e0182a00..987a920 100644
--- a/chrome/test/data/webui/new_tab_page/modules/drive/module_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/drive/module_test.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {DriveHandlerRemote} from 'chrome://new-tab-page/drive.mojom-webui.js';
 import {$$, driveDescriptor, DriveProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://test/chai_assert.js';
@@ -15,8 +16,7 @@
 
   setup(() => {
     document.body.innerHTML = '';
-    handler =
-        installMock(drive.mojom.DriveHandlerRemote, DriveProxy.setHandler);
+    handler = installMock(DriveHandlerRemote, DriveProxy.setHandler);
   });
 
   test('module appears on render', async () => {
diff --git a/chrome/test/data/webui/new_tab_page/modules/drive_v2/BUILD.gn b/chrome/test/data/webui/new_tab_page/modules/drive_v2/BUILD.gn
index 238a27e0..0015cc54 100644
--- a/chrome/test/data/webui/new_tab_page/modules/drive_v2/BUILD.gn
+++ b/chrome/test/data/webui/new_tab_page/modules/drive_v2/BUILD.gn
@@ -7,6 +7,7 @@
 js_library("module_test") {
   deps = [
     "../..:test_support",
+    "//chrome/browser/new_tab_page/modules/drive:mojo_bindings_webui_js",
     "//chrome/browser/resources/new_tab_page",
     "//chrome/test/data/webui:chai_assert",
     "//chrome/test/data/webui:test_browser_proxy",
diff --git a/chrome/test/data/webui/new_tab_page/modules/drive_v2/module_test.js b/chrome/test/data/webui/new_tab_page/modules/drive_v2/module_test.js
index 8ad8934..891a8d17 100644
--- a/chrome/test/data/webui/new_tab_page/modules/drive_v2/module_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/drive_v2/module_test.js
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {DriveHandlerRemote} from 'chrome://new-tab-page/drive.mojom-webui.js';
 import {$$, DriveProxy, driveV2Descriptor} from 'chrome://new-tab-page/new_tab_page.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {assertEquals, assertTrue} from 'chrome://test/chai_assert.js';
@@ -15,8 +16,7 @@
 
   setup(() => {
     document.body.innerHTML = '';
-    handler =
-        installMock(drive.mojom.DriveHandlerRemote, DriveProxy.setHandler);
+    handler = installMock(DriveHandlerRemote, DriveProxy.setHandler);
   });
 
   test('module appears on render', async () => {
diff --git a/chrome/test/data/webui/new_tab_page/modules/dummy/BUILD.gn b/chrome/test/data/webui/new_tab_page/modules/dummy/BUILD.gn
index 238a27e0..bb126a8 100644
--- a/chrome/test/data/webui/new_tab_page/modules/dummy/BUILD.gn
+++ b/chrome/test/data/webui/new_tab_page/modules/dummy/BUILD.gn
@@ -8,6 +8,7 @@
   deps = [
     "../..:test_support",
     "//chrome/browser/resources/new_tab_page",
+    "//chrome/browser/ui/webui/new_tab_page/foo:mojo_bindings_webui_js",
     "//chrome/test/data/webui:chai_assert",
     "//chrome/test/data/webui:test_browser_proxy",
     "//chrome/test/data/webui:test_util",
diff --git a/chrome/test/data/webui/new_tab_page/modules/dummy/module_test.js b/chrome/test/data/webui/new_tab_page/modules/dummy/module_test.js
index e927199..fbd847a 100644
--- a/chrome/test/data/webui/new_tab_page/modules/dummy/module_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/dummy/module_test.js
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {$$, dummyDescriptor, FooHandlerRemote, FooProxy} from 'chrome://new-tab-page/new_tab_page.js';
+import {FooHandlerRemote} from 'chrome://new-tab-page/foo.mojom-webui.js';
+import {$$, dummyDescriptor, FooProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://test/chai_assert.js';
 import {installMock} from 'chrome://test/new_tab_page/test_support.js';
diff --git a/chrome/test/data/webui/new_tab_page/modules/photos/BUILD.gn b/chrome/test/data/webui/new_tab_page/modules/photos/BUILD.gn
index 0025f80..3f3ae94b 100644
--- a/chrome/test/data/webui/new_tab_page/modules/photos/BUILD.gn
+++ b/chrome/test/data/webui/new_tab_page/modules/photos/BUILD.gn
@@ -8,6 +8,7 @@
   deps = [
     "../..:metrics_test_support",
     "../..:test_support",
+    "//chrome/browser/new_tab_page/modules/photos:mojo_bindings_webui_js",
     "//chrome/browser/resources/new_tab_page",
     "//chrome/test/data/webui:chai_assert",
     "//chrome/test/data/webui:test_browser_proxy",
diff --git a/chrome/test/data/webui/new_tab_page/modules/photos/module_test.js b/chrome/test/data/webui/new_tab_page/modules/photos/module_test.js
index ade9725c..8e13b96 100644
--- a/chrome/test/data/webui/new_tab_page/modules/photos/module_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/photos/module_test.js
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import {$$, photosDescriptor, PhotosProxy} from 'chrome://new-tab-page/new_tab_page.js';
+import {PhotosHandlerRemote} from 'chrome://new-tab-page/photos.mojom-webui.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://test/chai_assert.js';
@@ -20,8 +21,7 @@
 
   setup(() => {
     document.body.innerHTML = '';
-    handler =
-        installMock(photos.mojom.PhotosHandlerRemote, PhotosProxy.setHandler);
+    handler = installMock(PhotosHandlerRemote, PhotosProxy.setHandler);
     metrics = fakeMetricsPrivate();
   });
 
diff --git a/chrome/test/data/webui/settings/BUILD.gn b/chrome/test/data/webui/settings/BUILD.gn
index 96fe1e7..93d02099 100644
--- a/chrome/test/data/webui/settings/BUILD.gn
+++ b/chrome/test/data/webui/settings/BUILD.gn
@@ -92,6 +92,7 @@
   "secure_dns_interactive_test.ts",
   "secure_dns_test.ts",
   "security_keys_subpage_test.ts",
+  "security_keys_phones_subpage_test.ts",
   "settings_animated_pages_test.ts",
   "settings_category_default_radio_group_tests.ts",
   "settings_main_test.ts",
diff --git a/chrome/test/data/webui/settings/chromeos/os_bluetooth_devices_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/os_bluetooth_devices_subpage_tests.js
index ea9416ff..feef47c 100644
--- a/chrome/test/data/webui/settings/chromeos/os_bluetooth_devices_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_bluetooth_devices_subpage_tests.js
@@ -74,12 +74,31 @@
     assertTrue(!!bluetoothDevicesSubpage);
   });
 
-  test('Toggle button creation', async function() {
+  test('Toggle button creation and a11y', async function() {
     bluetoothConfig.setSystemState(
         chromeos.bluetoothConfig.mojom.BluetoothSystemState.kEnabled);
-    await flushAsync();
     await init();
-    assertTrue(bluetoothDevicesSubpage.$.enableBluetoothToggle.checked);
+    const toggle = bluetoothDevicesSubpage.shadowRoot.querySelector(
+        '#enableBluetoothToggle');
+    assertTrue(toggle.checked);
+
+    let ironAnnouncerPromise =
+        test_util.eventToPromise('iron-announce', bluetoothDevicesSubpage);
+
+    toggle.click();
+    let result = await ironAnnouncerPromise;
+    assertEquals(
+        result.detail.text,
+        bluetoothDevicesSubpage.i18n('bluetoothDisabledA11YLabel'));
+
+    ironAnnouncerPromise =
+        test_util.eventToPromise('iron-announce', bluetoothDevicesSubpage);
+    toggle.click();
+
+    result = await ironAnnouncerPromise;
+    assertEquals(
+        result.detail.text,
+        bluetoothDevicesSubpage.i18n('bluetoothEnabledA11YLabel'));
   });
 
   test('Toggle button states', async function() {
diff --git a/chrome/test/data/webui/settings/chromeos/os_bluetooth_summary_tests.js b/chrome/test/data/webui/settings/chromeos/os_bluetooth_summary_tests.js
index 0505a09a..f7d65403 100644
--- a/chrome/test/data/webui/settings/chromeos/os_bluetooth_summary_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_bluetooth_summary_tests.js
@@ -84,12 +84,30 @@
     assertEquals(iconButton, bluetoothSummary.shadowRoot.activeElement);
   });
 
-  test('Toggle button creation', async function() {
+  test('Toggle button creation and a11y', async function() {
     bluetoothConfig.setSystemState(
         chromeos.bluetoothConfig.mojom.BluetoothSystemState.kEnabled);
     await flushAsync();
     init();
-    assertTrue(bluetoothSummary.$$('#enableBluetoothToggle').checked);
+    let ironAnnouncerPromise =
+        test_util.eventToPromise('iron-announce', bluetoothSummary);
+
+    const toggle = bluetoothSummary.$$('#enableBluetoothToggle');
+    assertTrue(toggle.checked);
+
+    toggle.click();
+    let result = await ironAnnouncerPromise;
+    assertEquals(
+        result.detail.text,
+        bluetoothSummary.i18n('bluetoothDisabledA11YLabel'));
+
+    ironAnnouncerPromise =
+        test_util.eventToPromise('iron-announce', bluetoothSummary);
+    toggle.click();
+
+    result = await ironAnnouncerPromise;
+    assertEquals(
+        result.detail.text, bluetoothSummary.i18n('bluetoothEnabledA11YLabel'));
   });
 
   test('Toggle button states', async function() {
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index e62e0ccf..16f0262 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -586,6 +586,7 @@
  ['SearchPage', 'search_page_test.js'],
  ['Search', 'search_settings_test.js'],
  ['SecurityKeysSubpage', 'security_keys_subpage_test.js'],
+ ['SecurityKeysPhonesSubpage', 'security_keys_phones_subpage_test.js'],
  ['SecureDns', 'secure_dns_test.js'],
  ['SiteData', 'site_data_test.js'],
  ['SiteDataDetails', 'site_data_details_subpage_tests.js'],
diff --git a/chrome/test/data/webui/settings/people_page_sync_page_test.ts b/chrome/test/data/webui/settings/people_page_sync_page_test.ts
index 7819b36..b526fae8 100644
--- a/chrome/test/data/webui/settings/people_page_sync_page_test.ts
+++ b/chrome/test/data/webui/settings/people_page_sync_page_test.ts
@@ -533,6 +533,12 @@
     assertTrue(encryptionRadioGroup.disabled);
     assertEquals(-1, encryptWithGoogle.$.button.tabIndex);
     assertEquals(-1, encryptWithPassphrase.$.button.tabIndex);
+
+    // Confirm that the page navigates away form the sync setup.
+    await browserProxy.whenCalled('didNavigateAwayFromSyncPage');
+    const router = Router.getInstance();
+    assertEquals(
+        (router.getRoutes() as SyncRoutes).PEOPLE, router.getCurrentRoute());
   });
 
   test('SyncAdvancedRow', function() {
diff --git a/chrome/test/data/webui/settings/security_keys_phones_subpage_test.ts b/chrome/test/data/webui/settings/security_keys_phones_subpage_test.ts
new file mode 100644
index 0000000..690b28e
--- /dev/null
+++ b/chrome/test/data/webui/settings/security_keys_phones_subpage_test.ts
@@ -0,0 +1,219 @@
+// 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.
+
+/**
+ * @fileoverview Tests for the Phone as a Security Key settings page.
+ */
+
+import {CrInputElement, SecurityKeysPhone, SecurityKeysPhonesBrowserProxy, SecurityKeysPhonesBrowserProxyImpl, SecurityKeysPhonesList, SecurityKeysPhonesSubpageElement} from 'chrome://settings/lazy_load.js';
+import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {flushTasks} from 'chrome://webui-test/test_util.js';
+
+class TestSecurityKeysPhonesBrowserProxy extends TestBrowserProxy implements
+    SecurityKeysPhonesBrowserProxy {
+  constructor() {
+    super([
+      'enumerate',
+      'delete',
+      'rename',
+    ]);
+  }
+
+  // nextPhonesList_ is the next result to return from a call to `enumerate` or
+  // `delete`.
+  private nextPhonesList_: SecurityKeysPhonesList|null = null;
+
+  setNextPhonesList(syncedPhones: Array<string>, linkedPhones: Array<string>) {
+    this.nextPhonesList_ =
+        [syncedPhones.map(this.toPhone_), linkedPhones.map(this.toPhone_)];
+    this.resetResolver('enumerate');
+    this.resetResolver('delete');
+  }
+
+  /* override */ enumerate(): Promise<SecurityKeysPhonesList> {
+    this.methodCalled('enumerate');
+    return this.consumeNextPhonesList_();
+  }
+
+  /* override */ delete(publicKey: string): Promise<SecurityKeysPhonesList> {
+    this.methodCalled('delete', publicKey);
+    return this.consumeNextPhonesList_();
+  }
+
+  /* override */ rename(publicKey: string, newName: string): Promise<void> {
+    this.methodCalled('rename', publicKey, newName);
+    return Promise.resolve();
+  }
+
+  private consumeNextPhonesList_(): Promise<SecurityKeysPhonesList> {
+    const result = this.nextPhonesList_;
+    this.nextPhonesList_ = null;
+    assertTrue(
+        result !== null,
+        'browserProxy methods called without result have being set for it');
+    return Promise.resolve(result!);
+  }
+
+  /**
+   * Create a dummy phone given a name.
+   */
+  private toPhone_(name: string): SecurityKeysPhone {
+    return {name, publicKey: name};
+  }
+}
+
+type ShownPhones = Array<{name: string, hasDots: boolean}>;
+
+/**
+ * Get the phones currently listed by the given SecurityKeysPhonesListElement.
+ */
+function getPhonesFromList(list: HTMLElement): ShownPhones {
+  const items = list.shadowRoot!.querySelectorAll('.list-item');
+  const ret: ShownPhones = [];
+
+  for (const item of items) {
+    const nameSpan = item.querySelector<HTMLElement>('.name-column');
+    const dots = item.querySelector<HTMLElement>('.icon-more-vert');
+
+    if (nameSpan != null) {
+      ret.push({name: nameSpan.innerText, hasDots: dots !== null});
+    }
+  }
+
+  return ret;
+}
+
+/**
+ * Get the list phones currently shown on the page.
+ */
+function getPhones(page: HTMLElement): [ShownPhones, ShownPhones] {
+  return [
+    getPhonesFromList(
+        page.shadowRoot!.querySelector<HTMLElement>('#syncedPhonesList')!),
+    getPhonesFromList(
+        page.shadowRoot!.querySelector<HTMLElement>('#linkedPhonesList')!),
+  ];
+}
+
+/**
+ * Click the `num`th drop-down icon in the list of linked phones.
+ */
+function clickLinkedPhoneDots(page: HTMLElement, num: number) {
+  const list =
+      page.shadowRoot!.querySelector<HTMLElement>('#linkedPhonesList')!;
+  const items = list.shadowRoot!.querySelectorAll('.list-item');
+  const dots = items[num]!.querySelector<HTMLElement>('.icon-more-vert')!;
+  dots.click();
+}
+
+/**
+ * Click the button named `name` in the `num`th drop-down.
+ */
+function clickButton(page: HTMLElement, name: string) {
+  const list =
+      page.shadowRoot!.querySelector<HTMLElement>('#linkedPhonesList')!;
+  const menu = list.shadowRoot!.querySelector<HTMLElement>('#menu')!;
+  const button = menu.querySelector<HTMLElement>('#' + name);
+
+  assertTrue(button !== null, name + ' button missing');
+  if (button === null) {
+    return;
+  }
+
+  button.click();
+}
+
+suite('SecurityKeysPhonesSubpage', function() {
+  let browserProxy: TestSecurityKeysPhonesBrowserProxy;
+  let page: SecurityKeysPhonesSubpageElement;
+
+  // These are the initial lists of synced and linked phones that will be
+  // displayed when the test starts.
+  const initialSynced = ['Synced 1', 'Synced 2'];
+  const initialLinked = ['Linked 1', 'Linked 2'];
+
+  setup(async function() {
+    browserProxy = new TestSecurityKeysPhonesBrowserProxy();
+    SecurityKeysPhonesBrowserProxyImpl.setInstance(browserProxy);
+    document.body.innerHTML = '';
+    page = document.createElement('security-keys-phones-subpage');
+
+    browserProxy.setNextPhonesList(initialSynced, initialLinked);
+    document.body.appendChild(page);
+    await flushTasks();
+    assertEquals(browserProxy.getCallCount('enumerate'), 1);
+  });
+
+  test('Initialization', async function() {
+    const shown = getPhones(page);
+
+    // The default entries should be shown.
+    assertDeepEquals(shown[0].map(x => x.name), initialSynced);
+    assertDeepEquals(shown[1].map(x => x.name), initialLinked);
+    // The synced phones should not have the dropdown dots, the linked phones
+    // should.
+    assertTrue(
+        shown[0].every(x => !x.hasDots), 'Synced phones don\'t have dots');
+    assertTrue(shown[1].every(x => x.hasDots), 'Linked phones have dots');
+  });
+
+  test('Delete', async function() {
+    clickLinkedPhoneDots(page, 0);
+
+    // 'delete' should be called for the first linked phone.
+    browserProxy.whenCalled('delete').then((name: string) => {
+      assertEquals(name, initialLinked[0]);
+    });
+    browserProxy.setNextPhonesList(initialSynced, ['Linked 2']);
+    clickButton(page, 'delete');
+    await flushTasks();
+    assertEquals(browserProxy.getCallCount('delete'), 1);
+
+    const shown = getPhones(page);
+    assertDeepEquals(shown[0].map(x => x.name), initialSynced);
+    // The first phone should have disappeared.
+    assertDeepEquals(shown[1].map(x => x.name), ['Linked 2']);
+  });
+
+  test('Edit', async function() {
+    clickLinkedPhoneDots(page, 0);
+    clickButton(page, 'edit');
+
+    await flushTasks();
+
+    const dialogs =
+        page.shadowRoot!.querySelectorAll('security-keys-phones-dialog');
+    assertEquals(dialogs.length, 1, 'num dialogs');
+    const dialog = dialogs[0]!;
+
+    const name = dialog.shadowRoot!.querySelector<CrInputElement>('#name')!;
+    assertEquals(name.value, 'Linked 1');
+    const newName = 'New name';
+    name.value = newName;
+
+    const saveButton =
+        dialog.shadowRoot!.querySelector<HTMLElement>('#actionButton')!;
+    browserProxy.whenCalled('rename').then(
+        ([publicKey, requestedName]: [string, string]) => {
+          assertEquals(publicKey, initialLinked[0]);
+          assertEquals(requestedName, newName);
+        });
+    browserProxy.setNextPhonesList(initialSynced, [newName, 'Linked 2']);
+    saveButton.click();
+
+    await flushTasks();
+    assertEquals(browserProxy.getCallCount('rename'), 1, 'rename not called');
+
+    await browserProxy.whenCalled('enumerate');
+    await flushTasks();
+    assertEquals(
+        browserProxy.getCallCount('enumerate'), 1, 'enumerate not called');
+    const shown = getPhones(page);
+
+    assertDeepEquals(shown[0].map(x => x.name), initialSynced);
+    // The first phone should have been renamed.
+    assertDeepEquals(shown[1].map(x => x.name), [newName, 'Linked 2']);
+  });
+});
diff --git a/chrome/test/data/webui/tab_strip/tab_list_test.ts b/chrome/test/data/webui/tab_strip/tab_list_test.ts
index 54b5fd2..d12917e 100644
--- a/chrome/test/data/webui/tab_strip/tab_list_test.ts
+++ b/chrome/test/data/webui/tab_strip/tab_list_test.ts
@@ -4,7 +4,6 @@
 
 import 'chrome://tab-strip.top-chrome/tab_list.js';
 
-import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
 import {FocusOutlineManager} from 'chrome://resources/js/cr/ui/focus_outline_manager.m.js';
 import {TabElement} from 'chrome://tab-strip.top-chrome/tab.js';
 import {TabGroupElement} from 'chrome://tab-strip.top-chrome/tab_group.js';
@@ -129,7 +128,8 @@
       '--background-color': 'pink',
       '--foreground-color': 'blue',
     });
-    webUIListenerCallback('theme-changed');
+    callbackRouter.themeChanged();
+    await flushTasks();
     await testTabsApiProxy.whenCalled('getColors');
     assertEquals(tabList.style.getPropertyValue('--background-color'), 'pink');
     assertEquals(tabList.style.getPropertyValue('--foreground-color'), 'blue');
@@ -183,7 +183,8 @@
         textColor: 'black',
       },
     });
-    webUIListenerCallback('theme-changed');
+    callbackRouter.themeChanged();
+    await flushTasks();
     await testTabsApiProxy.whenCalled('getGroupVisualData');
   });
 
diff --git a/chrome/test/data/webui/test_browser_proxy.d.ts b/chrome/test/data/webui/test_browser_proxy.d.ts
index 78edd4c..d6ed004 100644
--- a/chrome/test/data/webui/test_browser_proxy.d.ts
+++ b/chrome/test/data/webui/test_browser_proxy.d.ts
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-export class TestBrowserProxy {
-  static fromClass<T>(clazz: {new(): T}): T&TestBrowserProxy;
-  constructor(methodNames?: Array<string>);
-  methodCalled(methodName: string, ...args: any[]): any;
-  whenCalled(methodName: string): Promise<any>;
-  resetResolver(methodName: string): void;
+export class TestBrowserProxy<T = any> {
+  static fromClass<T>(clazz: {new(): T}): T&TestBrowserProxy<T>;
+  constructor(methodNames?: Array<keyof T>);
+  methodCalled(methodName: keyof T, ...args: any[]): any;
+  whenCalled(methodName: keyof T): Promise<any>;
+  resetResolver(methodName: keyof T): void;
   reset(): void;
-  getCallCount(methodName: string): number;
-  getArgs(methodName: string): Array<any>;
-  setResultMapperFor(methodName: string, resultMapper: Function): void;
-  setResultFor(methodName: string, value: any): void;
+  getCallCount(methodName: keyof T): number;
+  getArgs(methodName: keyof T): Array<any>;
+  setResultMapperFor(methodName: keyof T, resultMapper: Function): void;
+  setResultFor(methodName: keyof T, value: any): void;
 }
diff --git a/chrome/test/data/webui/text_defaults_browsertest.js b/chrome/test/data/webui/text_defaults_browsertest.js
index 3a62b93..f8b095d 100644
--- a/chrome/test/data/webui/text_defaults_browsertest.js
+++ b/chrome/test/data/webui/text_defaults_browsertest.js
@@ -6,56 +6,35 @@
 
 /**
  * Test fixture for testing async methods of cr.js.
- * @constructor
- * @extends testing.Test
  */
-function TextDefaultsTest() {}
-
-TextDefaultsTest.prototype = {
-  __proto__: testing.Test.prototype,
-
+var TextDefaultsTest = class extends testing.Test {
   /**
-   * Must be on same domain as text_defaults.css (chrome://resources).
    * @override
    */
-  browsePreload: 'chrome://resources/html/assert.html',
+  get browsePreload() {
+    return 'chrome://test/test_loader.html?module=text_defaults_test.js';
+  }
 
   /** @override */
-  isAsync: true,
+  get isAsync() {
+    return true;
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return [
+      '//third_party/mocha/mocha.js',
+      '//chrome/test/data/webui/mocha_adapter.js',
+    ];
+  }
+
+  /** @override */
+  get webuiHost() {
+    return 'dummyurl';
+  }
 };
 
-/**
- * @param {string} html Text, possibly with HTML &entities; in it.
- * @return {string} The HTML decoded text.
- */
-function decodeHtmlEntities(html) {
-  var element = document.createElement('div');
-  element.innerHTML = html;
-  return element.textContent;
-}
 
-TEST_F('TextDefaultsTest', 'ScrapeStyles', function() {
-  var link = document.createElement('link');
-  link.rel = 'stylesheet';
-  link.href = 'chrome://resources/css/text_defaults.css';
-  link.onload = function() {
-    var fontFamily = link.sheet.rules[1].style['font-family'];
-    assertNotEquals('', fontFamily);
-    assertEquals(decodeHtmlEntities(fontFamily), fontFamily);
-    testDone();
-  };
-  document.body.appendChild(link);
-});
-
-TEST_F('TextDefaultsTest', 'ScrapeMDStyles', function() {
-  var link = document.createElement('link');
-  link.rel = 'stylesheet';
-  link.href = 'chrome://resources/css/text_defaults_md.css';
-  link.onload = function() {
-    var fontFamily = link.sheet.rules[2].style['font-family'];
-    assertNotEquals('', fontFamily);
-    assertEquals(decodeHtmlEntities(fontFamily), fontFamily);
-    testDone();
-  };
-  document.body.appendChild(link);
+TEST_F('TextDefaultsTest', 'All', function() {
+  mocha.run();
 });
diff --git a/chrome/test/data/webui/text_defaults_test.js b/chrome/test/data/webui/text_defaults_test.js
new file mode 100644
index 0000000..039cb9a
--- /dev/null
+++ b/chrome/test/data/webui/text_defaults_test.js
@@ -0,0 +1,41 @@
+// Copyright 2022 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.
+
+/**
+ * @param {string} html Text, possibly with HTML &entities; in it.
+ * @return {string} The HTML decoded text.
+ */
+function decodeHtmlEntities(html) {
+  const element = document.createElement('div');
+  element.innerHTML = html;
+  return element.textContent;
+}
+
+suite('TextDefaults', function() {
+  test('text_defaults.css', function(done) {
+    const link = document.createElement('link');
+    link.rel = 'stylesheet';
+    link.href = 'chrome://resources/css/text_defaults.css';
+    link.onload = function() {
+      const fontFamily = link.sheet.rules[1].style['font-family'];
+      assertNotEquals('', fontFamily);
+      assertEquals(decodeHtmlEntities(fontFamily), fontFamily);
+      done();
+    };
+    document.body.appendChild(link);
+  });
+
+  test('text_defaults_md.css', function(done) {
+    const link = document.createElement('link');
+    link.rel = 'stylesheet';
+    link.href = 'chrome://resources/css/text_defaults_md.css';
+    link.onload = function() {
+      const fontFamily = link.sheet.rules[2].style['font-family'];
+      assertNotEquals('', fontFamily);
+      assertEquals(decodeHtmlEntities(fontFamily), fontFamily);
+      done();
+    };
+    document.body.appendChild(link);
+  });
+});
diff --git a/chrome/test/media_router/README.md b/chrome/test/media_router/README.md
index 91e89fc..05c8ba6 100644
--- a/chrome/test/media_router/README.md
+++ b/chrome/test/media_router/README.md
@@ -7,24 +7,14 @@
 
 This directory contains the integration and end-to-end browser tests for Media
 Router.  The Media Router uses various Media Route Providers to connect to
-different types of displays.
-
-## Media Route Providers
-
-The tests are run with one of two component extensions:
-
-* The open source Media Router component extension in
-  chrome/browser/resources/media_router/extension includes a mock Media Route
-  Provider, `mr.TestProvider`.
-* The closed source Media Router external component extension includes the Cast
-  Media Route Provider.
+different types of receivers (sinks).
 
 ## Tests
 
 * `MediaRouterIntegrationBrowserTest`: Tests that Media Router behaves as
 specified by the Presentation API, and that its dialog is shown as expected
-using the test provider `mr.TestProvider`. Test cases that specifically test the
-functionalities of the Media Router dialog are in
+using the test provider `TestMediaRouteProvider`. Test cases that specifically
+test the functionalities of the Media Router dialog are in
 `media_router_integration_ui_browsertest.cc`.
 
 * `MediaRouterIntegrationIncognitoBrowserTest`: Same as
@@ -32,8 +22,8 @@
 incognito profile.
 
 * `MediaRouterE2EBrowserTest`: Tests Chromecast-specific functionality of Media
-Router using the Cast Media Route Provider.  Requires installing the Media
-Router external component extension and an actual Chromecast device.
+Router using the Cast Media Route Provider.  Requires an actual Chromecast
+device.
 
 * `MediaRouterIntegrationOneUABrowserTest`: Tests that the Presentation API can
 be used to start presentations using offscreen tabs, and that basic Presentation
diff --git a/chrome/test/media_router/media_router_e2e_browsertest.cc b/chrome/test/media_router/media_router_e2e_browsertest.cc
index 62fb1238c..da8cc7e 100644
--- a/chrome/test/media_router/media_router_e2e_browsertest.cc
+++ b/chrome/test/media_router/media_router_e2e_browsertest.cc
@@ -25,15 +25,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/origin.h"
 
-// Use the following command to run e2e browser tests:
-// ./out/Debug/browser_tests --user-data-dir=<empty user data dir>
-//   --extension-unpacked=<mr extension dir>
-//   --receiver=<chromecast device name>
-//   --enable-pixel-output-in-tests --run-manual
-//   --gtest_filter=MediaRouterE2EBrowserTest.<test case name>
-//   --enable-logging=stderr
-//   --ui-test-action-timeout=200000
-
 namespace {
 // URL to launch Castv2Player_Staging app on Chromecast
 const char kCastAppPresentationUrl[] =
diff --git a/chrome/test/media_router/media_router_e2e_browsertest.h b/chrome/test/media_router/media_router_e2e_browsertest.h
index e6827247..0f836d66 100644
--- a/chrome/test/media_router/media_router_e2e_browsertest.h
+++ b/chrome/test/media_router/media_router_e2e_browsertest.h
@@ -18,6 +18,17 @@
 class MediaRouter;
 class RouteRequestResult;
 
+// Tests Chromecast-specific functionality of Media Router using the Cast Media
+// Route Provider.  Requires an actual Chromecast device.
+//
+// Use the following command to run e2e browser tests:
+// ./out/Default/browser_tests --user-data-dir=<empty user data dir>
+//   --extension-unpacked=<mr extension dir>
+//   --receiver=<chromecast device name>
+//   --enable-pixel-output-in-tests --run-manual
+//   --gtest_filter=MediaRouterE2EBrowserTest.<test case name>
+//   --enable-logging=stderr
+//   --ui-test-action-timeout=200000
 class MediaRouterE2EBrowserTest : public MediaRouterIntegrationBrowserTest {
  public:
   MediaRouterE2EBrowserTest();
diff --git a/chrome/updater/persisted_data.cc b/chrome/updater/persisted_data.cc
index 7a0ce36..9e8273a 100644
--- a/chrome/updater/persisted_data.cc
+++ b/chrome/updater/persisted_data.cc
@@ -119,7 +119,8 @@
   if (!pref_service_)
     return false;
 
-  DictionaryPrefUpdate update(pref_service_, kPersistedDataPreference);
+  DictionaryPrefUpdateDeprecated update(pref_service_,
+                                        kPersistedDataPreference);
   base::Value* apps = update->FindDictKey("apps");
 
   return apps ? apps->RemoveKey(id) : false;
@@ -191,7 +192,8 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!pref_service_)
     return;
-  DictionaryPrefUpdate update(pref_service_, kPersistedDataPreference);
+  DictionaryPrefUpdateDeprecated update(pref_service_,
+                                        kPersistedDataPreference);
   GetOrCreateAppKey(id, update.Get())->SetStringKey(key, value);
 }
 
diff --git a/chrome/updater/prefs.cc b/chrome/updater/prefs.cc
index 9eec592..1d39808 100644
--- a/chrome/updater/prefs.cc
+++ b/chrome/updater/prefs.cc
@@ -61,7 +61,7 @@
   return prefs_->GetString(kPrefActiveVersion);
 }
 
-void UpdaterPrefsImpl::SetActiveVersion(std::string value) {
+void UpdaterPrefsImpl::SetActiveVersion(const std::string& value) {
   prefs_->SetString(kPrefActiveVersion, value);
 }
 
diff --git a/chrome/updater/prefs.h b/chrome/updater/prefs.h
index 2950677..ec71562 100644
--- a/chrome/updater/prefs.h
+++ b/chrome/updater/prefs.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_UPDATER_PREFS_H_
 #define CHROME_UPDATER_PREFS_H_
 
+#include <string>
+
 #include "base/memory/ref_counted.h"
 
 class PrefService;
@@ -44,7 +46,7 @@
   GlobalPrefs() = default;
 
   virtual std::string GetActiveVersion() const = 0;
-  virtual void SetActiveVersion(std::string value) = 0;
+  virtual void SetActiveVersion(const std::string& value) = 0;
   virtual bool GetSwapping() const = 0;
   virtual void SetSwapping(bool value) = 0;
   virtual bool GetMigratedLegacyUpdaters() const = 0;
diff --git a/chrome/updater/prefs_impl.h b/chrome/updater/prefs_impl.h
index ce6aa59..a23aba0 100644
--- a/chrome/updater/prefs_impl.h
+++ b/chrome/updater/prefs_impl.h
@@ -6,6 +6,7 @@
 #define CHROME_UPDATER_PREFS_IMPL_H_
 
 #include <memory>
+#include <string>
 
 #include "chrome/updater/prefs.h"
 
@@ -46,7 +47,7 @@
 
   // Overrides for GlobalPrefs
   std::string GetActiveVersion() const override;
-  void SetActiveVersion(std::string value) override;
+  void SetActiveVersion(const std::string& value) override;
   bool GetSwapping() const override;
   void SetSwapping(bool value) override;
   bool GetMigratedLegacyUpdaters() const override;
diff --git a/chrome/updater/test/integration_tests_win.cc b/chrome/updater/test/integration_tests_win.cc
index 54f4bb4..9a93e9ba 100644
--- a/chrome/updater/test/integration_tests_win.cc
+++ b/chrome/updater/test/integration_tests_win.cc
@@ -255,11 +255,14 @@
     }
   }
 
-  std::unique_ptr<TaskScheduler> task_scheduler =
-      TaskScheduler::CreateInstance();
-  const std::wstring task_name = GetTaskName(scope);
-  EXPECT_EQ(is_installed, task_scheduler->IsTaskRegistered(task_name.c_str()));
   if (is_installed) {
+    std::unique_ptr<TaskScheduler> task_scheduler =
+        TaskScheduler::CreateInstance();
+    const std::wstring task_name =
+        task_scheduler->FindFirstTaskName(GetTaskNamePrefix(scope));
+    EXPECT_TRUE(!task_name.empty());
+    EXPECT_TRUE(task_scheduler->IsTaskRegistered(task_name.c_str()));
+
     TaskScheduler::TaskInfo task_info;
     ASSERT_TRUE(task_scheduler->GetTaskInfo(task_name.c_str(), &task_info));
     ASSERT_EQ(task_info.exec_actions.size(), 1u);
@@ -353,7 +356,12 @@
 
   std::unique_ptr<TaskScheduler> task_scheduler =
       TaskScheduler::CreateInstance();
-  task_scheduler->DeleteTask(GetTaskName(scope).c_str());
+  const std::wstring task_name =
+      task_scheduler->FindFirstTaskName(GetTaskNamePrefix(scope));
+  if (!task_name.empty())
+    task_scheduler->DeleteTask(task_name.c_str());
+  EXPECT_TRUE(
+      task_scheduler->FindFirstTaskName(GetTaskNamePrefix(scope)).empty());
 
   absl::optional<base::FilePath> path = GetProductPath(scope);
   EXPECT_TRUE(path);
diff --git a/chrome/updater/util.cc b/chrome/updater/util.cc
index 2684a789..f425821 100644
--- a/chrome/updater/util.cc
+++ b/chrome/updater/util.cc
@@ -4,6 +4,10 @@
 
 #include "chrome/updater/util.h"
 
+#include <algorithm>
+#include <cctype>
+#include <string>
+
 #include "base/base_paths.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
@@ -11,7 +15,9 @@
 #include "base/logging.h"
 #include "base/notreached.h"
 #include "base/path_service.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/version.h"
 #include "build/build_config.h"
 #include "chrome/updater/constants.h"
@@ -232,4 +238,22 @@
 }
 
 #endif  // OS_LINUX
+
+#if defined(OS_WIN)
+
+std::wstring GetTaskNamePrefix(UpdaterScope scope) {
+  std::wstring task_name = GetTaskDisplayName(scope);
+  task_name.erase(std::remove_if(task_name.begin(), task_name.end(), isspace),
+                  task_name.end());
+  return task_name;
+}
+
+std::wstring GetTaskDisplayName(UpdaterScope scope) {
+  return base::StrCat({base::ASCIIToWide(PRODUCT_FULLNAME_STRING), L" Task ",
+                       scope == UpdaterScope::kSystem ? L"System " : L"User ",
+                       kUpdaterVersionUtf16});
+}
+
+#endif  // OS_WIN
+
 }  // namespace updater
diff --git a/chrome/updater/util.h b/chrome/updater/util.h
index 5f08c0e..f51e0b00 100644
--- a/chrome/updater/util.h
+++ b/chrome/updater/util.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_UPDATER_UTIL_H_
 #define CHROME_UPDATER_UTIL_H_
 
+#include <string>
+
 #include "base/files/file_path.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
@@ -151,6 +153,20 @@
                             int kPermissionsMask);
 #endif  // defined(OS_MAC)
 
+#if defined(OS_WIN)
+
+// Returns the versioned task name prefix in the following format:
+// "{ProductName}Task{System/User}{UpdaterVersion}".
+// For instance: "ChromiumUpdaterTaskSystem92.0.0.1".
+std::wstring GetTaskNamePrefix(UpdaterScope scope);
+
+// Returns the versioned task display name in the following format:
+// "{ProductName} Task {System/User} {UpdaterVersion}".
+// For instance: "ChromiumUpdater Task System 92.0.0.1".
+std::wstring GetTaskDisplayName(UpdaterScope scope);
+
+#endif  // OS_WIN
+
 }  // namespace updater
 
 #endif  // CHROME_UPDATER_UTIL_H_
diff --git a/chrome/updater/win/setup/setup_util.cc b/chrome/updater/win/setup/setup_util.cc
index 3173d9a2..a409606 100644
--- a/chrome/updater/win/setup/setup_util.cc
+++ b/chrome/updater/win/setup/setup_util.cc
@@ -36,23 +36,71 @@
 #include "chrome/updater/win/win_util.h"
 
 namespace updater {
+namespace {
+
+std::wstring GetTaskName(UpdaterScope scope) {
+  return TaskScheduler::CreateInstance()->FindFirstTaskName(
+      GetTaskNamePrefix(scope));
+}
+
+std::wstring CreateRandomTaskName(UpdaterScope scope) {
+  GUID random_guid = {0};
+  return SUCCEEDED(::CoCreateGuid(&random_guid))
+             ? base::StrCat({GetTaskNamePrefix(scope),
+                             base::win::WStringFromGUID(random_guid)})
+             : std::wstring();
+}
+
+}  // namespace
 
 bool RegisterWakeTask(const base::CommandLine& run_command,
                       UpdaterScope scope) {
   auto task_scheduler = TaskScheduler::CreateInstance();
-  if (!task_scheduler->RegisterTask(
-          scope, GetTaskName(scope).c_str(), GetTaskDisplayName(scope).c_str(),
-          run_command, TaskScheduler::TriggerType::TRIGGER_TYPE_HOURLY, true)) {
-    LOG(ERROR) << "RegisterWakeTask failed.";
+
+  std::wstring task_name = GetTaskName(scope);
+  if (!task_name.empty()) {
+    // Update the currently installed scheduled task.
+    if (task_scheduler->RegisterTask(
+            scope, task_name.c_str(), GetTaskDisplayName(scope).c_str(),
+            run_command, TaskScheduler::TriggerType::TRIGGER_TYPE_HOURLY,
+            true)) {
+      VLOG(1) << "RegisterWakeTask succeeded." << task_name;
+      return true;
+    } else {
+      task_scheduler->DeleteTask(task_name.c_str());
+    }
+  }
+
+  // Create a new task name and fall through to install that.
+  task_name = CreateRandomTaskName(scope);
+  if (task_name.empty()) {
+    LOG(ERROR) << "Unexpected empty task name.";
     return false;
   }
-  VLOG(1) << "RegisterWakeTask succeeded.";
-  return true;
+
+  DCHECK(!task_scheduler->IsTaskRegistered(task_name.c_str()));
+
+  if (task_scheduler->RegisterTask(
+          scope, task_name.c_str(), GetTaskDisplayName(scope).c_str(),
+          run_command, TaskScheduler::TriggerType::TRIGGER_TYPE_HOURLY, true)) {
+    VLOG(1) << "RegisterWakeTask succeeded: " << task_name;
+    return true;
+  }
+
+  LOG(ERROR) << "RegisterWakeTask failed: " << task_name;
+  return false;
 }
 
 void UnregisterWakeTask(UpdaterScope scope) {
   auto task_scheduler = TaskScheduler::CreateInstance();
-  task_scheduler->DeleteTask(GetTaskName(scope).c_str());
+
+  const std::wstring task_name = GetTaskName(scope);
+  if (task_name.empty()) {
+    LOG(ERROR) << "Empty task name during uninstall.";
+    return;
+  }
+
+  task_scheduler->DeleteTask(task_name.c_str());
 }
 
 std::vector<IID> GetSideBySideInterfaces() {
diff --git a/chrome/updater/win/signing/sign.py b/chrome/updater/win/signing/sign.py
index 75a6fd5..8c48c71a 100755
--- a/chrome/updater/win/signing/sign.py
+++ b/chrome/updater/win/signing/sign.py
@@ -94,7 +94,9 @@
         resed.UpdateResource('B7', 1033, resource, extracted_7z)
         resed.Commit()
         self._sign_item(out_metainstaller)
-        return self._add_tagging_cert(out_metainstaller)
+        return out_metainstaller
+        # TODO(crbug.com/1281938): Re-enable tagging.
+        # return self._add_tagging_cert(out_metainstaller)
 
 
 def main():
diff --git a/chrome/updater/win/task_scheduler.cc b/chrome/updater/win/task_scheduler.cc
index 20a4008..e85ef23 100644
--- a/chrome/updater/win/task_scheduler.cc
+++ b/chrome/updater/win/task_scheduler.cc
@@ -12,7 +12,9 @@
 
 #include <string>
 #include <utility>
+#include <vector>
 
+#include "base/check_op.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
@@ -20,6 +22,7 @@
 #include "base/notreached.h"
 #include "base/path_service.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
 #include "base/win/scoped_bstr.h"
@@ -262,6 +265,21 @@
     return true;
   }
 
+  std::wstring FindFirstTaskName(const std::wstring& task_prefix) override {
+    DCHECK(!task_prefix.empty());
+
+    std::vector<std::wstring> task_names;
+    if (!GetTaskNameList(&task_names))
+      return std::wstring();
+
+    for (const std::wstring& task_name : task_names) {
+      if (base::StartsWith(task_name, task_prefix))
+        return task_name;
+    }
+
+    return std::wstring();
+  }
+
   bool GetTaskInfo(const wchar_t* task_name, TaskInfo* info) override {
     DCHECK(task_name);
     DCHECK(info);
@@ -359,8 +377,6 @@
                     bool hidden) override {
     DCHECK(task_name);
     DCHECK(task_description);
-    if (!DeleteTask(task_name))
-      return false;
 
     // Create the task definition object to create the task.
     Microsoft::WRL::ComPtr<ITaskDefinition> task;
@@ -618,7 +634,8 @@
 
     DCHECK(task_folder_);
     hr = task_folder_->RegisterTaskDefinition(
-        base::win::ScopedBstr(task_name).Get(), task.Get(), TASK_CREATE,
+        base::win::ScopedBstr(task_name).Get(), task.Get(),
+        TASK_CREATE_OR_UPDATE,
         *user.AsInput(),  // Not really input, but API expect non-const.
         base::win::ScopedVariant::kEmptyVariant,
         scope == UpdaterScope::kSystem ? TASK_LOGON_SERVICE_ACCOUNT
diff --git a/chrome/updater/win/task_scheduler.h b/chrome/updater/win/task_scheduler.h
index 685ea800..c54b1750 100644
--- a/chrome/updater/win/task_scheduler.h
+++ b/chrome/updater/win/task_scheduler.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <string>
 #include <vector>
 
 #include "base/files/file_path.h"
@@ -115,6 +116,10 @@
   // List all currently registered scheduled tasks.
   virtual bool GetTaskNameList(std::vector<std::wstring>* task_names) = 0;
 
+  // Returns the first instance of a scheduled task installed with the given
+  // `task_prefix`.
+  virtual std::wstring FindFirstTaskName(const std::wstring& task_prefix) = 0;
+
   // Return detailed information about a task. Return true if no errors were
   // encountered. On error, the struct is left unmodified.
   virtual bool GetTaskInfo(const wchar_t* task_name, TaskInfo* info) = 0;
diff --git a/chrome/updater/win/task_scheduler_unittest.cc b/chrome/updater/win/task_scheduler_unittest.cc
index 9f9b2e28..79e2d798 100644
--- a/chrome/updater/win/task_scheduler_unittest.cc
+++ b/chrome/updater/win/task_scheduler_unittest.cc
@@ -38,6 +38,9 @@
 const wchar_t kTaskName1[] = L"Chrome Updater Test task 1 (delete me)";
 const wchar_t kTaskName2[] = L"Chrome Updater Test task 2 (delete me)";
 
+const wchar_t kPrefixTaskName1[] = L"Chrome Updater Test task 1";
+const wchar_t kPrefixTaskName2[] = L"Chrome Updater Test task 2";
+
 // Optional descriptions for the tasks above.
 const wchar_t kTaskDescription1[] =
     L"Task 1 used only for Chrome Updater unit testing.";
@@ -240,6 +243,30 @@
   EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName2));
 }
 
+TEST_F(TaskSchedulerTests, FindFirstTaskName) {
+  base::FilePath executable_path;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path));
+  base::CommandLine command_line(
+      executable_path.Append(kTestProcessExecutableName));
+
+  EXPECT_TRUE(task_scheduler_->RegisterTask(
+      GetTestScope(), kTaskName1, kTaskDescription1, command_line,
+      TaskScheduler::TRIGGER_TYPE_HOURLY, false));
+  EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName1));
+  EXPECT_TRUE(task_scheduler_->RegisterTask(
+      GetTestScope(), kTaskName2, kTaskDescription2, command_line,
+      TaskScheduler::TRIGGER_TYPE_HOURLY, false));
+  EXPECT_TRUE(task_scheduler_->IsTaskRegistered(kTaskName2));
+
+  EXPECT_STREQ(kTaskName1,
+               task_scheduler_->FindFirstTaskName(kPrefixTaskName1).c_str());
+  EXPECT_STREQ(kTaskName2,
+               task_scheduler_->FindFirstTaskName(kPrefixTaskName2).c_str());
+
+  EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName1));
+  EXPECT_TRUE(task_scheduler_->DeleteTask(kTaskName2));
+}
+
 TEST_F(TaskSchedulerTests, GetTasksIncludesHidden) {
   base::FilePath executable_path;
   ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &executable_path));
diff --git a/chrome/updater/win/win_util.cc b/chrome/updater/win/win_util.cc
index 34fe2fa..32cbd07 100644
--- a/chrome/updater/win/win_util.cc
+++ b/chrome/updater/win/win_util.cc
@@ -580,19 +580,6 @@
        L" ", kUpdaterVersionUtf16});
 }
 
-std::wstring GetTaskName(UpdaterScope scope) {
-  std::wstring task_name = GetTaskDisplayName(scope);
-  task_name.erase(std::remove_if(task_name.begin(), task_name.end(), isspace),
-                  task_name.end());
-  return task_name;
-}
-
-std::wstring GetTaskDisplayName(UpdaterScope scope) {
-  return base::StrCat({base::ASCIIToWide(PRODUCT_FULLNAME_STRING), L" Task ",
-                       scope == UpdaterScope::kSystem ? L" System " : L" User ",
-                       kUpdaterVersionUtf16});
-}
-
 REGSAM Wow6432(REGSAM access) {
   return KEY_WOW64_32KEY | access;
 }
diff --git a/chrome/updater/win/win_util.h b/chrome/updater/win/win_util.h
index 97ee823..d56f229e9 100644
--- a/chrome/updater/win/win_util.h
+++ b/chrome/updater/win/win_util.h
@@ -172,16 +172,6 @@
 // For instance: "ChromiumUpdater InternalService 92.0.0.1".
 std::wstring GetServiceDisplayName(bool is_internal_service);
 
-// Returns the versioned task name in the following format:
-// "{ProductName}Task{System/User}{UpdaterVersion}".
-// For instance: "ChromiumUpdaterTaskSystem92.0.0.1".
-std::wstring GetTaskName(UpdaterScope scope);
-
-// Returns the versioned task display name in the following format:
-// "{ProductName} Task {System/User} {UpdaterVersion}".
-// For instance: "ChromiumUpdater Task System 92.0.0.1".
-std::wstring GetTaskDisplayName(UpdaterScope scope);
-
 // Returns `KEY_WOW64_32KEY | access`. All registry access under the Updater key
 // should use `Wow6432(access)` as the `REGSAM`.
 REGSAM Wow6432(REGSAM access);
diff --git a/chromecast/cast_core/BUILD.gn b/chromecast/cast_core/BUILD.gn
index d39a64b..fb98901e3 100644
--- a/chromecast/cast_core/BUILD.gn
+++ b/chromecast/cast_core/BUILD.gn
@@ -28,6 +28,7 @@
   testonly = true
 
   deps = [
+    "//chromecast/cast_core/grpc:unit_tests",
     "//chromecast/cast_core/runtime/browser:unit_tests",
     "//chromecast/cast_core/runtime/browser/url_rewrite:unit_tests",
     "//chromecast/cast_core/runtime/common:unit_tests",
diff --git a/chromecast/cast_core/grpc/BUILD.gn b/chromecast/cast_core/grpc/BUILD.gn
new file mode 100644
index 0000000..1f22491
--- /dev/null
+++ b/chromecast/cast_core/grpc/BUILD.gn
@@ -0,0 +1,133 @@
+# 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("//chromecast/chromecast.gni")
+import("//third_party/grpc/grpc_library.gni")
+
+cast_source_set("calls") {
+  sources = [
+    "grpc_call.h",
+    "grpc_client_reactor.h",
+    "grpc_server_streaming_call.h",
+    "grpc_stub.h",
+    "grpc_unary_call.h",
+  ]
+
+  public_deps = [
+    ":grpc_call_options",
+    ":grpc_status_or",
+    "//base",
+    "//third_party/grpc:grpc++",
+  ]
+}
+
+cast_source_set("handlers") {
+  sources = [
+    "cancellable_reactor.h",
+    "grpc_handler.cc",
+    "grpc_handler.h",
+    "grpc_server.cc",
+    "grpc_server.h",
+    "grpc_server_reactor.h",
+    "grpc_server_streaming_handler.h",
+    "grpc_unary_handler.h",
+    "server_reactor_tracker.cc",
+    "server_reactor_tracker.h",
+    "trackable_reactor.h",
+  ]
+
+  public_deps = [
+    ":grpc_call_options",
+    ":grpc_status_or",
+    "//base",
+    "//third_party/abseil-cpp:absl",
+    "//third_party/grpc:grpc++",
+  ]
+}
+
+cast_source_set("grpc_call_options") {
+  sources = [
+    "grpc_call_options.cc",
+    "grpc_call_options.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//third_party/grpc:grpc++",
+  ]
+}
+
+cast_source_set("grpc_status_or") {
+  sources = [
+    "grpc_status_or.cc",
+    "grpc_status_or.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//third_party/abseil-cpp:absl",
+    "//third_party/grpc:grpc++",
+  ]
+}
+
+cast_source_set("status_matchers") {
+  testonly = true
+
+  sources = [
+    "status_matchers.cc",
+    "status_matchers.h",
+  ]
+
+  deps = [ "//testing/gmock" ]
+}
+
+grpc_library("test_service_proto") {
+  sources = [ "test_service.proto" ]
+}
+
+cast_source_set("test_service_handlers") {
+  testonly = true
+
+  sources = [
+    "test_service_handlers.cc",
+    "test_service_handlers.h",
+  ]
+
+  deps = [
+    ":handlers",
+    ":test_service_proto",
+  ]
+}
+
+cast_source_set("test_service_stubs") {
+  sources = [ "test_service_stubs.h" ]
+
+  deps = [
+    ":calls",
+    ":test_service_proto",
+  ]
+}
+
+cast_source_set("unit_tests") {
+  testonly = true
+
+  sources = [
+    "grpc_server_streaming_test.cc",
+    "grpc_status_or_test.cc",
+    "grpc_unary_test.cc",
+  ]
+
+  deps = [
+    ":calls",
+    ":handlers",
+    ":status_matchers",
+    ":test_service_handlers",
+    ":test_service_proto",
+    ":test_service_stubs",
+    "//base",
+    "//base/test:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/chromecast/cast_core/grpc/DEPS b/chromecast/cast_core/grpc/DEPS
new file mode 100644
index 0000000..58f6496
--- /dev/null
+++ b/chromecast/cast_core/grpc/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+third_party/cast_core/public/src/proto/common",
+  "+third_party/grpc/src/include",
+]
diff --git a/chromecast/cast_core/grpc/cancellable_reactor.h b/chromecast/cast_core/grpc/cancellable_reactor.h
new file mode 100644
index 0000000..3b4bf5b
--- /dev/null
+++ b/chromecast/cast_core/grpc/cancellable_reactor.h
@@ -0,0 +1,69 @@
+// 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 CHROMECAST_CAST_CORE_GRPC_CANCELLABLE_REACTOR_H_
+#define CHROMECAST_CAST_CORE_GRPC_CANCELLABLE_REACTOR_H_
+
+#include <grpcpp/grpcpp.h>
+
+#include <atomic>
+
+#include "base/logging.h"
+
+namespace grpc {
+class ByteBuffer;
+}
+
+namespace cast {
+namespace utils {
+
+// A facade around reactor implementation that allows to finish the reactor in
+// case it was cancelled. No actions will happen if reactor was already
+// finished.
+template <typename TReactor>
+class CancellableReactor : public TReactor {
+ public:
+  using TReactor::name;
+
+  template <typename... TArgs>
+  explicit CancellableReactor(TArgs&&... args)
+      : TReactor(std::forward<TArgs&&>(args)...) {}
+
+ protected:
+  // Implements GrpcServerReactor APIs.
+  void FinishWriting(const grpc::ByteBuffer* buffer,
+                     grpc::Status status) override {
+    bool expected = false;
+    if (!finished_.compare_exchange_strong(expected, true)) {
+      LOG(WARNING) << "Reactor was already cancelled: " << name();
+      return;
+    }
+
+    TReactor::FinishWriting(buffer, std::move(status));
+  }
+
+  // Implements grpc::ServerGenericBidiReactor APIs.
+  // OnCancel is called on pending reactors while the gRPC server is
+  // shutting down. At this point, we don't expect users to call the
+  // reactors. If they were not finished properly, we must finish them
+  // forcefully to unblock server shutdown process.
+  void OnCancel() override {
+    if (finished_.load()) {
+      LOG(INFO) << "Reactor cancelled in finished state: " << name();
+      return;
+    }
+
+    LOG(WARNING) << "Pending reactor got cancelled: " << name();
+    FinishWriting(nullptr, grpc::Status(grpc::StatusCode::ABORTED,
+                                        "Reactor was cancelled"));
+  }
+
+ private:
+  std::atomic<bool> finished_ = {false};
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_CANCELLABLE_REACTOR_H_
diff --git a/chromecast/cast_core/grpc/grpc_call.h b/chromecast/cast_core/grpc/grpc_call.h
new file mode 100644
index 0000000..12369ce
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_call.h
@@ -0,0 +1,71 @@
+// 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 CHROMECAST_CAST_CORE_GRPC_GRPC_CALL_H_
+#define CHROMECAST_CAST_CORE_GRPC_GRPC_CALL_H_
+
+#include <grpcpp/grpcpp.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "chromecast/cast_core/grpc/grpc_call_options.h"
+
+namespace cast {
+namespace utils {
+
+// The base class for all gRPC call implementations. Provides some common
+// functionality used to instantiate the call (ie request, stub interface etc).
+template <typename TGrpcStub, typename TRequest>
+class GrpcCall {
+ public:
+  using SyncInterface = typename TGrpcStub::SyncInterface;
+  using AsyncInterface = typename TGrpcStub::AsyncInterface;
+  using Request = TRequest;
+
+  explicit GrpcCall(SyncInterface* stub) : GrpcCall(stub, Request()) {}
+
+  GrpcCall(SyncInterface* stub, Request request)
+      : stub_(stub), request_(std::move(request)) {
+    DCHECK(stub_);
+    async_ = stub_->async();
+  }
+
+  virtual ~GrpcCall() = default;
+
+  // Returns the reference to the request.
+  Request& request() & { return request_; }
+
+  // Returns the move reference to the request.
+  Request&& request() && { return std::move(request_); }
+
+  // Sets a deadline for gRPC call.
+  void SetDeadline(const base::TimeDelta& deadline) {
+    options_.SetDeadline(deadline);
+  }
+
+ protected:
+  SyncInterface* sync() && {
+    DCHECK(stub_);
+    return std::exchange(stub_, nullptr);
+  }
+
+  AsyncInterface* async() && {
+    DCHECK(async_);
+    return std::exchange(async_, nullptr);
+  }
+
+  GrpcCallOptions&& options() && { return std::move(options_); }
+
+ private:
+  SyncInterface* stub_;
+  AsyncInterface* async_;
+  Request request_;
+  GrpcCallOptions options_;
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_GRPC_CALL_H_
diff --git a/chromecast/cast_core/grpc/grpc_call_options.cc b/chromecast/cast_core/grpc/grpc_call_options.cc
new file mode 100644
index 0000000..5f69718
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_call_options.cc
@@ -0,0 +1,55 @@
+// 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 "chromecast/cast_core/grpc/grpc_call_options.h"
+
+#include <grpcpp/grpcpp.h>
+
+#include "base/logging.h"
+#include "base/time/time.h"
+
+namespace cast {
+namespace utils {
+
+namespace {
+
+// Default gRPC call deadline.
+static const auto kDefaultCallDeadline = base::Seconds(60);
+
+}  // namespace
+
+GrpcCallOptions::GrpcCallOptions() : deadline_(kDefaultCallDeadline) {}
+
+GrpcCallOptions::~GrpcCallOptions() = default;
+
+void GrpcCallOptions::SetDeadline(base::TimeDelta deadline) {
+  DCHECK_GE(deadline.InMicroseconds(), 0);
+  deadline_ = std::move(deadline);
+}
+
+void GrpcCallOptions::ApplyOptionsToContext(
+    grpc::ClientContext* context) const {
+  if (!deadline_.is_zero() && !deadline_.is_inf()) {
+    context->set_deadline(ToGprTimespec(deadline_));
+    DVLOG(1) << "Call deadline is set to " << deadline_;
+  }
+}
+
+// static
+gpr_timespec GrpcCallOptions::ToGprTimespec(const base::TimeDelta& delta) {
+  DCHECK_GE(delta.InMicroseconds(), 0);
+  if (delta.is_inf() || delta.is_zero()) {
+    return gpr_inf_future(GPR_CLOCK_MONOTONIC);
+  }
+
+  timespec ts = delta.ToTimeSpec();
+  gpr_timespec span;
+  span.tv_sec = ts.tv_sec;
+  span.tv_nsec = static_cast<int32_t>(ts.tv_nsec);
+  span.clock_type = GPR_TIMESPAN;
+  return gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), span);
+}
+
+}  // namespace utils
+}  // namespace cast
diff --git a/chromecast/cast_core/grpc/grpc_call_options.h b/chromecast/cast_core/grpc/grpc_call_options.h
new file mode 100644
index 0000000..13dd56b
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_call_options.h
@@ -0,0 +1,42 @@
+// 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 CHROMECAST_CAST_CORE_GRPC_GRPC_CALL_OPTIONS_H_
+#define CHROMECAST_CAST_CORE_GRPC_GRPC_CALL_OPTIONS_H_
+
+#include <grpcpp/grpcpp.h>
+
+#include "base/time/time.h"
+
+namespace grpc {
+class ClientContext;
+}
+
+namespace cast {
+namespace utils {
+
+// Various options that control gRPC ClientContext behavior.
+class GrpcCallOptions {
+ public:
+  GrpcCallOptions();
+  ~GrpcCallOptions();
+
+  // Sets the client call deadline.
+  void SetDeadline(base::TimeDelta deadline);
+
+  // Applies the options to a give grpc client |context|.
+  void ApplyOptionsToContext(grpc::ClientContext* context) const;
+
+  // Converts a TimeDelta to gRPC's gpr_timespec.
+  static gpr_timespec ToGprTimespec(const base::TimeDelta& delta);
+
+ private:
+  // gRPC read request deadline.
+  base::TimeDelta deadline_;
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_GRPC_CALL_OPTIONS_H_
diff --git a/chromecast/cast_core/grpc/grpc_client_reactor.h b/chromecast/cast_core/grpc/grpc_client_reactor.h
new file mode 100644
index 0000000..eab27e9c
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_client_reactor.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 CHROMECAST_CAST_CORE_GRPC_GRPC_CLIENT_REACTOR_H_
+#define CHROMECAST_CAST_CORE_GRPC_GRPC_CLIENT_REACTOR_H_
+
+#include <grpcpp/grpcpp.h>
+
+#include <memory>
+
+#include "chromecast/cast_core/grpc/grpc_call_options.h"
+
+namespace cast {
+namespace utils {
+
+// A base class for all gRPC client reactor implementations.
+//
+// The gRPC Callback stack is built on the Reactor concept which serves as the
+// observer/callback mechanism for notifications on received responses. The
+// instance of the Reactor always belongs to the gRPC framework. It is created
+// in GrpcCall implementation and deleted in Reactor::OnDone callback from gRPC.
+//
+// |TRequest| is the gRPC API request type.
+// |TUnderlyingReactor| is the type of the gRPC framework reactor that is added
+// as a base class for GrpcClientReactor and used in implementation.
+template <typename TRequest, typename TUnderlyingReactor>
+class GrpcClientReactor : public TUnderlyingReactor {
+ public:
+  GrpcClientReactor() = default;
+  virtual ~GrpcClientReactor() = default;
+
+  // Copy and move are deleted.
+  GrpcClientReactor(const GrpcClientReactor&) = delete;
+  GrpcClientReactor(GrpcClientReactor&&) = delete;
+  GrpcClientReactor& operator=(const GrpcClientReactor&) = delete;
+  GrpcClientReactor& operator=(GrpcClientReactor&&) = delete;
+
+  // Returns the gRPC client context.
+  grpc::ClientContext* context() { return &context_; }
+
+  // Returns the original request.
+  const TRequest* request() const { return &request_; }
+
+  // Initiates the gRPC client call.
+  virtual void Start() { options_.ApplyOptionsToContext(&context_); }
+
+ protected:
+  explicit GrpcClientReactor(TRequest request, GrpcCallOptions options)
+      : request_(std::move(request)), options_(std::move(options)) {}
+
+ private:
+  grpc::ClientContext context_;
+  TRequest request_;
+  GrpcCallOptions options_;
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_GRPC_CLIENT_REACTOR_H_
diff --git a/chromecast/cast_core/grpc/grpc_handler.cc b/chromecast/cast_core/grpc/grpc_handler.cc
new file mode 100644
index 0000000..ab3ef7c6
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_handler.cc
@@ -0,0 +1,20 @@
+// 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 "chromecast/cast_core/grpc/grpc_handler.h"
+
+#include "base/check.h"
+
+namespace cast {
+namespace utils {
+
+GrpcHandler::GrpcHandler(ServerReactorTracker* server_reactor_tracker)
+    : server_reactor_tracker_(server_reactor_tracker) {
+  DCHECK(server_reactor_tracker_);
+}
+
+GrpcHandler::~GrpcHandler() = default;
+
+}  // namespace utils
+}  // namespace cast
diff --git a/chromecast/cast_core/grpc/grpc_handler.h b/chromecast/cast_core/grpc/grpc_handler.h
new file mode 100644
index 0000000..90dccd1
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_handler.h
@@ -0,0 +1,41 @@
+// 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 CHROMECAST_CAST_CORE_GRPC_GRPC_HANDLER_H_
+#define CHROMECAST_CAST_CORE_GRPC_GRPC_HANDLER_H_
+
+#include <grpcpp/generic/async_generic_service.h>
+#include <grpcpp/grpcpp.h>
+
+#include "chromecast/cast_core/grpc/server_reactor_tracker.h"
+
+namespace cast {
+namespace utils {
+
+// A base class for all gRPC server reactor implementations.
+//
+// This class provides a mechanism to track currently active reactors. The data
+// is used during server shutdown to notify about pending non-finished reactors.
+class GrpcHandler {
+ public:
+  explicit GrpcHandler(ServerReactorTracker* server_reactor_tracker);
+  virtual ~GrpcHandler();
+
+  // Returns a tracker of all the reactors of the implemented handler.
+  ServerReactorTracker* server_reactor_tracker() {
+    return server_reactor_tracker_;
+  }
+
+  // Creates a reactor used to process specific gRPC API.
+  virtual grpc::ServerGenericBidiReactor* CreateReactor(
+      grpc::CallbackServerContext* context) = 0;
+
+ private:
+  ServerReactorTracker* server_reactor_tracker_;
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_GRPC_HANDLER_H_
diff --git a/chromecast/cast_core/grpc/grpc_server.cc b/chromecast/cast_core/grpc/grpc_server.cc
new file mode 100644
index 0000000..c5222c3
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_server.cc
@@ -0,0 +1,122 @@
+// 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 "chromecast/cast_core/grpc/grpc_server.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "base/time/time.h"
+#include "chromecast/cast_core/grpc/grpc_call_options.h"
+
+namespace cast {
+namespace utils {
+
+namespace {
+
+static const base::TimeDelta kDefaultServerStopTimeout =
+    base::Milliseconds(100);
+
+// Stops gRPC server.
+static void StopGrpcServer(
+    std::unique_ptr<grpc::Server> server,
+    std::unique_ptr<ServerReactorTracker> server_reactor_tracker,
+    const base::TimeDelta& timeout,
+    base::OnceClosure server_stopped_callback) {
+  LOG(INFO) << "Shutting down gRPC server with "
+            << server_reactor_tracker->active_reactor_count()
+            << " active reactors: " << *server_reactor_tracker;
+
+  // The gRPC Reactors are owned by the gRPC framework and are 'pending'
+  // unless Reactor::Finish or similar (StartWriteAndFinish) API is called.
+  // This Shutdown call makes sure all the finished reactors are deleted via
+  // Reactor::OnDone API. As the timeout is reached, all pending reactors are
+  // cancelled via Reactor::OnCancel API. Hence, after Shutdow, all pending
+  // reactors can be treated as cancelled and manually destroyed.
+  auto gpr_timeout = GrpcCallOptions::ToGprTimespec(timeout);
+  server->Shutdown(gpr_timeout);
+
+  // As mentioned above, all the pending reactors are now cancelled and must
+  // be destroyed by the ServerReactorTracker.
+  DCHECK_EQ(server_reactor_tracker->active_reactor_count(), 0UL)
+      << "Not all reactors were cancelled: " << *server_reactor_tracker;
+  LOG(INFO) << "All active reactors are finished";
+
+  // Finish server shutdown.
+  server->Wait();
+  server.reset();
+  LOG(INFO) << "gRPC server is shut down";
+
+  std::move(server_stopped_callback).Run();
+}
+
+}  // namespace
+
+GrpcServer::GrpcServer()
+    : server_reactor_tracker_(std::make_unique<ServerReactorTracker>()) {}
+
+GrpcServer::~GrpcServer() {
+  DCHECK(!server_) << "gRPC server must be explicitly stopped";
+}
+
+void GrpcServer::Start(base::StringPiece endpoint) {
+  DCHECK(!server_) << "Server is already running";
+  DCHECK(server_reactor_tracker_) << "Server was alreadys shutdown";
+
+  server_ = grpc::ServerBuilder()
+                .AddListeningPort(std::string(endpoint),
+                                  grpc::InsecureServerCredentials())
+                .RegisterCallbackGenericService(this)
+                .BuildAndStart();
+  DCHECK(server_) << "Failed to start server";
+}
+
+void GrpcServer::Stop() {
+  if (!server_) {
+    LOG(WARNING) << "Grpc server was already stopped";
+    return;
+  }
+
+  StopGrpcServer(std::move(server_), std::move(server_reactor_tracker_),
+                 kDefaultServerStopTimeout, base::DoNothing());
+}
+
+void GrpcServer::Stop(const base::TimeDelta& timeout,
+                      base::OnceClosure server_stopped_callback) {
+  if (!server_) {
+    LOG(WARNING) << "Grpc server was already stopped";
+    std::move(server_stopped_callback).Run();
+    return;
+  }
+
+  base::ThreadPool::PostTask(
+      FROM_HERE, {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
+      base::BindOnce(&StopGrpcServer, std::move(server_),
+                     std::move(server_reactor_tracker_), timeout,
+                     std::move(server_stopped_callback)));
+}
+
+void GrpcServer::StopForTesting(const base::TimeDelta& timeout) {
+  base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+  Stop(timeout, run_loop.QuitClosure());
+  run_loop.Run();
+}
+
+grpc::ServerGenericBidiReactor* GrpcServer::CreateReactor(
+    grpc::GenericCallbackServerContext* ctx) {
+  auto iter = registered_handlers_.find(ctx->method());
+  if (iter != registered_handlers_.end()) {
+    DVLOG(1) << "Found a reactor for " << ctx->method();
+    return iter->second->CreateReactor(ctx);
+  }
+  LOG(WARNING) << "No reactor was specified for " << ctx->method()
+               << " - falling back to a default unimplemented reactor";
+  return grpc::CallbackGenericService::CreateReactor(ctx);
+}
+
+}  // namespace utils
+}  // namespace cast
diff --git a/chromecast/cast_core/grpc/grpc_server.h b/chromecast/cast_core/grpc/grpc_server.h
new file mode 100644
index 0000000..2e1bb49
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_server.h
@@ -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.
+
+#ifndef CHROMECAST_CAST_CORE_GRPC_GRPC_SERVER_H_
+#define CHROMECAST_CAST_CORE_GRPC_GRPC_SERVER_H_
+
+#include <grpcpp/grpcpp.h>
+
+#include "base/callback_forward.h"
+#include "base/containers/flat_map.h"
+#include "base/logging.h"
+#include "base/strings/string_piece_forward.h"
+#include "base/time/time.h"
+#include "chromecast/cast_core/grpc/grpc_handler.h"
+#include "chromecast/cast_core/grpc/server_reactor_tracker.h"
+
+namespace cast {
+namespace utils {
+
+// Generic gRPC server that allows seamless registration of API handlers by
+// specifying the API with GrpcHandler-based classes. The GrpcServer implements
+// grpc::CallbackGenericService and is registered with the ServerBuilder as
+// such. The implications are following:
+//  - All gRPC requests arrive to server.
+//  - The APIs are distiguished by the full RPC name (ie /<class>/method).
+//  - The user must register the full rpc name (as a string) with specific
+//  handler.
+//
+// An example usage that creates a server that combines below two services in
+// one:
+// PROTO:
+//   service Foo {
+//     rpc DoOneThing(OneThingRequest) returns (OneThingResponse);
+//   }
+//
+//   service Bar {
+//     rpc DoAnotherThing(AnotherThingRequest) returns (AnotherThingResponse);
+//   }
+//
+// C++:
+//   // Create a CastCoreService.Register handler.
+//   constexpr const char DoOneThingMethod[] = "DoOneThing";
+//   using DoOneThingHandler =
+//       GrpcUnaryHandler<Foo, OneThingRequest,
+//                        OneThingResponse, DoOneThingMethod>;
+//
+//   constexpr const char DoAnotherThingMethod[] = "DoAnotherThing";
+//   using DoAnotherThingHandler =
+//       GrpcUnaryHandler<Bar, AnotherThingRequest,
+//                        AnotherThingResponse, DoAnotherThingMethod>;
+//
+//   server.SetHandler<DoOneThingHandler>(
+//       base::BindOnce([](OneThingRequest request,
+//                             DoOneThing::Reactor* reactor) {
+//         reactor->Write(OneThingResponse());
+//       }));
+//   server.SetHandler<DoAnotherThingHandler>(
+//       base::BindOnce([](AnotherThingRequest request,
+//                             DoAnotherThing::Reactor* reactor) {
+//         reactor->Write(AnotherThingResponse());
+//       }));
+//
+//   // Start the server.
+//   server.Start("[::]:12345");
+//
+//   // Stop the server.
+//   server.Stop();
+//
+class GrpcServer : public grpc::CallbackGenericService {
+ public:
+  GrpcServer();
+  ~GrpcServer() override;
+
+  // Sets the request callback for an RPC defined by |Handler| type.
+  template <typename THandler>
+  void SetHandler(typename THandler::OnRequestCallback on_request_callback) {
+    // The full rpc name is /<fully-qualified-service-type>/method, ie
+    // /cast.core.CastCoreService/RegisterRuntime.
+    DCHECK(!registered_handlers_.contains(THandler::rpc_name()))
+        << "Duplicate handler: " << THandler::rpc_name();
+    registered_handlers_.emplace(
+        THandler::rpc_name(),
+        std::make_unique<THandler>(std::move(on_request_callback),
+                                   server_reactor_tracker_.get()));
+    DVLOG(1) << "Request handler is set for " << THandler::rpc_name();
+  }
+
+  // Starts the gRPC server.
+  void Start(base::StringPiece endpoint);
+
+  // Stops the gRPC server synchronously. May block indefinitely if there's a
+  // non-finished pending reactor created by the gRPC framework.
+  void Stop();
+
+  // Stops the gRPC server and calls the callback. The process will crash in
+  // case the |timeout| is reached as such case clearly points to a bug in
+  // reactor handling.
+  void Stop(const base::TimeDelta& timeout,
+            base::OnceClosure server_stopped_callback);
+
+  // Stops the gRPC server in a separate task runner to avoid blocking the main
+  // test thread, and keep latter's run loop spinning.  The process will crash
+  // in case the |timeout| is reached as such case clearly points to a bug in
+  // reactor handling.
+  void StopForTesting(const base::TimeDelta& timeout);
+
+ private:
+  // Implements grpc::CallbackGenericService APIs.
+  // Creates a reactor for a given rpc method from the |ctx|. If a handler is
+  // registered for that method, it's reactor will be created. Otherwise a
+  // default (unimplemented) reactor will be returned.
+  grpc::ServerGenericBidiReactor* CreateReactor(
+      grpc::GenericCallbackServerContext* ctx) override;
+
+  base::flat_map<std::string, std::unique_ptr<utils::GrpcHandler>>
+      registered_handlers_;
+  std::unique_ptr<grpc::Server> server_;
+  std::unique_ptr<ServerReactorTracker> server_reactor_tracker_;
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_GRPC_SERVER_H_
diff --git a/chromecast/cast_core/grpc/grpc_server_reactor.h b/chromecast/cast_core/grpc/grpc_server_reactor.h
new file mode 100644
index 0000000..a1d544c
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_server_reactor.h
@@ -0,0 +1,144 @@
+// 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 CHROMECAST_CAST_CORE_GRPC_GRPC_SERVER_REACTOR_H_
+#define CHROMECAST_CAST_CORE_GRPC_GRPC_SERVER_REACTOR_H_
+
+#include <grpcpp/generic/async_generic_service.h>
+#include <grpcpp/grpcpp.h>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "chromecast/cast_core/grpc/grpc_status_or.h"
+#include "chromecast/cast_core/grpc/server_reactor_tracker.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace cast {
+namespace utils {
+
+// A base class for gRPC server reactors.
+template <typename TRequest, typename TResponse>
+class GrpcServerReactor : public grpc::ServerGenericBidiReactor {
+ public:
+  GrpcServerReactor(base::StringPiece name,
+                    grpc::CallbackServerContext* context)
+      : name_(name), context_(context) {}
+  ~GrpcServerReactor() override = default;
+
+  // Copy and move are deleted.
+  GrpcServerReactor(const GrpcServerReactor&) = delete;
+  GrpcServerReactor(GrpcServerReactor&&) = delete;
+  GrpcServerReactor& operator=(const GrpcServerReactor&) = delete;
+  GrpcServerReactor& operator=(GrpcServerReactor&&) = delete;
+
+  // Set of overloaded methods to write responses or status to the clients.
+  // Writes a status. No writes can be done after this call.
+  void Write(grpc::Status status) { FinishWriting(nullptr, status); }
+
+  void Write(grpc::StatusCode status_code) { Write(status_code, ""); }
+
+  void Write(grpc::StatusCode status_code, const std::string& error_message) {
+    Write(grpc::Status(status_code, error_message));
+  }
+
+  // Writes a defined response.
+  void Write(TResponse response = TResponse()) {
+    DCHECK(!response_byte_buffer_) << "Writing is already in progress";
+    DVLOG(1) << "Writing response: " << name();
+    response_byte_buffer_.emplace();
+    Serialize(response, *response_byte_buffer_);
+    // The state machine expects the implementer to write the buffer which will
+    // trigger OnWriteDone and reset it back to allow more writes.
+    WriteResponse(&*response_byte_buffer_);
+  }
+
+  // Returns reactor RPC name.
+  base::StringPiece name() const { return name_; }
+
+ protected:
+  // Starts reading a request.
+  void ReadRequest() {
+    DCHECK(!request_byte_buffer_)
+        << "Reading is already in progress: " << name();
+    DVLOG(1) << "Reading request: " << name();
+    request_byte_buffer_.emplace();
+    StartRead(&*request_byte_buffer_);
+  }
+
+  // The following APIs must be implemented by a certain reactor, and allow
+  // proper state tracking while hiding the generic request\response processing.
+
+  // Called when response is written on the wire. An error is set if writes are
+  // terminated.
+  virtual void WriteResponse(const grpc::ByteBuffer* buffer) = 0;
+
+  // Called to actually write the status on the wire.
+  virtual void FinishWriting(const grpc::ByteBuffer* buffer,
+                             grpc::Status status) = 0;
+
+  // Called to actually write the response serialized into a buffer on the wire.
+  virtual void OnResponseDone(grpc::Status status) = 0;
+
+  // Called when request was read and deserialized. An error is set if reads are
+  // terminated.
+  virtual void OnRequestDone(GrpcStatusOr<TRequest> request) = 0;
+
+  // Implements grpc::ServerGenericBidiReactor APIs.
+  void OnReadDone(bool ok) override {
+    DVLOG(1) << "Reads done: " << name() << ", ok=" << ok;
+    if (!ok) {
+      DVLOG(1) << "Reads failed: " << name();
+      OnRequestDone(grpc::Status(grpc::StatusCode::ABORTED, "Reads failed"));
+      return;
+    }
+
+    auto request = Deserialize<TRequest>(*request_byte_buffer_);
+    request_byte_buffer_.reset();
+    OnRequestDone(std::move(request));
+  }
+
+  void OnWriteDone(bool ok) override {
+    DVLOG(1) << "Writes done: " << name() << ", ok=" << ok;
+    response_byte_buffer_.reset();
+    if (!ok) {
+      DVLOG(1) << "Writes failed: " << name();
+      OnResponseDone(grpc::Status(grpc::StatusCode::ABORTED, "Writes failed"));
+      return;
+    }
+
+    OnResponseDone(grpc::Status::OK);
+  }
+
+  void OnDone() override {
+    DVLOG(1) << "Reactor done: " << name();
+    delete this;
+  }
+
+ private:
+  template <typename T>
+  void Serialize(T t, grpc::ByteBuffer& buffer) {
+    bool own;
+    auto status = grpc::SerializationTraits<T>::Serialize(t, &buffer, &own);
+    DCHECK(status.ok()) << "Failed to serialize";
+  }
+
+  template <typename T>
+  T Deserialize(grpc::ByteBuffer& buffer) {
+    T t;
+    auto status = grpc::SerializationTraits<T>::Deserialize(&buffer, &t);
+    DCHECK(status.ok()) << "Failed to serialize";
+    return t;
+  }
+
+  const std::string name_;
+  grpc::CallbackServerContext* context_;
+
+  absl::optional<grpc::ByteBuffer> request_byte_buffer_;
+  absl::optional<grpc::ByteBuffer> response_byte_buffer_;
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_GRPC_SERVER_REACTOR_H_
diff --git a/chromecast/cast_core/grpc/grpc_server_streaming_call.h b/chromecast/cast_core/grpc/grpc_server_streaming_call.h
new file mode 100644
index 0000000..56d8730
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_server_streaming_call.h
@@ -0,0 +1,130 @@
+// 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 CHROMECAST_CAST_CORE_GRPC_GRPC_SERVER_STREAMING_CALL_H_
+#define CHROMECAST_CAST_CORE_GRPC_GRPC_SERVER_STREAMING_CALL_H_
+
+#include <grpcpp/grpcpp.h>
+#include <grpcpp/support/client_callback.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "chromecast/cast_core/grpc/grpc_call.h"
+#include "chromecast/cast_core/grpc/grpc_client_reactor.h"
+#include "chromecast/cast_core/grpc/grpc_status_or.h"
+
+namespace cast {
+namespace utils {
+
+// Typedef for the server streaming method generated by gRPC compiler.
+template <typename TAsyncInterface, typename TRequest, typename TResponse>
+using GrpcServerStreamingMethod =
+    void (TAsyncInterface::*)(grpc::ClientContext*,
+                              const TRequest*,
+                              grpc::ClientReadReactor<TResponse>*);
+
+// A GrpcCall implementation for unary gRPC calls specialized by the
+// |AsyncMethodPtr| function pointer.
+//  TGrpcStub - gRPC service stub type.
+//  TRequest - gRPC request type for a method in the stub.
+//  TResponse - gRPC response type for a method in the stub.
+//  AsyncMethodPtr - pointer to a method in the stub that handles a streaming
+//  call.
+template <typename TGrpcStub,
+          typename TRequest,
+          typename TResponse,
+          GrpcServerStreamingMethod<typename TGrpcStub::AsyncInterface,
+                                    TRequest,
+                                    TResponse> AsyncMethodPtr>
+class GrpcServerStreamingCall : public GrpcCall<TGrpcStub, TRequest> {
+ public:
+  using Base = GrpcCall<TGrpcStub, TRequest>;
+  using Base::async;
+  using Base::GrpcCall;
+  using Base::request;
+  using Base::sync;
+  using typename Base::AsyncInterface;
+  using typename Base::Request;
+
+  using Response = TResponse;
+  using ResponseCallback =
+      base::RepeatingCallback<void(GrpcStatusOr<Response>, bool /*done*/)>;
+
+  void InvokeAsync(ResponseCallback response_callback) && {
+    // gRPC doesn't support setting a deadline for individual streaming
+    // requests\responses. Hence, the zero timeout is set to allow for
+    // inifinitely long streaming connections.
+    Base::SetDeadline(base::Seconds(0));
+    auto reactor =
+        new Reactor(std::move(*this).async(), std::move(*this).request(),
+                    std::move(*this).options(), std::move(response_callback));
+    reactor->Start();
+  }
+
+ private:
+  using ReactorBase =
+      GrpcClientReactor<Request, grpc::ClientReadReactor<Response>>;
+
+  class Reactor final : public ReactorBase {
+   public:
+    using ReactorBase::context;
+    using ReactorBase::request;
+
+    Reactor(AsyncInterface* async_stub,
+            Request request,
+            GrpcCallOptions options,
+            ResponseCallback response_callback)
+        : ReactorBase(std::move(request), std::move(options)),
+          async_stub_call_(
+              base::BindOnce(AsyncMethodPtr, base::Unretained(async_stub))),
+          response_callback_(std::move(response_callback)) {}
+
+    void Start() override {
+      ReactorBase::Start();
+      std::move(async_stub_call_).Run(context(), request(), this);
+      grpc::ClientReadReactor<Response>::StartRead(&response_);
+      grpc::ClientReadReactor<Response>::StartCall();
+    }
+
+   private:
+    // Implements grpc::ClientReadReactor APIs.
+    void OnReadDone(bool ok) override {
+      DVLOG(1) << "Reads done: ok=" << ok;
+      if (!ok) {
+        return;
+      }
+
+      response_callback_.Run(std::move(response_), false);
+      Reactor::StartRead(&response_);
+    }
+
+    // The method is always called on completion of all operations associated
+    // with this call, and deletes itself on exit.
+    void OnDone(const grpc::Status& status) override {
+      DVLOG(1) << "Request done: " << GrpcStatusToString(status);
+      if (status.ok()) {
+        response_callback_.Run(Response(), true);
+      } else {
+        response_callback_.Run(status, true);
+      }
+      delete this;
+    }
+
+    using AsyncStubCall =
+        base::OnceCallback<void(grpc::ClientContext*,
+                                const Request*,
+                                grpc::ClientReadReactor<Response>*)>;
+
+    AsyncStubCall async_stub_call_;
+    ResponseCallback response_callback_;
+    Response response_;
+  };
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_GRPC_SERVER_STREAMING_CALL_H_
diff --git a/chromecast/cast_core/grpc/grpc_server_streaming_handler.h b/chromecast/cast_core/grpc/grpc_server_streaming_handler.h
new file mode 100644
index 0000000..d4d08965
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_server_streaming_handler.h
@@ -0,0 +1,137 @@
+// 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 CHROMECAST_CAST_CORE_GRPC_GRPC_SERVER_STREAMING_HANDLER_H_
+#define CHROMECAST_CAST_CORE_GRPC_GRPC_SERVER_STREAMING_HANDLER_H_
+
+#include <grpcpp/grpcpp.h>
+
+#include "base/callback.h"
+#include "base/strings/stringprintf.h"
+#include "chromecast/cast_core/grpc/cancellable_reactor.h"
+#include "chromecast/cast_core/grpc/grpc_server.h"
+#include "chromecast/cast_core/grpc/grpc_server_reactor.h"
+#include "chromecast/cast_core/grpc/grpc_status_or.h"
+#include "chromecast/cast_core/grpc/server_reactor_tracker.h"
+#include "chromecast/cast_core/grpc/trackable_reactor.h"
+
+namespace cast {
+namespace utils {
+
+// A generic handler for server streaming unary, ie request followed by multiple
+// responses from server, gRPC APIs. Can only be used with rpc that have the
+// following signature:
+//       rpc Foo(Request) returns (stream Response)
+//
+// - TService is the gRPC service type.
+// - TRequest is the service request type.
+// - TResponse is the service response type.
+// - MethodName is the rpc method as a string.
+//
+// This class is not thread-safe. Appropriate means have to be added by the
+// users to guarantee thread-safety (ie task runners, mutexes etc).
+template <typename TService,
+          typename TRequest,
+          typename TResponse,
+          const char* MethodName>
+class GrpcServerStreamingHandler : public GrpcHandler {
+ public:
+  using ReactorBase = GrpcServerReactor<TRequest, TResponse>;
+
+  class Reactor : public ReactorBase {
+   public:
+    using ReactorBase::name;
+    using ReactorBase::Write;
+
+    using OnRequestCallback = base::RepeatingCallback<void(TRequest, Reactor*)>;
+    using WritesAvailableCallback =
+        base::RepeatingCallback<void(GrpcStatusOr<Reactor*>)>;
+
+    template <typename... Args>
+    explicit Reactor(OnRequestCallback on_request_callback, Args&&... args)
+        : ReactorBase(std::forward<Args>(args)...),
+          on_request_callback_(std::move(on_request_callback)) {
+      ReadRequest();
+    }
+
+    void SetWritesAvailableCallback(
+        WritesAvailableCallback writes_available_callback) {
+      writes_available_callback_ = std::move(writes_available_callback);
+    }
+
+    void Write(TResponse response,
+               WritesAvailableCallback writes_available_callback) {
+      writes_available_callback_ = std::move(writes_available_callback);
+      ReactorBase::Write(std::move(response));
+    }
+
+   protected:
+    using ReactorBase::Finish;
+    using ReactorBase::ReadRequest;
+    using ReactorBase::StartRead;
+    using ReactorBase::StartWrite;
+
+    // Implements GrpcServerReactor APIs.
+    void WriteResponse(const grpc::ByteBuffer* buffer) override {
+      DCHECK(buffer);
+      StartWrite(buffer, grpc::WriteOptions());
+    }
+
+    void FinishWriting(const grpc::ByteBuffer* buffer,
+                       grpc::Status status) override {
+      DVLOG(1) << "Reactor finished: " << name()
+               << ", status=" << GrpcStatusToString(status);
+      DCHECK(!buffer)
+          << "Server streaming call can only be finished with a status";
+      Finish(status);
+    }
+
+    void OnResponseDone(grpc::Status status) override {
+      DCHECK(writes_available_callback_)
+          << "Writes available callback must be set";
+      writes_available_callback_.Run(status.ok() ? GrpcStatusOr<Reactor*>(this)
+                                                 : status);
+    }
+
+    void OnRequestDone(GrpcStatusOr<TRequest> request) override {
+      if (!request.ok()) {
+        Finish(request.status());
+        return;
+      }
+
+      on_request_callback_.Run(std::move(*request), this);
+    }
+
+    OnRequestCallback on_request_callback_;
+    WritesAvailableCallback writes_available_callback_;
+  };
+
+  using OnRequestCallback = typename Reactor::OnRequestCallback;
+  using WritesAvailableCallback = typename Reactor::WritesAvailableCallback;
+
+  GrpcServerStreamingHandler(OnRequestCallback on_request_callback,
+                             ServerReactorTracker* server_reactor_tracker)
+      : GrpcHandler(server_reactor_tracker),
+        on_request_callback_(std::move(on_request_callback)) {}
+
+  static std::string rpc_name() {
+    return base::StringPrintf("/%s/%s", TService::service_full_name(),
+                              MethodName);
+  }
+
+ private:
+  // Implements GrpcHandler APIs.
+  grpc::ServerGenericBidiReactor* CreateReactor(
+      grpc::CallbackServerContext* context) override {
+    return new CancellableReactor<TrackableReactor<Reactor>>(
+        server_reactor_tracker(), on_request_callback_, rpc_name(), context);
+  }
+
+  OnRequestCallback on_request_callback_;
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_GRPC_SERVER_STREAMING_HANDLER_H_
diff --git a/chromecast/cast_core/grpc/grpc_server_streaming_test.cc b/chromecast/cast_core/grpc/grpc_server_streaming_test.cc
new file mode 100644
index 0000000..e4baf415
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_server_streaming_test.cc
@@ -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.
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/guid.h"
+#include "base/rand_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task/bind_post_task.h"
+#include "base/task/thread_pool.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time/time.h"
+#include "chromecast/cast_core/grpc/grpc_server.h"
+#include "chromecast/cast_core/grpc/status_matchers.h"
+#include "chromecast/cast_core/grpc/test_service.grpc.pb.h"
+#include "chromecast/cast_core/grpc/test_service_handlers.h"
+#include "chromecast/cast_core/grpc/test_service_stubs.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cast {
+namespace utils {
+namespace {
+
+using ::cast::test::StatusIs;
+
+const auto kEventTimeout = base::Seconds(1);
+const auto kServerStopTimeout = base::Seconds(1);
+
+class GrpcServerStreamingTest : public ::testing::Test {
+ protected:
+  GrpcServerStreamingTest()
+      : grpc_client_task_runner_(
+            base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})) {
+    CHECK(temp_dir_.CreateUniqueTempDir());
+    endpoint_ = "unix:" +
+                temp_dir_.GetPath()
+                    .AppendASCII("cast-uds-" + base::GenerateGUID().substr(24))
+                    .value();
+  }
+
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  scoped_refptr<base::SequencedTaskRunner> grpc_client_task_runner_;
+  base::ScopedTempDir temp_dir_;
+  std::string endpoint_;
+};
+
+TEST_F(GrpcServerStreamingTest, ServerStreamingCallSucceeds) {
+  const int kMaxResponseCount = base::RandInt(10, 300);
+  int server_response_count = 0;
+  auto writes_available_callback = base::BindPostTask(
+      grpc_client_task_runner_,
+      base::BindLambdaForTesting(
+          [&](GrpcStatusOr<
+              ServerStreamingServiceHandler::StreamingCall::Reactor*> reactor) {
+            CU_CHECK_OK(reactor);
+            if (server_response_count < kMaxResponseCount) {
+              TestResponse response;
+              response.set_bar(
+                  base::StringPrintf("test_bar%d", ++server_response_count));
+              reactor.value()->Write(std::move(response));
+            } else {
+              LOG(INFO) << "Writing finished";
+              reactor.value()->Write(grpc::StatusCode::OK);
+            }
+          }));
+  auto call_handler = base::BindPostTask(
+      grpc_client_task_runner_,
+      base::BindLambdaForTesting(
+          [&](TestRequest request,
+              ServerStreamingServiceHandler::StreamingCall::Reactor* reactor) {
+            EXPECT_EQ(request.foo(), "test_foo");
+
+            reactor->SetWritesAvailableCallback(
+                std::move(writes_available_callback));
+
+            TestResponse response;
+            response.set_bar(
+                base::StringPrintf("test_bar%d", ++server_response_count));
+            reactor->Write(std::move(response));
+          }));
+
+  GrpcServer server;
+  server.SetHandler<ServerStreamingServiceHandler::StreamingCall>(
+      std::move(call_handler));
+  server.Start(endpoint_);
+
+  ServerStreamingServiceStub stub(endpoint_);
+  auto call = stub.CreateCall<ServerStreamingServiceStub::StreamingCall>();
+  call.request().set_foo("test_foo");
+  int call_count = 0;
+  base::WaitableEvent response_received_event;
+  std::move(call).InvokeAsync(base::BindPostTask(
+      grpc_client_task_runner_,
+      base::BindLambdaForTesting(
+          [&](GrpcStatusOr<TestResponse> response, bool done) {
+            CU_CHECK_OK(response);
+            if (done) {
+              response_received_event.Signal();
+            } else {
+              EXPECT_EQ(response->bar(),
+                        base::StringPrintf("test_bar%d", ++call_count));
+            }
+          })));
+  ASSERT_TRUE(response_received_event.TimedWait(kEventTimeout));
+  ASSERT_EQ(call_count, kMaxResponseCount);
+
+  server.StopForTesting(kServerStopTimeout);
+}
+
+TEST_F(GrpcServerStreamingTest, ServerStreamingCallFailsRightAway) {
+  GrpcServer server;
+  server.SetHandler<ServerStreamingServiceHandler::StreamingCall>(
+      base::BindPostTask(
+          grpc_client_task_runner_,
+          base::BindLambdaForTesting(
+              [&](TestRequest request,
+                  ServerStreamingServiceHandler::StreamingCall::Reactor*
+                      reactor) {
+                EXPECT_EQ(request.foo(), "test_foo");
+                reactor->Write(grpc::StatusCode::NOT_FOUND, "not found");
+              })));
+  server.Start(endpoint_);
+
+  ServerStreamingServiceStub stub(endpoint_);
+  auto call = stub.CreateCall<ServerStreamingServiceStub::StreamingCall>();
+  call.request().set_foo("test_foo");
+  base::WaitableEvent response_received_event;
+  std::move(call).InvokeAsync(base::BindPostTask(
+      grpc_client_task_runner_,
+      base::BindLambdaForTesting(
+          [&](GrpcStatusOr<TestResponse> response, bool done) {
+            CHECK(done);
+            ASSERT_THAT(response.status(),
+                        StatusIs(grpc::StatusCode::NOT_FOUND, "not found"));
+            response_received_event.Signal();
+          })));
+  {
+    base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync_primitives;
+    ASSERT_TRUE(response_received_event.TimedWait(kEventTimeout));
+  }
+
+  server.StopForTesting(kServerStopTimeout);
+}
+
+TEST_F(GrpcServerStreamingTest, ServerStreamingCallCancelledIfServerIsStopped) {
+  GrpcServer server;
+  base::WaitableEvent server_stopped_event;
+  server.SetHandler<ServerStreamingServiceHandler::StreamingCall>(
+      base::BindPostTask(
+          grpc_client_task_runner_,
+          base::BindLambdaForTesting(
+              [&](TestRequest request,
+                  ServerStreamingServiceHandler::StreamingCall::Reactor*
+                      reactor) {
+                // Stop the server to trigger call cancellation.
+                server.Stop(base::Milliseconds(100),
+                            base::BindLambdaForTesting(
+                                [&]() { server_stopped_event.Signal(); }));
+              })));
+  server.Start(endpoint_);
+
+  ServerStreamingServiceStub stub(endpoint_);
+  auto call = stub.CreateCall<ServerStreamingServiceStub::StreamingCall>();
+  call.request().set_foo("test_foo");
+  base::WaitableEvent response_received_event;
+  std::move(call).InvokeAsync(base::BindPostTask(
+      grpc_client_task_runner_,
+      base::BindLambdaForTesting(
+          [&](GrpcStatusOr<TestResponse> response, bool done) {
+            ASSERT_THAT(response, StatusIs(grpc::StatusCode::UNAVAILABLE));
+            response_received_event.Signal();
+          })));
+  {
+    base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync_primitives;
+    ASSERT_TRUE(server_stopped_event.TimedWait(kEventTimeout));
+    ASSERT_TRUE(response_received_event.TimedWait(kEventTimeout));
+  }
+}
+
+}  // namespace
+}  // namespace utils
+}  // namespace cast
diff --git a/chromecast/cast_core/grpc/grpc_status_or.cc b/chromecast/cast_core/grpc/grpc_status_or.cc
new file mode 100644
index 0000000..76a87c47
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_status_or.cc
@@ -0,0 +1,66 @@
+// 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 "chromecast/cast_core/grpc/grpc_status_or.h"
+
+#include "base/strings/stringprintf.h"
+
+namespace cast {
+namespace utils {
+
+namespace {
+
+// Converts gRPC StatusCode to string.
+static std::string GrpcStatusCodeToString(grpc::StatusCode code) {
+  switch (code) {
+    case grpc::StatusCode::OK:
+      return "OK";
+    case grpc::StatusCode::CANCELLED:
+      return "CANCELLED";
+    case grpc::StatusCode::UNKNOWN:
+      return "UNKNOWN";
+    case grpc::StatusCode::INVALID_ARGUMENT:
+      return "INVALID_ARGUMENT";
+    case grpc::StatusCode::DEADLINE_EXCEEDED:
+      return "DEADLINE_EXCEEDED";
+    case grpc::StatusCode::NOT_FOUND:
+      return "NOT_FOUND";
+    case grpc::StatusCode::ALREADY_EXISTS:
+      return "ALREADY_EXISTS";
+    case grpc::StatusCode::PERMISSION_DENIED:
+      return "PERMISSION_DENIED";
+    case grpc::StatusCode::UNAUTHENTICATED:
+      return "UNAUTHENTICATED";
+    case grpc::StatusCode::RESOURCE_EXHAUSTED:
+      return "RESOURCE_EXHAUSTED";
+    case grpc::StatusCode::FAILED_PRECONDITION:
+      return "FAILED_PRECONDITION";
+    case grpc::StatusCode::ABORTED:
+      return "ABORTED";
+    case grpc::StatusCode::OUT_OF_RANGE:
+      return "OUT_OF_RANGE";
+    case grpc::StatusCode::UNIMPLEMENTED:
+      return "UNIMPLEMENTED";
+    case grpc::StatusCode::INTERNAL:
+      return "INTERNAL";
+    case grpc::StatusCode::UNAVAILABLE:
+      return "UNAVAILABLE";
+    case grpc::StatusCode::DATA_LOSS:
+      return "DATA_LOSS";
+    default:
+      return "";
+  }
+}
+
+}  // namespace
+
+// static
+std::string GrpcStatusToString(const grpc::Status& status) {
+  return base::StringPrintf("[status=%s: %s]",
+                            GrpcStatusCodeToString(status.error_code()).c_str(),
+                            status.error_message().c_str());
+}
+
+}  // namespace utils
+}  // namespace cast
diff --git a/chromecast/cast_core/grpc/grpc_status_or.h b/chromecast/cast_core/grpc/grpc_status_or.h
new file mode 100644
index 0000000..7833856
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_status_or.h
@@ -0,0 +1,127 @@
+// 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 CHROMECAST_CAST_CORE_GRPC_GRPC_STATUS_OR_H_
+#define CHROMECAST_CAST_CORE_GRPC_GRPC_STATUS_OR_H_
+
+#include <grpcpp/support/status.h>
+
+#include <string>
+
+#include "base/check.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace cast {
+namespace utils {
+
+// Converts grpc::Status to a string message.
+std::string GrpcStatusToString(const grpc::Status& status);
+
+// Holds a value of type T with grpc::Status::OK or an error grpc::Status.
+template <typename T>
+class GrpcStatusOr {
+ public:
+  GrpcStatusOr() : status_(grpc::StatusCode::UNKNOWN, "") {}
+
+  // Constructs GrpcStatusOr from an error |status_code|.
+  GrpcStatusOr(grpc::StatusCode status_code)  // NOLINT
+      : GrpcStatusOr(status_code, "") {}
+
+  // Constructs GrpcStatusOr from an error |status_code| and |error_message|.
+  GrpcStatusOr(grpc::StatusCode status_code, const std::string& error_message)
+      : status_(status_code, error_message) {
+    DCHECK(!status_.ok());
+  }
+
+  // Constructs GrpcStatusOr from an error |status|.
+  GrpcStatusOr(grpc::Status status)  // NOLINT
+      : status_(status) {
+    DCHECK(!status_.ok());
+  }
+
+  // Constructs GrpcStatusOr from the |data|. Status code is set to OK.
+  GrpcStatusOr(const T& data)  // NOLINT
+      : status_(grpc::Status::OK), data_(data) {}
+
+  // Constructs GrpcStatusOr from the |data|. Status code is set to OK.
+  GrpcStatusOr(T&& data)  // NOLINT
+      : status_(grpc::Status::OK), data_(std::move(data)) {}
+
+  // Constructs GrpcStatusOr from the |data| of type U that is possible to
+  // convert to type |T|. For example, std::unique_ptr<MockClass> converted to
+  // std::unique_ptr<Class>. Status code is set to OK.
+  template <typename U,
+            typename std::enable_if<std::is_constructible<T, U>::value,
+                                    U>::type* = nullptr>
+  GrpcStatusOr(U&& data)  // NOLINT
+      : status_(grpc::Status::OK), data_(std::forward<U>(data)) {}
+
+  GrpcStatusOr(GrpcStatusOr&&) = default;
+  GrpcStatusOr& operator=(GrpcStatusOr&&) = default;
+  GrpcStatusOr(const GrpcStatusOr&) = default;
+  GrpcStatusOr& operator=(const GrpcStatusOr&) = default;
+  ~GrpcStatusOr() = default;
+
+  // Returns if status is OK.
+  bool ok() const { return status_.ok(); }
+
+  // Returns gRPC status.
+  const grpc::Status& status() const { return status_; }
+
+  // Returns the pointer to the stored data. Checks if status is not OK.
+  T* operator->() {
+    DCHECK(status_.ok());
+    return &*data_;
+  }
+
+  // Returns the const pointer to the stored data. Checks if status is not OK.
+  const T* operator->() const {
+    DCHECK(status_.ok());
+    return &*data_;
+  }
+
+  // Returns the reference to the stored data. Checks if status is not OK.
+  T& operator*() & {
+    DCHECK(status_.ok());
+    return *data_;
+  }
+
+  // Returns the const reference to the stored data. Checks if status is not OK.
+  const T& operator*() const& {
+    DCHECK(status_.ok());
+    return *data_;
+  }
+
+  // Returns the lvalue-ref to the underlying value. Checks if status is not OK.
+  T& value() & {
+    DCHECK(status_.ok());
+    return data_.value();
+  }
+
+  // Returns the rvalue-ref to the underlying value. To trigger this accessor,
+  // the status object needs to be moved first: std::move(status_).value().
+  // Checks if status is not OK.
+  T&& value() && {
+    DCHECK(status_.ok());
+    status_ = grpc::Status(grpc::StatusCode::UNKNOWN, "");
+    return std::move(data_).value();
+  }
+
+  // Sets the data.
+  void emplace(T&& data) {
+    status_ = grpc::Status::OK;
+    data_.emplace(std::move(data));
+  }
+
+  std::string ToString() const { return GrpcStatusToString(status()); }
+
+ private:
+  grpc::Status status_;
+  absl::optional<T> data_;
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_GRPC_STATUS_OR_H_
diff --git a/chromecast/cast_core/grpc/grpc_status_or_test.cc b/chromecast/cast_core/grpc/grpc_status_or_test.cc
new file mode 100644
index 0000000..e1f6993
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_status_or_test.cc
@@ -0,0 +1,86 @@
+// 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 "chromecast/cast_core/grpc/grpc_status_or.h"
+
+#include "chromecast/cast_core/grpc/status_matchers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cast {
+namespace utils {
+namespace {
+
+using ::cast::test::StatusIs;
+
+static_assert(test::internal::has_status_api<GrpcStatusOr<int>>::value,
+              "GrpcStatusOr::status() API must exist");
+static_assert(!test::internal::has_status_api<grpc::Status>::value,
+              "grps::Status::status() API must not exist");
+static_assert(!test::internal::has_code_api<grpc::Status>::value,
+              "grpc::Status uses error_* APIs");
+static_assert(test::internal::has_error_code_api<grpc::Status>::value,
+              "grpc::Status uses error_* APIs");
+
+TEST(GrpcStatusOrTest, DefaultConstructor) {
+  GrpcStatusOr<int> status_or;
+  EXPECT_THAT(status_or, StatusIs(grpc::StatusCode::UNKNOWN));
+}
+
+TEST(GrpcStatusOrTest, ConstructorWithValue) {
+  GrpcStatusOr<int> status_or(1);
+  CU_EXPECT_OK(status_or);
+  EXPECT_EQ(*status_or, 1);
+}
+
+TEST(GrpcStatusOrTest, ConstructorStatusCode) {
+  GrpcStatusOr<int> status_or(grpc::StatusCode::ABORTED);
+  EXPECT_THAT(status_or, StatusIs(grpc::StatusCode::ABORTED));
+}
+
+TEST(GrpcStatusOrTest, ConstructorStatusCodeAndErrorMessage) {
+  GrpcStatusOr<int> status_or(grpc::StatusCode::NOT_FOUND, "error");
+  EXPECT_THAT(status_or, StatusIs(grpc::StatusCode::NOT_FOUND, "error"));
+}
+
+TEST(GrpcStatusOrTest, MoveOperator) {
+  GrpcStatusOr<int> status_or(1);
+  CU_EXPECT_OK(status_or);
+  int value = std::move(status_or).value();
+  EXPECT_THAT(status_or, StatusIs(grpc::StatusCode::UNKNOWN));
+  EXPECT_EQ(value, 1);
+}
+
+TEST(GrpcStatusOrTest, Emplace) {
+  GrpcStatusOr<int> status_or;
+  status_or.emplace(1);
+  CU_EXPECT_OK(status_or);
+  EXPECT_EQ(*status_or, 1);
+}
+
+TEST(GrpcStatusOrTest, Accessor) {
+  GrpcStatusOr<std::string> status_or("12345");
+  EXPECT_EQ(status_or->size(), 5U);
+  EXPECT_EQ(*status_or, "12345");
+  *status_or = "567";
+  EXPECT_EQ(*status_or, "567");
+  status_or->append("89");
+  EXPECT_EQ(*status_or, "56789");
+  status_or.value() = "0";
+  EXPECT_EQ(*status_or, "0");
+}
+
+TEST(GrpcStatusOrTest, StreamOperator) {
+  GrpcStatusOr<int> status_or(1);
+  EXPECT_EQ(status_or.ToString(), "[status=OK: ]");
+}
+
+TEST(GrpcStatusOrTest, StreamOperatorWithStatus) {
+  GrpcStatusOr<int> status_or(grpc::StatusCode::CANCELLED, "method cancelled");
+  EXPECT_EQ(status_or.ToString(), "[status=CANCELLED: method cancelled]");
+}
+
+}  // namespace
+}  // namespace utils
+}  // namespace cast
diff --git a/chromecast/cast_core/grpc/grpc_stub.h b/chromecast/cast_core/grpc/grpc_stub.h
new file mode 100644
index 0000000..019fede
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_stub.h
@@ -0,0 +1,74 @@
+// 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 CHROMECAST_CAST_CORE_GRPC_GRPC_STUB_H_
+#define CHROMECAST_CAST_CORE_GRPC_GRPC_STUB_H_
+
+#include <grpcpp/grpcpp.h>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "chromecast/cast_core/grpc/grpc_server_streaming_call.h"
+#include "chromecast/cast_core/grpc/grpc_unary_call.h"
+
+namespace cast {
+namespace utils {
+
+// A gRPC stub definition with a copy/move enabled constructors and assignment
+// operators.
+template <typename TService>
+class GrpcStub {
+ public:
+  using SyncInterface = typename TService::StubInterface;
+  using AsyncInterface = typename TService::StubInterface::async_interface;
+
+  // Constructs a service stub on an |endpoint|. The is a fast call as gRPC
+  // creates actual resources for the channel in the background thread.
+  explicit GrpcStub(base::StringPiece endpoint)
+      : GrpcStub(grpc::CreateChannel(std::string(endpoint),
+                                     grpc::InsecureChannelCredentials())) {}
+
+  // Constructs a service stub with an existing |channel|. The is a fast call
+  // that shares an existing channel with a new stub.
+  explicit GrpcStub(std::shared_ptr<grpc::Channel> channel)
+      : channel_(std::move(channel)), stub_(TService::NewStub(channel_)) {
+    DCHECK(channel_);
+  }
+
+  // Copy constructor that reuses the |channel_|.
+  GrpcStub(const GrpcStub& rhs) : GrpcStub(rhs.channel_) {}
+
+  // Assignment operator that reuses the |channel_|.
+  GrpcStub& operator=(const GrpcStub& rhs) {
+    DCHECK(rhs.channel_);
+    channel_ = rhs.channel_;
+    stub_ = TService::NewStub(rhs.channel_);
+    return *this;
+  }
+
+  // Default specification of move ctor's - placeholder for future expansion.
+  GrpcStub(GrpcStub&& rhs) = default;
+  GrpcStub& operator=(GrpcStub&& rhs) = default;
+
+  virtual ~GrpcStub() = default;
+
+  template <typename TGrpcCall>
+  TGrpcCall CreateCall() {
+    return TGrpcCall(stub_.get());
+  }
+
+  template <typename TGrpcCall>
+  TGrpcCall CreateCall(typename TGrpcCall::Request request) {
+    return TGrpcCall(stub_.get(), std::move(request));
+  }
+
+ private:
+  std::shared_ptr<grpc::Channel> channel_;
+  std::unique_ptr<typename TService::StubInterface> stub_;
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_GRPC_STUB_H_
diff --git a/chromecast/cast_core/grpc/grpc_unary_call.h b/chromecast/cast_core/grpc/grpc_unary_call.h
new file mode 100644
index 0000000..2f4f5732
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_unary_call.h
@@ -0,0 +1,146 @@
+// 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 CHROMECAST_CAST_CORE_GRPC_GRPC_UNARY_CALL_H_
+#define CHROMECAST_CAST_CORE_GRPC_GRPC_UNARY_CALL_H_
+
+#include <grpcpp/grpcpp.h>
+#include <grpcpp/support/client_callback.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "chromecast/cast_core/grpc/grpc_call.h"
+#include "chromecast/cast_core/grpc/grpc_client_reactor.h"
+#include "chromecast/cast_core/grpc/grpc_status_or.h"
+
+namespace cast {
+namespace utils {
+
+// Typedef for the unary method generated by gRPC compiler.
+template <typename TSyncInterface, typename TRequest, typename TResponse>
+using SyncUnaryMethod = grpc::Status (TSyncInterface::*)(grpc::ClientContext*,
+                                                         const TRequest&,
+                                                         TResponse*);
+
+template <typename TAsyncInterface, typename TRequest, typename TResponse>
+using AsyncUnaryMethod = void (TAsyncInterface::*)(grpc::ClientContext*,
+                                                   const TRequest*,
+                                                   TResponse*,
+                                                   grpc::ClientUnaryReactor*);
+
+// A GrpcCall implementation for unary gRPC calls specialized by the
+// |AsyncMethodPtr| and SyncMethodPtr function pointers. The former must always
+// be specified and is used for Async calls. The latter is used for Sync calls
+// and could be omitted.
+//  TGrpcStub - gRPC service stub type.
+//  TRequest - gRPC request type for a method in the stub.
+//  TResponse - gRPC response type for a method in the stub.
+//  AsyncMethodPtr - pointer to an async method in the stub that handles a
+//  streaming call.
+//  SyncMethodPtr - pointer to a sync method in the stub that handles a
+//  streaming call.
+template <
+    typename TGrpcStub,
+    typename TRequest,
+    typename TResponse,
+    AsyncUnaryMethod<typename TGrpcStub::AsyncInterface, TRequest, TResponse>
+        AsyncMethodPtr,
+    SyncUnaryMethod<typename TGrpcStub::SyncInterface, TRequest, TResponse>
+        SyncMethodPtr = nullptr>
+class GrpcUnaryCall : public GrpcCall<TGrpcStub, TRequest> {
+ public:
+  static_assert(AsyncMethodPtr != nullptr, "AsyncMethodPtr must be specified");
+
+  using Base = GrpcCall<TGrpcStub, TRequest>;
+  using Base::async;
+  using Base::GrpcCall;
+  using Base::request;
+  using Base::sync;
+  using typename Base::AsyncInterface;
+  using typename Base::Request;
+
+  using Response = TResponse;
+  using ResponseCallback = base::OnceCallback<void(GrpcStatusOr<Response>)>;
+
+  GrpcStatusOr<Response> Invoke() && {
+    static_assert(SyncMethodPtr != nullptr,
+                  "The sync interface is not defined for the stub. Please, add "
+                  "&StubInterface::<method_name> to the GrpcUnaryCall "
+                  "definition in the stub class.");
+    Response response;
+    grpc::ClientContext context;
+    std::move(*this).options().ApplyOptionsToContext(&context);
+    auto call = base::BindOnce(SyncMethodPtr,
+                               base::Unretained(std::move(*this).sync()));
+    auto status =
+        std::move(call).Run(&context, std::move(*this).request(), &response);
+    if (status.ok()) {
+      return response;
+    }
+    return status;
+  }
+
+  // Invokes a unary gRPC call for a given tuple of AsyncInterface,
+  // AsyncMethodPtr, Request and Response. The |service| must have a |method| of
+  // a certain signature generated by the proto compiler.
+  void InvokeAsync(ResponseCallback response_callback) && {
+    // Although |reactor| is a plain pointer, its ownership is transferred to
+    // the gRPC stack in the |Start| call.
+    auto reactor =
+        new Reactor(std::move(*this).async(), std::move(*this).request(),
+                    std::move(*this).options(), std::move(response_callback));
+    reactor->Start();
+  }
+
+ private:
+  using ReactorBase = GrpcClientReactor<Request, grpc::ClientUnaryReactor>;
+
+  class Reactor final : public ReactorBase {
+   public:
+    using ReactorBase::context;
+    using ReactorBase::request;
+
+    Reactor(AsyncInterface* async_stub,
+            Request request,
+            GrpcCallOptions options,
+            ResponseCallback response_callback)
+        : ReactorBase(std::move(request), std::move(options)),
+          async_stub_call_(
+              base::BindOnce(AsyncMethodPtr, base::Unretained(async_stub))),
+          response_callback_(std::move(response_callback)) {}
+
+    void Start() override {
+      ReactorBase::Start();
+      std::move(async_stub_call_).Run(context(), request(), &response_, this);
+      grpc::ClientUnaryReactor::StartCall();
+    }
+
+   private:
+    // Implements grpc::ClientUnaryReactor APIs.
+    // The method is always called on completion of all operations associated
+    // with this call, and deletes itself on exit.
+    void OnDone(const grpc::Status& status) override {
+      if (status.ok()) {
+        std::move(response_callback_).Run(std::move(response_));
+      } else {
+        std::move(response_callback_).Run(status);
+      }
+      delete this;
+    }
+
+    using AsyncStubCall = base::OnceCallback<void(grpc::ClientContext*,
+                                                  const Request*,
+                                                  Response*,
+                                                  grpc::ClientUnaryReactor*)>;
+
+    AsyncStubCall async_stub_call_;
+    ResponseCallback response_callback_;
+    Response response_;
+  };
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_GRPC_UNARY_CALL_H_
diff --git a/chromecast/cast_core/grpc/grpc_unary_handler.h b/chromecast/cast_core/grpc/grpc_unary_handler.h
new file mode 100644
index 0000000..0a998a06
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_unary_handler.h
@@ -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.
+
+#ifndef CHROMECAST_CAST_CORE_GRPC_GRPC_UNARY_HANDLER_H_
+#define CHROMECAST_CAST_CORE_GRPC_GRPC_UNARY_HANDLER_H_
+
+#include <grpcpp/grpcpp.h>
+
+#include "base/callback.h"
+#include "base/strings/stringprintf.h"
+#include "chromecast/cast_core/grpc/cancellable_reactor.h"
+#include "chromecast/cast_core/grpc/grpc_handler.h"
+#include "chromecast/cast_core/grpc/grpc_server_reactor.h"
+#include "chromecast/cast_core/grpc/grpc_status_or.h"
+#include "chromecast/cast_core/grpc/server_reactor_tracker.h"
+#include "chromecast/cast_core/grpc/trackable_reactor.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace cast {
+namespace utils {
+
+// A generic handler for unary, ie request/response, gRPC APIs. Can only be used
+// with rpc that have the following signature:
+//       rpc Foo(Request) returns (Response)
+//
+// - TService is the gRPC service type.
+// - TRequest is the service request type.
+// - TResponse is the service response type.
+// - MethodName is the rpc method as a string.
+//
+// This class is not thread-safe. Appropriate means have to be added by the
+// users to guarantee thread-safety (ie task runners, mutexes etc).
+template <typename TService,
+          typename TRequest,
+          typename TResponse,
+          const char* MethodName>
+class GrpcUnaryHandler final : public GrpcHandler {
+ public:
+  using ReactorBase = GrpcServerReactor<TRequest, TResponse>;
+
+  class Reactor : public ReactorBase {
+   public:
+    using ReactorBase::name;
+    using ReactorBase::Write;
+
+    using OnRequestCallback = base::RepeatingCallback<void(TRequest, Reactor*)>;
+
+    template <typename... TArgs>
+    explicit Reactor(OnRequestCallback on_request_callback, TArgs&&... args)
+        : ReactorBase(std::forward<TArgs>(args)...),
+          on_request_callback_(std::move(on_request_callback)) {
+      ReadRequest();
+    }
+
+   protected:
+    using ReactorBase::Finish;
+    using ReactorBase::ReadRequest;
+    using ReactorBase::StartRead;
+    using ReactorBase::StartWriteAndFinish;
+
+    // Implements GrpcServerReactor APIs.
+    void WriteResponse(const grpc::ByteBuffer* buffer) override {
+      DCHECK(buffer);
+      FinishWriting(buffer, grpc::Status::OK);
+    }
+    void FinishWriting(const grpc::ByteBuffer* buffer,
+                       grpc::Status status) override {
+      DCHECK((status.ok() && buffer) || !status.ok())
+          << "Either buffer must be set or status must flag an error";
+      DVLOG(1) << "Reactor is finished: " << name()
+               << ", status=" << GrpcStatusToString(status);
+      if (status.ok()) {
+        StartWriteAndFinish(buffer, grpc::WriteOptions(), grpc::Status::OK);
+      } else {
+        Finish(status);
+      }
+    }
+    void OnResponseDone(grpc::Status status) override {
+      LOG(FATAL)
+          << "Unary handler writes must finish the reactor at the same time";
+    }
+    void OnRequestDone(GrpcStatusOr<TRequest> request) override {
+      if (!request.ok()) {
+        Finish(request.status());
+        return;
+      }
+      on_request_callback_.Run(std::move(*request), this);
+    }
+
+    OnRequestCallback on_request_callback_;
+  };
+
+  using Response = TResponse;
+  using OnRequestCallback = typename Reactor::OnRequestCallback;
+
+  GrpcUnaryHandler(OnRequestCallback on_request_callback,
+                   ServerReactorTracker* server_reactor_tracker)
+      : GrpcHandler(server_reactor_tracker),
+        on_request_callback_(std::move(on_request_callback)) {}
+
+  static std::string rpc_name() {
+    return base::StringPrintf("/%s/%s", TService::service_full_name(),
+                              MethodName);
+  }
+
+ private:
+  // Implements GrpcHandler APIs.
+  grpc::ServerGenericBidiReactor* CreateReactor(
+      grpc::CallbackServerContext* context) override {
+    return new CancellableReactor<TrackableReactor<Reactor>>(
+        server_reactor_tracker(), on_request_callback_, rpc_name(), context);
+  }
+
+  OnRequestCallback on_request_callback_;
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_GRPC_UNARY_HANDLER_H_
diff --git a/chromecast/cast_core/grpc/grpc_unary_test.cc b/chromecast/cast_core/grpc/grpc_unary_test.cc
new file mode 100644
index 0000000..48b2266a
--- /dev/null
+++ b/chromecast/cast_core/grpc/grpc_unary_test.cc
@@ -0,0 +1,223 @@
+// 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 "base/bind.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/guid.h"
+#include "base/strings/strcat.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task/bind_post_task.h"
+#include "base/task/post_task.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/task/thread_pool.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time/time.h"
+#include "chromecast/cast_core/grpc/grpc_server.h"
+#include "chromecast/cast_core/grpc/status_matchers.h"
+#include "chromecast/cast_core/grpc/test_service.grpc.pb.h"
+#include "chromecast/cast_core/grpc/test_service_handlers.h"
+#include "chromecast/cast_core/grpc/test_service_stubs.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cast {
+namespace utils {
+namespace {
+
+using ::cast::test::StatusIs;
+
+static const auto kEventTimeout = base::Seconds(1);
+static const auto kServerStopTimeout = base::Seconds(1);
+
+class GrpcUnaryTest : public ::testing::Test {
+ protected:
+  GrpcUnaryTest()
+      : grpc_client_task_runner_(
+            base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})) {
+    CHECK(temp_dir_.CreateUniqueTempDir());
+    endpoint_ = "unix:" +
+                temp_dir_.GetPath()
+                    .AppendASCII("cast-uds-" + base::GenerateGUID().substr(24))
+                    .value();
+  }
+
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  scoped_refptr<base::SequencedTaskRunner> grpc_client_task_runner_;
+  base::ScopedTempDir temp_dir_;
+  std::string endpoint_;
+};
+
+TEST_F(GrpcUnaryTest, SyncUnaryCallSucceeds) {
+  GrpcServer server;
+  server.SetHandler<SimpleServiceHandler::SimpleCall>(
+      base::BindLambdaForTesting(
+          [](TestRequest request,
+             SimpleServiceHandler::SimpleCall::Reactor* reactor) {
+            EXPECT_EQ(request.foo(), "test_foo");
+            TestResponse response;
+            response.set_bar("test_bar");
+            reactor->Write(std::move(response));
+          }));
+  server.Start(endpoint_);
+
+  SimpleServiceStub stub(endpoint_);
+  auto call = stub.CreateCall<SimpleServiceStub::SimpleCall>();
+  call.request().set_foo("test_foo");
+  auto response = std::move(call).Invoke();
+  CU_ASSERT_OK(response);
+  EXPECT_EQ(response->bar(), "test_bar");
+
+  server.StopForTesting(kServerStopTimeout);
+}
+
+TEST_F(GrpcUnaryTest, SyncUnaryCallReturnsErrorStatus) {
+  GrpcServer server;
+  server.SetHandler<SimpleServiceHandler::SimpleCall>(
+      base::BindLambdaForTesting(
+          [&](TestRequest request,
+              SimpleServiceHandler::SimpleCall::Reactor* reactor) {
+            EXPECT_EQ(request.foo(), "test_foo");
+            reactor->Write(grpc::StatusCode::NOT_FOUND, "Not found");
+          }));
+  server.Start(endpoint_);
+
+  SimpleServiceStub stub(endpoint_);
+  auto call = stub.CreateCall<SimpleServiceStub::SimpleCall>();
+  call.request().set_foo("test_foo");
+  auto response = std::move(call).Invoke();
+  ASSERT_THAT(response.status(),
+              StatusIs(grpc::StatusCode::NOT_FOUND, "Not found"));
+
+  server.StopForTesting(kServerStopTimeout);
+}
+
+TEST_F(GrpcUnaryTest, SyncUnaryCallCancelledIfServerIsStopped) {
+  GrpcServer server;
+  base::WaitableEvent server_stopped_event;
+  server.SetHandler<SimpleServiceHandler::SimpleCall>(
+      base::BindLambdaForTesting(
+          [&](TestRequest request,
+              SimpleServiceHandler::SimpleCall::Reactor* reactor) {
+            // Stop the server to trigger call cancellation.
+            server.Stop(base::Milliseconds(100),
+                        base::BindLambdaForTesting(
+                            [&]() { server_stopped_event.Signal(); }));
+          }));
+  server.Start(endpoint_);
+
+  SimpleServiceStub stub(endpoint_);
+  auto call = stub.CreateCall<SimpleServiceStub::SimpleCall>();
+  call.request().set_foo("test_foo");
+  auto response = std::move(call).Invoke();
+  ASSERT_THAT(response, StatusIs(grpc::StatusCode::UNAVAILABLE));
+
+  {
+    base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync_primitives;
+    ASSERT_TRUE(server_stopped_event.TimedWait(kEventTimeout));
+  }
+}
+
+TEST_F(GrpcUnaryTest, AsyncUnaryCallSucceeds) {
+  GrpcServer server;
+  server.SetHandler<SimpleServiceHandler::SimpleCall>(base::BindPostTask(
+      grpc_client_task_runner_,
+      base::BindLambdaForTesting(
+          [](TestRequest request,
+             SimpleServiceHandler::SimpleCall::Reactor* reactor) {
+            EXPECT_EQ(request.foo(), "test_foo");
+            TestResponse response;
+            response.set_bar("test_bar");
+            reactor->Write(std::move(response));
+          })));
+  server.Start(endpoint_);
+
+  SimpleServiceStub stub(endpoint_);
+  auto call = stub.CreateCall<SimpleServiceStub::SimpleCall>();
+  call.request().set_foo("test_foo");
+  base::WaitableEvent response_received_event;
+  std::move(call).InvokeAsync(
+      base::BindLambdaForTesting([&](GrpcStatusOr<TestResponse> response) {
+        CU_CHECK_OK(response);
+        EXPECT_EQ(response->bar(), "test_bar");
+        response_received_event.Signal();
+      }));
+  {
+    base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync_primitives;
+    ASSERT_TRUE(response_received_event.TimedWait(kEventTimeout));
+  }
+
+  server.StopForTesting(kServerStopTimeout);
+}
+
+TEST_F(GrpcUnaryTest, AsyncUnaryCallReturnsErrorStatus) {
+  GrpcServer server;
+  server.SetHandler<SimpleServiceHandler::SimpleCall>(base::BindPostTask(
+      grpc_client_task_runner_,
+      base::BindLambdaForTesting(
+          [&](TestRequest request,
+              SimpleServiceHandler::SimpleCall::Reactor* reactor) {
+            EXPECT_EQ(request.foo(), "test_foo");
+            reactor->Write(grpc::StatusCode::NOT_FOUND, "Not Found");
+          })));
+  server.Start(endpoint_);
+
+  SimpleServiceStub stub(endpoint_);
+  auto call = stub.CreateCall<SimpleServiceStub::SimpleCall>();
+  call.request().set_foo("test_foo");
+  base::WaitableEvent response_received_event;
+  std::move(call).InvokeAsync(base::BindPostTask(
+      grpc_client_task_runner_,
+      base::BindLambdaForTesting([&](GrpcStatusOr<TestResponse> response) {
+        ASSERT_THAT(response.status(),
+                    StatusIs(grpc::StatusCode::NOT_FOUND, "Not Found"));
+        response_received_event.Signal();
+      })));
+  {
+    base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync_primitives;
+    ASSERT_TRUE(response_received_event.TimedWait(kEventTimeout));
+  }
+
+  server.StopForTesting(kServerStopTimeout);
+}
+
+TEST_F(GrpcUnaryTest, AsyncUnaryCallCancelledIfServerIsStopped) {
+  GrpcServer server;
+  base::WaitableEvent server_stopped_event;
+  server.SetHandler<SimpleServiceHandler::SimpleCall>(base::BindPostTask(
+      grpc_client_task_runner_,
+      base::BindLambdaForTesting(
+          [&](TestRequest request,
+              SimpleServiceHandler::SimpleCall::Reactor* reactor) {
+            // Stop the server to trigger call cancellation.
+            server.Stop(base::Milliseconds(100),
+                        base::BindLambdaForTesting(
+                            [&]() { server_stopped_event.Signal(); }));
+          })));
+  server.Start(endpoint_);
+
+  SimpleServiceStub stub(endpoint_);
+  auto call = stub.CreateCall<SimpleServiceStub::SimpleCall>();
+  call.request().set_foo("test_foo");
+  base::WaitableEvent response_received_event;
+  std::move(call).InvokeAsync(base::BindPostTask(
+      grpc_client_task_runner_,
+      base::BindLambdaForTesting([&](GrpcStatusOr<TestResponse> response) {
+        ASSERT_THAT(response, StatusIs(grpc::StatusCode::UNAVAILABLE));
+        response_received_event.Signal();
+      })));
+  {
+    base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync_primitives;
+    ASSERT_TRUE(response_received_event.TimedWait(kEventTimeout));
+
+    // Need to wait for server to fully stop.
+    ASSERT_TRUE(server_stopped_event.TimedWait(kEventTimeout));
+  }
+}
+
+}  // namespace
+}  // namespace utils
+}  // namespace cast
diff --git a/chromecast/cast_core/grpc/server_reactor_tracker.cc b/chromecast/cast_core/grpc/server_reactor_tracker.cc
new file mode 100644
index 0000000..80a8e35
--- /dev/null
+++ b/chromecast/cast_core/grpc/server_reactor_tracker.cc
@@ -0,0 +1,24 @@
+// 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 "chromecast/cast_core/grpc/server_reactor_tracker.h"
+
+namespace cast {
+namespace utils {
+
+ServerReactorTracker::ServerReactorTracker() = default;
+
+ServerReactorTracker::~ServerReactorTracker() = default;
+
+std::ostream& operator<<(std::ostream& os,
+                         const ServerReactorTracker& tracker) {
+  base::AutoLock l(tracker.lock_);
+  for (const auto& entry : tracker.active_reactors_) {
+    os << entry.second << " ";
+  }
+  return os;
+}
+
+}  // namespace utils
+}  // namespace cast
diff --git a/chromecast/cast_core/grpc/server_reactor_tracker.h b/chromecast/cast_core/grpc/server_reactor_tracker.h
new file mode 100644
index 0000000..10632492
--- /dev/null
+++ b/chromecast/cast_core/grpc/server_reactor_tracker.h
@@ -0,0 +1,51 @@
+// 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 CHROMECAST_CAST_CORE_GRPC_SERVER_REACTOR_TRACKER_H_
+#define CHROMECAST_CAST_CORE_GRPC_SERVER_REACTOR_TRACKER_H_
+
+#include "base/containers/flat_map.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
+
+namespace cast {
+namespace utils {
+
+// Tracks number of currently active reactors per server and allows to quickly
+// diagnose the rpc names of the reactors that are left pending via ostream
+// operator.
+class ServerReactorTracker {
+ public:
+  ServerReactorTracker();
+  ~ServerReactorTracker();
+
+  template <typename TReactor>
+  void AddReactor(TReactor* reactor) {
+    base::AutoLock l(lock_);
+    active_reactors_.emplace(static_cast<void*>(reactor), reactor->name());
+  }
+
+  template <typename TReactor>
+  void RemoveReactor(TReactor* reactor) {
+    base::AutoLock l(lock_);
+    active_reactors_.erase(static_cast<void*>(reactor));
+  }
+
+  size_t active_reactor_count() {
+    base::AutoLock l(lock_);
+    return active_reactors_.size();
+  }
+
+  friend std::ostream& operator<<(std::ostream& os,
+                                  const ServerReactorTracker& tracker);
+
+ private:
+  mutable base::Lock lock_;
+  base::flat_map<void*, std::string> active_reactors_ GUARDED_BY(lock_);
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_SERVER_REACTOR_TRACKER_H_
diff --git a/chromecast/cast_core/grpc/status_matchers.cc b/chromecast/cast_core/grpc/status_matchers.cc
new file mode 100644
index 0000000..acca32b
--- /dev/null
+++ b/chromecast/cast_core/grpc/status_matchers.cc
@@ -0,0 +1,20 @@
+// 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 "chromecast/cast_core/grpc/status_matchers.h"
+
+namespace cast {
+namespace test {
+namespace internal {
+
+StatusResolver::~StatusResolver() = default;
+
+StatusIsPolymorphicWrapper::StatusIsPolymorphicWrapper(
+    const StatusIsPolymorphicWrapper&) = default;
+
+StatusIsPolymorphicWrapper::~StatusIsPolymorphicWrapper() = default;
+
+}  // namespace internal
+}  // namespace test
+}  // namespace cast
diff --git a/chromecast/cast_core/grpc/status_matchers.h b/chromecast/cast_core/grpc/status_matchers.h
new file mode 100644
index 0000000..115aa25d
--- /dev/null
+++ b/chromecast/cast_core/grpc/status_matchers.h
@@ -0,0 +1,247 @@
+// 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 CHROMECAST_CAST_CORE_GRPC_STATUS_MATCHERS_H_
+#define CHROMECAST_CAST_CORE_GRPC_STATUS_MATCHERS_H_
+
+#include <string>
+#include <type_traits>
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace cast {
+namespace test {
+namespace internal {
+
+// Checks if a type T has a status() API helping distinguish if Status impl is
+// nested inside T (eg GrpcStatusOr etc) or T is the Status impl.
+template <typename T>
+class has_status_api {
+  struct no {};
+
+  template <typename C>
+  static decltype(std::declval<C>().status()) test(int);
+  template <typename C>
+  static no test(...);
+
+ public:
+  enum { value = !std::is_same<decltype(test<T>(0)), no>::value };
+};
+
+// Checks if a Status impl has 'code' API deducing that status code and message
+// can be retrieved using T::code() and T::message APIs (eg absl::Status).
+template <typename T>
+class has_code_api {
+  struct no {};
+
+  template <typename C>
+  static decltype(&C::code) test(int);
+  template <typename C>
+  static no test(...);
+
+ public:
+  enum { value = !std::is_same<decltype(test<T>(0)), no>::value };
+};
+
+// Checks if a Status impl has 'error_code' API deducing that status code and
+// message can be retrieved using T::error_code() and T::error_message APIs (eg
+// grpc::Status).
+template <typename T>
+class has_error_code_api {
+  struct no {};
+
+  template <typename C>
+  static decltype(&C::error_code) test(int);
+  template <typename C>
+  static no test(...);
+
+ public:
+  enum { value = !std::is_same<decltype(test<T>(0)), no>::value };
+};
+
+// Reads the error code and message of the generic TStatus which can be either a
+// wrapper object with TStatus::status() defined or the actual status object
+// with TStatus::code\message() or TStatus::error_code\error_message APIs. The
+// resolution works based on SFINAE when invalid type substitution results in
+// dropping the method from compilation.
+class StatusResolver {
+ public:
+  // Constructor that deduces the actual APIs of TStatus and sets the code and
+  // message.
+  template <typename TStatus>
+  explicit StatusResolver(const TStatus& status)
+      : code_and_message_(ResolveCodeAndMessage(ResolveStatus(status))) {}
+  ~StatusResolver();
+
+  // Returns the status code (as an integer) and message.
+  const std::pair<int, std::string>& code_and_message() const {
+    return code_and_message_;
+  }
+
+  // Checks if status is ok.
+  bool ok() const { return code_and_message_.first == 0; }
+
+ private:
+  // Returns the actual status from the wrapper.
+  template <typename TWrappedStatus,
+            typename std::enable_if<has_status_api<TWrappedStatus>::value,
+                                    TWrappedStatus>::type* = nullptr>
+  static auto ResolveStatus(const TWrappedStatus& wrapped_status)
+      -> decltype(std::declval<TWrappedStatus>().status()) {
+    return wrapped_status.status();
+  }
+
+  // Returns itself.
+  template <typename TStatus,
+            typename std::enable_if<!has_status_api<TStatus>::value,
+                                    TStatus>::type* = nullptr>
+  static auto ResolveStatus(const TStatus& status) -> TStatus {
+    return status;
+  }
+
+  // Returns the pair of integer code and error message using TStatus::code()
+  // and TStatus::message() APIs.
+  template <typename TStatus,
+            typename std::enable_if<has_code_api<TStatus>::value,
+                                    TStatus>::type* = nullptr>
+  static std::pair<int, std::string> ResolveCodeAndMessage(
+      const TStatus& status) {
+    return std::make_pair(static_cast<int>(status.code()),
+                          std::string(status.message()));
+  }
+
+  // Returns the pair of integer code and error message using
+  // TStatus::error_code() and TStatus::error_message() APIs.
+  template <typename TStatus,
+            typename std::enable_if<has_error_code_api<TStatus>::value,
+                                    TStatus>::type* = nullptr>
+  static std::pair<int, std::string> ResolveCodeAndMessage(
+      const TStatus& status) {
+    return std::make_pair(static_cast<int>(status.error_code()),
+                          std::string(status.error_message()));
+  }
+
+  const std::pair<int, std::string> code_and_message_;
+};
+
+// Implementation of MatcherInterface for StatusIs matcher.
+template <typename TStatus>
+class StatusIsImpl : public ::testing::MatcherInterface<TStatus> {
+ public:
+  StatusIsImpl(::testing::Matcher<int> code,
+               ::testing::Matcher<const std::string&> message)
+      : code_(std::move(code)), message_(std::move(message)) {}
+
+  // MatcherInterface implementation.
+  void DescribeTo(std::ostream* os) const override {
+    *os << "has code() that ";
+    code_.DescribeTo(os);
+    *os << " and message() that ";
+    message_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(std::ostream* os) const override {
+    *os << "has code() that ";
+    code_.DescribeNegationTo(os);
+    *os << " and message() that ";
+    message_.DescribeNegationTo(os);
+  }
+
+  bool MatchAndExplain(
+      TStatus status,
+      ::testing::MatchResultListener* result_listener) const override {
+    ::testing::StringMatchResultListener listener;
+    StatusResolver status_resolver(status);
+    auto code_and_message = status_resolver.code_and_message();
+    if (!code_.MatchAndExplain(code_and_message.first, &listener)) {
+      *result_listener << "has wrong status code " << listener.str();
+      return false;
+    }
+
+    if (!message_.Matches(code_and_message.second)) {
+      *result_listener << "has wrong error message " << code_and_message.second;
+      return false;
+    }
+
+    return true;
+  }
+
+ private:
+  const ::testing::Matcher<int> code_;
+  const ::testing::Matcher<const std::string&> message_;
+};
+
+// Allows usage of StatusIs without template parameter
+class StatusIsPolymorphicWrapper {
+ public:
+  template <typename TStatusCode>
+  StatusIsPolymorphicWrapper(TStatusCode code,
+                             ::testing::Matcher<const std::string&>&& message)
+      : code_(static_cast<int>(code)), message_(std::move(message)) {}
+  StatusIsPolymorphicWrapper(const StatusIsPolymorphicWrapper&);
+  ~StatusIsPolymorphicWrapper();
+
+  // Converts this polymorphic matcher to a monomorphic matcher.
+  template <typename TStatus>
+  operator ::testing::Matcher<TStatus>() const {
+    return ::testing::Matcher<TStatus>(
+        new StatusIsImpl<TStatus>(code_, message_));
+  }
+
+ private:
+  ::testing::Matcher<int> code_;
+  ::testing::Matcher<const std::string&> message_;
+};
+
+// Allows usage of IsOk without template parameter
+template <typename TStatus>
+class IsOkImpl : public ::testing::MatcherInterface<TStatus> {
+ public:
+  void DescribeTo(std::ostream* os) const override {
+    *os << "is OK";
+  }  // namespace internal
+  void DescribeNegationTo(std::ostream* os) const override {
+    *os << "is not OK";
+  }
+  bool MatchAndExplain(TStatus actual_value,
+                       ::testing::MatchResultListener*) const override {
+    return StatusResolver(actual_value).ok();
+  }
+};  // namespace test
+
+class IsOkPolymorphicWrapper {
+ public:
+  template <typename TStatus>
+  operator ::testing::Matcher<TStatus>() const {  // NOLINT
+    return ::testing::Matcher<TStatus>(new IsOkImpl<const TStatus&>());
+  }
+};
+
+}  // namespace internal
+
+template <typename TStatusCode>
+inline internal::StatusIsPolymorphicWrapper StatusIs(TStatusCode code) {
+  return internal::StatusIsPolymorphicWrapper(code, ::testing::_);
+}
+
+template <typename TStatusCode>
+inline internal::StatusIsPolymorphicWrapper StatusIs(
+    TStatusCode code,
+    ::testing::Matcher<const std::string&>&& message_matcher) {
+  return internal::StatusIsPolymorphicWrapper(code, std::move(message_matcher));
+}
+
+inline internal::IsOkPolymorphicWrapper IsOk() {
+  return internal::IsOkPolymorphicWrapper();
+}
+
+#define CU_EXPECT_OK(expression) EXPECT_THAT(expression, ::cast::test::IsOk())
+#define CU_ASSERT_OK(expression) ASSERT_THAT(expression, ::cast::test::IsOk())
+#define CU_CHECK_OK(expression) \
+  CHECK(::cast::test::internal::StatusResolver(expression).ok())
+
+}  // namespace test
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_STATUS_MATCHERS_H_
diff --git a/chromecast/cast_core/grpc/test_service.proto b/chromecast/cast_core/grpc/test_service.proto
new file mode 100644
index 0000000..7b049143b
--- /dev/null
+++ b/chromecast/cast_core/grpc/test_service.proto
@@ -0,0 +1,31 @@
+// 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.
+
+syntax = "proto3";
+
+package cast.utils;
+
+option optimize_for = LITE_RUNTIME;
+
+// An unary test service.
+service SimpleService {
+  // Makes an unary test call.
+  rpc SimpleCall(TestRequest) returns (TestResponse);
+}
+
+// A server streaming test service.
+service ServerStreamingService {
+  // Makes a server streaming test call.
+  rpc StreamingCall(TestRequest) returns (stream TestResponse);
+}
+
+// Test request.
+message TestRequest {
+  string foo = 1;
+}
+
+// Test response.
+message TestResponse {
+  string bar = 1;
+}
diff --git a/chromecast/cast_core/grpc/test_service_handlers.cc b/chromecast/cast_core/grpc/test_service_handlers.cc
new file mode 100644
index 0000000..a7c2bca
--- /dev/null
+++ b/chromecast/cast_core/grpc/test_service_handlers.cc
@@ -0,0 +1,15 @@
+// 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 "chromecast/cast_core/grpc/test_service_handlers.h"
+
+namespace cast {
+namespace utils {
+
+const char SimpleServiceHandler::kSimpleCall[] = "SimpleCall";
+
+const char ServerStreamingServiceHandler::kStreamingCall[] = "StreamingCall";
+
+}  // namespace utils
+}  // namespace cast
diff --git a/chromecast/cast_core/grpc/test_service_handlers.h b/chromecast/cast_core/grpc/test_service_handlers.h
new file mode 100644
index 0000000..d4280ee
--- /dev/null
+++ b/chromecast/cast_core/grpc/test_service_handlers.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 CHROMECAST_CAST_CORE_GRPC_TEST_SERVICE_HANDLERS_H_
+#define CHROMECAST_CAST_CORE_GRPC_TEST_SERVICE_HANDLERS_H_
+
+#include "chromecast/cast_core/grpc/grpc_server_streaming_handler.h"
+#include "chromecast/cast_core/grpc/grpc_unary_handler.h"
+#include "chromecast/cast_core/grpc/test_service.grpc.pb.h"
+#include "chromecast/cast_core/grpc/test_service.pb.h"
+
+namespace cast {
+namespace utils {
+
+class SimpleServiceHandler {
+ private:
+  static const char kSimpleCall[];
+
+ public:
+  using SimpleCall = utils::
+      GrpcUnaryHandler<SimpleService, TestRequest, TestResponse, kSimpleCall>;
+};
+
+class ServerStreamingServiceHandler {
+ private:
+  static const char kStreamingCall[];
+
+ public:
+  using StreamingCall =
+      utils::GrpcServerStreamingHandler<ServerStreamingService,
+                                        TestRequest,
+                                        TestResponse,
+                                        kStreamingCall>;
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_TEST_SERVICE_HANDLERS_H_
diff --git a/chromecast/cast_core/grpc/test_service_stubs.h b/chromecast/cast_core/grpc/test_service_stubs.h
new file mode 100644
index 0000000..217da5a
--- /dev/null
+++ b/chromecast/cast_core/grpc/test_service_stubs.h
@@ -0,0 +1,51 @@
+// 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 CHROMECAST_CAST_CORE_GRPC_TEST_SERVICE_STUBS_H_
+#define CHROMECAST_CAST_CORE_GRPC_TEST_SERVICE_STUBS_H_
+
+#include "chromecast/cast_core/grpc/grpc_server_streaming_call.h"
+#include "chromecast/cast_core/grpc/grpc_stub.h"
+#include "chromecast/cast_core/grpc/grpc_unary_call.h"
+#include "chromecast/cast_core/grpc/test_service.grpc.pb.h"
+#include "chromecast/cast_core/grpc/test_service.pb.h"
+
+namespace cast {
+namespace utils {
+
+class SimpleServiceStub final : public utils::GrpcStub<SimpleService> {
+ public:
+  using GrpcStub::GrpcStub;
+  using GrpcStub::operator=;
+  using GrpcStub::AsyncInterface;
+  using GrpcStub::CreateCall;
+  using GrpcStub::SyncInterface;
+
+  using SimpleCall = utils::GrpcUnaryCall<SimpleServiceStub,
+                                          TestRequest,
+                                          TestResponse,
+                                          &AsyncInterface::SimpleCall,
+                                          &SyncInterface::SimpleCall>;
+};
+
+class ServerStreamingServiceStub
+    : public utils::GrpcStub<ServerStreamingService> {
+ public:
+  using GrpcStub::GrpcStub;
+  using GrpcStub::operator=;
+  using GrpcStub::AsyncInterface;
+  using GrpcStub::CreateCall;
+  using GrpcStub::SyncInterface;
+
+  using StreamingCall =
+      utils::GrpcServerStreamingCall<ServerStreamingServiceStub,
+                                     TestRequest,
+                                     TestResponse,
+                                     &AsyncInterface::StreamingCall>;
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_TEST_SERVICE_STUBS_H_
diff --git a/chromecast/cast_core/grpc/trackable_reactor.h b/chromecast/cast_core/grpc/trackable_reactor.h
new file mode 100644
index 0000000..d728d56
--- /dev/null
+++ b/chromecast/cast_core/grpc/trackable_reactor.h
@@ -0,0 +1,36 @@
+// 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 CHROMECAST_CAST_CORE_GRPC_TRACKABLE_REACTOR_H_
+#define CHROMECAST_CAST_CORE_GRPC_TRACKABLE_REACTOR_H_
+
+#include "chromecast/cast_core/grpc/server_reactor_tracker.h"
+
+namespace cast {
+namespace utils {
+
+// A facade around reactor implementation that allows to easily track active
+// reactors in a given ServerReactorTracker.
+template <typename TReactor>
+class TrackableReactor : public TReactor {
+ public:
+  template <typename... TArgs>
+  explicit TrackableReactor(ServerReactorTracker* server_reactor_tracker,
+                            TArgs&&... args)
+      : TReactor(std::forward<TArgs&&>(args)...),
+        server_reactor_tracker_(server_reactor_tracker) {
+    DCHECK(server_reactor_tracker_);
+    server_reactor_tracker_->AddReactor(this);
+  }
+
+  ~TrackableReactor() override { server_reactor_tracker_->RemoveReactor(this); }
+
+ private:
+  ServerReactorTracker* const server_reactor_tracker_;
+};
+
+}  // namespace utils
+}  // namespace cast
+
+#endif  // CHROMECAST_CAST_CORE_GRPC_TRACKABLE_REACTOR_H_
diff --git a/chromecast/common/cast_extensions_client.cc b/chromecast/common/cast_extensions_client.cc
index 70bef7b..6406f79 100644
--- a/chromecast/common/cast_extensions_client.cc
+++ b/chromecast/common/cast_extensions_client.cc
@@ -131,7 +131,7 @@
   return webstore_update_url_;
 }
 
-bool CastExtensionsClient::IsBlacklistUpdateURL(const GURL& url) const {
+bool CastExtensionsClient::IsBlocklistUpdateURL(const GURL& url) const {
   return true;
 }
 
diff --git a/chromecast/common/cast_extensions_client.h b/chromecast/common/cast_extensions_client.h
index c9276d0..fbdecd03 100644
--- a/chromecast/common/cast_extensions_client.h
+++ b/chromecast/common/cast_extensions_client.h
@@ -38,7 +38,7 @@
   bool IsScriptableURL(const GURL& url, std::string* error) const override;
   const GURL& GetWebstoreBaseURL() const override;
   const GURL& GetWebstoreUpdateURL() const override;
-  bool IsBlacklistUpdateURL(const GURL& url) const override;
+  bool IsBlocklistUpdateURL(const GURL& url) const override;
 
  private:
   ScriptingAllowlist scripting_allowlist_;
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 7aa9f16..b00a0375 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-14447.0.0
\ No newline at end of file
+14450.0.0
\ No newline at end of file
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index df7cab35..860907ac 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -6,6 +6,7 @@
     <output filename="grit/chromeos_strings.h" type="rc_header">
       <emit emit_type='prepend'></emit>
     </output>
+    <output filename="chromeos_strings_af.pak" type="data_package" lang="af" />
     <output filename="chromeos_strings_am.pak" type="data_package" lang="am" />
     <output filename="chromeos_strings_ar.pak" type="data_package" lang="ar" />
     <output filename="chromeos_strings_bg.pak" type="data_package" lang="bg" />
@@ -62,6 +63,7 @@
     <output filename="chromeos_strings_vi.pak" type="data_package" lang="vi" />
     <output filename="chromeos_strings_zh-CN.pak" type="data_package" lang="zh-CN" />
     <output filename="chromeos_strings_zh-TW.pak" type="data_package" lang="zh-TW" />
+    <output filename="chromeos_strings_zu.pak" type="data_package" lang="zu" />
 
     <!-- Pseudolocales -->
     <output filename="chromeos_strings_ar-XB.pak" type="data_package" lang="ar-XB" />
diff --git a/chromeos/crosapi/mojom/browser_app_instance_registry.mojom b/chromeos/crosapi/mojom/browser_app_instance_registry.mojom
index 267c139..e3e6e11ee 100644
--- a/chromeos/crosapi/mojom/browser_app_instance_registry.mojom
+++ b/chromeos/crosapi/mojom/browser_app_instance_registry.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 2
+// Next MinVersion: 3
 
 module crosapi.mojom;
 
@@ -19,6 +19,16 @@
 
   // Browser window is currently activated.
   bool is_active@2;
+
+  // Unique session id for the browser's window from the current browser
+  // session.
+  [MinVersion=2]
+  uint32 browser_session_id@3;
+
+  // Unique restored session id for the browser's window from the restored
+  // browser session.
+  [MinVersion=2]
+  uint32 restored_browser_session_id@4;
 };
 
 [Stable, Extensible]
@@ -54,6 +64,16 @@
   // May change for apps of type kAppTab, stays the same (true) for instances
   // of type kAppWindow.
   bool is_web_contents_active@6;
+
+  // Unique session id for the browser's window from the current browser
+  // session.
+  [MinVersion=2]
+  uint32 browser_session_id@7;
+
+  // Unique restored session id for the browser's window from the restored
+  // browser session.
+  [MinVersion=2]
+  uint32 restored_browser_session_id@8;
 };
 
 // Implemented in ash-chrome, and is used to receive browser app instance
diff --git a/chromeos/crosapi/mojom/browser_app_instance_registry_mojom_traits.cc b/chromeos/crosapi/mojom/browser_app_instance_registry_mojom_traits.cc
index b145f37..c4da552 100644
--- a/chromeos/crosapi/mojom/browser_app_instance_registry_mojom_traits.cc
+++ b/chromeos/crosapi/mojom/browser_app_instance_registry_mojom_traits.cc
@@ -13,6 +13,8 @@
   apps::BrowserWindowInstanceUpdate update;
   if (input.ReadId(&update.id) && input.ReadWindowId(&update.window_id)) {
     update.is_active = input.is_active();
+    update.browser_session_id = input.browser_session_id();
+    update.restored_browser_session_id = input.restored_browser_session_id();
     *output = std::move(update);
     return true;
   }
@@ -29,6 +31,8 @@
       input.ReadWindowId(&update.window_id) && input.ReadTitle(&update.title)) {
     update.is_browser_active = input.is_browser_active();
     update.is_web_contents_active = input.is_web_contents_active();
+    update.browser_session_id = input.browser_session_id();
+    update.restored_browser_session_id = input.restored_browser_session_id();
     *output = std::move(update);
     return true;
   }
diff --git a/chromeos/crosapi/mojom/browser_app_instance_registry_mojom_traits.h b/chromeos/crosapi/mojom/browser_app_instance_registry_mojom_traits.h
index 57567a8..28200d1 100644
--- a/chromeos/crosapi/mojom/browser_app_instance_registry_mojom_traits.h
+++ b/chromeos/crosapi/mojom/browser_app_instance_registry_mojom_traits.h
@@ -31,6 +31,16 @@
   static bool is_active(const apps::BrowserWindowInstanceUpdate& update) {
     return update.is_active;
   }
+
+  static uint32_t browser_session_id(
+      const apps::BrowserWindowInstanceUpdate& update) {
+    return update.browser_session_id;
+  }
+
+  static uint32_t restored_browser_session_id(
+      const apps::BrowserWindowInstanceUpdate& update) {
+    return update.restored_browser_session_id;
+  }
 };
 
 template <>
@@ -70,6 +80,16 @@
       const apps::BrowserAppInstanceUpdate& update) {
     return update.is_web_contents_active;
   }
+
+  static uint32_t browser_session_id(
+      const apps::BrowserAppInstanceUpdate& update) {
+    return update.browser_session_id;
+  }
+
+  static uint32_t restored_browser_session_id(
+      const apps::BrowserAppInstanceUpdate& update) {
+    return update.restored_browser_session_id;
+  }
 };
 
 template <>
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index 4865dd5..35d1279 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -473,32 +473,38 @@
   kManaged = 2,
 };
 
-// Default directories on the system.
+// Default directories on the system. We send the full path for each value for
+// future compatibility, to avoid assumptions about where on disk the directory
+// is located.
 [Stable]
 struct DefaultPaths {
   // The default (non-configurable) directory for documents. For example,
-  // /home/chronos/u-<hash>/MyFiles. We send the full path for future
-  // compatibility, to avoid assumptions about where on disk the directory is
-  // located.
+  // /home/chronos/u-<hash>/MyFiles.
   mojo_base.mojom.FilePath documents@0;
 
   // The default (non-configurable) directory for downloads. For example,
-  // /home/chronos/u-<hash>/MyFiles/Downloads.  We send the full path for future
-  // compatibility, to avoid assumptions about where on disk the directory is
-  // located.
+  // /home/chronos/u-<hash>/MyFiles/Downloads.
   mojo_base.mojom.FilePath downloads@1;
 
   // The (non-configurable) path of the mount point for drive in ChromeOS. For
-  // example, /media/fuse/drivefs-<hash>. We send the full path for future
-  // compatibility, to avoid assumptions about where on disk the directory is
-  // located.
+  // example, /media/fuse/drivefs-<hash>.
   [MinVersion=23] mojo_base.mojom.FilePath? drivefs@2;
 
   // The (non-configurable) path of the software user NSS database. For
-  // example, /home/chronos/u-<hash>/.pki/nssdb. We send the full path for
-  // future compatibility, to avoid assumptions about where on disk the
-  // directory is located.
+  // example, /home/chronos/u-<hash>/.pki/nssdb.
   [MinVersion=30] mojo_base.mojom.FilePath? user_nss_database@3;
+
+  // The (non-configurable) path at which removable media is mounted. For
+  // example, /media/removable.
+  [MinVersion=31] mojo_base.mojom.FilePath? removable_media@4;
+
+  // The (non-configurable) path at which ARC's storage is located (shown as
+  // "Play Files" in Files app). For example, /run/arc/sdcard/write/emulated/0.
+  [MinVersion=31] mojo_base.mojom.FilePath? android_files@5;
+
+  // The (non-configurable) path at which Crostini's home directory is
+  // mounted. For example, /media/fuse/crostini_<hash>_termina_penguin.
+  [MinVersion=31] mojo_base.mojom.FilePath? linux_files@6;
 };
 
 // The device specific data needed in Lacros.
diff --git a/chromeos/dbus/cros_disks/cros_disks_client.cc b/chromeos/dbus/cros_disks/cros_disks_client.cc
index a17689a..65b6d81 100644
--- a/chromeos/dbus/cros_disks/cros_disks_client.cc
+++ b/chromeos/dbus/cros_disks/cros_disks_client.cc
@@ -711,69 +711,64 @@
 // ]
 void DiskInfo::InitializeFromResponse(dbus::Response* response) {
   dbus::MessageReader reader(response);
-  std::unique_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
-  base::DictionaryValue* properties = NULL;
-  if (!value || !value->GetAsDictionary(&properties))
+  base::Value value(dbus::PopDataAsValue(&reader));
+  if (!value.is_dict())
     return;
 
-  is_drive_ =
-      properties->FindBoolKey(cros_disks::kDeviceIsDrive).value_or(is_drive_);
-  is_read_only_ = properties->FindBoolKey(cros_disks::kDeviceIsReadOnly)
-                      .value_or(is_read_only_);
-  is_hidden_ = properties->FindBoolKey(cros_disks::kDevicePresentationHide)
+  is_drive_ = value.FindBoolKey(cros_disks::kDeviceIsDrive).value_or(is_drive_);
+  is_read_only_ =
+      value.FindBoolKey(cros_disks::kDeviceIsReadOnly).value_or(is_read_only_);
+  is_hidden_ = value.FindBoolKey(cros_disks::kDevicePresentationHide)
                    .value_or(is_hidden_);
-  has_media_ = properties->FindBoolKey(cros_disks::kDeviceIsMediaAvailable)
+  has_media_ = value.FindBoolKey(cros_disks::kDeviceIsMediaAvailable)
                    .value_or(has_media_);
-  on_boot_device_ = properties->FindBoolKey(cros_disks::kDeviceIsOnBootDevice)
+  on_boot_device_ = value.FindBoolKey(cros_disks::kDeviceIsOnBootDevice)
                         .value_or(on_boot_device_);
   on_removable_device_ =
-      properties->FindBoolKey(cros_disks::kDeviceIsOnRemovableDevice)
+      value.FindBoolKey(cros_disks::kDeviceIsOnRemovableDevice)
           .value_or(on_removable_device_);
-  is_virtual_ = properties->FindBoolKey(cros_disks::kDeviceIsVirtual)
-                    .value_or(is_virtual_);
-  is_auto_mountable_ = properties->FindBoolKey(cros_disks::kIsAutoMountable)
+  is_virtual_ =
+      value.FindBoolKey(cros_disks::kDeviceIsVirtual).value_or(is_virtual_);
+  is_auto_mountable_ = value.FindBoolKey(cros_disks::kIsAutoMountable)
                            .value_or(is_auto_mountable_);
-  MaybeGetStringFromDictionaryValue(*properties, cros_disks::kStorageDevicePath,
+  MaybeGetStringFromDictionaryValue(value, cros_disks::kStorageDevicePath,
                                     &storage_device_path_);
-  MaybeGetStringFromDictionaryValue(*properties, cros_disks::kDeviceFile,
+  MaybeGetStringFromDictionaryValue(value, cros_disks::kDeviceFile,
                                     &file_path_);
-  MaybeGetStringFromDictionaryValue(*properties, cros_disks::kVendorId,
-                                    &vendor_id_);
-  MaybeGetStringFromDictionaryValue(*properties, cros_disks::kVendorName,
+  MaybeGetStringFromDictionaryValue(value, cros_disks::kVendorId, &vendor_id_);
+  MaybeGetStringFromDictionaryValue(value, cros_disks::kVendorName,
                                     &vendor_name_);
-  MaybeGetStringFromDictionaryValue(*properties, cros_disks::kProductId,
+  MaybeGetStringFromDictionaryValue(value, cros_disks::kProductId,
                                     &product_id_);
-  MaybeGetStringFromDictionaryValue(*properties, cros_disks::kProductName,
+  MaybeGetStringFromDictionaryValue(value, cros_disks::kProductName,
                                     &product_name_);
-  MaybeGetStringFromDictionaryValue(*properties, cros_disks::kDriveModel,
+  MaybeGetStringFromDictionaryValue(value, cros_disks::kDriveModel,
                                     &drive_model_);
-  MaybeGetStringFromDictionaryValue(*properties, cros_disks::kIdLabel, &label_);
-  MaybeGetStringFromDictionaryValue(*properties, cros_disks::kIdUuid, &uuid_);
-  MaybeGetStringFromDictionaryValue(*properties, cros_disks::kFileSystemType,
+  MaybeGetStringFromDictionaryValue(value, cros_disks::kIdLabel, &label_);
+  MaybeGetStringFromDictionaryValue(value, cros_disks::kIdUuid, &uuid_);
+  MaybeGetStringFromDictionaryValue(value, cros_disks::kFileSystemType,
                                     &file_system_type_);
 
-  bus_number_ =
-      properties->FindIntKey(cros_disks::kBusNumber).value_or(bus_number_);
-  device_number_ = properties->FindIntKey(cros_disks::kDeviceNumber)
-                       .value_or(device_number_);
+  bus_number_ = value.FindIntKey(cros_disks::kBusNumber).value_or(bus_number_);
+  device_number_ =
+      value.FindIntKey(cros_disks::kDeviceNumber).value_or(device_number_);
 
   // dbus::PopDataAsValue() pops uint64_t as double.
   // The top 11 bits of uint64_t are dropped by the use of double. But, this
   // works
   // unless the size exceeds 8 PB.
   absl::optional<double> device_size_double =
-      properties->FindDoubleKey(cros_disks::kDeviceSize);
+      value.FindDoubleKey(cros_disks::kDeviceSize);
   if (device_size_double.has_value())
     total_size_in_bytes_ = device_size_double.value();
 
   // dbus::PopDataAsValue() pops uint32_t as double.
   absl::optional<double> media_type_double =
-      properties->FindDoubleKey(cros_disks::kDeviceMediaType);
+      value.FindDoubleKey(cros_disks::kDeviceMediaType);
   if (media_type_double.has_value())
     device_type_ = DeviceMediaTypeToDeviceType(media_type_double.value());
 
-  base::Value* mount_paths =
-      properties->FindListKey(cros_disks::kDeviceMountPaths);
+  base::Value* mount_paths = value.FindListKey(cros_disks::kDeviceMountPaths);
   if (mount_paths && !mount_paths->GetList().empty() &&
       mount_paths->GetList()[0].is_string()) {
     mount_path_ = mount_paths->GetList()[0].GetString();
diff --git a/chromeos/dbus/fwupd/BUILD.gn b/chromeos/dbus/fwupd/BUILD.gn
index 9ffb989..2e491e2b 100644
--- a/chromeos/dbus/fwupd/BUILD.gn
+++ b/chromeos/dbus/fwupd/BUILD.gn
@@ -10,8 +10,10 @@
 
   deps = [
     "//ash/constants",
+    "//base",
     "//chromeos/dbus:common",
     "//dbus",
+    "//url",
   ]
 
   sources = [
diff --git a/chromeos/dbus/fwupd/fwupd_client.cc b/chromeos/dbus/fwupd/fwupd_client.cc
index 7682587..82765a4 100644
--- a/chromeos/dbus/fwupd/fwupd_client.cc
+++ b/chromeos/dbus/fwupd/fwupd_client.cc
@@ -9,12 +9,14 @@
 
 #include "ash/constants/ash_features.h"
 #include "base/bind.h"
+#include "base/files/file_path.h"
 #include "base/values.h"
 #include "chromeos/dbus/fwupd/dbus_constants.h"
 #include "chromeos/dbus/fwupd/fwupd_properties.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_proxy.h"
+#include "url/gurl.h"
 
 namespace chromeos {
 
@@ -22,6 +24,28 @@
 
 FwupdClient* g_instance = nullptr;
 
+const char kCabFileExtension[] = ".cab";
+
+base::FilePath GetFilePathFromUri(const GURL uri) {
+  const std::string filepath = uri.ExtractFileName();
+
+  if (!filepath.empty()) {
+    // Verify that the extension is .cab.
+    std::size_t extension_delim = filepath.find_last_of(".");
+    if (extension_delim == std::string::npos ||
+        filepath.substr(extension_delim) != kCabFileExtension) {
+      // Bad file, return with empty file path;
+      LOG(ERROR) << "Bad file found: " << uri.spec();
+      return base::FilePath();
+    }
+
+    return base::FilePath(FILE_PATH_LITERAL(filepath));
+  }
+
+  // Return empty file path if filename can't be found.
+  return base::FilePath();
+}
+
 }  // namespace
 
 class FwupdClientImpl : public FwupdClient {
@@ -186,16 +210,24 @@
       const auto* version = dict->FindKey("Version");
       const auto* description = dict->FindKey("Description");
       const auto* priority = dict->FindKey("Urgency");
+      const auto* uri = dict->FindKey("Uri");
+      base::FilePath filepath;
 
-      const bool success = version && description && priority;
+      if (uri) {
+        filepath = GetFilePathFromUri(GURL(uri->GetString()));
+      }
+
+      const bool success =
+          version && description && priority && !filepath.empty();
       // TODO(michaelcheco): Confirm that this is the expected behavior.
       if (success) {
         VLOG(1) << "fwupd: Found update version for device: " << device_id
                 << " with version: " << version->GetString();
         updates.emplace_back(version->GetString(), description->GetString(),
-                             priority->GetInt());
+                             priority->GetInt(), filepath);
       } else {
-        LOG(ERROR) << "Update version, description or priority is not found.";
+        LOG(ERROR) << "Update version, description, filepath or priority is "
+                   << "not found.";
       }
     }
 
diff --git a/chromeos/dbus/fwupd/fwupd_client.h b/chromeos/dbus/fwupd/fwupd_client.h
index c7f289a8..add0572 100644
--- a/chromeos/dbus/fwupd/fwupd_client.h
+++ b/chromeos/dbus/fwupd/fwupd_client.h
@@ -51,6 +51,11 @@
   // Used to call the protected initialization in unit tests.
   void InitForTesting(dbus::Bus* bus) { Init(bus); }
 
+  void SetPropertiesForTesting(uint32_t percentage, uint32_t status) {
+    properties_->percentage.ReplaceValue(percentage);
+    properties_->status.ReplaceValue(status);
+  }
+
   // Query fwupd for updates that are available for a particular device.
   virtual void RequestUpdates(const std::string& device_id) = 0;
 
diff --git a/chromeos/dbus/fwupd/fwupd_client_unittest.cc b/chromeos/dbus/fwupd/fwupd_client_unittest.cc
index d5c47c2..80dd6d8 100644
--- a/chromeos/dbus/fwupd/fwupd_client_unittest.cc
+++ b/chromeos/dbus/fwupd/fwupd_client_unittest.cc
@@ -29,11 +29,15 @@
 const char kFakeUpdateDescriptionForTesting[] =
     "This is a fake update for testing.";
 const uint32_t kFakeUpdatePriorityForTesting = 1;
+const char kFakeUpdateUriForTesting[] =
+    "file:///usr/share/fwupd/remotes.d/vendor/firmware/testFirmwarePath-V1.cab";
+const char kFakeUpdateFileNameForTesting[] = "testFirmwarePath-V1.cab";
 const char kNameKey[] = "Name";
 const char kIdKey[] = "DeviceId";
 const char kVersionKey[] = "Version";
 const char kDescriptionKey[] = "Description";
 const char kPriorityKey[] = "Urgency";
+const char kUriKey[] = "Uri";
 
 void RunResponseOrErrorCallback(
     dbus::ObjectProxy::ResponseOrErrorCallback callback,
@@ -128,6 +132,7 @@
     // that doesn't support unsigned numbers. So it needs to be casted to int.
     CHECK_EQ(static_cast<int>(kFakeUpdatePriorityForTesting),
              (*updates)[0].priority);
+    CHECK_EQ(kFakeUpdateFileNameForTesting, (*updates)[0].filepath.value());
   }
 
   void CheckInstallState(bool success) { CHECK_EQ(install_success_, success); }
@@ -293,6 +298,11 @@
   dict_writer.AppendVariantOfUint32(kFakeUpdatePriorityForTesting);
   device_array_writer.CloseContainer(&dict_writer);
 
+  device_array_writer.OpenDictEntry(&dict_writer);
+  dict_writer.AppendString(kUriKey);
+  dict_writer.AppendVariantOfString(kFakeUpdateUriForTesting);
+  device_array_writer.CloseContainer(&dict_writer);
+
   response_array_writer.CloseContainer(&device_array_writer);
   response_writer.CloseContainer(&response_array_writer);
 
diff --git a/chromeos/dbus/fwupd/fwupd_update.cc b/chromeos/dbus/fwupd/fwupd_update.cc
index 6cbda59..2346f36 100644
--- a/chromeos/dbus/fwupd/fwupd_update.cc
+++ b/chromeos/dbus/fwupd/fwupd_update.cc
@@ -4,14 +4,20 @@
 
 #include "chromeos/dbus/fwupd/fwupd_update.h"
 
+#include "base/files/file_path.h"
+
 namespace chromeos {
 
 FwupdUpdate::FwupdUpdate() = default;
 
 FwupdUpdate::FwupdUpdate(const std::string& version,
                          const std::string& description,
-                         int priority)
-    : version(version), description(description), priority(priority) {}
+                         int priority,
+                         const base::FilePath& filepath)
+    : version(version),
+      description(description),
+      priority(priority),
+      filepath(filepath) {}
 
 FwupdUpdate::FwupdUpdate(FwupdUpdate&& other) = default;
 FwupdUpdate& FwupdUpdate::operator=(FwupdUpdate&& other) = default;
diff --git a/chromeos/dbus/fwupd/fwupd_update.h b/chromeos/dbus/fwupd/fwupd_update.h
index e5cb25b..466afe4 100644
--- a/chromeos/dbus/fwupd/fwupd_update.h
+++ b/chromeos/dbus/fwupd/fwupd_update.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/component_export.h"
+#include "base/files/file_path.h"
 
 namespace chromeos {
 
@@ -16,7 +17,8 @@
   FwupdUpdate();
   FwupdUpdate(const std::string& version,
               const std::string& description,
-              int priority);
+              int priority,
+              const base::FilePath& filename);
   FwupdUpdate(FwupdUpdate&& other);
   FwupdUpdate& operator=(FwupdUpdate&& other);
   ~FwupdUpdate();
@@ -24,6 +26,7 @@
   std::string version;
   std::string description;
   int priority;
+  base::FilePath filepath;
 };
 
 using FwupdUpdateList = std::vector<FwupdUpdate>;
diff --git a/chromeos/dbus/shill/shill_client_helper.cc b/chromeos/dbus/shill/shill_client_helper.cc
index ab8f8f4..7c2d42dd 100644
--- a/chromeos/dbus/shill/shill_client_helper.cc
+++ b/chromeos/dbus/shill/shill_client_helper.cc
@@ -21,7 +21,7 @@
 
 namespace chromeos {
 
-// Class to hold onto a reference to a ShillClientHelper. This calss
+// Class to hold onto a reference to a ShillClientHelper. This class
 // is owned by callbacks and released once the callback completes.
 // Note: Only success callbacks hold the reference. If an error callback is
 // invoked instead, the success callback will still be destroyed and the
@@ -145,12 +145,12 @@
     return;
   }
   dbus::MessageReader reader(response);
-  std::unique_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
-  if (!value.get()) {
+  base::Value value(dbus::PopDataAsValue(&reader));
+  if (value.is_none()) {
     std::move(callback).Run(absl::nullopt);
     return;
   }
-  std::move(callback).Run(std::move(*value));
+  std::move(callback).Run(std::move(value));
 }
 
 // Handles responses for methods without results.
@@ -168,13 +168,13 @@
     ShillClientHelper::ErrorCallback error_callback,
     dbus::Response* response) {
   dbus::MessageReader reader(response);
-  std::unique_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
-  if (!value.get() || !value->is_dict()) {
+  base::Value value(dbus::PopDataAsValue(&reader));
+  if (!value.is_dict()) {
     std::move(error_callback)
         .Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
     return;
   }
-  std::move(callback).Run(std::move(*value));
+  std::move(callback).Run(std::move(value));
 }
 
 // Handles responses for methods with ListValue results.
@@ -184,13 +184,13 @@
     ShillClientHelper::ErrorCallback error_callback,
     dbus::Response* response) {
   dbus::MessageReader reader(response);
-  std::unique_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
-  if (!value.get() || !value->is_list()) {
+  base::Value value(dbus::PopDataAsValue(&reader));
+  if (!value.is_list()) {
     std::move(error_callback)
         .Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
     return;
   }
-  std::move(callback).Run(base::Value::AsListValue(*value));
+  std::move(callback).Run(base::Value::AsListValue(value));
 }
 
 // Handles running appropriate error callbacks.
@@ -434,8 +434,8 @@
         writer->OpenVariant("aa{ss}", &variant_writer);
         dbus::MessageWriter array_writer(nullptr);
         variant_writer.OpenArray("a{ss}", &array_writer);
-        for (const auto& value : list_view) {
-          AppendStringDictionary(value, &array_writer);
+        for (const auto& item : list_view) {
+          AppendStringDictionary(item, &array_writer);
         }
         variant_writer.CloseContainer(&array_writer);
         writer->CloseContainer(&variant_writer);
@@ -537,12 +537,12 @@
   std::string name;
   if (!reader.PopString(&name))
     return;
-  std::unique_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
-  if (!value.get())
+  base::Value value(dbus::PopDataAsValue(&reader));
+  if (value.is_none())
     return;
 
   for (auto& observer : observer_list_)
-    observer.OnPropertyChanged(name, *value);
+    observer.OnPropertyChanged(name, value);
 }
 
 }  // namespace chromeos
diff --git a/chromeos/dbus/shill/shill_client_unittest_base.cc b/chromeos/dbus/shill/shill_client_unittest_base.cc
index 397896a4..692e7871 100644
--- a/chromeos/dbus/shill/shill_client_unittest_base.cc
+++ b/chromeos/dbus/shill/shill_client_unittest_base.cc
@@ -240,9 +240,9 @@
   std::string str;
   ASSERT_TRUE(reader->PopString(&str));
   EXPECT_EQ(expected_string, str);
-  std::unique_ptr<base::Value> value(dbus::PopDataAsValue(reader));
-  ASSERT_TRUE(value.get());
-  EXPECT_EQ(*value, *expected_value);
+  base::Value value(dbus::PopDataAsValue(reader));
+  ASSERT_TRUE(!value.is_none());
+  EXPECT_EQ(value, *expected_value);
   EXPECT_FALSE(reader->HasMoreData());
 }
 
@@ -280,7 +280,9 @@
       case dbus::Message::BOOL:
       case dbus::Message::INT32:
       case dbus::Message::STRING:
-        value = dbus::PopDataAsValue(&variant_reader);
+        value = base::Value::ToUniquePtrValue(
+            dbus::PopDataAsValue(&variant_reader));
+        ASSERT_FALSE(value->is_none());
         break;
       default:
         NOTREACHED();
diff --git a/chromeos/network/auto_connect_handler_unittest.cc b/chromeos/network/auto_connect_handler_unittest.cc
index 45dab5e..b41386c 100644
--- a/chromeos/network/auto_connect_handler_unittest.cc
+++ b/chromeos/network/auto_connect_handler_unittest.cc
@@ -265,15 +265,14 @@
   void SetupPolicy(const std::string& network_configs_json,
                    const base::DictionaryValue& global_config,
                    bool user_policy) {
-    base::ListValue network_configs;
+    base::Value network_configs(base::Value::Type::LIST);
     if (!network_configs_json.empty()) {
       base::JSONReader::ValueWithError parsed_json =
           base::JSONReader::ReadAndReturnValueWithError(
               network_configs_json, base::JSON_ALLOW_TRAILING_COMMAS);
       ASSERT_TRUE(parsed_json.value) << parsed_json.error_message;
-      base::ListValue* network_configs_list = nullptr;
-      ASSERT_TRUE(parsed_json.value->GetAsList(&network_configs_list));
-      network_configs = std::move(*network_configs_list);
+      ASSERT_TRUE(parsed_json.value->is_list());
+      network_configs = std::move(*parsed_json.value);
     }
 
     if (user_policy) {
diff --git a/chromeos/network/client_cert_resolver.cc b/chromeos/network/client_cert_resolver.cc
index a321c57..f0ef488 100644
--- a/chromeos/network/client_cert_resolver.cc
+++ b/chromeos/network/client_cert_resolver.cc
@@ -16,6 +16,7 @@
 #include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/strings/string_util.h"
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "base/time/clock.h"
@@ -40,6 +41,10 @@
 
 namespace {
 
+std::string GetNetworkIdWithGuid(const NetworkState* network_state) {
+  return NetworkId(network_state) + "[guid='" + network_state->guid() + "']";
+}
+
 // Global override for the getter function. This is used for testing purposes.
 // See SetProvisioningIdForCertGetterForTesting for details.
 ClientCertResolver::ProvisioningProfileIdGetter
@@ -627,7 +632,7 @@
     return;
   if (!network->IsConnectingOrConnected()) {
     NET_LOG(EVENT) << "ClientCertResolver: ConnectionStateChanged: "
-                   << NetworkId(network);
+                   << GetNetworkIdWithGuid(network);
     ResolveNetworks(NetworkStateHandler::NetworkStateList(1, network));
   }
 }
@@ -664,16 +669,27 @@
     return;
   }
   NET_LOG(EVENT) << "ClientCertResolver: PolicyAppliedToNetwork: "
-                 << NetworkId(network);
+                 << GetNetworkIdWithGuid(network);
+
+  // Policy application may have wiped e.g. the 'EAP.Identity' field, so
+  // forget the resolved certificate and make sure to re-apply it.
+  ForgetResolvedCert(network);
+
   NetworkStateHandler::NetworkStateList networks;
   networks.push_back(network);
   ResolveNetworks(networks);
 }
 
+void ClientCertResolver::ForgetResolvedCert(const NetworkState* network) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  networks_status_.erase(network->path());
+}
+
 void ClientCertResolver::ResolveNetworks(
     const NetworkStateHandler::NetworkStateList& networks) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::vector<NetworkAndCertConfig> networks_to_resolve;
+  std::vector<std::string> networks_to_resolve_info;
 
   // Filter networks with ClientCertPattern, ClientCertRef, or
   // ProvisioningProfileId. As these can only be set by policy, we check there.
@@ -712,6 +728,7 @@
 
     networks_to_resolve.push_back(
         NetworkAndCertConfig(network->path(), cert_config));
+    networks_to_resolve_info.push_back(GetNetworkIdWithGuid(network));
   }
 
   if (networks_to_resolve.empty()) {
@@ -730,7 +747,8 @@
     return;
   }
 
-  VLOG(2) << "Start task for resolving client cert patterns.";
+  NET_LOG(EVENT) << "Start task for resolving client cert patterns for "
+                 << base::JoinString(networks_to_resolve_info, ", ");
   resolve_task_running_ = true;
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE,
@@ -765,11 +783,23 @@
 void ClientCertResolver::ConfigureCertificates(
     std::vector<NetworkAndMatchingCert> matches) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  std::vector<std::string> resolved_networks_info;
   for (const NetworkAndMatchingCert& match : matches) {
+    const NetworkState* network_state =
+        network_state_handler_->GetNetworkStateFromServicePath(
+            match.service_path, /*configured_only=*/true);
+    if (!network_state) {
+      resolved_networks_info.push_back(
+          "Skipping " + match.service_path +
+          " because it is not configured anymore.");
+      continue;
+    }
     MatchingCertAndResolveStatus& network_status =
         networks_status_[match.service_path];
     if (network_status.resolve_status == ResolveStatus::kResolved &&
         network_status.matching_cert == match.matching_cert) {
+      resolved_networks_info.push_back(GetNetworkIdWithGuid(network_state) +
+                                       ":match,no_change");
       // The same certificate was configured in the last ConfigureCertificates
       // call, so don't do anything for this network.
       continue;
@@ -779,7 +809,7 @@
     network_properties_changed_ = true;
 
     NET_LOG(EVENT) << "Configuring certificate for network: "
-                   << NetworkPathId(match.service_path);
+                   << GetNetworkIdWithGuid(network_state);
 
     base::DictionaryValue shill_properties;
     if (match.matching_cert.has_value()) {
@@ -787,11 +817,16 @@
       client_cert::SetShillProperties(
           match.cert_config_type, matching_cert.key_slot_id,
           matching_cert.pkcs11_id, &shill_properties);
+      resolved_networks_info.push_back(GetNetworkIdWithGuid(network_state) +
+                                       ":match,identity='" +
+                                       matching_cert.identity + "'");
       if (!matching_cert.identity.empty()) {
         shill_properties.SetKey(shill::kEapIdentityProperty,
                                 base::Value(matching_cert.identity));
       }
     } else {
+      resolved_networks_info.push_back(GetNetworkIdWithGuid(network_state) +
+                                       ":no_match");
       client_cert::SetEmptyShillProperties(match.cert_config_type,
                                            &shill_properties);
     }
@@ -800,6 +835,9 @@
         base::DoNothing(), base::BindOnce(&LogError, match.service_path));
     network_state_handler_->RequestUpdateForNetwork(match.service_path);
   }
+
+  NET_LOG(EVENT) << "Summary: "
+                 << base::JoinString(resolved_networks_info, ", ");
   resolve_task_running_ = false;
   if (queued_networks_to_resolve_.empty())
     NotifyResolveRequestCompleted();
diff --git a/chromeos/network/client_cert_resolver.h b/chromeos/network/client_cert_resolver.h
index 81da5ff6..a7508a2a 100644
--- a/chromeos/network/client_cert_resolver.h
+++ b/chromeos/network/client_cert_resolver.h
@@ -114,6 +114,11 @@
   // NetworkPolicyObserver overrides
   void PolicyAppliedToNetwork(const std::string& service_path) override;
 
+  // Forget the resolved certificate of |network| - ensures that the next
+  // ResolveNetwork run will reconfigure the certificate (and EAP Identity
+  // field) even if the certificate has not changed.
+  void ForgetResolvedCert(const NetworkState* network);
+
   // Check which networks of |networks| are configured with a client certificate
   // pattern. Search for certificates, on the worker thread, and configure the
   // networks for which a matching cert is found (see ConfigureCertificates).
diff --git a/chromeos/network/client_cert_resolver_unittest.cc b/chromeos/network/client_cert_resolver_unittest.cc
index aa07f02..5443d18 100644
--- a/chromeos/network/client_cert_resolver_unittest.cc
+++ b/chromeos/network/client_cert_resolver_unittest.cc
@@ -41,11 +41,15 @@
 #include "net/cert/x509_util_nss.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
 namespace chromeos {
 
+using ::testing::IsEmpty;
+using ::testing::Not;
+
 namespace {
 
 constexpr char kWifiStub[] = "wifi_stub";
@@ -379,14 +383,12 @@
             policy_json,
             base::JSON_ALLOW_TRAILING_COMMAS | base::JSON_ALLOW_CONTROL_CHARS);
     ASSERT_TRUE(parsed_json.value) << parsed_json.error_message;
-
-    base::ListValue* policy = nullptr;
-    ASSERT_TRUE(parsed_json.value->GetAsList(&policy));
+    ASSERT_TRUE(parsed_json.value->is_list());
 
     std::string user_hash =
         onc_source == ::onc::ONC_SOURCE_USER_POLICY ? kUserHash : "";
     managed_config_handler_->SetPolicy(
-        onc_source, user_hash, *policy,
+        onc_source, user_hash, *parsed_json.value,
         base::DictionaryValue() /* no global network config */);
   }
 
@@ -498,6 +500,7 @@
       ++network_properties_changed_count_;
   }
 
+ protected:
   ShillServiceClient::TestInterface* service_test_ = nullptr;
   ShillProfileClient::TestInterface* profile_test_ = nullptr;
   std::unique_ptr<NetworkStateHandler> network_state_handler_;
@@ -892,6 +895,59 @@
   EXPECT_EQ(test_cert_id_, pkcs11_id);
 }
 
+// Cert and Identity are reconfigured after policy has been applied.
+// Regression test for b/209084821 .
+TEST_F(ClientCertResolverTest, ReresolveAfterPolicyApplication) {
+  SetupTestCerts("client_3", true /* import issuer */);
+  SetupWifi();
+  task_environment_.RunUntilIdle();
+
+  SetupNetworkHandlers();
+  ASSERT_NO_FATAL_FAILURE(SetupPolicyMatchingIssuerPEM(
+      ::onc::ONC_SOURCE_USER_POLICY, "${CERT_SAN_UPN}"));
+  task_environment_.RunUntilIdle();
+
+  network_properties_changed_count_ = 0;
+  StartNetworkCertLoader();
+  task_environment_.RunUntilIdle();
+
+  // Verify that cert id and identity have been resolved
+  {
+    EXPECT_EQ(1, network_properties_changed_count_);
+
+    std::string pkcs11_id;
+    GetServiceProperty(shill::kEapCertIdProperty, &pkcs11_id);
+    EXPECT_THAT(pkcs11_id, Not(IsEmpty()));
+
+    std::string identity;
+    GetServiceProperty(shill::kEapIdentityProperty, &identity);
+    EXPECT_EQ(identity, "santest@ad.corp.example.com");
+  }
+
+  // Mangle cert id and identity
+  ASSERT_TRUE(service_test_->SetServiceProperty(
+      kWifiStub, shill::kEapCertIdProperty, base::Value("modified_cert_id")));
+  ASSERT_TRUE(service_test_->SetServiceProperty(
+      kWifiStub, shill::kEapIdentityProperty, base::Value(std::string())));
+
+  // Pretend that network policy was (re)applied. This should trigger
+  // re-configuration of the cert and EAP.Identity.
+  static_cast<NetworkPolicyObserver*>(client_cert_resolver_.get())
+      ->PolicyAppliedToNetwork(kWifiStub);
+  task_environment_.RunUntilIdle();
+  {
+    EXPECT_EQ(2, network_properties_changed_count_);
+
+    std::string pkcs11_id;
+    GetServiceProperty(shill::kEapCertIdProperty, &pkcs11_id);
+    EXPECT_THAT(pkcs11_id, Not(IsEmpty()));
+
+    std::string identity;
+    GetServiceProperty(shill::kEapIdentityProperty, &identity);
+    EXPECT_EQ(identity, "santest@ad.corp.example.com");
+  }
+}
+
 // Tests that a ClientCertRef reference is resolved by |ClientCertResolver|.
 // Uses the |CertificateImporterImpl| to import the client certificate from ONC
 // policy, ensuring that setting the cert's key's nickname (in the import step)
diff --git a/chromeos/network/network_connection_handler_impl_unittest.cc b/chromeos/network/network_connection_handler_impl_unittest.cc
index 1bc475b..e311b72 100644
--- a/chromeos/network/network_connection_handler_impl_unittest.cc
+++ b/chromeos/network/network_connection_handler_impl_unittest.cc
@@ -342,18 +342,16 @@
         base::JSONReader::ReadAndReturnValueWithError(
             network_configs_json, base::JSON_ALLOW_TRAILING_COMMAS);
     ASSERT_TRUE(parsed_json.value) << parsed_json.error_message;
-
-    base::ListValue* network_configs = nullptr;
-    ASSERT_TRUE(parsed_json.value->GetAsList(&network_configs));
+    ASSERT_TRUE(parsed_json.value->is_list());
 
     if (user_policy) {
       managed_config_handler_->SetPolicy(::onc::ONC_SOURCE_USER_POLICY,
-                                         helper_.UserHash(), *network_configs,
+                                         helper_.UserHash(), *parsed_json.value,
                                          global_config);
     } else {
       managed_config_handler_->SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY,
                                          std::string(),  // no username hash
-                                         *network_configs, global_config);
+                                         *parsed_json.value, global_config);
     }
     task_environment_.RunUntilIdle();
   }
diff --git a/chromeos/network/network_state_handler.cc b/chromeos/network/network_state_handler.cc
index 49ef0910..4c6cbd0 100644
--- a/chromeos/network/network_state_handler.cc
+++ b/chromeos/network/network_state_handler.cc
@@ -10,6 +10,7 @@
 #include <memory>
 #include <utility>
 
+#include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/containers/contains.h"
@@ -1321,6 +1322,8 @@
   }
 
   UpdateManagedWifiNetworkAvailable();
+  if (features::IsESimPolicyEnabled())
+    UpdateBlockedNetworksInternal(NetworkTypePattern::Cellular());
 
   if (type != ManagedState::ManagedType::MANAGED_TYPE_NETWORK)
     return;
@@ -1575,6 +1578,10 @@
 
     if (device->type() == shill::kTypeWifi && !device->scanning())
       UpdateManagedWifiNetworkAvailable();
+    if (device->type() == shill::kTypeCellular && !device->scanning() &&
+        features::IsESimPolicyEnabled()) {
+      UpdateBlockedNetworksInternal(NetworkTypePattern::Cellular());
+    }
   }
   if (key == shill::kEapAuthenticationCompletedProperty) {
     // Notify a change for each Ethernet service using this device.
@@ -1661,6 +1668,8 @@
       UpdateNetworkStats();
       NotifyIfActiveNetworksChanged();
       NotifyNetworkListChanged();
+      if (features::IsESimPolicyEnabled())
+        UpdateBlockedNetworksInternal(NetworkTypePattern::Cellular());
       UpdateManagedWifiNetworkAvailable();
       // ManagedStateListChanged only gets executed if all pending updates have
       // completed. Profile networks are loaded if a user is logged in and all
diff --git a/chromeos/network/network_state_handler.h b/chromeos/network/network_state_handler.h
index 8e2f2699..840bc13d 100644
--- a/chromeos/network/network_state_handler.h
+++ b/chromeos/network/network_state_handler.h
@@ -545,6 +545,8 @@
   FRIEND_TEST_ALL_PREFIXES(NetworkStateHandlerTest, SyncStubCellularNetworks);
   FRIEND_TEST_ALL_PREFIXES(NetworkStateHandlerTest,
                            GetNetworkListAfterUpdateManagedList);
+  FRIEND_TEST_ALL_PREFIXES(NetworkStateHandlerTest,
+                           UpdateBlockedCellularNetworkAfterUpdateManagedList);
 
   // Implementation for GetNetworkListByType and GetActiveNetworkListByType.
   void GetNetworkListByTypeImpl(const NetworkTypePattern& type,
diff --git a/chromeos/network/network_state_handler_unittest.cc b/chromeos/network/network_state_handler_unittest.cc
index f49287b..e2f60586 100644
--- a/chromeos/network/network_state_handler_unittest.cc
+++ b/chromeos/network/network_state_handler_unittest.cc
@@ -12,12 +12,14 @@
 #include <set>
 #include <string>
 
+#include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/values.h"
 #include "chromeos/dbus/shill/shill_clients.h"
@@ -2439,6 +2441,49 @@
   EXPECT_TRUE(cellular2->blocked_by_policy());
 }
 
+TEST_F(NetworkStateHandlerTest,
+       UpdateBlockedCellularNetworkAfterUpdateManagedList) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(ash::features::kESimPolicy);
+  const char kTestCellularServicePath2[] = "test_cellular_service_path2";
+  const char kTestCellularServiceGuid2[] = "test_cellular_guid2";
+  const char kTestCellularServiceName2[] = "test_cellular2";
+  AddService(kTestCellularServicePath2, kTestCellularServiceGuid2,
+             kTestCellularServiceName2, shill::kTypeCellular,
+             shill::kStateIdle);
+  base::RunLoop().RunUntilIdle();
+
+  NetworkState* cellular1 = network_state_handler_->GetModifiableNetworkState(
+      kShillManagerClientStubCellular);
+  NetworkState* cellular2 = network_state_handler_->GetModifiableNetworkState(
+      kTestCellularServicePath2);
+  EXPECT_FALSE(cellular1->IsManagedByPolicy());
+  EXPECT_FALSE(cellular1->blocked_by_policy());
+  EXPECT_FALSE(cellular2->IsManagedByPolicy());
+  EXPECT_FALSE(cellular2->blocked_by_policy());
+
+  network_state_handler_->allow_only_policy_cellular_networks_to_connect_ =
+      true;
+  network_state_handler_->UpdateManagedList(
+      ManagedState::ManagedType::MANAGED_TYPE_NETWORK,
+      manager_test_->GetEnabledServiceList());
+  EXPECT_TRUE(cellular1->blocked_by_policy());
+  EXPECT_TRUE(cellular2->blocked_by_policy());
+
+  // Emulate 'cellular1' being a managed network.
+  std::unique_ptr<NetworkUIData> ui_data =
+      NetworkUIData::CreateFromONC(::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY);
+  base::Value properties(base::Value::Type::DICTIONARY);
+  properties.SetKey(shill::kProfileProperty, base::Value(kProfilePath));
+  properties.SetKey(shill::kUIDataProperty, base::Value(ui_data->GetAsJson()));
+  SetProperties(cellular1, properties);
+
+  EXPECT_TRUE(cellular1->IsManagedByPolicy());
+  EXPECT_FALSE(cellular1->blocked_by_policy());
+  EXPECT_FALSE(cellular2->IsManagedByPolicy());
+  EXPECT_TRUE(cellular2->blocked_by_policy());
+}
+
 TEST_F(NetworkStateHandlerTest, BlockedWifiByPolicyOnlyManagedIfAvailable) {
   NetworkState* wifi1 = network_state_handler_->GetModifiableNetworkState(
       kShillManagerClientStubDefaultWifi);
diff --git a/chromeos/network/policy_applicator.cc b/chromeos/network/policy_applicator.cc
index f57dc038..b1b58482 100644
--- a/chromeos/network/policy_applicator.cc
+++ b/chromeos/network/policy_applicator.cc
@@ -327,13 +327,13 @@
   if (old_guid == new_guid &&
       shill_property_util::DoIdentifyingPropertiesMatch(
           new_shill_properties, *entry_properties_as_dict)) {
-    VLOG(1) << "Updating previously managed configuration with the "
-            << "updated policy " << new_guid << ".";
+    NET_LOG(EVENT) << "Updating previously managed configuration with the "
+                   << "updated policy " << new_guid << ".";
     WriteNewShillConfiguration(std::move(new_shill_properties),
                                new_policy.Clone(), std::move(callback));
   } else {
-    VLOG(1) << "Deleting profile entry before writing new policy " << new_guid
-            << " because of identifying properties changed.";
+    NET_LOG(EVENT) << "Deleting profile entry before writing new policy "
+                   << new_guid << " because of identifying properties changed.";
     // In general, old entries should at first be deleted before new
     // configurations are written to prevent inconsistencies. Therefore, we
     // delay the writing of the new config here until ~PolicyApplicator.
@@ -368,7 +368,8 @@
     std::move(callback).Run();
     return;
   }
-  VLOG(2) << "Apply global network config to unmanaged entry.";
+  NET_LOG(EVENT) << "Apply global network config to unmanaged entry "
+                 << entry_identifier << ".";
   const base::DictionaryValue* entry_properties_as_dict = nullptr;
   entry_properties.GetAsDictionary(&entry_properties_as_dict);
   DCHECK(entry_properties_as_dict);
@@ -446,8 +447,8 @@
         GetByGUID(all_policies_, guid);
     DCHECK(network_policy);
 
-    VLOG(1) << "Creating new configuration managed by policy " << *it
-            << " in profile " << profile_.ToDebugString() << ".";
+    NET_LOG(EVENT) << "Creating new configuration managed by policy " << guid
+                   << " in profile " << profile_.ToDebugString() << ".";
 
     if (IsCellularPolicy(*network_policy)) {
       const std::string* smdp_address = GetSMDPAddressFromONC(*network_policy);
diff --git a/chromeos/services/bluetooth_config/BUILD.gn b/chromeos/services/bluetooth_config/BUILD.gn
index fe6d89a..b859d8c 100644
--- a/chromeos/services/bluetooth_config/BUILD.gn
+++ b/chromeos/services/bluetooth_config/BUILD.gn
@@ -164,7 +164,7 @@
     "//ash/constants",
     "//base",
     "//base/test:test_support",
-    "//chromeos/services/bluetooth_config/public/cpp:unit_tests",
+    "//chromeos/services/bluetooth_config/public/cpp",
     "//components/session_manager/core",
     "//components/sync_preferences:test_support",
     "//components/user_manager:test_support",
diff --git a/chromeos/services/bluetooth_config/cros_bluetooth_config.cc b/chromeos/services/bluetooth_config/cros_bluetooth_config.cc
index 7ad73cf..9692606a 100644
--- a/chromeos/services/bluetooth_config/cros_bluetooth_config.cc
+++ b/chromeos/services/bluetooth_config/cros_bluetooth_config.cc
@@ -31,7 +31,8 @@
       device_cache_(
           initializer.CreateDeviceCache(adapter_state_controller_.get(),
                                         bluetooth_adapter,
-                                        device_name_manager_.get())),
+                                        device_name_manager_.get(),
+                                        fast_pair_delegate)),
       system_properties_provider_(
           std::make_unique<SystemPropertiesProviderImpl>(
               adapter_state_controller_.get(),
diff --git a/chromeos/services/bluetooth_config/device_cache_impl.cc b/chromeos/services/bluetooth_config/device_cache_impl.cc
index 2fc57b1..bdd2d80 100644
--- a/chromeos/services/bluetooth_config/device_cache_impl.cc
+++ b/chromeos/services/bluetooth_config/device_cache_impl.cc
@@ -15,7 +15,9 @@
 
 DeviceCacheImpl::UnpairedDevice::UnpairedDevice(
     const device::BluetoothDevice* device)
-    : device_properties(GenerateBluetoothDeviceMojoProperties(device)),
+    : device_properties(GenerateBluetoothDeviceMojoProperties(
+          device,
+          /*fast_pair_delegate=*/nullptr)),
       inquiry_rssi(device->GetInquiryRSSI()) {}
 
 DeviceCacheImpl::UnpairedDevice::~UnpairedDevice() = default;
@@ -23,10 +25,12 @@
 DeviceCacheImpl::DeviceCacheImpl(
     AdapterStateController* adapter_state_controller_param,
     scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
-    DeviceNameManager* device_name_manager)
+    DeviceNameManager* device_name_manager,
+    FastPairDelegate* fast_pair_delegate)
     : DeviceCache(adapter_state_controller_param),
       bluetooth_adapter_(std::move(bluetooth_adapter)),
-      device_name_manager_(device_name_manager) {
+      device_name_manager_(device_name_manager),
+      fast_pair_delegate_(fast_pair_delegate) {
   adapter_state_controller_observation_.Observe(adapter_state_controller());
   adapter_observation_.Observe(bluetooth_adapter_.get());
   device_name_manager_observation_.Observe(device_name_manager_);
@@ -293,7 +297,8 @@
     const device::BluetoothDevice* device) {
   mojom::PairedBluetoothDevicePropertiesPtr properties =
       mojom::PairedBluetoothDeviceProperties::New();
-  properties->device_properties = GenerateBluetoothDeviceMojoProperties(device);
+  properties->device_properties =
+      GenerateBluetoothDeviceMojoProperties(device, fast_pair_delegate_);
   properties->nickname =
       device_name_manager_->GetDeviceNickname(device->GetIdentifier());
   return properties;
diff --git a/chromeos/services/bluetooth_config/device_cache_impl.h b/chromeos/services/bluetooth_config/device_cache_impl.h
index 9702511..e88f8bdd 100644
--- a/chromeos/services/bluetooth_config/device_cache_impl.h
+++ b/chromeos/services/bluetooth_config/device_cache_impl.h
@@ -10,6 +10,7 @@
 #include "chromeos/services/bluetooth_config/adapter_state_controller.h"
 #include "chromeos/services/bluetooth_config/device_cache.h"
 #include "chromeos/services/bluetooth_config/device_name_manager.h"
+#include "chromeos/services/bluetooth_config/fast_pair_delegate.h"
 #include "chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 
@@ -30,7 +31,8 @@
  public:
   DeviceCacheImpl(AdapterStateController* adapter_state_controller,
                   scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
-                  DeviceNameManager* device_name_manager);
+                  DeviceNameManager* device_name_manager,
+                  FastPairDelegate* fast_pair_delegate);
   ~DeviceCacheImpl() override;
 
  private:
@@ -136,6 +138,7 @@
 
   scoped_refptr<device::BluetoothAdapter> bluetooth_adapter_;
   DeviceNameManager* device_name_manager_;
+  FastPairDelegate* fast_pair_delegate_;
 
   // Sorted by connection status.
   std::vector<mojom::PairedBluetoothDevicePropertiesPtr> paired_devices_;
diff --git a/chromeos/services/bluetooth_config/device_cache_impl_unittest.cc b/chromeos/services/bluetooth_config/device_cache_impl_unittest.cc
index 6ef722f..6c002ea 100644
--- a/chromeos/services/bluetooth_config/device_cache_impl_unittest.cc
+++ b/chromeos/services/bluetooth_config/device_cache_impl_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/test/task_environment.h"
 #include "chromeos/services/bluetooth_config/fake_adapter_state_controller.h"
 #include "chromeos/services/bluetooth_config/fake_device_name_manager.h"
+#include "chromeos/services/bluetooth_config/fake_fast_pair_delegate.h"
 #include "device/bluetooth/bluetooth_common.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "device/bluetooth/test/mock_bluetooth_device.h"
@@ -83,7 +84,7 @@
   void Init() {
     device_cache_ = std::make_unique<DeviceCacheImpl>(
         &fake_adapter_state_controller_, mock_adapter_,
-        &fake_device_name_manager_);
+        &fake_device_name_manager_, &fake_fast_pair_delegate_);
     device_cache_->AddObserver(&fake_observer_);
   }
 
@@ -238,6 +239,7 @@
 
   FakeAdapterStateController fake_adapter_state_controller_;
   FakeDeviceNameManager fake_device_name_manager_;
+  FakeFastPairDelegate fake_fast_pair_delegate_;
   scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> mock_adapter_;
   FakeObserver fake_observer_;
 
diff --git a/chromeos/services/bluetooth_config/device_conversion_util.cc b/chromeos/services/bluetooth_config/device_conversion_util.cc
index 12257d59..cbcf89d9 100644
--- a/chromeos/services/bluetooth_config/device_conversion_util.cc
+++ b/chromeos/services/bluetooth_config/device_conversion_util.cc
@@ -7,9 +7,12 @@
 #include <bitset>
 
 #include "base/strings/utf_string_conversions.h"
+#include "chromeos/services/bluetooth_config/fast_pair_delegate.h"
+#include "chromeos/services/bluetooth_config/public/cpp/device_image_info.h"
 #include "device/bluetooth/bluetooth_common.h"
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/string_util_icu.h"
+#include "url/gurl.h"
 
 namespace chromeos {
 namespace bluetooth_config {
@@ -149,10 +152,55 @@
   return base::UTF8ToUTF16(device->GetAddress());
 }
 
+mojom::DeviceImageInfoPtr ComputeImageInfo(
+    const device::BluetoothDevice* device,
+    FastPairDelegate* fast_pair_delegate) {
+  absl::optional<DeviceImageInfo> images =
+      fast_pair_delegate->GetDeviceImageInfo(device->GetIdentifier());
+  if (!images) {
+    return nullptr;
+  }
+
+  mojom::DeviceImageInfoPtr device_image_info = mojom::DeviceImageInfo::New();
+  if (!images->default_image().empty()) {
+    GURL default_image_url = GURL(images->default_image());
+    DCHECK(default_image_url.is_valid() &&
+           default_image_url.SchemeIs(url::kDataScheme));
+    device_image_info->default_image_url = default_image_url;
+  }
+
+  // Only add true_wireless_images if all fields are non-null.
+  if (images->left_bud_image().empty() || images->right_bud_image().empty() ||
+      images->case_image().empty()) {
+    return device_image_info;
+  }
+
+  mojom::TrueWirelessImageInfoPtr true_wireless_images =
+      mojom::TrueWirelessImageInfo::New();
+  GURL left_bud_image_url = GURL(images->left_bud_image());
+  DCHECK(left_bud_image_url.is_valid() &&
+         left_bud_image_url.SchemeIs(url::kDataScheme));
+  true_wireless_images->left_bud_image_url = GURL(images->left_bud_image());
+
+  GURL right_bud_image_url = GURL(images->right_bud_image());
+  DCHECK(right_bud_image_url.is_valid() &&
+         right_bud_image_url.SchemeIs(url::kDataScheme));
+  true_wireless_images->right_bud_image_url = GURL(images->right_bud_image());
+
+  GURL case_image_url = GURL(images->case_image());
+  DCHECK(case_image_url.is_valid() &&
+         case_image_url.SchemeIs(url::kDataScheme));
+  true_wireless_images->case_image_url = GURL(images->case_image());
+
+  device_image_info->true_wireless_images = std::move(true_wireless_images);
+  return device_image_info;
+}
+
 }  // namespace
 
 mojom::BluetoothDevicePropertiesPtr GenerateBluetoothDeviceMojoProperties(
-    const device::BluetoothDevice* device) {
+    const device::BluetoothDevice* device,
+    FastPairDelegate* fast_pair_delegate) {
   auto properties = mojom::BluetoothDeviceProperties::New();
   properties->id = device->GetIdentifier();
   properties->address = device->GetAddress();
@@ -162,6 +210,8 @@
   properties->battery_info = ComputeBatteryInfo(device);
   properties->connection_state = ComputeConnectionState(device);
   properties->is_blocked_by_policy = device->IsBlockedByPolicy();
+  if (fast_pair_delegate)
+    properties->image_info = ComputeImageInfo(device, fast_pair_delegate);
   return properties;
 }
 
diff --git a/chromeos/services/bluetooth_config/device_conversion_util.h b/chromeos/services/bluetooth_config/device_conversion_util.h
index fb3360ba..8af602c 100644
--- a/chromeos/services/bluetooth_config/device_conversion_util.h
+++ b/chromeos/services/bluetooth_config/device_conversion_util.h
@@ -14,8 +14,13 @@
 namespace chromeos {
 namespace bluetooth_config {
 
+class FastPairDelegate;
+
+// If |fast_pair_delegate| is non-null it will load device images into
+// the returned BluetoothDevicePropertiesPtr.
 mojom::BluetoothDevicePropertiesPtr GenerateBluetoothDeviceMojoProperties(
-    const device::BluetoothDevice* device);
+    const device::BluetoothDevice* device,
+    FastPairDelegate* fast_pair_delegate);
 
 }  // namespace bluetooth_config
 }  // namespace chromeos
diff --git a/chromeos/services/bluetooth_config/device_conversion_util_unittest.cc b/chromeos/services/bluetooth_config/device_conversion_util_unittest.cc
index a9d7ad3..79b00b3 100644
--- a/chromeos/services/bluetooth_config/device_conversion_util_unittest.cc
+++ b/chromeos/services/bluetooth_config/device_conversion_util_unittest.cc
@@ -7,20 +7,33 @@
 #include <memory>
 
 #include "base/strings/utf_string_conversions.h"
+#include "chromeos/services/bluetooth_config/fake_fast_pair_delegate.h"
+#include "chromeos/services/bluetooth_config/public/cpp/device_image_info.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "device/bluetooth/test/mock_bluetooth_device.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
 
 namespace chromeos {
 namespace bluetooth_config {
 
+namespace {
+
+constexpr char kTestDefaultImage[] = "data:image/png;base64,TestDefaultImage";
+constexpr char kTestLeftBudImage[] = "data:image/png;base64,TestLeftBudImage";
+constexpr char kTestRightBudImage[] = "data:image/png;base64,TestRightBudImage";
+constexpr char kTestCaseImage[] = "data:image/png;base64,TestCaseImage";
+
+}  // namespace
+
 // Tests basic usage of the GenerateBluetoothDeviceMojoProperties() conversion
 // function. Not meant to be an exhaustive test of each possible Bluetooth
 // property.
 class DeviceConversionUtilTest : public testing::Test {
  protected:
-  DeviceConversionUtilTest() = default;
+  DeviceConversionUtilTest()
+      : fake_fast_pair_delegate_(FakeFastPairDelegate()) {}
   DeviceConversionUtilTest(const DeviceConversionUtilTest&) = delete;
   DeviceConversionUtilTest& operator=(const DeviceConversionUtilTest&) = delete;
   ~DeviceConversionUtilTest() override = default;
@@ -51,9 +64,14 @@
         .WillByDefault(testing::Return(connected));
   }
 
+  FakeFastPairDelegate* fake_fast_pair_delegate() {
+    return &fake_fast_pair_delegate_;
+  }
+
  private:
   scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> mock_adapter_;
   std::unique_ptr<testing::NiceMock<device::MockBluetoothDevice>> mock_device_;
+  FakeFastPairDelegate fake_fast_pair_delegate_;
 };
 
 TEST_F(DeviceConversionUtilTest, TestConversion) {
@@ -61,7 +79,8 @@
       /*bluetooth_class=*/0u, /*name=*/"name", /*address=*/"address",
       /*paired=*/true, /*connected=*/true, /*is_blocked_by_policy=*/true);
   mojom::BluetoothDevicePropertiesPtr properties =
-      GenerateBluetoothDeviceMojoProperties(device);
+      GenerateBluetoothDeviceMojoProperties(device,
+                                            /*fast_pair_delegate=*/nullptr);
   ASSERT_TRUE(properties);
   EXPECT_EQ("address-Identifier", properties->id);
   EXPECT_EQ("address", properties->address);
@@ -80,7 +99,8 @@
       /*bluetooth_class=*/0u, /*name=*/nullptr, /*address=*/"address",
       /*paired=*/true, /*connected=*/true, /*is_blocked_by_policy=*/true);
   mojom::BluetoothDevicePropertiesPtr properties =
-      GenerateBluetoothDeviceMojoProperties(device);
+      GenerateBluetoothDeviceMojoProperties(device,
+                                            /*fast_pair_delegate=*/nullptr);
   ASSERT_TRUE(properties);
 
   // A device with no name should have its name set as its address.
@@ -100,7 +120,8 @@
   device->SetBatteryInfo(battery_info);
 
   mojom::BluetoothDevicePropertiesPtr properties =
-      GenerateBluetoothDeviceMojoProperties(device);
+      GenerateBluetoothDeviceMojoProperties(device,
+                                            /*fast_pair_delegate=*/nullptr);
   ASSERT_TRUE(properties);
 
   // If device is not connected, |battery_info| should be null.
@@ -109,7 +130,8 @@
   // Set the device to connected.
   ChangeDeviceConnected(/*connected=*/true);
 
-  properties = GenerateBluetoothDeviceMojoProperties(device);
+  properties = GenerateBluetoothDeviceMojoProperties(
+      device, /*fast_pair_delegate=*/nullptr);
   ASSERT_TRUE(properties);
 
   EXPECT_TRUE(properties->battery_info->default_properties);
@@ -149,7 +171,8 @@
   device->SetBatteryInfo(case_battery_info);
 
   mojom::BluetoothDevicePropertiesPtr properties =
-      GenerateBluetoothDeviceMojoProperties(device);
+      GenerateBluetoothDeviceMojoProperties(device,
+                                            /*fast_pair_delegate=*/nullptr);
   ASSERT_TRUE(properties);
 
   // If device is not connected, |battery_info| should be null.
@@ -158,7 +181,8 @@
   // Set the device to connected.
   ChangeDeviceConnected(/*connected=*/true);
 
-  properties = GenerateBluetoothDeviceMojoProperties(device);
+  properties = GenerateBluetoothDeviceMojoProperties(
+      device, /*fast_pair_delegate=*/nullptr);
   ASSERT_TRUE(properties);
 
   EXPECT_FALSE(properties->battery_info->default_properties);
@@ -170,5 +194,88 @@
   EXPECT_EQ(properties->battery_info->case_info->battery_percentage, 50);
 }
 
+TEST_F(DeviceConversionUtilTest, TestConversion_NoImages) {
+  device::BluetoothDevice* device = InitDevice(
+      /*bluetooth_class=*/0u, /*name=*/"name", /*address=*/"address",
+      /*paired=*/true, /*connected=*/false, /*is_blocked_by_policy=*/false);
+
+  mojom::BluetoothDevicePropertiesPtr properties =
+      GenerateBluetoothDeviceMojoProperties(device, fake_fast_pair_delegate());
+  EXPECT_TRUE(properties);
+
+  EXPECT_FALSE(properties->image_info);
+}
+
+TEST_F(DeviceConversionUtilTest, TestConversion_DefaultDeviceImage) {
+  device::BluetoothDevice* device = InitDevice(
+      /*bluetooth_class=*/0u, /*name=*/"name", /*address=*/"address",
+      /*paired=*/true, /*connected=*/false, /*is_blocked_by_policy=*/false);
+  // Save a default image to be returned by the delegate.
+  DeviceImageInfo images = DeviceImageInfo(
+      /*default_image=*/kTestDefaultImage, /*left_bud_image=*/"",
+      /*right_bud_image=*/"", /*case_image=*/"");
+  // MockBluetoothDevice sets identifier as |address|-Identifier.
+  fake_fast_pair_delegate()->SetDeviceImageInfo("address-Identifier", images);
+
+  mojom::BluetoothDevicePropertiesPtr properties =
+      GenerateBluetoothDeviceMojoProperties(device, fake_fast_pair_delegate());
+  EXPECT_TRUE(properties);
+
+  EXPECT_TRUE(properties->image_info);
+  EXPECT_TRUE(properties->image_info->default_image_url);
+  EXPECT_EQ(properties->image_info->default_image_url, GURL(kTestDefaultImage));
+  EXPECT_FALSE(properties->image_info->true_wireless_images);
+}
+
+TEST_F(DeviceConversionUtilTest, TestConversion_TrueWirelessImages) {
+  device::BluetoothDevice* device = InitDevice(
+      /*bluetooth_class=*/0u, /*name=*/"name", /*address=*/"address",
+      /*paired=*/true, /*connected=*/false, /*is_blocked_by_policy=*/false);
+  // Save a default image and True Wireless images to be returned by the
+  // delegate.
+  DeviceImageInfo images = DeviceImageInfo(
+      /*default_image=*/kTestDefaultImage, /*left_bud_image=*/kTestLeftBudImage,
+      /*right_bud_image=*/kTestRightBudImage, /*case_image=*/kTestCaseImage);
+  // MockBluetoothDevice sets identifier as |address|-Identifier.
+  fake_fast_pair_delegate()->SetDeviceImageInfo("address-Identifier", images);
+
+  mojom::BluetoothDevicePropertiesPtr properties =
+      GenerateBluetoothDeviceMojoProperties(device, fake_fast_pair_delegate());
+  EXPECT_TRUE(properties);
+
+  EXPECT_TRUE(properties->image_info);
+  EXPECT_TRUE(properties->image_info->default_image_url);
+  EXPECT_EQ(properties->image_info->default_image_url, GURL(kTestDefaultImage));
+  EXPECT_TRUE(properties->image_info->true_wireless_images);
+  EXPECT_EQ(properties->image_info->true_wireless_images->left_bud_image_url,
+            GURL(kTestLeftBudImage));
+  EXPECT_EQ(properties->image_info->true_wireless_images->right_bud_image_url,
+            GURL(kTestRightBudImage));
+  EXPECT_EQ(properties->image_info->true_wireless_images->case_image_url,
+            GURL(kTestCaseImage));
+}
+
+TEST_F(DeviceConversionUtilTest, TestConversion_PartialTrueWirelessImages) {
+  device::BluetoothDevice* device = InitDevice(
+      /*bluetooth_class=*/0u, /*name=*/"name", /*address=*/"address",
+      /*paired=*/true, /*connected=*/false, /*is_blocked_by_policy=*/false);
+  // Simulate the case image being missing.
+  DeviceImageInfo images = DeviceImageInfo(
+      /*default_image=*/kTestDefaultImage, /*left_bud_image=*/kTestLeftBudImage,
+      /*right_bud_image=*/kTestRightBudImage, /*case_image=*/"");
+  // MockBluetoothDevice sets identifier as |address|-Identifier.
+  fake_fast_pair_delegate()->SetDeviceImageInfo("address-Identifier", images);
+
+  mojom::BluetoothDevicePropertiesPtr properties =
+      GenerateBluetoothDeviceMojoProperties(device, fake_fast_pair_delegate());
+  EXPECT_TRUE(properties);
+
+  EXPECT_TRUE(properties->image_info);
+  EXPECT_TRUE(properties->image_info->default_image_url);
+  EXPECT_EQ(properties->image_info->default_image_url, GURL(kTestDefaultImage));
+  // True Wireless images should only display if the full set exists.
+  EXPECT_FALSE(properties->image_info->true_wireless_images);
+}
+
 }  // namespace bluetooth_config
 }  // namespace chromeos
diff --git a/chromeos/services/bluetooth_config/device_name_manager_impl.cc b/chromeos/services/bluetooth_config/device_name_manager_impl.cc
index 8bf786aa..5f602e5d 100644
--- a/chromeos/services/bluetooth_config/device_name_manager_impl.cc
+++ b/chromeos/services/bluetooth_config/device_name_manager_impl.cc
@@ -83,7 +83,9 @@
   }
 
   base::DictionaryValue* device_id_to_nickname_map =
-      DictionaryPrefUpdate(local_state_, kDeviceIdToNicknameMapPrefName).Get();
+      DictionaryPrefUpdateDeprecated(local_state_,
+                                     kDeviceIdToNicknameMapPrefName)
+          .Get();
   DCHECK(device_id_to_nickname_map)
       << "Device ID to nickname map pref is unregistered.";
   device_id_to_nickname_map->SetStringKey(device_id, nickname);
@@ -100,7 +102,9 @@
   }
 
   base::DictionaryValue* device_id_to_nickname_map =
-      DictionaryPrefUpdate(local_state_, kDeviceIdToNicknameMapPrefName).Get();
+      DictionaryPrefUpdateDeprecated(local_state_,
+                                     kDeviceIdToNicknameMapPrefName)
+          .Get();
   DCHECK(device_id_to_nickname_map)
       << "Device ID to nickname map pref is unregistered.";
 
diff --git a/chromeos/services/bluetooth_config/discovered_devices_provider_impl_unittest.cc b/chromeos/services/bluetooth_config/discovered_devices_provider_impl_unittest.cc
index e6f13b2..98dfb899 100644
--- a/chromeos/services/bluetooth_config/discovered_devices_provider_impl_unittest.cc
+++ b/chromeos/services/bluetooth_config/discovered_devices_provider_impl_unittest.cc
@@ -151,8 +151,8 @@
   void UpdateDeviceCache() {
     std::vector<mojom::BluetoothDevicePropertiesPtr> unpaired_devices;
     for (auto& device : mock_devices_) {
-      unpaired_devices.push_back(
-          GenerateBluetoothDeviceMojoProperties(device.get()));
+      unpaired_devices.push_back(GenerateBluetoothDeviceMojoProperties(
+          device.get(), /*fast_pair_delegate=*/nullptr));
     }
     fake_device_cache_.SetUnpairedDevices(std::move(unpaired_devices));
   }
diff --git a/chromeos/services/bluetooth_config/discovery_session_manager_impl_unittest.cc b/chromeos/services/bluetooth_config/discovery_session_manager_impl_unittest.cc
index 0be7a21..8ce64d1a 100644
--- a/chromeos/services/bluetooth_config/discovery_session_manager_impl_unittest.cc
+++ b/chromeos/services/bluetooth_config/discovery_session_manager_impl_unittest.cc
@@ -154,8 +154,8 @@
     // Add the device to the discovered devices provider's discovered devices.
     std::vector<mojom::BluetoothDevicePropertiesPtr> discovered_devices;
     for (auto& device : mock_devices_) {
-      discovered_devices.push_back(
-          GenerateBluetoothDeviceMojoProperties(device.get()));
+      discovered_devices.push_back(GenerateBluetoothDeviceMojoProperties(
+          device.get(), /*fast_pair_delegate=*/nullptr));
     }
     fake_discovered_devices_provider_.SetDiscoveredDevices(
         std::move(discovered_devices));
diff --git a/chromeos/services/bluetooth_config/fake_device_operation_handler.cc b/chromeos/services/bluetooth_config/fake_device_operation_handler.cc
index a467ed7a..d1c51f0 100644
--- a/chromeos/services/bluetooth_config/fake_device_operation_handler.cc
+++ b/chromeos/services/bluetooth_config/fake_device_operation_handler.cc
@@ -17,7 +17,10 @@
   HandleFinishedOperation(success);
 }
 
-void FakeDeviceOperationHandler::PerformConnect(const std::string& device_id) {}
+void FakeDeviceOperationHandler::PerformConnect(const std::string& device_id) {
+  perform_connect_call_count_++;
+  last_perform_connect_device_id_ = device_id;
+}
 
 void FakeDeviceOperationHandler::PerformDisconnect(
     const std::string& device_id) {}
diff --git a/chromeos/services/bluetooth_config/fake_device_operation_handler.h b/chromeos/services/bluetooth_config/fake_device_operation_handler.h
index 1a73092..ee0c81a 100644
--- a/chromeos/services/bluetooth_config/fake_device_operation_handler.h
+++ b/chromeos/services/bluetooth_config/fake_device_operation_handler.h
@@ -5,6 +5,9 @@
 #ifndef CHROMEOS_SERVICES_BLUETOOTH_CONFIG_FAKE_DEVICE_OPERATION_HANDLER_H_
 #define CHROMEOS_SERVICES_BLUETOOTH_CONFIG_FAKE_DEVICE_OPERATION_HANDLER_H_
 
+#include <cstddef>
+#include <string>
+
 #include "chromeos/services/bluetooth_config/adapter_state_controller.h"
 #include "chromeos/services/bluetooth_config/device_operation_handler.h"
 
@@ -19,6 +22,14 @@
 
   void CompleteCurrentOperation(bool success);
 
+  size_t perform_connect_call_count() const {
+    return perform_connect_call_count_;
+  }
+
+  const std::string& last_perform_connect_device_id() const {
+    return last_perform_connect_device_id_;
+  }
+
  private:
   // DeviceOperationHandler:
   void PerformConnect(const std::string& device_id) override;
@@ -32,6 +43,9 @@
       absl::optional<base::Time> reconnection_attempt_start,
       absl::optional<device::BluetoothDevice::ConnectErrorCode> error_code)
       const override {}
+
+  size_t perform_connect_call_count_ = 0;
+  std::string last_perform_connect_device_id_;
 };
 
 }  // namespace bluetooth_config
diff --git a/chromeos/services/bluetooth_config/fake_fast_pair_delegate.cc b/chromeos/services/bluetooth_config/fake_fast_pair_delegate.cc
index cb65dcc..55975c8f 100644
--- a/chromeos/services/bluetooth_config/fake_fast_pair_delegate.cc
+++ b/chromeos/services/bluetooth_config/fake_fast_pair_delegate.cc
@@ -11,10 +11,23 @@
 
 FakeFastPairDelegate::~FakeFastPairDelegate() = default;
 
+void FakeFastPairDelegate::SetDeviceImageInfo(const std::string& device_id,
+                                              DeviceImageInfo& images) {
+  device_id_to_images_[device_id] = images;
+}
+
 void FakeFastPairDelegate::SetDeviceNameManager(
     DeviceNameManager* device_name_manager) {
   device_name_manager_ = device_name_manager;
 }
 
+absl::optional<DeviceImageInfo> FakeFastPairDelegate::GetDeviceImageInfo(
+    const std::string& device_id) {
+  const auto it = device_id_to_images_.find(device_id);
+  if (it == device_id_to_images_.end())
+    return absl::nullopt;
+  return it->second;
+}
+
 }  // namespace bluetooth_config
 }  // namespace chromeos
diff --git a/chromeos/services/bluetooth_config/fake_fast_pair_delegate.h b/chromeos/services/bluetooth_config/fake_fast_pair_delegate.h
index bfed9e7..9ef254de 100644
--- a/chromeos/services/bluetooth_config/fake_fast_pair_delegate.h
+++ b/chromeos/services/bluetooth_config/fake_fast_pair_delegate.h
@@ -6,6 +6,7 @@
 #define CHROMEOS_SERVICES_BLUETOOTH_CONFIG_FAKE_FAST_PAIR_DELEGATE_H_
 
 #include "chromeos/services/bluetooth_config/fast_pair_delegate.h"
+#include "chromeos/services/bluetooth_config/public/cpp/device_image_info.h"
 
 namespace chromeos {
 namespace bluetooth_config {
@@ -21,14 +22,22 @@
 
   DeviceNameManager* device_name_manager() { return device_name_manager_; }
 
+  // Sets |images| for |device_id| that will be returned
+  // by GetDeviceImageInfo(|device_id|).
+  void SetDeviceImageInfo(const std::string& device_id,
+                          DeviceImageInfo& images);
+
   // FastPairDelegate:
+  absl::optional<DeviceImageInfo> GetDeviceImageInfo(
+      const std::string& device_id) override;
   void SetDeviceNameManager(DeviceNameManager* device_name_manager) override;
 
  private:
+  base::flat_map<std::string, DeviceImageInfo> device_id_to_images_;
   DeviceNameManager* device_name_manager_ = nullptr;
 };
 
 }  // namespace bluetooth_config
 }  // namespace chromeos
 
-#endif  // CHROMEOS_SERVICES_BLUETOOTH_CONFIG_MOCK_FAST_PAIR_DELEGATE_H_
+#endif  // CHROMEOS_SERVICES_BLUETOOTH_CONFIG_FAKE_FAST_PAIR_DELEGATE_H_
diff --git a/chromeos/services/bluetooth_config/fast_pair_delegate.h b/chromeos/services/bluetooth_config/fast_pair_delegate.h
index 3b43339b..ead7450 100644
--- a/chromeos/services/bluetooth_config/fast_pair_delegate.h
+++ b/chromeos/services/bluetooth_config/fast_pair_delegate.h
@@ -5,9 +5,12 @@
 #ifndef CHROMEOS_SERVICES_BLUETOOTH_CONFIG_FAST_PAIR_DELEGATE_H_
 #define CHROMEOS_SERVICES_BLUETOOTH_CONFIG_FAST_PAIR_DELEGATE_H_
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
 namespace chromeos {
 namespace bluetooth_config {
 
+class DeviceImageInfo;
 class DeviceNameManager;
 
 // Delegate class used to connect the bluetooth_config and Fast Pair systems,
@@ -17,6 +20,8 @@
  public:
   virtual ~FastPairDelegate() = default;
 
+  virtual absl::optional<DeviceImageInfo> GetDeviceImageInfo(
+      const std::string& device_id) = 0;
   virtual void SetDeviceNameManager(DeviceNameManager* device_name_manager) = 0;
 };
 
diff --git a/chromeos/services/bluetooth_config/initializer.h b/chromeos/services/bluetooth_config/initializer.h
index ca7824dc1..b9e57d1 100644
--- a/chromeos/services/bluetooth_config/initializer.h
+++ b/chromeos/services/bluetooth_config/initializer.h
@@ -24,6 +24,7 @@
 class DeviceOperationHandler;
 class DiscoveredDevicesProvider;
 class DiscoverySessionManager;
+class FastPairDelegate;
 
 // Responsible for initializing the classes needed by the CrosBluetoothConfig
 // API.
@@ -45,7 +46,8 @@
   virtual std::unique_ptr<DeviceCache> CreateDeviceCache(
       AdapterStateController* adapter_state_controller,
       scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
-      DeviceNameManager* device_name_manager) = 0;
+      DeviceNameManager* device_name_manager,
+      FastPairDelegate* fast_pair_delegate) = 0;
   virtual std::unique_ptr<DiscoveredDevicesProvider>
   CreateDiscoveredDevicesProvider(DeviceCache* device_cache) = 0;
   virtual std::unique_ptr<DiscoverySessionManager>
diff --git a/chromeos/services/bluetooth_config/initializer_impl.cc b/chromeos/services/bluetooth_config/initializer_impl.cc
index 9f7b5af..c9dc27f 100644
--- a/chromeos/services/bluetooth_config/initializer_impl.cc
+++ b/chromeos/services/bluetooth_config/initializer_impl.cc
@@ -12,6 +12,7 @@
 #include "chromeos/services/bluetooth_config/device_operation_handler_impl.h"
 #include "chromeos/services/bluetooth_config/discovered_devices_provider_impl.h"
 #include "chromeos/services/bluetooth_config/discovery_session_manager_impl.h"
+#include "chromeos/services/bluetooth_config/fast_pair_delegate.h"
 
 namespace chromeos {
 namespace bluetooth_config {
@@ -48,10 +49,11 @@
 std::unique_ptr<DeviceCache> InitializerImpl::CreateDeviceCache(
     AdapterStateController* adapter_state_controller,
     scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
-    DeviceNameManager* device_name_manager) {
-  return std::make_unique<DeviceCacheImpl>(adapter_state_controller,
-                                           std::move(bluetooth_adapter),
-                                           device_name_manager);
+    DeviceNameManager* device_name_manager,
+    FastPairDelegate* fast_pair_delegate) {
+  return std::make_unique<DeviceCacheImpl>(
+      adapter_state_controller, std::move(bluetooth_adapter),
+      device_name_manager, fast_pair_delegate);
 }
 
 std::unique_ptr<DiscoveredDevicesProvider>
diff --git a/chromeos/services/bluetooth_config/initializer_impl.h b/chromeos/services/bluetooth_config/initializer_impl.h
index df24f574..bddca2c 100644
--- a/chromeos/services/bluetooth_config/initializer_impl.h
+++ b/chromeos/services/bluetooth_config/initializer_impl.h
@@ -31,7 +31,8 @@
   std::unique_ptr<DeviceCache> CreateDeviceCache(
       AdapterStateController* adapter_state_controller,
       scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
-      DeviceNameManager* device_name_manager) override;
+      DeviceNameManager* device_name_manager,
+      FastPairDelegate* fast_pair_delegate) override;
   std::unique_ptr<DiscoveredDevicesProvider> CreateDiscoveredDevicesProvider(
       DeviceCache* device_cache) override;
   std::unique_ptr<DiscoverySessionManager> CreateDiscoverySessionManager(
diff --git a/chromeos/services/bluetooth_config/public/mojom/BUILD.gn b/chromeos/services/bluetooth_config/public/mojom/BUILD.gn
index bbbf5ce..82c0be8e 100644
--- a/chromeos/services/bluetooth_config/public/mojom/BUILD.gn
+++ b/chromeos/services/bluetooth_config/public/mojom/BUILD.gn
@@ -6,5 +6,8 @@
 
 mojom("mojom") {
   sources = [ "cros_bluetooth_config.mojom" ]
-  public_deps = [ "//mojo/public/mojom/base" ]
+  public_deps = [
+    "//mojo/public/mojom/base",
+    "//url/mojom:url_mojom_gurl",
+  ]
 }
diff --git a/chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom b/chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom
index 4dd1c302..16b7350 100644
--- a/chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom
+++ b/chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom
@@ -5,6 +5,7 @@
 module chromeos.bluetooth_config.mojom;
 
 import "mojo/public/mojom/base/string16.mojom";
+import "url/mojom/url.mojom";
 
 // The maximum number of characters a Bluetooth device's nickname can be set
 // to.
@@ -83,6 +84,22 @@
   BatteryProperties? case_info;
 };
 
+// True Wireless image info belonging to a device. Contains fields for
+// True Wireless images (stored as base64-encoded data URLs) for the left
+// earbud, right earbud, and case.
+struct TrueWirelessImageInfo {
+  url.mojom.Url left_bud_image_url;
+  url.mojom.Url right_bud_image_url;
+  url.mojom.Url case_image_url;
+};
+
+// Image info belonging to a device. Contains an optional default image (stored
+// as a base64-encoded data URL) as well as optional True Wireless images.
+struct DeviceImageInfo {
+  url.mojom.Url? default_image_url;
+  TrueWirelessImageInfo? true_wireless_images;
+};
+
 // Properties belonging to a Bluetooth device.
 struct BluetoothDeviceProperties {
   // Unique identifier for this device, which is stable across device reboots.
@@ -105,6 +122,9 @@
   // Null if no battery information is available for the device.
   DeviceBatteryInfo? battery_info;
 
+  // Null if no device images are available for the device.
+  DeviceImageInfo? image_info;
+
   DeviceConnectionState connection_state;
 
   // Indicates if the device is blocked by an admin policy.
diff --git a/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.cc b/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.cc
index 906e83ce..32f05ff 100644
--- a/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.cc
+++ b/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.cc
@@ -65,7 +65,8 @@
 std::unique_ptr<DeviceCache> ScopedBluetoothConfigTestHelper::CreateDeviceCache(
     AdapterStateController* adapter_state_controller,
     scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
-    DeviceNameManager* device_name_manager) {
+    DeviceNameManager* device_name_manager,
+    FastPairDelegate* fast_pair_delegate) {
   auto fake_device_cache =
       std::make_unique<FakeDeviceCache>(adapter_state_controller);
   fake_device_cache_ = fake_device_cache.get();
diff --git a/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.h b/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.h
index f5edc59..eebb53e3 100644
--- a/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.h
+++ b/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.h
@@ -80,7 +80,8 @@
   std::unique_ptr<DeviceCache> CreateDeviceCache(
       AdapterStateController* adapter_state_controller,
       scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
-      DeviceNameManager* device_name_manager) override;
+      DeviceNameManager* device_name_manager,
+      FastPairDelegate* fast_pair_delegate) override;
   std::unique_ptr<DiscoveredDevicesProvider> CreateDiscoveredDevicesProvider(
       DeviceCache* device_cache) override;
   std::unique_ptr<DiscoverySessionManager> CreateDiscoverySessionManager(
diff --git a/chromeos/services/device_sync/cryptauth_device_manager_impl.cc b/chromeos/services/device_sync/cryptauth_device_manager_impl.cc
index 49e7ed1d..796997b 100644
--- a/chromeos/services/device_sync/cryptauth_device_manager_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_device_manager_impl.cc
@@ -71,9 +71,9 @@
 const char kDictionaryKeySoftwareFeatures[] = "software_features";
 
 // Converts BeaconSeed protos to a list value that can be stored in user prefs.
-std::unique_ptr<base::ListValue> BeaconSeedsToListValue(
+base::Value BeaconSeedsToListValue(
     const google::protobuf::RepeatedPtrField<cryptauth::BeaconSeed>& seeds) {
-  std::unique_ptr<base::ListValue> list(new base::ListValue());
+  base::Value list(base::Value::Type::LIST);
 
   for (int i = 0; i < seeds.size(); i++) {
     cryptauth::BeaconSeed seed = seeds.Get(i);
@@ -85,8 +85,7 @@
       continue;
     }
 
-    std::unique_ptr<base::DictionaryValue> beacon_seed_value(
-        new base::DictionaryValue());
+    base::Value beacon_seed_value(base::Value::Type::DICTIONARY);
 
     // Note that the |BeaconSeed|s' data is stored in Base64Url encoding because
     // dictionary values must be valid UTF8 strings.
@@ -94,17 +93,17 @@
     base::Base64UrlEncode(seed.data(),
                           base::Base64UrlEncodePolicy::INCLUDE_PADDING,
                           &seed_data_b64);
-    beacon_seed_value->SetString(kExternalDeviceKeyBeaconSeedData,
-                                 seed_data_b64);
+    beacon_seed_value.SetStringKey(kExternalDeviceKeyBeaconSeedData,
+                                   seed_data_b64);
 
     // Set the timestamps as string representations of their numeric value
     // since there is no notion of a base::LongValue.
-    beacon_seed_value->SetString(kExternalDeviceKeyBeaconSeedStartMs,
-                                 std::to_string(seed.start_time_millis()));
-    beacon_seed_value->SetString(kExternalDeviceKeyBeaconSeedEndMs,
-                                 std::to_string(seed.end_time_millis()));
+    beacon_seed_value.SetStringKey(kExternalDeviceKeyBeaconSeedStartMs,
+                                   std::to_string(seed.start_time_millis()));
+    beacon_seed_value.SetStringKey(kExternalDeviceKeyBeaconSeedEndMs,
+                                   std::to_string(seed.end_time_millis()));
 
-    list->Append(std::move(beacon_seed_value));
+    list.Append(std::move(beacon_seed_value));
   }
 
   return list;
@@ -127,19 +126,17 @@
 
 // Converts supported and enabled SoftwareFeature protos to a single dictionary
 // value that can be stored in user prefs.
-std::unique_ptr<base::DictionaryValue>
-SupportedAndEnabledSoftwareFeaturesToDictionaryValue(
+base::Value SupportedAndEnabledSoftwareFeaturesToDictionaryValue(
     const google::protobuf::RepeatedPtrField<std::string>&
         supported_software_features,
     const google::protobuf::RepeatedPtrField<std::string>&
         enabled_software_features,
     bool legacy_unlock_key,
     bool legacy_mobile_hotspot_supported) {
-  std::unique_ptr<base::DictionaryValue> dictionary =
-      std::make_unique<base::DictionaryValue>();
+  base::Value dictionary(base::Value::Type::DICTIONARY);
 
   for (const auto& supported_software_feature : supported_software_features) {
-    dictionary->SetInteger(
+    dictionary.SetIntKey(
         supported_software_feature,
         static_cast<int>(multidevice::SoftwareFeatureState::kSupported));
   }
@@ -149,12 +146,12 @@
     cryptauth::SoftwareFeature software_feature =
         SoftwareFeatureStringToEnum(software_feature_key);
 
-    int software_feature_state;
+    absl::optional<int> software_feature_state =
+        dictionary.FindIntKey(software_feature_key);
     bool software_feature_success_result = true;
-    if (!dictionary->GetInteger(software_feature_key,
-                                &software_feature_state) ||
+    if (!software_feature_state ||
         static_cast<multidevice::SoftwareFeatureState>(
-            software_feature_state) !=
+            *software_feature_state) !=
             multidevice::SoftwareFeatureState::kSupported) {
       if (software_feature == cryptauth::SoftwareFeature::EASY_UNLOCK_HOST) {
         // Allow this known special-case for legacy purposes; fall-through to
@@ -172,7 +169,7 @@
     RecordDeviceSyncSoftwareFeaturesResult(
         software_feature_success_result /* success */, software_feature);
 
-    dictionary->SetInteger(
+    dictionary.SetIntKey(
         software_feature_key,
         static_cast<int>(multidevice::SoftwareFeatureState::kEnabled));
   }
@@ -181,21 +178,18 @@
   // been set, check to see if the deprecated corresponding booleans are
   // enabled. This can happen if the CryptAuth server is not yet serving
   // software features, and only serving the deprecated booleans.
-  int software_feature_state;
-  std::string software_feature_key;
-  software_feature_key =
+  std::string software_feature_key =
       SoftwareFeatureEnumToString(cryptauth::SoftwareFeature::EASY_UNLOCK_HOST);
-  if (legacy_unlock_key &&
-      !dictionary->GetInteger(software_feature_key, &software_feature_state)) {
-    dictionary->SetInteger(
+  if (legacy_unlock_key && !dictionary.FindIntKey(software_feature_key)) {
+    dictionary.SetIntKey(
         software_feature_key,
         static_cast<int>(multidevice::SoftwareFeatureState::kEnabled));
   }
   software_feature_key = SoftwareFeatureEnumToString(
       cryptauth::SoftwareFeature::MAGIC_TETHER_HOST);
   if (legacy_mobile_hotspot_supported &&
-      !dictionary->GetInteger(software_feature_key, &software_feature_state)) {
-    dictionary->SetInteger(
+      !dictionary.FindIntKey(software_feature_key)) {
+    dictionary.SetIntKey(
         software_feature_key,
         static_cast<int>(multidevice::SoftwareFeatureState::kSupported));
   }
@@ -255,8 +249,7 @@
   }
 
   dictionary.SetKey(kExternalDeviceKeyBeaconSeeds,
-                    base::Value::FromUniquePtrValue(
-                        BeaconSeedsToListValue(device.beacon_seeds())));
+                    BeaconSeedsToListValue(device.beacon_seeds()));
 
   if (device.has_arc_plus_plus()) {
     dictionary.SetBoolKey(kExternalDeviceKeyArcPlusPlus,
@@ -284,13 +277,11 @@
   bool legacy_mobile_hotspot_supported =
       device.has_mobile_hotspot_supported() &&
       device.mobile_hotspot_supported();
-  dictionary.SetKey(
-      kDictionaryKeySoftwareFeatures,
-      base::Value::FromUniquePtrValue(
-          SupportedAndEnabledSoftwareFeaturesToDictionaryValue(
-              device.supported_software_features(),
-              device.enabled_software_features(), legacy_unlock_key,
-              legacy_mobile_hotspot_supported)));
+  dictionary.SetKey(kDictionaryKeySoftwareFeatures,
+                    SupportedAndEnabledSoftwareFeaturesToDictionaryValue(
+                        device.supported_software_features(),
+                        device.enabled_software_features(), legacy_unlock_key,
+                        legacy_mobile_hotspot_supported));
 
   return dictionary;
 }
@@ -718,7 +709,8 @@
       *devices_as_list !=
       *pref_service_->GetList(prefs::kCryptAuthDeviceSyncUnlockKeys);
   {
-    ListPrefUpdate update(pref_service_, prefs::kCryptAuthDeviceSyncUnlockKeys);
+    ListPrefUpdateDeprecated update(pref_service_,
+                                    prefs::kCryptAuthDeviceSyncUnlockKeys);
     update.Get()->Swap(devices_as_list.get());
   }
   UpdateUnlockKeysFromPrefs();
diff --git a/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc b/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc
index 98c20d3..7f8b92f7 100644
--- a/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc
+++ b/chromeos/services/device_sync/cryptauth_device_manager_impl_unittest.cc
@@ -551,8 +551,8 @@
     device_dictionary->SetKey("software_features", base::DictionaryValue());
 
     {
-      ListPrefUpdate update(&pref_service_,
-                            prefs::kCryptAuthDeviceSyncUnlockKeys);
+      ListPrefUpdateDeprecated update(&pref_service_,
+                                      prefs::kCryptAuthDeviceSyncUnlockKeys);
       update.Get()->Append(std::move(device_dictionary));
     }
 
@@ -723,8 +723,8 @@
 TEST_F(
     DeviceSyncCryptAuthDeviceManagerImplTest,
     InitWithExistingPrefs_MigrateDeprecateBooleansFromPrefsToSoftwareFeature) {
-  ListPrefUpdate update_clear(&pref_service_,
-                              prefs::kCryptAuthDeviceSyncUnlockKeys);
+  ListPrefUpdateDeprecated update_clear(&pref_service_,
+                                        prefs::kCryptAuthDeviceSyncUnlockKeys);
   update_clear.Get()->ClearList();
 
   // Simulate a deprecated device being persisted to prefs.
@@ -738,7 +738,8 @@
   device_dictionary->SetBoolean("mobile_hotspot_supported", true);
   device_dictionary->SetKey("software_features", base::DictionaryValue());
 
-  ListPrefUpdate update(&pref_service_, prefs::kCryptAuthDeviceSyncUnlockKeys);
+  ListPrefUpdateDeprecated update(&pref_service_,
+                                  prefs::kCryptAuthDeviceSyncUnlockKeys);
   update.Get()->Append(std::move(device_dictionary));
 
   device_manager_ = std::make_unique<TestCryptAuthDeviceManager>(
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index f26e837..34c0aead 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -104,10 +104,6 @@
   # https://crbug.com/1279285: Flaky.
   "policy.AllowWakeLocks",
 
-  # https://crbug.com/1281802
-  "graphics.TraceReplay",
-  "graphics.TraceReplay.glxgears_stable",
-
   # https://crbug.com/1281645
   "quicksettings.LockScreen",
 
diff --git a/chromeos/ui/frame/desks/move_to_desks_menu_delegate.cc b/chromeos/ui/frame/desks/move_to_desks_menu_delegate.cc
index e164ad3..280041e1 100644
--- a/chromeos/ui/frame/desks/move_to_desks_menu_delegate.cc
+++ b/chromeos/ui/frame/desks/move_to_desks_menu_delegate.cc
@@ -23,6 +23,11 @@
          chromeos::MoveToDesksMenuModel::TOGGLE_ASSIGN_TO_ALL_DESKS;
 }
 
+bool IsMoveToDeskCommand(int command_id) {
+  return command_id >= chromeos::MoveToDesksMenuModel::MOVE_TO_DESK_1 &&
+         command_id <= chromeos::MoveToDesksMenuModel::MOVE_TO_DESK_8;
+}
+
 }  // namespace
 
 namespace chromeos {
@@ -49,6 +54,9 @@
   if (IsAssignToAllDesksCommand(command_id))
     return true;
 
+  if (!IsMoveToDeskCommand(command_id))
+    return false;
+
   return MapCommandIdToDeskIndex(command_id) <
          DesksHelper::Get(widget_->GetNativeWindow())->GetNumberOfDesks();
 }
diff --git a/components/BUILD.gn b/components/BUILD.gn
index f2d3a25..475179a 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -782,7 +782,7 @@
 
     if (is_android) {
       sources += [
-        "autofill_assistant/browser/js_flow_executor_impl_unittest.cc",
+        "autofill_assistant/browser/js_flow_executor_impl_browsertest.cc",
         "autofill_assistant/browser/mock_script_executor_delegate.cc",
         "autofill_assistant/browser/mock_script_executor_delegate.h",
         "autofill_assistant/browser/web/web_controller_browsertest.cc",
diff --git a/components/accuracy_tips/accuracy_service.cc b/components/accuracy_tips/accuracy_service.cc
index d033e7e..8af0080 100644
--- a/components/accuracy_tips/accuracy_service.cc
+++ b/components/accuracy_tips/accuracy_service.cc
@@ -233,8 +233,8 @@
                                           ukm::SourceId ukm_source_id,
                                           AccuracyTipInteraction interaction) {
   DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
-  ListPrefUpdate update(pref_service_,
-                        GetPreviousInteractionsPrefName(disable_ui_));
+  ListPrefUpdateDeprecated update(pref_service_,
+                                  GetPreviousInteractionsPrefName(disable_ui_));
   base::Value* interaction_list = update.Get();
   interaction_list->Append(static_cast<int>(interaction));
 
diff --git a/components/autofill/content/browser/form_forest_unittest.cc b/components/autofill/content/browser/form_forest_unittest.cc
index 30728c6..c001326 100644
--- a/components/autofill/content/browser/form_forest_unittest.cc
+++ b/components/autofill/content/browser/form_forest_unittest.cc
@@ -1549,8 +1549,8 @@
 TEST(FormForestTest, FrameDataComparator) {
   FrameData::CompareByFrameToken less;
   std::unique_ptr<FrameData> null;
-  auto x = std::make_unique<FrameData>(test::GetLocalFrameToken());
-  auto xx = std::make_unique<FrameData>(test::GetLocalFrameToken());
+  auto x = std::make_unique<FrameData>(test::MakeLocalFrameToken());
+  auto xx = std::make_unique<FrameData>(test::MakeLocalFrameToken());
   auto y = std::make_unique<FrameData>(
       LocalFrameToken(base::UnguessableToken::Deserialize(
           x->frame_token->GetHighForSerialization() + 1,
diff --git a/components/autofill/core/browser/autofill_download_manager.cc b/components/autofill/core/browser/autofill_download_manager.cc
index 37a26b3..905712758 100644
--- a/components/autofill/core/browser/autofill_download_manager.cc
+++ b/components/autofill/core/browser/autofill_download_manager.cc
@@ -514,7 +514,8 @@
   // event pref to set the appropriate bit.
   bool is_first_upload_for_event = ((value & mask) == 0);
   if (is_first_upload_for_event) {
-    DictionaryPrefUpdate update(pref_service, prefs::kAutofillUploadEvents);
+    DictionaryPrefUpdateDeprecated update(pref_service,
+                                          prefs::kAutofillUploadEvents);
     update->SetKey(std::move(key), base::Value(value | mask));
   }
 
diff --git a/components/autofill/core/browser/autofill_form_test_utils.cc b/components/autofill/core/browser/autofill_form_test_utils.cc
index 5f63478..e151487 100644
--- a/components/autofill/core/browser/autofill_form_test_utils.cc
+++ b/components/autofill/core/browser/autofill_form_test_utils.cc
@@ -24,7 +24,6 @@
 
 FormFieldData CreateFieldByRole(ServerFieldType role) {
   FormFieldData field;
-
   switch (role) {
     case ServerFieldType::USERNAME:
       field.label = u"Username";
@@ -82,57 +81,42 @@
     default:
       break;
   }
-
   return field;
 }
 
-FormData GetFormData(const TestFormAttributes& test_form_attributes) {
-  FormData form_data;
-
-  form_data.url = GURL(test_form_attributes.url);
-  form_data.action = GURL(test_form_attributes.action);
-  form_data.name = test_form_attributes.name.data();
-  form_data.host_frame =
-      test_form_attributes.host_frame.value_or(GetLocalFrameToken());
-  form_data.unique_renderer_id =
-      test_form_attributes.unique_renderer_id.value_or(MakeFormRendererId());
-  if (test_form_attributes.main_frame_origin)
-    form_data.main_frame_origin = *test_form_attributes.main_frame_origin;
-
-  for (const FieldDataDescription& field_description :
-       test_form_attributes.fields) {
-    FormFieldData field = CreateFieldByRole(field_description.role);
-    field.form_control_type = field_description.form_control_type.data();
-    // Add selection options if the field control type is "select-one".
-    if (field.form_control_type == "select-one" &&
-        field_description.select_options.size() > 0) {
-      field.options = field_description.select_options;
-    }
-    field.host_frame =
-        field_description.host_frame.value_or(form_data.host_frame);
-    field.unique_renderer_id =
-        field_description.unique_renderer_id.value_or(MakeFieldRendererId());
-    field.is_focusable = field_description.is_focusable;
-    if (!field_description.autocomplete_attribute.empty()) {
-      field.autocomplete_attribute =
-          field_description.autocomplete_attribute.data();
-    }
-    if (field_description.label != kLabelText)
-      field.label = field_description.label.data();
-    if (field_description.name != kNameText)
-      field.name = field_description.name.data();
-    if (field_description.value)
-      field.value = *field_description.value;
-    if (field_description.is_autofilled)
-      field.is_autofilled = *field_description.is_autofilled;
-    field.origin =
-        field_description.origin.value_or(form_data.main_frame_origin);
-    field.should_autocomplete = field_description.should_autocomplete;
-    form_data.fields.push_back(field);
+FormData GetFormData(const FormDataDescription& d) {
+  FormData f;
+  f.url = GURL(d.url);
+  f.action = GURL(d.action);
+  f.name = d.name;
+  f.host_frame = d.host_frame.value_or(MakeLocalFrameToken());
+  f.unique_renderer_id = d.unique_renderer_id.value_or(MakeFormRendererId());
+  if (d.main_frame_origin)
+    f.main_frame_origin = *d.main_frame_origin;
+  f.is_form_tag = d.is_form_tag;
+  for (const FieldDataDescription& dd : d.fields) {
+    FormFieldData ff = CreateFieldByRole(dd.role);
+    ff.form_control_type = dd.form_control_type;
+    if (ff.form_control_type == "select-one" && !dd.select_options.empty())
+      ff.options = dd.select_options;
+    ff.host_frame = dd.host_frame.value_or(f.host_frame);
+    ff.unique_renderer_id =
+        dd.unique_renderer_id.value_or(MakeFieldRendererId());
+    ff.is_focusable = dd.is_focusable;
+    if (!dd.autocomplete_attribute.empty())
+      ff.autocomplete_attribute = dd.autocomplete_attribute;
+    if (dd.label)
+      ff.label = *dd.label;
+    if (dd.name)
+      ff.name = *dd.name;
+    if (dd.value)
+      ff.value = *dd.value;
+    ff.is_autofilled = dd.is_autofilled.value_or(false);
+    ff.origin = dd.origin.value_or(f.main_frame_origin);
+    ff.should_autocomplete = dd.should_autocomplete;
+    f.fields.push_back(ff);
   }
-  form_data.is_form_tag = test_form_attributes.is_form_tag;
-
-  return form_data;
+  return f;
 }
 
 // static
@@ -161,52 +145,46 @@
     if (test_case.form_flags.has_author_specified_upi_vpa_hint)
       EXPECT_TRUE(form_structure->has_author_specified_upi_vpa_hint());
 
-    if (test_case.form_flags.is_complete_credit_card_form.first) {
-      if (test_case.form_flags.is_complete_credit_card_form.second)
-        EXPECT_TRUE(form_structure->IsCompleteCreditCardForm());
-      else
-        EXPECT_FALSE(form_structure->IsCompleteCreditCardForm());
+    if (test_case.form_flags.is_complete_credit_card_form.has_value()) {
+      EXPECT_EQ(form_structure->IsCompleteCreditCardForm(),
+                *test_case.form_flags.is_complete_credit_card_form);
     }
-
-    if (test_case.form_flags.field_count)
+    if (test_case.form_flags.field_count) {
       ASSERT_EQ(*test_case.form_flags.field_count,
                 static_cast<int>(form_structure->field_count()));
-    if (test_case.form_flags.autofill_count)
+    }
+    if (test_case.form_flags.autofill_count) {
       ASSERT_EQ(*test_case.form_flags.autofill_count,
                 static_cast<int>(form_structure->autofill_count()));
+    }
     if (test_case.form_flags.section_count) {
       std::set<std::string> section_names;
-      for (size_t i = 0; i < 9; ++i) {
-        section_names.insert(form_structure->field(i)->section);
-      }
+      for (const auto& field : *form_structure)
+        section_names.insert(field->section);
       EXPECT_EQ(*test_case.form_flags.section_count,
                 static_cast<int>(section_names.size()));
     }
 
-    if (!test_case.expected_field_types.expected_html_type.empty()) {
-      for (size_t i = 0;
-           i < test_case.expected_field_types.expected_html_type.size(); i++)
-        EXPECT_EQ(test_case.expected_field_types.expected_html_type[i],
-                  form_structure->field(i)->html_type());
+    for (size_t i = 0;
+         i < test_case.expected_field_types.expected_html_type.size(); i++) {
+      EXPECT_EQ(test_case.expected_field_types.expected_html_type[i],
+                form_structure->field(i)->html_type());
     }
-    if (!test_case.expected_field_types.expected_phone_part.empty()) {
-      for (size_t i = 0;
-           i < test_case.expected_field_types.expected_phone_part.size(); i++)
-        EXPECT_EQ(test_case.expected_field_types.expected_phone_part[i],
-                  form_structure->field(i)->phone_part());
+    for (size_t i = 0;
+         i < test_case.expected_field_types.expected_phone_part.size(); i++) {
+      EXPECT_EQ(test_case.expected_field_types.expected_phone_part[i],
+                form_structure->field(i)->phone_part());
     }
-    if (!test_case.expected_field_types.expected_heuristic_type.empty()) {
-      for (size_t i = 0;
-           i < test_case.expected_field_types.expected_heuristic_type.size();
-           i++)
-        EXPECT_EQ(test_case.expected_field_types.expected_heuristic_type[i],
-                  form_structure->field(i)->heuristic_type());
+    for (size_t i = 0;
+         i < test_case.expected_field_types.expected_heuristic_type.size();
+         i++) {
+      EXPECT_EQ(test_case.expected_field_types.expected_heuristic_type[i],
+                form_structure->field(i)->heuristic_type());
     }
-    if (!test_case.expected_field_types.expected_overall_type.empty()) {
-      for (size_t i = 0;
-           i < test_case.expected_field_types.expected_overall_type.size(); i++)
-        EXPECT_EQ(test_case.expected_field_types.expected_overall_type[i],
-                  form_structure->field(i)->Type().GetStorableType());
+    for (size_t i = 0;
+         i < test_case.expected_field_types.expected_overall_type.size(); i++) {
+      EXPECT_EQ(test_case.expected_field_types.expected_overall_type[i],
+                form_structure->field(i)->Type().GetStorableType());
     }
   }
 }
diff --git a/components/autofill/core/browser/autofill_form_test_utils.h b/components/autofill/core/browser/autofill_form_test_utils.h
index 2bf698d..b8c68f8 100644
--- a/components/autofill/core/browser/autofill_form_test_utils.h
+++ b/components/autofill/core/browser/autofill_form_test_utils.h
@@ -20,12 +20,6 @@
 
 namespace {
 
-// Default label assigned to fields.
-constexpr char16_t kLabelText[] = u"label";
-
-// Default name attribute assigned to fields.
-constexpr char16_t kNameText[] = u"name";
-
 // Default form url.
 constexpr char kFormUrl[] = "http://example.com/form.html";
 
@@ -43,11 +37,11 @@
   absl::optional<LocalFrameToken> host_frame;
   absl::optional<FieldRendererId> unique_renderer_id;
   bool is_focusable = true;
-  const base::StringPiece16 label = kLabelText;
-  const base::StringPiece16 name = kNameText;
-  absl::optional<const char16_t*> value;
-  const base::StringPiece autocomplete_attribute;
-  const base::StringPiece form_control_type = "text";
+  absl::optional<std::u16string> label;
+  absl::optional<std::u16string> name;
+  absl::optional<std::u16string> value;
+  const std::string autocomplete_attribute;
+  const std::string form_control_type = "text";
   bool should_autocomplete = true;
   absl::optional<bool> is_autofilled;
   absl::optional<url::Origin> origin;
@@ -56,14 +50,14 @@
 
 // Attributes provided to the test form.
 template <typename = void>
-struct TestFormAttributes {
-  const base::StringPiece description_for_logging;
+struct FormDataDescription {
+  const std::string description_for_logging;
   std::vector<FieldDataDescription<>> fields;
   absl::optional<LocalFrameToken> host_frame;
   absl::optional<FormRendererId> unique_renderer_id;
-  const base::StringPiece16 name = u"TestForm";
-  const base::StringPiece url = kFormUrl;
-  const base::StringPiece action = kFormActionUrl;
+  const std::u16string name = u"TestForm";
+  const std::string url = kFormUrl;
+  const std::string action = kFormActionUrl;
   absl::optional<url::Origin> main_frame_origin;
   bool is_form_tag = true;
 };
@@ -82,10 +76,8 @@
   bool should_be_uploaded = false;
   bool has_author_specified_types = false;
   bool has_author_specified_upi_vpa_hint = false;
-  // first value denotes whether the comparison is to be done while second
-  // denotes EXPECT_TRUE for true and EXPECT_FALSE for false.
-  std::pair<bool, bool> is_complete_credit_card_form = {false, false};
   // The implicit default value `absl::nullopt` means no checking.
+  absl::optional<bool> is_complete_credit_card_form;
   absl::optional<int> field_count;
   absl::optional<int> autofill_count;
   absl::optional<int> section_count;
@@ -104,7 +96,7 @@
 // Describes a test case for the parser.
 template <typename = void>
 struct FormStructureTestCase {
-  TestFormAttributes<> form_attributes;
+  FormDataDescription<> form_attributes;
   TestFormFlags<> form_flags;
   ExpectedFieldTypeValues<> expected_field_types;
 };
@@ -112,7 +104,7 @@
 }  // namespace internal
 
 using FieldDataDescription = internal::FieldDataDescription<>;
-using TestFormAttributes = internal::TestFormAttributes<>;
+using FormDataDescription = internal::FormDataDescription<>;
 using FormStructureTestCase = internal::FormStructureTestCase<>;
 
 // Describes the |form_data|. Use this in SCOPED_TRACE if other logging
@@ -123,7 +115,7 @@
 FormFieldData CreateFieldByRole(ServerFieldType role);
 
 // Creates a FormData to be fed to the parser.
-FormData GetFormData(const TestFormAttributes& test_form_attributes);
+FormData GetFormData(const FormDataDescription& test_form_attributes);
 
 class FormStructureTest : public testing::Test {
  protected:
diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc
index 4e1ffd0..4c80d57 100644
--- a/components/autofill/core/browser/autofill_test_utils.cc
+++ b/components/autofill/core/browser/autofill_test_utils.cc
@@ -75,7 +75,7 @@
 
 }  // namespace
 
-LocalFrameToken GetLocalFrameToken(RandomizeFrame randomize) {
+LocalFrameToken MakeLocalFrameToken(RandomizeFrame randomize) {
   if (*randomize) {
     return LocalFrameToken(base::UnguessableToken::Create());
   } else {
@@ -94,11 +94,11 @@
 }
 
 FormGlobalId MakeFormGlobalId(RandomizeFrame randomize) {
-  return {GetLocalFrameToken(randomize), MakeFormRendererId()};
+  return {MakeLocalFrameToken(randomize), MakeFormRendererId()};
 }
 
 FieldGlobalId MakeFieldGlobalId(RandomizeFrame randomize) {
-  return {GetLocalFrameToken(randomize), MakeFieldRendererId()};
+  return {MakeLocalFrameToken(randomize), MakeFieldRendererId()};
 }
 
 void SetFormGroupValues(FormGroup& form_group,
@@ -155,7 +155,7 @@
                          const char* value,
                          const char* type,
                          FormFieldData* field) {
-  field->host_frame = GetLocalFrameToken();
+  field->host_frame = MakeLocalFrameToken();
   field->unique_renderer_id = MakeFieldRendererId();
   field->label = ASCIIToUTF16(label);
   field->name = ASCIIToUTF16(name);
@@ -218,7 +218,7 @@
 void CreateTestAddressFormData(FormData* form,
                                std::vector<ServerFieldTypeSet>* types,
                                const char* unique_id) {
-  form->host_frame = GetLocalFrameToken();
+  form->host_frame = MakeLocalFrameToken();
   form->unique_renderer_id = MakeFormRendererId();
   form->name = u"MyForm" + ASCIIToUTF16(unique_id ? unique_id : "");
   form->button_titles = {std::make_pair(
diff --git a/components/autofill/core/browser/autofill_test_utils.h b/components/autofill/core/browser/autofill_test_utils.h
index a403a8b..a505074 100644
--- a/components/autofill/core/browser/autofill_test_utils.h
+++ b/components/autofill/core/browser/autofill_test_utils.h
@@ -70,9 +70,9 @@
 
 using RandomizeFrame = base::StrongAlias<struct RandomizeFrameTag, bool>;
 
-// Creates non-empty LocalFrameToken. If `randomize` is true, the
-// LocalFrameToken is generated randomly, otherwise it is stable.
-LocalFrameToken GetLocalFrameToken(
+// Creates non-empty LocalFrameToken. If `randomize` is false, the
+// LocalFrameToken is stable across multiple calls.
+LocalFrameToken MakeLocalFrameToken(
     RandomizeFrame randomize = RandomizeFrame(false));
 
 // Creates new, pairwise distinct FormRendererIds.
@@ -82,7 +82,8 @@
 FieldRendererId MakeFieldRendererId();
 
 // Creates new, pairwise distinct FormGlobalIds. If `randomize` is true, the
-// LocalFrameToken is generated randomly, otherwise it is stable.
+// LocalFrameToken is generated randomly, otherwise it is stable across multiple
+// calls.
 FormGlobalId MakeFormGlobalId(
     RandomizeFrame randomize_frame = RandomizeFrame(false));
 
diff --git a/components/autofill/core/browser/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
index 33d0682..eb7a99e2 100644
--- a/components/autofill/core/browser/browser_autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
@@ -960,7 +960,7 @@
 
   // Different form structure.
   FormData form2;
-  form2.host_frame = test::GetLocalFrameToken();
+  form2.host_frame = test::MakeLocalFrameToken();
   form2.unique_renderer_id = test::MakeFormRendererId();
   form2.name = u"MyForm";
   form2.url = GURL("https://myform.com/form.html");
@@ -993,7 +993,7 @@
   FormData form2;
   FormFieldData field;
   test::CreateTestFormField("Querty", "qwerty", "", "text", &field);
-  form2.host_frame = test::GetLocalFrameToken();
+  form2.host_frame = test::MakeLocalFrameToken();
   form2.unique_renderer_id = test::MakeFormRendererId();
   form2.name = u"NonQueryable";
   form2.url = form1.url;
@@ -1021,7 +1021,7 @@
   FormData form2;
   FormFieldData field;
   test::CreateTestFormField("Querty", "qwerty", "", "text", &field);
-  form2.host_frame = test::GetLocalFrameToken();
+  form2.host_frame = test::MakeLocalFrameToken();
   form2.unique_renderer_id = test::MakeFormRendererId();
   form2.name = u"NonQueryable";
   form2.url = form1.url;
@@ -5951,7 +5951,7 @@
        OnLoadedServerPredictionsFromApi) {
   // First form on the page.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"MyForm";
   form.url = GURL("https://myform.com/form.html");
@@ -5977,7 +5977,7 @@
 
   // Second form on the page.
   FormData form2;
-  form2.host_frame = test::GetLocalFrameToken();
+  form2.host_frame = test::MakeLocalFrameToken();
   form2.unique_renderer_id = test::MakeFormRendererId();
   form2.name = u"MyForm2";
   form2.url = GURL("https://myform.com/form.html");
diff --git a/components/autofill/core/browser/data_model/credit_card.h b/components/autofill/core/browser/data_model/credit_card.h
index 29a008a..32b8d49 100644
--- a/components/autofill/core/browser/data_model/credit_card.h
+++ b/components/autofill/core/browser/data_model/credit_card.h
@@ -72,10 +72,14 @@
     // State unspecified. This is the default value of this enum. Should not be
     // ever used with cards.
     UNSPECIFIED = 0,
-    // Card is not enrolled and does not have related virtual card.
+    // Deprecated. Card is not enrolled and does not have related virtual card.
     UNENROLLED = 1,
     // Card is enrolled and has related virtual cards.
     ENROLLED = 2,
+    // Card is not enrolled and is not eligible for enrollment.
+    UNENROLLED_AND_NOT_ELIGIBLE = 3,
+    // Card is not enrolled but is eligible for enrollment.
+    UNENROLLED_AND_ELIGIBLE = 4,
   };
 
   CreditCard(const std::string& guid, const std::string& origin);
diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc
index a0e005f..63eb25b0 100644
--- a/components/autofill/core/browser/form_structure_unittest.cc
+++ b/components/autofill/core/browser/form_structure_unittest.cc
@@ -863,7 +863,7 @@
                     {.label = u"Expiration", .name = u"cc_exp"},
                     {.role = ServerFieldType::ADDRESS_HOME_ZIP}}},
         {.determine_heuristic_type = true,
-         .is_complete_credit_card_form = {true, true}},
+         .is_complete_credit_card_form = true},
         {}}});
 }
 
@@ -879,7 +879,7 @@
                      .name = u"submit",
                      .form_control_type = "submit"}}},
         {.determine_heuristic_type = true,
-         .is_complete_credit_card_form = {true, true}},
+         .is_complete_credit_card_form = true},
         {}}});
 }
 
@@ -889,7 +889,7 @@
       {{{.description_for_logging = "IsCompleteCreditCardForm_OnlyCCNumber",
          .fields = {{.role = ServerFieldType::CREDIT_CARD_NUMBER}}},
         {.determine_heuristic_type = true,
-         .is_complete_credit_card_form = {true, false}},
+         .is_complete_credit_card_form = false},
         {}}});
 }
 
@@ -905,7 +905,7 @@
                     {.label = u"Address", .name = u""},
                     {.role = ServerFieldType::ADDRESS_HOME_ZIP, .name = u""}}},
         {.determine_heuristic_type = true,
-         .is_complete_credit_card_form = {true, false}},
+         .is_complete_credit_card_form = false},
         {}}});
 }
 
diff --git a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
index 8b8b528..4746334 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
@@ -296,7 +296,7 @@
 }
 
 void CreateSimpleForm(const GURL& origin, FormData& form) {
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -1926,7 +1926,7 @@
        QualityMetrics_LoggedCorrecltyForRationalizationOk) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -2008,7 +2008,7 @@
        QualityMetrics_LoggedCorrecltyForRationalizationGood) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -2077,7 +2077,7 @@
 
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -2233,7 +2233,7 @@
 TEST_F(AutofillMetricsTest, LogRepeatedAddressTypeRationalized) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -2341,7 +2341,7 @@
 TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -2482,7 +2482,7 @@
        QualityMetrics_LoggedCorrecltyForRationalizationBad) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -2550,7 +2550,7 @@
        QualityMetrics_LoggedCorrecltyForOnlyFillWhenFocusedField) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -2859,7 +2859,7 @@
 
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -3036,7 +3036,7 @@
   base::HistogramTester histogram_tester;
 
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -3080,7 +3080,7 @@
 TEST_F(AutofillMetricsTest, QualityMetrics_NoSubmission) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -3271,7 +3271,7 @@
 // on autocomplete attributes present on the fields.
 TEST_F(AutofillMetricsTest, QualityMetrics_BasedOnAutocomplete) {
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"MyForm";
   form.url = GURL("http://myform.com/form.html");
@@ -3388,7 +3388,7 @@
 TEST_F(AutofillMetricsTest, UpiVirtualPaymentAddress) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -3441,7 +3441,7 @@
 TEST_F(AutofillMetricsTest, SaneMetricsWithCacheMismatch) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -3544,7 +3544,7 @@
 TEST_F(AutofillMetricsTest, StoredProfileCountAutofillableFormSubmission) {
   // Construct a fillable form.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -3581,7 +3581,7 @@
 TEST_F(AutofillMetricsTest, StoredProfileCountNonAutofillableFormSubmission) {
   // Construct a non-fillable form.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -3754,7 +3754,7 @@
 TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields) {
   // Construct a fillable form.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -3810,7 +3810,7 @@
 TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields_NoSubmission) {
   // Construct a fillable form.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -3861,7 +3861,7 @@
 TEST_F(AutofillMetricsTest, DeveloperEngagement) {
   // Start with a non-fillable form.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -3958,7 +3958,7 @@
        UkmDeveloperEngagement_LogFillableFormParsedWithoutTypeHints) {
   // Start with a non-fillable form.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -4005,7 +4005,7 @@
 TEST_F(AutofillMetricsTest,
        UkmDeveloperEngagement_LogFillableFormParsedWithTypeHints) {
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -4057,7 +4057,7 @@
 // developer engagement.
 TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) {
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -4378,7 +4378,7 @@
 TEST_F(AutofillMetricsTest, AddressSuggestionsCount) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("https://example.com/form.html");
@@ -4452,7 +4452,7 @@
 TEST_F(AutofillMetricsTest, CompanyNameSuggestions) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("https://example.com/form.html");
@@ -4495,7 +4495,7 @@
 
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("https://example.com/form.html");
@@ -4684,7 +4684,7 @@
 // Test that the UPI Checkout flow form submit is correctly logged
 TEST_F(AutofillMetricsTest, UpiVpaUkmTest) {
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -4718,7 +4718,7 @@
 
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("https://example.com/form.html");
@@ -4863,7 +4863,7 @@
 
   // Set up the form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://www.foo.com/");
@@ -4926,7 +4926,7 @@
 
   // Set up the form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -4947,7 +4947,7 @@
 
   {
     // Simulate having seen this insecure form on page load.
-    form.host_frame = test::GetLocalFrameToken();
+    form.host_frame = test::MakeLocalFrameToken();
     form.unique_renderer_id = test::MakeFormRendererId();
     form.url = GURL("http://example.com/form.html");
     form.action = GURL("http://example.com/submit.html");
@@ -4978,7 +4978,7 @@
   {
     // Simulate having seen this secure form on page load.
     browser_autofill_manager_->Reset();
-    form.host_frame = test::GetLocalFrameToken();
+    form.host_frame = test::MakeLocalFrameToken();
     form.unique_renderer_id = test::MakeFormRendererId();
     form.url = GURL("https://example.com/form.html");
     form.action = GURL("https://example.com/submit.html");
@@ -5003,7 +5003,7 @@
 
   // Set up the form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("https://example.com/form.html");
@@ -5062,7 +5062,7 @@
 TEST_P(AutofillMetricsIFrameTest, CreditCardParsedFormEvents) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -5097,7 +5097,7 @@
 TEST_P(AutofillMetricsIFrameTest, CreditCardInteractedFormEvents) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -5155,7 +5155,7 @@
 TEST_P(AutofillMetricsIFrameTest, CreditCardPopupSuppressedFormEvents) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -5215,7 +5215,7 @@
 TEST_P(AutofillMetricsIFrameTest, CreditCardShownFormEvents) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -5481,7 +5481,7 @@
                       true /* masked_card_is_enrolled_for_virtual_card */);
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -5625,7 +5625,7 @@
                       true /* masked_card_is_enrolled_for_virtual_card */);
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -5915,7 +5915,7 @@
                       false /* masked_card_is_enrolled_for_virtual_card */);
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -6115,7 +6115,7 @@
 
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -6161,7 +6161,7 @@
 
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -6209,7 +6209,7 @@
 
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -6258,7 +6258,7 @@
 
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -6308,7 +6308,7 @@
 
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -6358,7 +6358,7 @@
 
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -6420,7 +6420,7 @@
   RecreateProfile(/*is_server=*/false);
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -6465,7 +6465,7 @@
                       true /* masked_card_is_enrolled_for_virtual_card */);
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -7016,7 +7016,7 @@
                       true /* masked_card_is_enrolled_for_virtual_card */);
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -7834,7 +7834,7 @@
 TEST_F(AutofillMetricsTest, MixedParsedFormEvents) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -7880,7 +7880,7 @@
 TEST_F(AutofillMetricsTest, AddressParsedFormEvents) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -7925,7 +7925,7 @@
 TEST_F(AutofillMetricsTest, AddressInteractedFormEvents) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -8003,7 +8003,7 @@
   RecreateProfile(/*is_server=*/false);
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -8098,7 +8098,7 @@
   RecreateProfile(/*is_server=*/false);
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -8214,7 +8214,7 @@
   RecreateProfile(/*is_server=*/false);
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -8335,7 +8335,7 @@
   RecreateProfile(/*is_server=*/false);
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -8551,7 +8551,7 @@
   RecreateProfile(/*is_server=*/false);
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -8739,7 +8739,7 @@
 TEST_F(AutofillMetricsTest, RecordStandalonePhoneField) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -8764,7 +8764,7 @@
 TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -8882,7 +8882,7 @@
 TEST_F(AutofillMetricsTest, AddressFormEventsAreSegmented) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -9085,7 +9085,7 @@
 TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) {
   // Start with a form with insufficiently many fields.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -9352,7 +9352,7 @@
     AutofillMetricsTest,
     AutofillFormSubmittedState_DontCountUnfilledFieldsWithOnlyFillWhenFocused) {
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -9490,7 +9490,7 @@
 TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_EmptyForm) {
   // Load a fillable form.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -9520,7 +9520,7 @@
 
   // Load a fillable form.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("https://example.com/form.html");
@@ -9692,7 +9692,7 @@
 TEST_F(AutofillMetricsTest, UserHappinessFormInteraction_AddressForm) {
   // Load a fillable form.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -9953,7 +9953,7 @@
 
   // Load a fillable form.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -9972,7 +9972,7 @@
 
   // Fill additional form.
   FormData second_form = form;
-  second_form.host_frame = test::GetLocalFrameToken();
+  second_form.host_frame = test::MakeLocalFrameToken();
   second_form.unique_renderer_id = test::MakeFormRendererId();
   test::CreateTestFormField("Second Phone", "second_phone", "", "text", &field);
   second_form.fields.push_back(field);
@@ -10389,7 +10389,7 @@
 
   // Load a fillable form.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -10521,7 +10521,7 @@
  public:
   void SetUp() override {
     FormData form;
-    form.host_frame = test::GetLocalFrameToken();
+    form.host_frame = test::MakeLocalFrameToken();
     form.unique_renderer_id = test::MakeFormRendererId();
     form.url = GURL("http://foo.com");
     form.main_frame_origin = url::Origin::Create(GURL("http://foo_root.com"));
@@ -10681,7 +10681,7 @@
 
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -10742,7 +10742,7 @@
 
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("https://example.com/form.html");
@@ -10871,7 +10871,7 @@
                       false /* include_full_server_credit_card */,
                       false /* masked_card_is_enrolled_for_virtual_card */);
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example_cc.com/form.html");
@@ -10909,7 +10909,7 @@
 TEST_F(AutofillMetricsTest, DynamicFormMetrics) {
   // Set up our form data.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -11034,7 +11034,7 @@
 TEST_F(AutofillMetricsTest, LogUserHappinessBySecurityLevel_FromFormEvents) {
   // Load a fillable form.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -11340,7 +11340,7 @@
 // autocomplete="one-time-code".
 TEST_F(AutofillMetricsTest, FrameHasAutocompleteOneTimeCode) {
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -11374,7 +11374,7 @@
 // autocomplete="one-time-code".
 TEST_F(AutofillMetricsTest, FrameDoesNotHaveAutocompleteOneTimeCode) {
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -11403,7 +11403,7 @@
 // autocomplete attribute but there are at least 3 fields in the form.
 TEST_F(AutofillMetricsTest, FrameHasPhoneNumberFieldWithoutAutocomplete) {
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -11439,7 +11439,7 @@
 // autocomplete attribute and there are less than 3 fields in the form.
 TEST_F(AutofillMetricsTest, FrameHasSinglePhoneNumberFieldWithoutAutocomplete) {
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -11491,7 +11491,7 @@
 // field.
 TEST_F(AutofillMetricsTest, FrameDoesNotHavePhoneNumberField) {
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -11720,7 +11720,7 @@
   test_clock.SetNowTicks(now);
 
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -11976,7 +11976,7 @@
 
   // Load a fillable form.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("http://example.com/form.html");
@@ -12132,7 +12132,7 @@
 
   // Load a fillable form.
   FormData form;
-  form.host_frame = test::GetLocalFrameToken();
+  form.host_frame = test::MakeLocalFrameToken();
   form.unique_renderer_id = test::MakeFormRendererId();
   form.name = u"TestForm";
   form.url = GURL("https://example.com/form.html");
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index b7f2736..49a356c 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -1238,7 +1238,17 @@
   return promo_code_offers_for_origin;
 }
 
-gfx::Image* PersonalDataManager::GetCreditCardArtImageForUrl(
+raw_ptr<gfx::Image> PersonalDataManager::GetCreditCardArtImageForUrl(
+    const GURL& card_art_url) const {
+  raw_ptr<gfx::Image> cached_image = GetCachedCardArtImageForUrl(card_art_url);
+  if (cached_image)
+    return cached_image;
+
+  FetchImagesForUrls({card_art_url});
+  return nullptr;
+}
+
+raw_ptr<gfx::Image> PersonalDataManager::GetCachedCardArtImageForUrl(
     const GURL& card_art_url) const {
   if (!IsAutofillWalletImportEnabled())
     return nullptr;
@@ -1248,14 +1258,14 @@
 
   auto images_iterator = credit_card_art_images_.find(card_art_url);
 
-  // Found an image and return it.
+  // If the cache contains the image, return it.
   if (images_iterator != credit_card_art_images_.end()) {
-    gfx::Image* image = images_iterator->second.get();
+    raw_ptr<gfx::Image> image = images_iterator->second.get();
     if (!image->IsEmpty())
       return image;
   }
 
-  FetchImagesForUrls({card_art_url});
+  // The cache does not contain the image, return nullptr.
   return nullptr;
 }
 
diff --git a/components/autofill/core/browser/personal_data_manager.h b/components/autofill/core/browser/personal_data_manager.h
index b8cf8b36..9b3b967 100644
--- a/components/autofill/core/browser/personal_data_manager.h
+++ b/components/autofill/core/browser/personal_data_manager.h
@@ -294,7 +294,16 @@
   GetActiveAutofillPromoCodeOffersForOrigin(GURL origin) const;
 
   // Returns the customized credit card art image for the |card_art_url|.
-  virtual gfx::Image* GetCreditCardArtImageForUrl(
+  virtual raw_ptr<gfx::Image> GetCreditCardArtImageForUrl(
+      const GURL& card_art_url) const;
+
+  // Returns the cached card art image for the |card_art_url| if it was synced
+  // locally to the client. This function is called within
+  // GetCreditCardArtImageForUrl(), but can also be called separately as an
+  // optimization for situations where a separate fetch request after trying to
+  // retrieve local card art images is not needed. If the card art image is not
+  // present in the cache, this function will return a nullptr.
+  raw_ptr<gfx::Image> GetCachedCardArtImageForUrl(
       const GURL& card_art_url) const;
 
   // Updates the validity states of |profiles| according to server validity map.
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index 667417d..d74bfe1d 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -1314,10 +1314,21 @@
 
   personal_data_->OnCardArtImagesFetched(std::move(images));
 
-  gfx::Image* actual_image = personal_data_->GetCreditCardArtImageForUrl(
-      GURL("https://www.example.com"));
+  raw_ptr<gfx::Image> actual_image =
+      personal_data_->GetCreditCardArtImageForUrl(
+          GURL("https://www.example.com"));
   ASSERT_TRUE(actual_image);
   EXPECT_TRUE(gfx::test::AreImagesEqual(expected_image, *actual_image));
+
+  // TODO(crbug.com/1284788): Look into integrating with PersonalDataManagerMock
+  // and checking that PersonalDataManager::FetchImagesForUrls() does not get
+  // triggered when PersonalDataManager::GetCachedCardArtImageForUrl() is
+  // called.
+  raw_ptr<gfx::Image> cached_image =
+      personal_data_->GetCachedCardArtImageForUrl(
+          GURL("https://www.example.com"));
+  ASSERT_TRUE(cached_image);
+  EXPECT_TRUE(gfx::test::AreImagesEqual(expected_image, *cached_image));
 }
 
 TEST_F(PersonalDataManagerMockTest, ProcessVirtualCardMetadataChanges) {
diff --git a/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc b/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc
index 988170a..0650aabf 100644
--- a/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc
+++ b/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc
@@ -103,8 +103,13 @@
     case sync_pb::WalletMaskedCreditCard::ENROLLED:
       state = CreditCard::ENROLLED;
       break;
+    case sync_pb::WalletMaskedCreditCard::UNENROLLED_AND_NOT_ELIGIBLE:
+      state = CreditCard::UNENROLLED_AND_NOT_ELIGIBLE;
+      break;
+    case sync_pb::WalletMaskedCreditCard::UNENROLLED_AND_ELIGIBLE:
+      state = CreditCard::UNENROLLED_AND_ELIGIBLE;
+      break;
     case sync_pb::WalletMaskedCreditCard::UNSPECIFIED:
-      state = CreditCard::UNSPECIFIED;
       break;
   }
   result.set_virtual_card_enrollment_state(state);
@@ -275,6 +280,12 @@
     case CreditCard::ENROLLED:
       state = sync_pb::WalletMaskedCreditCard::ENROLLED;
       break;
+    case CreditCard::UNENROLLED_AND_NOT_ELIGIBLE:
+      state = sync_pb::WalletMaskedCreditCard::UNENROLLED_AND_NOT_ELIGIBLE;
+      break;
+    case CreditCard::UNENROLLED_AND_ELIGIBLE:
+      state = sync_pb::WalletMaskedCreditCard::UNENROLLED_AND_ELIGIBLE;
+      break;
     case CreditCard::UNSPECIFIED:
       state = sync_pb::WalletMaskedCreditCard::UNSPECIFIED;
       break;
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc
index aa13212..4a942ba 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc
@@ -547,13 +547,7 @@
     const std::vector<CreditCard>& new_data) {
   std::vector<std::string> updated_server_ids;
   for (const CreditCard& new_card : new_data) {
-    // If this new card is not enrolled for virtual cards, continue.
-    if (new_card.virtual_card_enrollment_state() !=
-        CreditCard::VirtualCardEnrollmentState::ENROLLED) {
-      continue;
-    }
-
-    // Otherwise try to find the old card with same server id.
+    // Try to find the old card with same server id.
     auto old_data_iterator =
         std::find_if(old_data.begin(), old_data.end(),
                      [&new_card](const std::unique_ptr<CreditCard>& old_card) {
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index f91ab04..d5aa7ef1 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -289,7 +289,7 @@
 // Uses the pattern provider to retrieve parsing patterns for the heuristic
 // field type detection.
 // TODO(crbug/1121990): Remove once launched.
-extern const base::Feature kAutofillParsingPatternProvider{
+const base::Feature kAutofillParsingPatternProvider{
     "AutofillParsingPatternProvider", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Controls if language-specific patterns are used for the heuristic field type
diff --git a/components/autofill/core/common/autofill_prefs.cc b/components/autofill/core/common/autofill_prefs.cc
index 2c25dae1..d31887e 100644
--- a/components/autofill/core/common/autofill_prefs.cc
+++ b/components/autofill/core/common/autofill_prefs.cc
@@ -286,7 +286,8 @@
   base::Base64Encode(crypto::SHA256HashString(account_id.ToString()),
                      &account_hash);
 
-  DictionaryPrefUpdate update(prefs, prefs::kAutofillSyncTransportOptIn);
+  DictionaryPrefUpdateDeprecated update(prefs,
+                                        prefs::kAutofillSyncTransportOptIn);
   int value = GetSyncTransportOptInBitFieldForAccount(prefs, account_hash);
 
   // If the user has opted in, set that bit while leaving the others intact.
@@ -324,7 +325,8 @@
 }
 
 void ClearSyncTransportOptIns(PrefService* prefs) {
-  DictionaryPrefUpdate update(prefs, prefs::kAutofillSyncTransportOptIn);
+  DictionaryPrefUpdateDeprecated update(prefs,
+                                        prefs::kAutofillSyncTransportOptIn);
   update->DictClear();
 }
 
diff --git a/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc b/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
index 93cf0ebbc..98bfa5d8 100644
--- a/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
+++ b/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
@@ -250,7 +250,7 @@
   test::CreateTestSelectField("TestLabel", "TestName", "TestValue", kOptions,
                               kOptions, 4, &input);
   // Set other attributes to check if they are passed correctly.
-  input.host_frame = test::GetLocalFrameToken();
+  input.host_frame = test::MakeLocalFrameToken();
   input.unique_renderer_id = FieldRendererId(1234);
   input.id_attribute = u"id";
   input.name_attribute = u"name";
@@ -283,7 +283,7 @@
   test::CreateTestDatalistField("DatalistLabel", "DatalistName",
                                 "DatalistValue", kOptions, kOptions, &input);
   // Set other attributes to check if they are passed correctly.
-  input.host_frame = test::GetLocalFrameToken();
+  input.host_frame = test::MakeLocalFrameToken();
   input.unique_renderer_id = FieldRendererId(1234);
   input.id_attribute = u"id";
   input.name_attribute = u"name";
diff --git a/components/autofill_assistant/android/BUILD.gn b/components/autofill_assistant/android/BUILD.gn
index 251403c..24c43a0 100644
--- a/components/autofill_assistant/android/BUILD.gn
+++ b/components/autofill_assistant/android/BUILD.gn
@@ -84,7 +84,6 @@
     "internal/java/res/layout/autofill_assistant_button_hairline.xml",
     "internal/java/res/layout/autofill_assistant_contact_full.xml",
     "internal/java/res/layout/autofill_assistant_contact_summary.xml",
-    "internal/java/res/layout/autofill_assistant_datetime.xml",
     "internal/java/res/layout/autofill_assistant_details.xml",
     "internal/java/res/layout/autofill_assistant_form_checkbox.xml",
     "internal/java/res/layout/autofill_assistant_form_counter.xml",
diff --git a/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_datetime.xml b/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_datetime.xml
deleted file mode 100644
index 9dd42a6..0000000
--- a/components/autofill_assistant/android/internal/java/res/layout/autofill_assistant_datetime.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="horizontal"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content">
-
-    <org.chromium.chrome.browser.autofill_assistant.user_data.AssistantVerticalExpander
-        android:id="@+id/date_expander"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="0.5"
-        android:layout_marginStart="@dimen/autofill_assistant_bottombar_horizontal_spacing"
-        android:layout_marginEnd="@dimen/autofill_assistant_bottombar_horizontal_spacing">
-    </org.chromium.chrome.browser.autofill_assistant.user_data.AssistantVerticalExpander>
-
-    <org.chromium.chrome.browser.autofill_assistant.user_data.AssistantVerticalExpander
-        android:id="@+id/time_expander"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="0.5"
-        android:layout_marginStart="@dimen/autofill_assistant_bottombar_horizontal_spacing"
-        android:layout_marginEnd="@dimen/autofill_assistant_bottombar_horizontal_spacing">
-    </org.chromium.chrome.browser.autofill_assistant.user_data.AssistantVerticalExpander>
-</LinearLayout>
\ No newline at end of file
diff --git a/components/autofill_assistant/android/internal/java/res_poodle/drawable/ic_autofill_assistant_24dp.xml b/components/autofill_assistant/android/internal/java/res_poodle/drawable/ic_autofill_assistant_24dp.xml
index 1085685..d697ee89 100644
--- a/components/autofill_assistant/android/internal/java/res_poodle/drawable/ic_autofill_assistant_24dp.xml
+++ b/components/autofill_assistant/android/internal/java/res_poodle/drawable/ic_autofill_assistant_24dp.xml
@@ -5,8 +5,6 @@
 
 <vector
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="192"
diff --git a/components/autofill_assistant/browser/actions/collect_user_data_action.cc b/components/autofill_assistant/browser/actions/collect_user_data_action.cc
index d476bad..8b4783c0 100644
--- a/components/autofill_assistant/browser/actions/collect_user_data_action.cc
+++ b/components/autofill_assistant/browser/actions/collect_user_data_action.cc
@@ -82,7 +82,6 @@
          !collect_user_data_options.request_payer_phone &&
          !collect_user_data_options.request_shipping &&
          !collect_user_data_options.request_payment_method &&
-         !collect_user_data_options.request_date_time_range &&
          collect_user_data_options.accept_terms_and_conditions_text.empty() &&
          !collect_user_data_options.additional_model_identifier_to_check
               .has_value();
@@ -102,69 +101,6 @@
          terms_state != TermsAndConditionsState::NOT_SELECTED;
 }
 
-// Checks |proto| and writes an error message to |error| if fields are missing.
-bool IsValidDateTimeRangeProto(
-    const autofill_assistant::DateTimeRangeProto& proto,
-    std::string* error) {
-  std::vector<std::string> missing_fields;
-  if (proto.start_date_label().empty())
-    missing_fields.emplace_back("start_date_label");
-  if (proto.start_time_label().empty())
-    missing_fields.emplace_back("start_time_label");
-  if (proto.end_date_label().empty())
-    missing_fields.emplace_back("end_date_label");
-  if (proto.end_time_label().empty())
-    missing_fields.emplace_back("end_time_label");
-  if (!proto.has_start_date())
-    missing_fields.emplace_back("start_date");
-  if (!proto.has_start_time_slot())
-    missing_fields.emplace_back("start_time_slot");
-  if (!proto.has_end_date())
-    missing_fields.emplace_back("end_date");
-  if (!proto.has_end_time_slot())
-    missing_fields.emplace_back("end_time_slot");
-  if (!proto.has_min_date())
-    missing_fields.emplace_back("min_date");
-  if (!proto.has_max_date())
-    missing_fields.emplace_back("max_date");
-  if (proto.time_slots().empty())
-    missing_fields.emplace_back("time_slots");
-  if (proto.date_not_set_error().empty())
-    missing_fields.emplace_back("date_not_set_error");
-  if (proto.time_not_set_error().empty())
-    missing_fields.emplace_back("time_not_set_error");
-
-  if (error != nullptr && !missing_fields.empty()) {
-    error->assign("The following fields are missing or empty: ");
-    error->append(base::JoinString(missing_fields, ", "));
-  }
-
-  return missing_fields.empty();
-}
-
-bool IsValidDateTimeRange(
-    const absl::optional<autofill_assistant::DateProto>& start_date,
-    const absl::optional<int> start_timeslot,
-    const absl::optional<autofill_assistant::DateProto> end_date,
-    const absl::optional<int> end_timeslot,
-    const CollectUserDataOptions& collect_user_data_options) {
-  if (!collect_user_data_options.request_date_time_range) {
-    return true;
-  }
-  if (!start_date.has_value() || !start_timeslot.has_value() ||
-      !end_date.has_value() || !end_timeslot.has_value()) {
-    return false;
-  }
-
-  auto temp_start_date = start_date;
-  auto temp_start_timeslot = start_timeslot;
-  auto temp_end_date = end_date;
-  auto temp_end_timeslot = end_timeslot;
-  return !autofill_assistant::CollectUserDataAction::SanitizeDateTimeRange(
-      &temp_start_date, &temp_start_timeslot, &temp_end_date,
-      &temp_end_timeslot, collect_user_data_options, false);
-}
-
 bool IsValidUserFormSection(
     const autofill_assistant::UserFormSectionProto& proto) {
   if (proto.title().empty()) {
@@ -678,8 +614,6 @@
     UpdatePersonalDataManagerProfiles(user_data);
     UpdatePersonalDataManagerCards(user_data);
   }
-  UpdateDateTimeRangeStart(user_data);
-  UpdateDateTimeRangeEnd(user_data);
 
   UpdateMetrics(user_data);
 
@@ -977,27 +911,6 @@
     }
   }
 
-  if (collect_user_data.has_date_time_range()) {
-    std::string error_message;
-    if (!IsValidDateTimeRangeProto(collect_user_data.date_time_range(),
-                                   &error_message)) {
-      VLOG(1) << "Invalid action: " << error_message;
-      return false;
-    }
-    if (collect_user_data.date_time_range().start_time_slot() < 0 ||
-        collect_user_data.date_time_range().end_time_slot() < 0 ||
-        collect_user_data.date_time_range().start_time_slot() >=
-            collect_user_data.date_time_range().time_slots().size() ||
-        collect_user_data.date_time_range().end_time_slot() >=
-            collect_user_data.date_time_range().time_slots().size()) {
-      VLOG(1) << "Invalid action: time slot index out of range";
-      return false;
-    }
-    collect_user_data_options_->request_date_time_range = true;
-    collect_user_data_options_->date_time_range =
-        collect_user_data.date_time_range();
-  }
-
   for (const auto& section :
        collect_user_data.additional_prepended_sections()) {
     if (!IsValidUserFormSection(section)) {
@@ -1193,99 +1106,10 @@
              .empty() &&
          IsValidLoginChoice(user_data.selected_login_choice(), options) &&
          IsValidTermsChoice(user_data.terms_and_conditions_, options) &&
-         IsValidDateTimeRange(user_data.date_time_range_start_date_,
-                              user_data.date_time_range_start_timeslot_,
-                              user_data.date_time_range_end_date_,
-                              user_data.date_time_range_end_timeslot_,
-                              options) &&
          AreAdditionalSectionsComplete(user_data, options) &&
          IsValidUserModel(user_model, options);
 }
 
-// TODO(b/148448649): Move to dedicated helper namespace.
-// static
-int CollectUserDataAction::CompareDates(const DateProto& first,
-                                        const DateProto& second) {
-  auto first_tuple = std::make_tuple(first.year(), first.month(), first.day());
-  auto second_tuple =
-      std::make_tuple(second.year(), second.month(), second.day());
-  if (first_tuple < second_tuple) {
-    return -1;
-  } else if (second_tuple < first_tuple) {
-    return 1;
-  }
-  return 0;
-}
-
-// TODO(b/148448649): Move to dedicated helper namespace.
-// static
-bool CollectUserDataAction::SanitizeDateTimeRange(
-    absl::optional<DateProto>* start_date,
-    absl::optional<int>* start_timeslot,
-    absl::optional<DateProto>* end_date,
-    absl::optional<int>* end_timeslot,
-    const CollectUserDataOptions& collect_user_data_options,
-    bool change_start) {
-  if (!collect_user_data_options.request_date_time_range) {
-    return false;
-  }
-  DCHECK(start_date);
-  DCHECK(start_timeslot);
-  DCHECK(end_date);
-  DCHECK(end_timeslot);
-  if (!start_date->has_value() || !end_date->has_value()) {
-    return false;
-  }
-
-  auto date_comparison = CompareDates(**start_date, **end_date);
-  if (date_comparison < 0) {
-    return false;
-  }
-
-  // Start date > end date, reset date.
-  if (date_comparison > 0) {
-    if (change_start) {
-      start_date->reset();
-    } else {
-      end_date->reset();
-    }
-    return true;
-  }
-
-  if (!start_timeslot->has_value() || !end_timeslot->has_value()) {
-    return false;
-  }
-
-  DCHECK(**start_timeslot >= 0 &&
-         **start_timeslot <
-             collect_user_data_options.date_time_range.time_slots().size());
-  DCHECK(**end_timeslot >= 0 &&
-         **end_timeslot <
-             collect_user_data_options.date_time_range.time_slots().size());
-  auto start_time =
-      collect_user_data_options.date_time_range.time_slots(**start_timeslot);
-  auto end_time =
-      collect_user_data_options.date_time_range.time_slots(**end_timeslot);
-  auto time_comparison =
-      start_time.comparison_value() - end_time.comparison_value();
-  if (time_comparison < 0) {
-    return false;
-  }
-
-  // Start date == end date and start time >= end time, reset time.
-  if (time_comparison >= 0) {
-    if (change_start) {
-      start_timeslot->reset();
-    } else {
-      end_timeslot->reset();
-    }
-    return true;
-  }
-
-  NOTREACHED();
-  return false;
-}
-
 void CollectUserDataAction::WriteProcessedAction(UserData* user_data,
                                                  const UserModel* user_model) {
   if (proto().collect_user_data().request_payment_method() &&
@@ -1335,28 +1159,6 @@
     }
   }
 
-  if (proto().collect_user_data().has_date_time_range()) {
-    if (user_data->date_time_range_start_date_.has_value()) {
-      *processed_action_proto_->mutable_collect_user_data_result()
-           ->mutable_date_range_start_date() =
-          *user_data->date_time_range_start_date_;
-    }
-    if (user_data->date_time_range_start_timeslot_.has_value()) {
-      processed_action_proto_->mutable_collect_user_data_result()
-          ->set_date_range_start_timeslot(
-              *user_data->date_time_range_start_timeslot_);
-    }
-    if (user_data->date_time_range_end_date_.has_value()) {
-      *processed_action_proto_->mutable_collect_user_data_result()
-           ->mutable_date_range_end_date() =
-          *user_data->date_time_range_end_date_;
-    }
-    if (user_data->date_time_range_end_timeslot_.has_value()) {
-      processed_action_proto_->mutable_collect_user_data_result()
-          ->set_date_range_end_timeslot(
-              *user_data->date_time_range_end_timeslot_);
-    }
-  }
   for (const auto& section :
        proto().collect_user_data().additional_prepended_sections()) {
     FillProtoForAdditionalSection(section, *user_data,
@@ -1778,50 +1580,4 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
-void CollectUserDataAction::UpdateDateTimeRangeStart(
-    UserData* user_data,
-    UserData::FieldChange* field_change) {
-  DCHECK(user_data != nullptr);
-  DCHECK(collect_user_data_options_ != nullptr);
-
-  UserData::FieldChange changed = UserData::FieldChange::NONE;
-  if (!user_data->date_time_range_start_date_.has_value()) {
-    user_data->date_time_range_start_date_ =
-        collect_user_data_options_->date_time_range.start_date();
-    changed = UserData::FieldChange::DATE_TIME_RANGE_START;
-  }
-  if (!user_data->date_time_range_start_timeslot_.has_value()) {
-    user_data->date_time_range_start_timeslot_ =
-        collect_user_data_options_->date_time_range.start_time_slot();
-    changed = UserData::FieldChange::DATE_TIME_RANGE_START;
-  }
-
-  if (field_change != nullptr && changed != UserData::FieldChange::NONE) {
-    *field_change = changed;
-  }
-}
-
-void CollectUserDataAction::UpdateDateTimeRangeEnd(
-    UserData* user_data,
-    UserData::FieldChange* field_change) {
-  DCHECK(user_data != nullptr);
-  DCHECK(collect_user_data_options_ != nullptr);
-
-  UserData::FieldChange changed = UserData::FieldChange::NONE;
-  if (!user_data->date_time_range_end_date_.has_value()) {
-    user_data->date_time_range_end_date_ =
-        collect_user_data_options_->date_time_range.end_date();
-    changed = UserData::FieldChange::DATE_TIME_RANGE_END;
-  }
-  if (!user_data->date_time_range_end_timeslot_.has_value()) {
-    user_data->date_time_range_end_timeslot_ =
-        collect_user_data_options_->date_time_range.end_time_slot();
-    changed = UserData::FieldChange::DATE_TIME_RANGE_END;
-  }
-
-  if (field_change != nullptr && changed != UserData::FieldChange::NONE) {
-    *field_change = changed;
-  }
-}
-
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/collect_user_data_action.h b/components/autofill_assistant/browser/actions/collect_user_data_action.h
index 5ddd78b..aeedef0 100644
--- a/components/autofill_assistant/browser/actions/collect_user_data_action.h
+++ b/components/autofill_assistant/browser/actions/collect_user_data_action.h
@@ -47,20 +47,6 @@
       const UserModel& user_model,
       const CollectUserDataOptions& collect_user_data_options);
 
-  // Ensures that |end| is > |start| by modifying either |start| or |end|,
-  // depending on |change_start|. Returns true if changes were performed.
-  static bool SanitizeDateTimeRange(
-      absl::optional<DateProto>* start_date,
-      absl::optional<int>* start_timeslot,
-      absl::optional<DateProto>* end_date,
-      absl::optional<int>* end_timeslot,
-      const CollectUserDataOptions& collect_user_data_options,
-      bool change_start);
-
-  // Comparison function for |DateProto|.
-  // Returns 0 if equal, < 0 if |first| < |second|, > 0 if |second| > |first|.
-  static int CompareDates(const DateProto& first, const DateProto& second);
-
  private:
   struct LoginDetails {
     LoginDetails(bool choose_automatically_if_no_stored_login,
@@ -127,10 +113,6 @@
   void UpdateSelectedContact(UserData* user_data);
   void UpdateSelectedShippingAddress(UserData* user_data);
   void UpdateSelectedCreditCard(UserData* user_data);
-  void UpdateDateTimeRangeStart(UserData* user_data,
-                                UserData::FieldChange* field_change = nullptr);
-  void UpdateDateTimeRangeEnd(UserData* user_data,
-                              UserData::FieldChange* field_change = nullptr);
   void MaybeLogMetrics();
 
   UserDataMetrics metrics_data_;
diff --git a/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc b/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
index 3df465c2..57444bd 100644
--- a/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
@@ -118,12 +118,6 @@
   (*values)[55] = MakeAutofillEntry("2050");
 }
 
-void SetDateProto(DateProto* proto, int year, int month, int day) {
-  proto->set_year(year);
-  proto->set_month(month);
-  proto->set_day(day);
-}
-
 using ::base::test::RunOnceCallback;
 using ::testing::_;
 using ::testing::AnyOf;
@@ -673,36 +667,9 @@
     action.ProcessAction(callback_.Get());
   }
 
-  // Date/time range info requested, no early return.
+  // Generic UI model identifier set, no early return.
   proto->clear_request_payment_method();
   proto->clear_billing_address_name();
-  auto* date_time_range = proto->mutable_date_time_range();
-  SetDateProto(date_time_range->mutable_start_date(), 2020, 1, 1);
-  SetDateProto(date_time_range->mutable_end_date(), 2020, 1, 15);
-  SetDateProto(date_time_range->mutable_min_date(), 2020, 1, 1);
-  SetDateProto(date_time_range->mutable_max_date(), 2020, 12, 31);
-  date_time_range->set_start_time_slot(0);
-  date_time_range->set_end_time_slot(0);
-  date_time_range->set_start_date_label("Start date");
-  date_time_range->set_end_date_label("End date");
-  date_time_range->set_start_time_label("Start time");
-  date_time_range->set_end_time_label("End time");
-  date_time_range->set_date_not_set_error("Date not set");
-  date_time_range->set_time_not_set_error("Time not set");
-  auto* time_slot = date_time_range->add_time_slots();
-  time_slot->set_label("08:00 AM");
-  time_slot->set_comparison_value(0);
-  time_slot = date_time_range->add_time_slots();
-  time_slot->set_label("09:00 AM");
-  time_slot->set_comparison_value(1);
-  {
-    EXPECT_CALL(mock_action_delegate_, CollectUserData(_)).Times(1);
-    CollectUserDataAction action(&mock_action_delegate_, action_proto);
-    action.ProcessAction(callback_.Get());
-  }
-
-  // Generic UI model identifier set, no early return.
-  proto->clear_date_time_range();
   proto->set_additional_model_identifier_to_check("identifier");
   {
     EXPECT_CALL(mock_action_delegate_, CollectUserData(_)).Times(1);
@@ -1222,75 +1189,6 @@
                                                         options));
 }
 
-TEST_F(CollectUserDataActionTest, UserDataCompleteDateTimeRange) {
-  UserData user_data;
-  CollectUserDataOptions options;
-  options.request_date_time_range = true;
-  auto* time_slot = options.date_time_range.add_time_slots();
-  time_slot->set_label("08:00 AM");
-  time_slot->set_comparison_value(0);
-  time_slot = options.date_time_range.add_time_slots();
-  time_slot->set_label("09:00 AM");
-  time_slot->set_comparison_value(1);
-
-  DateProto start_date;
-  SetDateProto(&start_date, 2020, 1, 1);
-  DateProto end_date;
-  SetDateProto(&end_date, 2020, 1, 15);
-  user_data.date_time_range_start_date_ = start_date;
-  user_data.date_time_range_end_date_ = end_date;
-  user_data.date_time_range_start_timeslot_ = 0;
-  user_data.date_time_range_end_timeslot_ = 0;
-
-  // Initial selection is valid.
-  EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, user_model_,
-                                                        options));
-
-  // Start date not before end date is not ok.
-  SetDateProto(&*user_data.date_time_range_start_date_, 2020, 2, 7);
-  SetDateProto(&*user_data.date_time_range_end_date_, 2020, 1, 15);
-  EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, user_model_,
-                                                         options));
-
-  // Same date with end time > start time is ok.
-  SetDateProto(&*user_data.date_time_range_start_date_, 2020, 1, 15);
-  SetDateProto(&*user_data.date_time_range_end_date_, 2020, 1, 15);
-  user_data.date_time_range_start_timeslot_ = 0;
-  user_data.date_time_range_end_timeslot_ = 1;
-  EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, user_model_,
-                                                        options));
-
-  // Same date and same time is not ok.
-  user_data.date_time_range_start_timeslot_ = 0;
-  user_data.date_time_range_end_timeslot_ = 0;
-  EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, user_model_,
-                                                         options));
-
-  // Same date and start time > end time is not ok.
-  user_data.date_time_range_start_timeslot_ = 1;
-  user_data.date_time_range_end_timeslot_ = 0;
-  EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(user_data, user_model_,
-                                                         options));
-
-  // Start date before end date is ok.
-  SetDateProto(&*user_data.date_time_range_start_date_, 2020, 3, 1);
-  SetDateProto(&*user_data.date_time_range_end_date_, 2020, 3, 31);
-  user_data.date_time_range_start_timeslot_ = 0;
-  user_data.date_time_range_end_timeslot_ = 1;
-  EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, user_model_,
-                                                        options));
-  user_data.date_time_range_start_timeslot_ = 1;
-  user_data.date_time_range_end_timeslot_ = 0;
-  EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, user_model_,
-                                                        options));
-
-  // Proper date comparison across years.
-  SetDateProto(&*user_data.date_time_range_start_date_, 2019, 11, 10);
-  SetDateProto(&*user_data.date_time_range_end_date_, 2020, 1, 5);
-  EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(user_data, user_model_,
-                                                        options));
-}
-
 TEST_F(CollectUserDataActionTest, UserDataCompleteChecksGenericUiCompleteness) {
   UserData user_data;
   CollectUserDataOptions options;
@@ -1314,71 +1212,6 @@
                                                         options));
 }
 
-TEST_F(CollectUserDataActionTest, SelectDateTimeRange) {
-  ActionProto action_proto;
-  auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
-  collect_user_data_proto->set_request_terms_and_conditions(false);
-
-  auto* date_time_range = collect_user_data_proto->mutable_date_time_range();
-  SetDateProto(date_time_range->mutable_start_date(), 2020, 1, 1);
-  SetDateProto(date_time_range->mutable_end_date(), 2020, 1, 15);
-  SetDateProto(date_time_range->mutable_min_date(), 2020, 1, 1);
-  SetDateProto(date_time_range->mutable_max_date(), 2020, 12, 31);
-  date_time_range->set_start_time_slot(0);
-  date_time_range->set_end_time_slot(0);
-  date_time_range->set_start_date_label("Start date");
-  date_time_range->set_end_date_label("End date");
-  date_time_range->set_start_time_label("Start time");
-  date_time_range->set_end_time_label("End time");
-  date_time_range->set_date_not_set_error("Date not set");
-  date_time_range->set_time_not_set_error("Time not set");
-
-  auto* time_slot = date_time_range->add_time_slots();
-  time_slot->set_label("08:00 AM");
-  time_slot->set_comparison_value(0);
-  time_slot = date_time_range->add_time_slots();
-  time_slot->set_label("09:00 AM");
-  time_slot->set_comparison_value(1);
-
-  DateProto actual_pickup_date;
-  DateProto actual_return_date;
-  SetDateProto(&actual_pickup_date, 2020, 10, 21);
-  SetDateProto(&actual_return_date, 2020, 10, 25);
-  int actual_pickup_time = 1;
-  int actual_return_time = 1;
-  ON_CALL(mock_action_delegate_, CollectUserData(_))
-      .WillByDefault(
-          Invoke([&](CollectUserDataOptions* collect_user_data_options) {
-            user_data_.date_time_range_start_date_ = actual_pickup_date;
-            user_data_.date_time_range_start_timeslot_ = actual_pickup_time;
-            user_data_.date_time_range_end_date_ = actual_return_date;
-            user_data_.date_time_range_end_timeslot_ = actual_return_time;
-            std::move(collect_user_data_options->confirm_callback)
-                .Run(&user_data_, &user_model_);
-          }));
-
-  EXPECT_CALL(
-      callback_,
-      Run(Pointee(AllOf(
-          Property(&ProcessedActionProto::status, ACTION_APPLIED),
-          Property(&ProcessedActionProto::collect_user_data_result,
-                   Property(&CollectUserDataResultProto::date_range_start_date,
-                            Eq(actual_pickup_date))),
-          Property(
-              &ProcessedActionProto::collect_user_data_result,
-              Property(&CollectUserDataResultProto::date_range_start_timeslot,
-                       Eq(actual_pickup_time))),
-          Property(&ProcessedActionProto::collect_user_data_result,
-                   Property(&CollectUserDataResultProto::date_range_end_date,
-                            Eq(actual_return_date))),
-          Property(
-              &ProcessedActionProto::collect_user_data_result,
-              Property(&CollectUserDataResultProto::date_range_end_timeslot,
-                       Eq(actual_return_time)))))));
-  CollectUserDataAction action(&mock_action_delegate_, action_proto);
-  action.ProcessAction(callback_.Get());
-}
-
 TEST_F(CollectUserDataActionTest, StaticSectionValid) {
   ActionProto action_proto;
   auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 2acba43..07501e3 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -1591,126 +1591,6 @@
   }
 }
 
-void Controller::SetDateTimeRangeStartDate(
-    const absl::optional<DateProto>& date) {
-  if (user_data_.date_time_range_start_date_.has_value() && date.has_value() &&
-      CollectUserDataAction::CompareDates(
-          *user_data_.date_time_range_start_date_, *date) == 0) {
-    return;
-  }
-
-  user_data_.date_time_range_start_date_ = date;
-  for (ControllerObserver& observer : observers_) {
-    observer.OnUserDataChanged(user_data_,
-                               UserData::FieldChange::DATE_TIME_RANGE_START);
-  }
-
-  if (CollectUserDataAction::SanitizeDateTimeRange(
-          &user_data_.date_time_range_start_date_,
-          &user_data_.date_time_range_start_timeslot_,
-          &user_data_.date_time_range_end_date_,
-          &user_data_.date_time_range_end_timeslot_,
-          *collect_user_data_options_,
-          /* change_start = */ false)) {
-    for (ControllerObserver& observer : observers_) {
-      observer.OnUserDataChanged(user_data_,
-                                 UserData::FieldChange::DATE_TIME_RANGE_END);
-    }
-  }
-
-  UpdateCollectUserDataActions();
-}
-
-void Controller::SetDateTimeRangeStartTimeSlot(
-    const absl::optional<int>& timeslot_index) {
-  if (user_data_.date_time_range_start_timeslot_.has_value() &&
-      timeslot_index.has_value() &&
-      *user_data_.date_time_range_start_timeslot_ == *timeslot_index) {
-    return;
-  }
-
-  user_data_.date_time_range_start_timeslot_ = timeslot_index;
-  for (ControllerObserver& observer : observers_) {
-    observer.OnUserDataChanged(user_data_,
-                               UserData::FieldChange::DATE_TIME_RANGE_START);
-  }
-
-  if (CollectUserDataAction::SanitizeDateTimeRange(
-          &user_data_.date_time_range_start_date_,
-          &user_data_.date_time_range_start_timeslot_,
-          &user_data_.date_time_range_end_date_,
-          &user_data_.date_time_range_end_timeslot_,
-          *collect_user_data_options_,
-          /* change_start = */ false)) {
-    for (ControllerObserver& observer : observers_) {
-      observer.OnUserDataChanged(user_data_,
-                                 UserData::FieldChange::DATE_TIME_RANGE_END);
-    }
-  }
-
-  UpdateCollectUserDataActions();
-}
-
-void Controller::SetDateTimeRangeEndDate(
-    const absl::optional<DateProto>& date) {
-  if (user_data_.date_time_range_end_date_.has_value() && date.has_value() &&
-      CollectUserDataAction::CompareDates(*user_data_.date_time_range_end_date_,
-                                          *date) == 0) {
-    return;
-  }
-
-  user_data_.date_time_range_end_date_ = date;
-  for (ControllerObserver& observer : observers_) {
-    observer.OnUserDataChanged(user_data_,
-                               UserData::FieldChange::DATE_TIME_RANGE_END);
-  }
-
-  if (CollectUserDataAction::SanitizeDateTimeRange(
-          &user_data_.date_time_range_start_date_,
-          &user_data_.date_time_range_start_timeslot_,
-          &user_data_.date_time_range_end_date_,
-          &user_data_.date_time_range_end_timeslot_,
-          *collect_user_data_options_,
-          /* change_start = */ true)) {
-    for (ControllerObserver& observer : observers_) {
-      observer.OnUserDataChanged(user_data_,
-                                 UserData::FieldChange::DATE_TIME_RANGE_START);
-    }
-  }
-
-  UpdateCollectUserDataActions();
-}
-
-void Controller::SetDateTimeRangeEndTimeSlot(
-    const absl::optional<int>& timeslot_index) {
-  if (user_data_.date_time_range_end_timeslot_.has_value() &&
-      timeslot_index.has_value() &&
-      *user_data_.date_time_range_end_timeslot_ == *timeslot_index) {
-    return;
-  }
-
-  user_data_.date_time_range_end_timeslot_ = timeslot_index;
-  for (ControllerObserver& observer : observers_) {
-    observer.OnUserDataChanged(user_data_,
-                               UserData::FieldChange::DATE_TIME_RANGE_END);
-  }
-
-  if (CollectUserDataAction::SanitizeDateTimeRange(
-          &user_data_.date_time_range_start_date_,
-          &user_data_.date_time_range_start_timeslot_,
-          &user_data_.date_time_range_end_date_,
-          &user_data_.date_time_range_end_timeslot_,
-          *collect_user_data_options_,
-          /* change_start = */ true)) {
-    for (ControllerObserver& observer : observers_) {
-      observer.OnUserDataChanged(user_data_,
-                                 UserData::FieldChange::DATE_TIME_RANGE_START);
-    }
-  }
-
-  UpdateCollectUserDataActions();
-}
-
 void Controller::SetAdditionalValue(const std::string& client_memory_key,
                                     const ValueProto& value) {
   if (!user_data_.HasAdditionalValue(client_memory_key)) {
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index 6ed39af1..9679163a 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -243,13 +243,6 @@
   void OnTextLinkClicked(int link) override;
   void OnFormActionLinkClicked(int link) override;
   void OnTtsButtonClicked() override;
-  void SetDateTimeRangeStartDate(
-      const absl::optional<DateProto>& date) override;
-  void SetDateTimeRangeStartTimeSlot(
-      const absl::optional<int>& timeslot_index) override;
-  void SetDateTimeRangeEndDate(const absl::optional<DateProto>& date) override;
-  void SetDateTimeRangeEndTimeSlot(
-      const absl::optional<int>& timeslot_index) override;
   void SetAdditionalValue(const std::string& client_memory_key,
                           const ValueProto& value) override;
   void GetTouchableArea(std::vector<RectF>* area) const override;
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index 16278ab7..3c4ebd8 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -2694,255 +2694,6 @@
                   ->is_client_side_only());
 }
 
-TEST_F(ControllerTest, SetDateTimeRange) {
-  testing::InSequence seq;
-
-  auto options = std::make_unique<MockCollectUserDataOptions>();
-  options->request_date_time_range = true;
-  auto* time_slot = options->date_time_range.add_time_slots();
-  time_slot->set_label("08:00 AM");
-  time_slot->set_comparison_value(0);
-  time_slot = options->date_time_range.add_time_slots();
-  time_slot->set_label("09:00 AM");
-  time_slot->set_comparison_value(1);
-
-  controller_->SetCollectUserDataOptions(options.get());
-
-  EXPECT_CALL(
-      mock_observer_,
-      OnUserDataChanged(_, UserData::FieldChange::DATE_TIME_RANGE_START))
-      .Times(1);
-  DateProto start_date;
-  start_date.set_year(2020);
-  start_date.set_month(1);
-  start_date.set_day(20);
-  controller_->SetDateTimeRangeStartDate(start_date);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_start_date_->year(),
-            2020);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_start_date_->month(),
-            1);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_start_date_->day(), 20);
-
-  EXPECT_CALL(
-      mock_observer_,
-      OnUserDataChanged(_, UserData::FieldChange::DATE_TIME_RANGE_START))
-      .Times(1);
-  controller_->SetDateTimeRangeStartTimeSlot(0);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_start_timeslot_, 0);
-
-  EXPECT_CALL(mock_observer_,
-              OnUserDataChanged(_, UserData::FieldChange::DATE_TIME_RANGE_END))
-      .Times(1);
-  DateProto end_date;
-  end_date.set_year(2020);
-  end_date.set_month(1);
-  end_date.set_day(25);
-  controller_->SetDateTimeRangeEndDate(end_date);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_end_date_->year(),
-            2020);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_end_date_->month(), 1);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_end_date_->day(), 25);
-
-  EXPECT_CALL(mock_observer_,
-              OnUserDataChanged(_, UserData::FieldChange::DATE_TIME_RANGE_END))
-      .Times(1);
-  controller_->SetDateTimeRangeEndTimeSlot(1);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_end_timeslot_, 1);
-}
-
-TEST_F(ControllerTest, SetDateTimeRangeStartDateAfterEndDate) {
-  testing::InSequence seq;
-
-  auto options = std::make_unique<MockCollectUserDataOptions>();
-  options->request_date_time_range = true;
-  auto* time_slot = options->date_time_range.add_time_slots();
-  time_slot->set_label("08:00 AM");
-  time_slot->set_comparison_value(0);
-  time_slot = options->date_time_range.add_time_slots();
-  time_slot->set_label("09:00 AM");
-  time_slot->set_comparison_value(1);
-
-  DateProto date;
-  date.set_year(2020);
-  date.set_month(1);
-  date.set_day(20);
-  GetUserData()->date_time_range_start_date_ = date;
-  GetUserData()->date_time_range_end_date_ = date;
-
-  controller_->SetCollectUserDataOptions(options.get());
-
-  EXPECT_CALL(
-      mock_observer_,
-      OnUserDataChanged(_, UserData::FieldChange::DATE_TIME_RANGE_START))
-      .Times(1);
-  EXPECT_CALL(mock_observer_,
-              OnUserDataChanged(_, UserData::FieldChange::DATE_TIME_RANGE_END))
-      .Times(1);
-
-  date.set_day(21);
-  controller_->SetDateTimeRangeStartDate(date);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_start_date_->year(),
-            2020);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_start_date_->month(),
-            1);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_start_date_->day(), 21);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_end_date_,
-            absl::nullopt);
-}
-
-TEST_F(ControllerTest, SetDateTimeRangeEndDateBeforeStartDate) {
-  testing::InSequence seq;
-
-  auto options = std::make_unique<MockCollectUserDataOptions>();
-  options->request_date_time_range = true;
-  auto* time_slot = options->date_time_range.add_time_slots();
-  time_slot->set_label("08:00 AM");
-  time_slot->set_comparison_value(0);
-  time_slot = options->date_time_range.add_time_slots();
-  time_slot->set_label("09:00 AM");
-  time_slot->set_comparison_value(1);
-
-  DateProto date;
-  date.set_year(2020);
-  date.set_month(1);
-  date.set_day(20);
-  GetUserData()->date_time_range_start_date_ = date;
-  GetUserData()->date_time_range_end_date_ = date;
-
-  controller_->SetCollectUserDataOptions(options.get());
-
-  EXPECT_CALL(mock_observer_,
-              OnUserDataChanged(_, UserData::FieldChange::DATE_TIME_RANGE_END))
-      .Times(1);
-  EXPECT_CALL(
-      mock_observer_,
-      OnUserDataChanged(_, UserData::FieldChange::DATE_TIME_RANGE_START))
-      .Times(1);
-
-  date.set_day(19);
-  controller_->SetDateTimeRangeEndDate(date);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_end_date_->year(),
-            2020);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_end_date_->month(), 1);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_end_date_->day(), 19);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_start_date_,
-            absl::nullopt);
-}
-
-TEST_F(ControllerTest, SetDateTimeRangeSameDatesStartTimeAfterEndTime) {
-  testing::InSequence seq;
-
-  auto options = std::make_unique<MockCollectUserDataOptions>();
-  options->request_date_time_range = true;
-  auto* time_slot = options->date_time_range.add_time_slots();
-  time_slot->set_label("08:00 AM");
-  time_slot->set_comparison_value(0);
-  time_slot = options->date_time_range.add_time_slots();
-  time_slot->set_label("09:00 AM");
-  time_slot->set_comparison_value(1);
-
-  DateProto date;
-  date.set_year(2020);
-  date.set_month(1);
-  date.set_day(20);
-  GetUserData()->date_time_range_start_date_ = date;
-  GetUserData()->date_time_range_end_date_ = date;
-  GetUserData()->date_time_range_end_timeslot_ = 0;
-
-  controller_->SetCollectUserDataOptions(options.get());
-
-  EXPECT_CALL(
-      mock_observer_,
-      OnUserDataChanged(_, UserData::FieldChange::DATE_TIME_RANGE_START))
-      .Times(1);
-  EXPECT_CALL(mock_observer_,
-              OnUserDataChanged(_, UserData::FieldChange::DATE_TIME_RANGE_END))
-      .Times(1);
-
-  controller_->SetDateTimeRangeStartTimeSlot(1);
-  EXPECT_EQ(*controller_->GetUserData()->date_time_range_start_timeslot_, 1);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_end_timeslot_,
-            absl::nullopt);
-}
-
-TEST_F(ControllerTest, SetDateTimeRangeSameDatesEndTimeBeforeStartTime) {
-  testing::InSequence seq;
-
-  auto options = std::make_unique<MockCollectUserDataOptions>();
-  options->request_date_time_range = true;
-  auto* time_slot = options->date_time_range.add_time_slots();
-  time_slot->set_label("08:00 AM");
-  time_slot->set_comparison_value(0);
-  time_slot = options->date_time_range.add_time_slots();
-  time_slot->set_label("09:00 AM");
-  time_slot->set_comparison_value(1);
-
-  DateProto date;
-  date.set_year(2020);
-  date.set_month(1);
-  date.set_day(20);
-  GetUserData()->date_time_range_start_date_ = date;
-  GetUserData()->date_time_range_end_date_ = date;
-  GetUserData()->date_time_range_start_timeslot_ = 1;
-
-  controller_->SetCollectUserDataOptions(options.get());
-
-  EXPECT_CALL(mock_observer_,
-              OnUserDataChanged(_, UserData::FieldChange::DATE_TIME_RANGE_END))
-      .Times(1);
-  EXPECT_CALL(
-      mock_observer_,
-      OnUserDataChanged(_, UserData::FieldChange::DATE_TIME_RANGE_START))
-      .Times(1);
-
-  controller_->SetDateTimeRangeEndTimeSlot(0);
-  EXPECT_EQ(*controller_->GetUserData()->date_time_range_end_timeslot_, 0);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_start_timeslot_,
-            absl::nullopt);
-}
-
-TEST_F(ControllerTest, SetDateTimeRangeSameDateValidTime) {
-  testing::InSequence seq;
-
-  auto options = std::make_unique<MockCollectUserDataOptions>();
-  options->request_date_time_range = true;
-  auto* time_slot = options->date_time_range.add_time_slots();
-  time_slot->set_label("08:00 AM");
-  time_slot->set_comparison_value(0);
-  time_slot = options->date_time_range.add_time_slots();
-  time_slot->set_label("09:00 AM");
-  time_slot->set_comparison_value(1);
-
-  DateProto date;
-  date.set_year(2020);
-  date.set_month(1);
-  date.set_day(20);
-  GetUserData()->date_time_range_start_date_ = date;
-  GetUserData()->date_time_range_end_date_ = date;
-
-  controller_->SetCollectUserDataOptions(options.get());
-  EXPECT_CALL(
-      mock_observer_,
-      OnUserDataChanged(_, UserData::FieldChange::DATE_TIME_RANGE_START))
-      .Times(1);
-  EXPECT_CALL(mock_observer_,
-              OnUserDataChanged(_, UserData::FieldChange::DATE_TIME_RANGE_END))
-      .Times(1);
-  controller_->SetDateTimeRangeStartTimeSlot(0);
-  controller_->SetDateTimeRangeEndTimeSlot(1);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_start_date_->year(),
-            2020);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_start_date_->month(),
-            1);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_start_date_->day(), 20);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_end_date_->year(),
-            2020);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_end_date_->month(), 1);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_end_date_->day(), 20);
-  EXPECT_EQ(controller_->GetUserData()->date_time_range_start_timeslot_, 0);
-  EXPECT_EQ(*controller_->GetUserData()->date_time_range_end_timeslot_, 1);
-}
-
 TEST_F(ControllerTest, WriteUserData) {
   auto options = std::make_unique<MockCollectUserDataOptions>();
   controller_->SetCollectUserDataOptions(options.get());
diff --git a/components/autofill_assistant/browser/js_flow_executor_impl_browsertest.cc b/components/autofill_assistant/browser/js_flow_executor_impl_browsertest.cc
new file mode 100644
index 0000000..917d0f5
--- /dev/null
+++ b/components/autofill_assistant/browser/js_flow_executor_impl_browsertest.cc
@@ -0,0 +1,447 @@
+// 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_assistant/browser/js_flow_executor_impl.h"
+
+#include "base/callback.h"
+#include "base/json/json_reader.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "base/test/task_environment.h"
+#include "base/time/tick_clock.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_renderer_host.h"
+#include "content/public/test/web_contents_tester.h"
+#include "content/shell/browser/shell.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/switches.h"
+
+namespace autofill_assistant {
+namespace {
+
+using ::base::test::RunOnceCallback;
+using ::testing::_;
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::Ne;
+using ::testing::NiceMock;
+using ::testing::Pair;
+using ::testing::Pointee;
+using ::testing::Property;
+using ::testing::SizeIs;
+using ::testing::WithArg;
+
+// Parses |json| as a base::Value. No error handling - this will crash for
+// invalid json inputs.
+std::unique_ptr<base::Value> UniqueValueFromJson(const std::string& json) {
+  return std::make_unique<base::Value>(
+      std::move(*base::JSONReader::Read(json)));
+}
+
+class MockJsFlowExecutorImplDelegate : public JsFlowExecutorImpl::Delegate {
+ public:
+  MockJsFlowExecutorImplDelegate() = default;
+  ~MockJsFlowExecutorImplDelegate() override = default;
+
+  MOCK_METHOD(
+      void,
+      RunNativeAction,
+      (std::unique_ptr<base::Value> native_action,
+       base::OnceCallback<void(const ClientStatus& result_status,
+                               std::unique_ptr<base::Value> result_value)>
+           callback),
+      (override));
+};
+
+class JsFlowExecutorImplTest : public content::ContentBrowserTest {
+ public:
+  JsFlowExecutorImplTest() {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch("site-per-process");
+    // Necessary to avoid flakiness or failure due to input arriving
+    // before the first compositor commit.
+    command_line->AppendSwitch(blink::switches::kAllowPreCommitInput);
+  }
+
+  void SetUpOnMainThread() override {
+    ContentBrowserTest::SetUpOnMainThread();
+
+    // Start a mock server for hosting an OOPIF.
+    http_server_iframe_ = std::make_unique<net::EmbeddedTestServer>(
+        net::EmbeddedTestServer::TYPE_HTTP);
+    http_server_iframe_->ServeFilesFromSourceDirectory(
+        "components/test/data/autofill_assistant/html_iframe");
+    ASSERT_TRUE(http_server_iframe_->Start(8081));
+
+    // Start the main server hosting the test page.
+    http_server_ = std::make_unique<net::EmbeddedTestServer>(
+        net::EmbeddedTestServer::TYPE_HTTP);
+    http_server_->ServeFilesFromSourceDirectory(
+        "components/test/data/autofill_assistant/html");
+    ASSERT_TRUE(http_server_->Start(8080));
+    ASSERT_TRUE(NavigateToURL(
+        shell(),
+        http_server_->GetURL("/autofill_assistant_target_website.html")));
+
+    flow_executor_ = std::make_unique<JsFlowExecutorImpl>(
+        shell()->web_contents(), &mock_delegate_);
+  }
+
+  // Overload, ignore result value, just return the client status.
+  ClientStatus RunTest(const std::string& js_flow) {
+    std::unique_ptr<base::Value> ignored_result;
+    return RunTest(js_flow, ignored_result);
+  }
+
+  ClientStatus RunTest(const std::string& js_flow,
+                       std::unique_ptr<base::Value>& result_value) {
+    ClientStatus status;
+    base::RunLoop run_loop;
+    flow_executor_->Start(
+        js_flow, base::BindOnce(&JsFlowExecutorImplTest::OnFlowFinished,
+                                base::Unretained(this), run_loop.QuitClosure(),
+                                &status, std::ref(result_value)));
+    run_loop.Run();
+    return status;
+  }
+
+  void OnFlowFinished(base::OnceClosure done_callback,
+                      ClientStatus* status_output,
+                      std::unique_ptr<base::Value>& result_output,
+                      const ClientStatus& status,
+                      std::unique_ptr<base::Value> result_value) {
+    *status_output = status;
+    result_output = std::move(result_value);
+    std::move(done_callback).Run();
+  }
+
+ protected:
+  NiceMock<MockJsFlowExecutorImplDelegate> mock_delegate_;
+  std::unique_ptr<JsFlowExecutorImpl> flow_executor_;
+  std::unique_ptr<net::EmbeddedTestServer> http_server_;
+  std::unique_ptr<net::EmbeddedTestServer> http_server_iframe_;
+};
+
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, SmokeTest) {
+  EXPECT_THAT(RunTest(std::string()),
+              Property(&ClientStatus::proto_status, ACTION_APPLIED));
+}
+
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, InvalidJs) {
+  EXPECT_THAT(RunTest("Not valid Javascript"),
+              Property(&ClientStatus::proto_status, UNEXPECTED_JS_ERROR));
+}
+
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, RunNativeActionWithReturnValue) {
+  std::unique_ptr<base::Value> native_return_value =
+      std::make_unique<base::Value>(std::move(*base::JSONReader::Read(
+          R"(
+          {
+            "keyA":12345,
+            "keyB":"Hello world",
+            "keyC": ["array", "of", "strings"],
+            "keyD": {
+              "keyE": "nested",
+              "keyF": true,
+              "keyG": 123.45,
+              "keyH": null
+            }
+          }
+        )")));
+
+  EXPECT_CALL(mock_delegate_, RunNativeAction)
+      .WillOnce([&](auto value, auto callback) {
+        EXPECT_EQ(*value, *UniqueValueFromJson(R"(
+          {"type":"string",
+           "value":"test"})"));
+        std::move(callback).Run(ClientStatus(ACTION_APPLIED),
+                                std::move(native_return_value));
+      });
+
+  std::unique_ptr<base::Value> js_return_value;
+  EXPECT_THAT(RunTest(R"(
+                        let [status, value] = await runNativeAction('test');
+                        if (status != 2) { // ACTION_APPLIED
+                          return status;
+                        }
+                        value.keyA += 3;
+                        value.keyB += '!';
+                        value.keyD.keyF = false;
+                        return value;
+                      )",
+                      js_return_value),
+              Property(&ClientStatus::proto_status, ACTION_APPLIED));
+  EXPECT_EQ(*js_return_value, *base::JSONReader::Read(R"(
+    {
+       "result": {
+          "type": "object",
+          "value": {
+             "keyA": 12348,
+             "keyB": "Hello world!",
+             "keyC": [ "array", "of", "strings" ],
+             "keyD": {
+                "keyE": "nested",
+                "keyF": false,
+                "keyG": 123.45,
+                "keyH": null
+             }
+          }
+       }
+    }
+    )"));
+}
+
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, RunMultipleNativeActions) {
+  EXPECT_CALL(mock_delegate_, RunNativeAction)
+      .WillOnce([&](auto value, auto callback) {
+        EXPECT_EQ(*value, *UniqueValueFromJson(R"(
+          {"type":"string",
+           "value":"test1"})"));
+        std::move(callback).Run(ClientStatus(ACTION_APPLIED), nullptr);
+      })
+      .WillOnce([&](auto value, auto callback) {
+        EXPECT_EQ(*value, *UniqueValueFromJson(R"(
+          {"type":"string",
+           "value":"test2"})"));
+        std::move(callback).Run(ClientStatus(OTHER_ACTION_STATUS), nullptr);
+      });
+
+  // Note: the overall flow should report ACTION_APPLIED since the flow
+  // completed successfully, but the return value should hold
+  // OTHER_ACTION_STATUS, i.e., 3.
+  std::unique_ptr<base::Value> result;
+  EXPECT_THAT(RunTest(R"(
+                        let [status, value] = await runNativeAction('test1');
+                        if (status == 2) { // ACTION_APPLIED
+                          [status, value] = await runNativeAction('test2');
+                        }
+                        return status;
+                      )",
+                      result),
+              Property(&ClientStatus::proto_status, ACTION_APPLIED));
+  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
+      {
+        "result": {
+          "description": "3",
+          "type": "number",
+          "value": 3
+        }
+      }
+    )"));
+}
+
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnInteger) {
+  std::unique_ptr<base::Value> result;
+  ClientStatus status = RunTest("return 12345;", result);
+  EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
+  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
+      {
+        "result": {
+          "description": "12345",
+          "type": "number",
+          "value": 12345
+        }
+      }
+    )"));
+}
+
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnString) {
+  std::unique_ptr<base::Value> result;
+  ClientStatus status = RunTest("return 'Hello world!';", result);
+  EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
+  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
+      {
+        "result": {
+          "type": "string",
+          "value": "Hello world!"
+        }
+      }
+    )"));
+}
+
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnDictionary) {
+  std::unique_ptr<base::Value> result;
+  ClientStatus status = RunTest(
+      R"(
+          return {
+            "keyA":12345,
+            "keyB":"Hello world!",
+            "keyC": ["array", "of", "strings"],
+            "keyD": {
+              "keyE": "nested",
+              "keyF": true,
+              "keyG": 123.45,
+              "keyH": null
+            }
+          };
+        )",
+      result);
+  EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
+  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
+      {
+        "result": {
+          "type": "object",
+          "value": {
+            "keyA": 12345,
+            "keyB": "Hello world!",
+            "keyC": ["array", "of", "strings"],
+            "keyD": {
+                "keyE": "nested",
+                "keyF": true,
+                "keyG": 123.45,
+                "keyH": null
+            }
+          }
+        }
+      }
+    )"));
+}
+
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnNothing) {
+  std::unique_ptr<base::Value> result;
+  ClientStatus status = RunTest("", result);
+  EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
+  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
+      {
+        "result": {
+          "type": "undefined"
+        }
+      }
+    )"));
+}
+
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnNull) {
+  std::unique_ptr<base::Value> result;
+  ClientStatus status = RunTest("return null;", result);
+  EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
+  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
+      {
+        "result": {
+          "subtype": "null",
+          "type": "object",
+          "value": null
+        }
+      }
+    )"));
+}
+
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ExceptionReporting) {
+  std::unique_ptr<base::Value> result;
+  ClientStatus status = RunTest("throw new Error('Hello world!');", result);
+  EXPECT_EQ(status.proto_status(), UNEXPECTED_JS_ERROR);
+  ASSERT_NE(result, nullptr);
+
+  absl::optional<base::Value> exceptionDetails =
+      result->ExtractKey("exceptionDetails");
+  ASSERT_NE(exceptionDetails, absl::nullopt);
+
+  EXPECT_THAT(exceptionDetails->ExtractKey("text")->GetIfString(),
+              Pointee(Eq("Uncaught (in promise) Error: Hello world!")));
+
+  // We can't currently check the contents of the reported stack frames since
+  // they depend on the internal wrapper. For now, we simply test that this is
+  // not empty. For reference, at the time of writing, this was the full output:
+  // "exceptionDetails": {
+  //    "columnNumber": 0,
+  //    "exception": {
+  //       "className": "Error",
+  //       "description": "Error: Hello world!
+  //                          at <anonymous>:13:11
+  //                          at <anonymous>:13:41",
+  //       "objectId": "1450023673453216843.4.1",
+  //       "subtype": "error",
+  //       "type": "object"
+  //    },
+  //    "exceptionId": 2,
+  //    "lineNumber": 0,
+  //    "text": "Uncaught (in promise) Error: Hello world!"
+  // }
+  EXPECT_THAT(exceptionDetails->ExtractKey("exception"), Ne(absl::nullopt));
+}
+
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, RunMultipleConsecutiveFlows) {
+  for (int i = 0; i < 10; ++i) {
+    std::unique_ptr<base::Value> result;
+    ClientStatus status =
+        RunTest(base::StrCat({"return ", base::NumberToString(i)}), result);
+    EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
+    EXPECT_EQ(result->ExtractKey("result")->ExtractKey("value")->GetIfInt(), i);
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest,
+                       UnserializableRunNativeActionArgument) {
+  std::unique_ptr<base::Value> result;
+  EXPECT_CALL(mock_delegate_, RunNativeAction).Times(0);
+  ClientStatus status = RunTest(
+      R"(
+        function foo(){}
+        // foo cannot be serialized as a JSON object, so this should fail.
+        let [status, result] = await runNativeAction(foo);
+        return status;
+      )",
+      result);
+  EXPECT_EQ(result, nullptr);
+  EXPECT_EQ(status.proto_status(), UNEXPECTED_JS_ERROR);
+  EXPECT_TRUE(status.details().has_unexpected_error_info());
+}
+
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, StartWhileAlreadyRunningFails) {
+  EXPECT_CALL(mock_delegate_, RunNativeAction)
+      .WillOnce(WithArg<1>([&](auto callback) {
+        // Starting a second flow while the first one is running should fail.
+        EXPECT_EQ(RunTest(std::string()).proto_status(), INVALID_ACTION);
+
+        // The first flow should be able to finish successfully.
+        std::move(callback).Run(ClientStatus(ACTION_APPLIED), nullptr);
+      }));
+
+  std::unique_ptr<base::Value> result;
+  ClientStatus status = RunTest(
+      R"(
+      let [status, result] = await runNativeAction('');
+      return status;
+      )",
+      result);
+  EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
+  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
+      {
+        "result": {
+          "description": "2",
+          "type": "number",
+          "value": 2
+        }
+      }
+    )"));
+}
+
+IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest,
+                       EnvironmentIsPreservedBetweenRuns) {
+  EXPECT_EQ(RunTest("globalFlowState.i = 5;").proto_status(), ACTION_APPLIED);
+
+  std::unique_ptr<base::Value> result;
+  EXPECT_EQ(RunTest("return globalFlowState.i;", result).proto_status(),
+            ACTION_APPLIED);
+  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
+      {
+        "result": {
+          "description": "5",
+          "type": "number",
+          "value": 5
+        }
+      }
+    )"));
+}
+
+}  // namespace
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/js_flow_executor_impl_unittest.cc b/components/autofill_assistant/browser/js_flow_executor_impl_unittest.cc
deleted file mode 100644
index 74c4a53..0000000
--- a/components/autofill_assistant/browser/js_flow_executor_impl_unittest.cc
+++ /dev/null
@@ -1,446 +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 "components/autofill_assistant/browser/js_flow_executor_impl.h"
-#include "base/callback.h"
-#include "base/json/json_reader.h"
-#include "base/strings/strcat.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/test/gmock_callback_support.h"
-#include "base/test/mock_callback.h"
-#include "base/test/task_environment.h"
-#include "base/time/tick_clock.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/content_browser_test.h"
-#include "content/public/test/content_browser_test_utils.h"
-#include "content/public/test/test_browser_context.h"
-#include "content/public/test/test_renderer_host.h"
-#include "content/public/test/web_contents_tester.h"
-#include "content/shell/browser/shell.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/public/common/switches.h"
-
-namespace autofill_assistant {
-namespace {
-
-using ::base::test::RunOnceCallback;
-using ::testing::_;
-using ::testing::ElementsAre;
-using ::testing::Eq;
-using ::testing::Ne;
-using ::testing::NiceMock;
-using ::testing::Pair;
-using ::testing::Pointee;
-using ::testing::Property;
-using ::testing::SizeIs;
-using ::testing::WithArg;
-
-// Parses |json| as a base::Value. No error handling - this will crash for
-// invalid json inputs.
-std::unique_ptr<base::Value> UniqueValueFromJson(const std::string& json) {
-  return std::make_unique<base::Value>(
-      std::move(*base::JSONReader::Read(json)));
-}
-
-class MockJsFlowExecutorImplDelegate : public JsFlowExecutorImpl::Delegate {
- public:
-  MockJsFlowExecutorImplDelegate() = default;
-  ~MockJsFlowExecutorImplDelegate() override = default;
-
-  MOCK_METHOD(
-      void,
-      RunNativeAction,
-      (std::unique_ptr<base::Value> native_action,
-       base::OnceCallback<void(const ClientStatus& result_status,
-                               std::unique_ptr<base::Value> result_value)>
-           callback),
-      (override));
-};
-
-class JsFlowExecutorImplTest : public content::ContentBrowserTest {
- public:
-  JsFlowExecutorImplTest() {}
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch("site-per-process");
-    // Necessary to avoid flakiness or failure due to input arriving
-    // before the first compositor commit.
-    command_line->AppendSwitch(blink::switches::kAllowPreCommitInput);
-  }
-
-  void SetUpOnMainThread() override {
-    ContentBrowserTest::SetUpOnMainThread();
-
-    // Start a mock server for hosting an OOPIF.
-    http_server_iframe_ = std::make_unique<net::EmbeddedTestServer>(
-        net::EmbeddedTestServer::TYPE_HTTP);
-    http_server_iframe_->ServeFilesFromSourceDirectory(
-        "components/test/data/autofill_assistant/html_iframe");
-    ASSERT_TRUE(http_server_iframe_->Start(8081));
-
-    // Start the main server hosting the test page.
-    http_server_ = std::make_unique<net::EmbeddedTestServer>(
-        net::EmbeddedTestServer::TYPE_HTTP);
-    http_server_->ServeFilesFromSourceDirectory(
-        "components/test/data/autofill_assistant/html");
-    ASSERT_TRUE(http_server_->Start(8080));
-    ASSERT_TRUE(NavigateToURL(
-        shell(),
-        http_server_->GetURL("/autofill_assistant_target_website.html")));
-
-    flow_executor_ = std::make_unique<JsFlowExecutorImpl>(
-        shell()->web_contents(), &mock_delegate_);
-  }
-
-  // Overload, ignore result value, just return the client status.
-  ClientStatus RunTest(const std::string& js_flow) {
-    std::unique_ptr<base::Value> ignored_result;
-    return RunTest(js_flow, ignored_result);
-  }
-
-  ClientStatus RunTest(const std::string& js_flow,
-                       std::unique_ptr<base::Value>& result_value) {
-    ClientStatus status;
-    base::RunLoop run_loop;
-    flow_executor_->Start(
-        js_flow, base::BindOnce(&JsFlowExecutorImplTest::OnFlowFinished,
-                                base::Unretained(this), run_loop.QuitClosure(),
-                                &status, std::ref(result_value)));
-    run_loop.Run();
-    return status;
-  }
-
-  void OnFlowFinished(base::OnceClosure done_callback,
-                      ClientStatus* status_output,
-                      std::unique_ptr<base::Value>& result_output,
-                      const ClientStatus& status,
-                      std::unique_ptr<base::Value> result_value) {
-    *status_output = status;
-    result_output = std::move(result_value);
-    std::move(done_callback).Run();
-  }
-
- protected:
-  NiceMock<MockJsFlowExecutorImplDelegate> mock_delegate_;
-  std::unique_ptr<JsFlowExecutorImpl> flow_executor_;
-  std::unique_ptr<net::EmbeddedTestServer> http_server_;
-  std::unique_ptr<net::EmbeddedTestServer> http_server_iframe_;
-};
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, SmokeTest) {
-  EXPECT_THAT(RunTest(std::string()),
-              Property(&ClientStatus::proto_status, ACTION_APPLIED));
-}
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, InvalidJs) {
-  EXPECT_THAT(RunTest("Not valid Javascript"),
-              Property(&ClientStatus::proto_status, UNEXPECTED_JS_ERROR));
-}
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, RunNativeActionWithReturnValue) {
-  std::unique_ptr<base::Value> native_return_value =
-      std::make_unique<base::Value>(std::move(*base::JSONReader::Read(
-          R"(
-          {
-            "keyA":12345,
-            "keyB":"Hello world",
-            "keyC": ["array", "of", "strings"],
-            "keyD": {
-              "keyE": "nested",
-              "keyF": true,
-              "keyG": 123.45,
-              "keyH": null
-            }
-          }
-        )")));
-
-  EXPECT_CALL(mock_delegate_, RunNativeAction)
-      .WillOnce([&](auto value, auto callback) {
-        EXPECT_EQ(*value, *UniqueValueFromJson(R"(
-          {"type":"string",
-           "value":"test"})"));
-        std::move(callback).Run(ClientStatus(ACTION_APPLIED),
-                                std::move(native_return_value));
-      });
-
-  std::unique_ptr<base::Value> js_return_value;
-  EXPECT_THAT(RunTest(R"(
-                        let [status, value] = await runNativeAction('test');
-                        if (status != 2) { // ACTION_APPLIED
-                          return status;
-                        }
-                        value.keyA += 3;
-                        value.keyB += '!';
-                        value.keyD.keyF = false;
-                        return value;
-                      )",
-                      js_return_value),
-              Property(&ClientStatus::proto_status, ACTION_APPLIED));
-  EXPECT_EQ(*js_return_value, *base::JSONReader::Read(R"(
-    {
-       "result": {
-          "type": "object",
-          "value": {
-             "keyA": 12348,
-             "keyB": "Hello world!",
-             "keyC": [ "array", "of", "strings" ],
-             "keyD": {
-                "keyE": "nested",
-                "keyF": false,
-                "keyG": 123.45,
-                "keyH": null
-             }
-          }
-       }
-    }
-    )"));
-}
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, RunMultipleNativeActions) {
-  EXPECT_CALL(mock_delegate_, RunNativeAction)
-      .WillOnce([&](auto value, auto callback) {
-        EXPECT_EQ(*value, *UniqueValueFromJson(R"(
-          {"type":"string",
-           "value":"test1"})"));
-        std::move(callback).Run(ClientStatus(ACTION_APPLIED), nullptr);
-      })
-      .WillOnce([&](auto value, auto callback) {
-        EXPECT_EQ(*value, *UniqueValueFromJson(R"(
-          {"type":"string",
-           "value":"test2"})"));
-        std::move(callback).Run(ClientStatus(OTHER_ACTION_STATUS), nullptr);
-      });
-
-  // Note: the overall flow should report ACTION_APPLIED since the flow
-  // completed successfully, but the return value should hold
-  // OTHER_ACTION_STATUS, i.e., 3.
-  std::unique_ptr<base::Value> result;
-  EXPECT_THAT(RunTest(R"(
-                        let [status, value] = await runNativeAction('test1');
-                        if (status == 2) { // ACTION_APPLIED
-                          [status, value] = await runNativeAction('test2');
-                        }
-                        return status;
-                      )",
-                      result),
-              Property(&ClientStatus::proto_status, ACTION_APPLIED));
-  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
-      {
-        "result": {
-          "description": "3",
-          "type": "number",
-          "value": 3
-        }
-      }
-    )"));
-}
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnInteger) {
-  std::unique_ptr<base::Value> result;
-  ClientStatus status = RunTest("return 12345;", result);
-  EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
-  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
-      {
-        "result": {
-          "description": "12345",
-          "type": "number",
-          "value": 12345
-        }
-      }
-    )"));
-}
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnString) {
-  std::unique_ptr<base::Value> result;
-  ClientStatus status = RunTest("return 'Hello world!';", result);
-  EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
-  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
-      {
-        "result": {
-          "type": "string",
-          "value": "Hello world!"
-        }
-      }
-    )"));
-}
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnDictionary) {
-  std::unique_ptr<base::Value> result;
-  ClientStatus status = RunTest(
-      R"(
-          return {
-            "keyA":12345,
-            "keyB":"Hello world!",
-            "keyC": ["array", "of", "strings"],
-            "keyD": {
-              "keyE": "nested",
-              "keyF": true,
-              "keyG": 123.45,
-              "keyH": null
-            }
-          };
-        )",
-      result);
-  EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
-  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
-      {
-        "result": {
-          "type": "object",
-          "value": {
-            "keyA": 12345,
-            "keyB": "Hello world!",
-            "keyC": ["array", "of", "strings"],
-            "keyD": {
-                "keyE": "nested",
-                "keyF": true,
-                "keyG": 123.45,
-                "keyH": null
-            }
-          }
-        }
-      }
-    )"));
-}
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnNothing) {
-  std::unique_ptr<base::Value> result;
-  ClientStatus status = RunTest("", result);
-  EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
-  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
-      {
-        "result": {
-          "type": "undefined"
-        }
-      }
-    )"));
-}
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ReturnNull) {
-  std::unique_ptr<base::Value> result;
-  ClientStatus status = RunTest("return null;", result);
-  EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
-  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
-      {
-        "result": {
-          "subtype": "null",
-          "type": "object",
-          "value": null
-        }
-      }
-    )"));
-}
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, ExceptionReporting) {
-  std::unique_ptr<base::Value> result;
-  ClientStatus status = RunTest("throw new Error('Hello world!');", result);
-  EXPECT_EQ(status.proto_status(), UNEXPECTED_JS_ERROR);
-  ASSERT_NE(result, nullptr);
-
-  absl::optional<base::Value> exceptionDetails =
-      result->ExtractKey("exceptionDetails");
-  ASSERT_NE(exceptionDetails, absl::nullopt);
-
-  EXPECT_THAT(exceptionDetails->ExtractKey("text")->GetIfString(),
-              Pointee(Eq("Uncaught (in promise) Error: Hello world!")));
-
-  // We can't currently check the contents of the reported stack frames since
-  // they depend on the internal wrapper. For now, we simply test that this is
-  // not empty. For reference, at the time of writing, this was the full output:
-  // "exceptionDetails": {
-  //    "columnNumber": 0,
-  //    "exception": {
-  //       "className": "Error",
-  //       "description": "Error: Hello world!
-  //                          at <anonymous>:13:11
-  //                          at <anonymous>:13:41",
-  //       "objectId": "1450023673453216843.4.1",
-  //       "subtype": "error",
-  //       "type": "object"
-  //    },
-  //    "exceptionId": 2,
-  //    "lineNumber": 0,
-  //    "text": "Uncaught (in promise) Error: Hello world!"
-  // }
-  EXPECT_THAT(exceptionDetails->ExtractKey("exception"), Ne(absl::nullopt));
-}
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, RunMultipleConsecutiveFlows) {
-  for (int i = 0; i < 10; ++i) {
-    std::unique_ptr<base::Value> result;
-    ClientStatus status =
-        RunTest(base::StrCat({"return ", base::NumberToString(i)}), result);
-    EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
-    EXPECT_EQ(result->ExtractKey("result")->ExtractKey("value")->GetIfInt(), i);
-  }
-}
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest,
-                       UnserializableRunNativeActionArgument) {
-  std::unique_ptr<base::Value> result;
-  EXPECT_CALL(mock_delegate_, RunNativeAction).Times(0);
-  ClientStatus status = RunTest(
-      R"(
-        function foo(){}
-        // foo cannot be serialized as a JSON object, so this should fail.
-        let [status, result] = await runNativeAction(foo);
-        return status;
-      )",
-      result);
-  EXPECT_EQ(result, nullptr);
-  EXPECT_EQ(status.proto_status(), UNEXPECTED_JS_ERROR);
-  EXPECT_TRUE(status.details().has_unexpected_error_info());
-}
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest, StartWhileAlreadyRunningFails) {
-  EXPECT_CALL(mock_delegate_, RunNativeAction)
-      .WillOnce(WithArg<1>([&](auto callback) {
-        // Starting a second flow while the first one is running should fail.
-        EXPECT_EQ(RunTest(std::string()).proto_status(), INVALID_ACTION);
-
-        // The first flow should be able to finish successfully.
-        std::move(callback).Run(ClientStatus(ACTION_APPLIED), nullptr);
-      }));
-
-  std::unique_ptr<base::Value> result;
-  ClientStatus status = RunTest(
-      R"(
-      let [status, result] = await runNativeAction('');
-      return status;
-      )",
-      result);
-  EXPECT_EQ(status.proto_status(), ACTION_APPLIED);
-  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
-      {
-        "result": {
-          "description": "2",
-          "type": "number",
-          "value": 2
-        }
-      }
-    )"));
-}
-
-IN_PROC_BROWSER_TEST_F(JsFlowExecutorImplTest,
-                       EnvironmentIsPreservedBetweenRuns) {
-  EXPECT_EQ(RunTest("globalFlowState.i = 5;").proto_status(), ACTION_APPLIED);
-
-  std::unique_ptr<base::Value> result;
-  EXPECT_EQ(RunTest("return globalFlowState.i;", result).proto_status(),
-            ACTION_APPLIED);
-  EXPECT_EQ(*result, *base::JSONReader::Read(R"(
-      {
-        "result": {
-          "description": "5",
-          "type": "number",
-          "value": 5
-        }
-      }
-    )"));
-}
-
-}  // namespace
-}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 3e4963d..c7e0792 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -912,14 +912,6 @@
   // keys specified in |TextInputProto|. The values themselves are stored in the
   // client and do not leave the device.
   repeated string set_text_input_memory_keys = 10;
-  // The start date of the date/time range, if requested.
-  optional DateProto date_range_start_date = 11;
-  // The index of the selected timeslot for the start of the date/time range.
-  optional int32 date_range_start_timeslot = 12;
-  // The end date of the date/time range, if requested.
-  optional DateProto date_range_end_date = 13;
-  // The index of the selected timeslot for the end of the date/time range.
-  optional int32 date_range_end_timeslot = 14;
   // The values obtained from the additional sections.
   repeated ModelProto.ModelValue additional_sections_values = 15;
   // Indicates whether the UI was shown to the user. This can be false if
@@ -928,7 +920,7 @@
   // Indicates that the chosen login option was missing the username.
   optional bool login_missing_username = 17;
 
-  reserved 7, 8, 18 to 20;
+  reserved 7, 8, 11 to 14, 18 to 20;
 }
 
 message ActionTimingStats {
@@ -2274,46 +2266,6 @@
   repeated LoginOptionProto login_options = 2;
 }
 
-message DateTimeRangeProto {
-  message TimeSlot {
-    // The label to display.
-    optional string label = 1;
-    // The comparison value to be used to compare this timeslot
-    // to others. Smaller values indicate earlier times. This will be used to
-    // prevent start > end and vice-versa.
-    optional int32 comparison_value = 2;
-  }
-
-  // The initial start date of the date/time range.
-  optional DateProto start_date = 15;
-  // The index of the initial start time slot to select.
-  optional int32 start_time_slot = 13;
-  // The initial end date of the date/time range.
-  optional DateProto end_date = 16;
-  // The index of the initial end time slot to select.
-  optional int32 end_time_slot = 14;
-  // The minimum allowed date of the date/time range.
-  optional DateProto min_date = 3;
-  // The maximum allowed date of the date/time range.
-  optional DateProto max_date = 4;
-  // The label for the start date (e.g., 'Pick up date').
-  optional string start_date_label = 8;
-  // The label for the start time (e.g., 'Pick up time').
-  optional string start_time_label = 9;
-  // The label for the end date (e.g., 'Drop off date').
-  optional string end_date_label = 10;
-  // The label for the end time (e.g., 'Drop off time').
-  optional string end_time_label = 11;
-  // The time slots to offer (e.g., 08:00 AM, 08:30 AM, ...)
-  repeated TimeSlot time_slots = 12;
-  // The error message to display if the date is not set.
-  optional string date_not_set_error = 17;
-  // The error message to display if the time is not set.
-  optional string time_not_set_error = 18;
-
-  reserved 1, 2, 5, 6, 7;
-}
-
 // A section showing a simple text message.
 message StaticTextSectionProto {
   // The text to display. Can contain markup tags like <b>.
@@ -2479,8 +2431,6 @@
   optional string credit_card_expired_text = 23;
   // The login details that should be gathered.
   optional LoginDetailsProto login_details = 16;
-  // The date/time range that should be gathered.
-  optional DateTimeRangeProto date_time_range = 17;
   // An optional list of additional sections, which is above all other sections.
   repeated UserFormSectionProto additional_prepended_sections = 18;
   // An optional list of additional sections, which is below all other sections.
@@ -2541,7 +2491,7 @@
   }
   optional UserDataProto user_data = 37;
 
-  reserved 7, 10, 14, 15, 26;
+  reserved 7, 10, 14, 15, 17, 26;
 }
 
 // Stop Autofill Assistant.
diff --git a/components/autofill_assistant/browser/ui_delegate.h b/components/autofill_assistant/browser/ui_delegate.h
index d8c28e1..430f9c8 100644
--- a/components/autofill_assistant/browser/ui_delegate.h
+++ b/components/autofill_assistant/browser/ui_delegate.h
@@ -137,22 +137,6 @@
   // Called when the user clicks the TTS button.
   virtual void OnTtsButtonClicked() = 0;
 
-  // Sets the start date of the date/time range.
-  virtual void SetDateTimeRangeStartDate(
-      const absl::optional<DateProto>& date) = 0;
-
-  // Sets the start timeslot of the date/time range.
-  virtual void SetDateTimeRangeStartTimeSlot(
-      const absl::optional<int>& timeslot_index) = 0;
-
-  // Sets the end date of the date/time range.
-  virtual void SetDateTimeRangeEndDate(
-      const absl::optional<DateProto>& date) = 0;
-
-  // Sets the end timeslot of the date/time range.
-  virtual void SetDateTimeRangeEndTimeSlot(
-      const absl::optional<int>& timeslot_index) = 0;
-
   // Sets an additional value.
   virtual void SetAdditionalValue(const std::string& client_memory_key,
                                   const ValueProto& value) = 0;
diff --git a/components/autofill_assistant/browser/user_data.h b/components/autofill_assistant/browser/user_data.h
index c1cd1c2..3595dfd0 100644
--- a/components/autofill_assistant/browser/user_data.h
+++ b/components/autofill_assistant/browser/user_data.h
@@ -187,18 +187,12 @@
     BILLING_ADDRESS,
     LOGIN_CHOICE,
     TERMS_AND_CONDITIONS,
-    DATE_TIME_RANGE_START,
-    DATE_TIME_RANGE_END,
     ADDITIONAL_VALUES,
     AVAILABLE_PROFILES,
     AVAILABLE_PAYMENT_INSTRUMENTS,
   };
 
   TermsAndConditionsState terms_and_conditions_ = NOT_SELECTED;
-  absl::optional<DateProto> date_time_range_start_date_;
-  absl::optional<DateProto> date_time_range_end_date_;
-  absl::optional<int> date_time_range_start_timeslot_;
-  absl::optional<int> date_time_range_end_timeslot_;
 
   std::vector<std::unique_ptr<Contact>> available_contacts_;
   std::vector<std::unique_ptr<Address>> available_addresses_;
@@ -275,7 +269,6 @@
   bool request_shipping = false;
   bool request_payment_method = false;
   bool request_login_choice = false;
-  bool request_date_time_range = false;
   std::vector<AutofillContactField> contact_summary_fields;
   int contact_summary_max_lines;
   std::vector<AutofillContactField> contact_full_fields;
@@ -314,7 +307,6 @@
   UserActionProto confirm_action;
   std::vector<UserActionProto> additional_actions;
   TermsAndConditionsState initial_terms_and_conditions = NOT_SELECTED;
-  DateTimeRangeProto date_time_range;
   std::vector<UserFormSectionProto> additional_prepended_sections;
   std::vector<UserFormSectionProto> additional_appended_sections;
   absl::optional<GenericUserInterfaceProto> generic_user_interface_prepended;
diff --git a/components/browser_ui/photo_picker/android/java/res/drawable/ic_full_screen_exit_white_24dp.xml b/components/browser_ui/photo_picker/android/java/res/drawable/ic_full_screen_exit_white_24dp.xml
index 0731615..8c468ee 100644
--- a/components/browser_ui/photo_picker/android/java/res/drawable/ic_full_screen_exit_white_24dp.xml
+++ b/components/browser_ui/photo_picker/android/java/res/drawable/ic_full_screen_exit_white_24dp.xml
@@ -5,8 +5,6 @@
 
 <vector
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
diff --git a/components/browser_ui/photo_picker/android/java/res/drawable/ic_full_screen_white_24dp.xml b/components/browser_ui/photo_picker/android/java/res/drawable/ic_full_screen_white_24dp.xml
index 42720ca..b6b15ae 100644
--- a/components/browser_ui/photo_picker/android/java/res/drawable/ic_full_screen_white_24dp.xml
+++ b/components/browser_ui/photo_picker/android/java/res/drawable/ic_full_screen_white_24dp.xml
@@ -5,8 +5,6 @@
 
 <vector
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
diff --git a/components/browser_ui/photo_picker/android/java/res/drawable/ic_photo_camera_grey.xml b/components/browser_ui/photo_picker/android/java/res/drawable/ic_photo_camera_grey.xml
index 811bd335..8dc5a27 100644
--- a/components/browser_ui/photo_picker/android/java/res/drawable/ic_photo_camera_grey.xml
+++ b/components/browser_ui/photo_picker/android/java/res/drawable/ic_photo_camera_grey.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
diff --git a/components/browser_ui/photo_picker/android/java/res/drawable/ic_play_circle_filled_white_24dp.xml b/components/browser_ui/photo_picker/android/java/res/drawable/ic_play_circle_filled_white_24dp.xml
index 13dffab..c458ddde 100644
--- a/components/browser_ui/photo_picker/android/java/res/drawable/ic_play_circle_filled_white_24dp.xml
+++ b/components/browser_ui/photo_picker/android/java/res/drawable/ic_play_circle_filled_white_24dp.xml
@@ -5,8 +5,6 @@
 
 <vector
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FourStateCookieSettingsPreference.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FourStateCookieSettingsPreference.java
index 6077320..0affbd42 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FourStateCookieSettingsPreference.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FourStateCookieSettingsPreference.java
@@ -42,6 +42,9 @@
         // An enum indicating when to block third-party cookies.
         public @CookieControlsMode int cookieControlsMode;
 
+        // Whether the incognito mode is enabled.
+        public boolean isIncognitoModeEnabled;
+
         // Whether the cookies content setting is enforced.
         public boolean cookiesContentSettingEnforced;
         //  Whether third-party blocking is enforced.
@@ -146,7 +149,8 @@
             return CookieSettingsState.BLOCK;
         } else if (params.cookieControlsMode == CookieControlsMode.BLOCK_THIRD_PARTY) {
             return CookieSettingsState.BLOCK_THIRD_PARTY;
-        } else if (params.cookieControlsMode == CookieControlsMode.INCOGNITO_ONLY) {
+        } else if (params.cookieControlsMode == CookieControlsMode.INCOGNITO_ONLY
+                && params.isIncognitoModeEnabled) {
             return CookieSettingsState.BLOCK_THIRD_PARTY_INCOGNITO;
         } else {
             return CookieSettingsState.ALLOW;
@@ -207,7 +211,11 @@
     private RadioButtonWithDescription[] getEnforcedButtons(Params params) {
         if (!params.cookiesContentSettingEnforced && !params.cookieControlsModeEnforced) {
             // Nothing is enforced.
-            return buttons();
+            if (!params.isIncognitoModeEnabled) {
+                return buttons(mBlockThirdPartyIncognitoButton);
+            } else {
+                return buttons();
+            }
         }
         if (params.cookiesContentSettingEnforced && params.cookieControlsModeEnforced) {
             return buttons(mAllowButton, mBlockThirdPartyIncognitoButton, mBlockThirdPartyButton,
@@ -215,7 +223,11 @@
         }
         if (params.cookiesContentSettingEnforced) {
             if (params.allowCookies) {
-                return buttons(mBlockButton);
+                if (!params.isIncognitoModeEnabled) {
+                    return buttons(mBlockButton, mBlockThirdPartyIncognitoButton);
+                } else {
+                    return buttons(mBlockButton);
+                }
             } else {
                 return buttons(mAllowButton, mBlockThirdPartyIncognitoButton,
                         mBlockThirdPartyButton, mBlockButton);
@@ -234,4 +246,10 @@
         assert getButton(state) != null;
         return getButton(state).isEnabled();
     }
+
+    @VisibleForTesting
+    public boolean isButtonCheckedForTesting(CookieSettingsState state) {
+        assert getButton(state) != null;
+        return getButton(state).isChecked();
+    }
 }
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
index bce5a10..ace917f 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
@@ -1011,6 +1011,7 @@
         params.cookieControlsMode = prefService.getInteger(COOKIE_CONTROLS_MODE);
         params.cookiesContentSettingEnforced = mCategory.isManaged();
         params.cookieControlsModeEnforced = prefService.isManagedPreference(COOKIE_CONTROLS_MODE);
+        params.isIncognitoModeEnabled = getSiteSettingsDelegate().isIncognitoModeEnabled();
         fourStateCookieToggle.setState(params);
     }
 
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java
index ff313ee7..5f6f342a 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java
@@ -645,7 +645,7 @@
                     createReadOnlyCopyOf(preference, overrideSummary, value);
             newPreference.setImageView(R.drawable.permission_popups, 0,
                     unused -> launchOsChannelSettingsFromPreference(preference));
-            newPreference.setImageColor(R.color.default_icon_color_secondary);
+            newPreference.setImageColor(R.color.default_icon_color_secondary_tint_list);
             newPreference.setDefaultValue(value);
 
             newPreference.setOnPreferenceClickListener(unused -> {
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsDelegate.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsDelegate.java
index 8232924..5469897 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsDelegate.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsDelegate.java
@@ -51,6 +51,11 @@
     boolean isCategoryVisible(@SiteSettingsCategory.Type int type);
 
     /**
+     * @return true if Incognito mode is enabled.
+     */
+    boolean isIncognitoModeEnabled();
+
+    /**
      * @return true if the QuietNotificationPrompts Feature is enabled.
      */
     boolean isQuietNotificationPromptsFeatureEnabled();
diff --git a/components/browser_ui/styles/android/BUILD.gn b/components/browser_ui/styles/android/BUILD.gn
index 9d34b879..7568ba5 100644
--- a/components/browser_ui/styles/android/BUILD.gn
+++ b/components/browser_ui/styles/android/BUILD.gn
@@ -200,6 +200,7 @@
     "java/res/drawable/smartphone_black_24dp.xml",
     "java/res/drawable/toolbar_hairline.xml",
     "java/res/values-night/colors.xml",
+    "java/res/values-night/dimens.xml",
     "java/res/values-night/drawables.xml",
     "java/res/values-night/styles.xml",
     "java/res/values-night/themes.xml",
diff --git a/components/browser_ui/styles/android/java/res/color/default_icon_color_secondary_tint_list.xml b/components/browser_ui/styles/android/java/res/color/default_icon_color_secondary_tint_list.xml
index 800cc34..ccd605d 100644
--- a/components/browser_ui/styles/android/java/res/color/default_icon_color_secondary_tint_list.xml
+++ b/components/browser_ui/styles/android/java/res/color/default_icon_color_secondary_tint_list.xml
@@ -7,5 +7,5 @@
     xmlns:tools="http://schemas.android.com/tools" tools:ignore="UnusedResources">
     <item android:alpha="@dimen/default_disabled_alpha"
         android:color="@macro/default_icon_color" android:state_enabled="false"/>
-    <item android:color="?attr/default_icon_color_secondary" />
+    <item android:color="@macro/default_icon_color_secondary" />
 </selector>
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_add.xml b/components/browser_ui/styles/android/java/res/drawable/ic_add.xml
index 6232d099..ee1aa43 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_add.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_add.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_drive_document_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_drive_document_24dp.xml
index 32734fe..7ce13fa 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_drive_document_24dp.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_drive_document_24dp.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_drive_file_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_drive_file_24dp.xml
index 17025ed..ca6e8a1 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_drive_file_24dp.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_drive_file_24dp.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_drive_image_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_drive_image_24dp.xml
index 167949ec..6b17011 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_drive_image_24dp.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_drive_image_24dp.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_file_download_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_file_download_24dp.xml
index 9adc8e258..31759b9 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_file_download_24dp.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_file_download_24dp.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_file_download_36dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_file_download_36dp.xml
index 235108f0..7c4b6575 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_file_download_36dp.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_file_download_36dp.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="36dp"
     android:height="36dp"
     android:viewportWidth="24.0"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_globe_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_globe_24dp.xml
index f9faae5..a259616 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_globe_24dp.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_globe_24dp.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_music_note_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_music_note_24dp.xml
index ffe6016..018a63f 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_music_note_24dp.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_music_note_24dp.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_offline_pin_24dp_on_dark_bg.xml b/components/browser_ui/styles/android/java/res/drawable/ic_offline_pin_24dp_on_dark_bg.xml
index 2337734a..5308c158 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_offline_pin_24dp_on_dark_bg.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_offline_pin_24dp_on_dark_bg.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_offline_pin_24dp_on_light_bg.xml b/components/browser_ui/styles/android/java/res/drawable/ic_offline_pin_24dp_on_light_bg.xml
index f388441..74ba83f1 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_offline_pin_24dp_on_light_bg.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_offline_pin_24dp_on_light_bg.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_play_circle_filled_24dp_on_dark_bg.xml b/components/browser_ui/styles/android/java/res/drawable/ic_play_circle_filled_24dp_on_dark_bg.xml
index aebbf6e..9d873d6 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_play_circle_filled_24dp_on_dark_bg.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_play_circle_filled_24dp_on_dark_bg.xml
@@ -6,8 +6,6 @@
 <!-- Note: hand-tweaked for foreground and background colors, not canonical icon. -->
 <vector
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_play_circle_filled_24dp_on_light_bg.xml b/components/browser_ui/styles/android/java/res/drawable/ic_play_circle_filled_24dp_on_light_bg.xml
index 03fff3c5..c426282 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_play_circle_filled_24dp_on_light_bg.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_play_circle_filled_24dp_on_light_bg.xml
@@ -6,8 +6,6 @@
 <!-- Note: hand-tweaked for foreground and background colors, not canonical icon. -->
 <vector
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_settings_black.xml b/components/browser_ui/styles/android/java/res/drawable/ic_settings_black.xml
index a70f20c..6f6a16a 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_settings_black.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_settings_black.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="32dp"
     android:height="20dp"
     android:viewportWidth="128"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_videocam_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_videocam_24dp.xml
index 7610713..f1d5a90 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_videocam_24dp.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_videocam_24dp.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_visibility_black.xml b/components/browser_ui/styles/android/java/res/drawable/ic_visibility_black.xml
index 454781f..24e682e0 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_visibility_black.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_visibility_black.xml
@@ -1,6 +1,4 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        tools:targetApi="21"
         android:width="24dp"
         android:height="24dp"
         android:viewportWidth="24.0"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_visibility_off_black.xml b/components/browser_ui/styles/android/java/res/drawable/ic_visibility_off_black.xml
index dd6818b..4b1dd5e 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_visibility_off_black.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_visibility_off_black.xml
@@ -1,6 +1,4 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        tools:targetApi="21"
         android:width="24dp"
         android:height="24dp"
         android:viewportWidth="24.0"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_volume_off_white_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_volume_off_white_24dp.xml
index f71197f..1312accb 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_volume_off_white_24dp.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_volume_off_white_24dp.xml
@@ -5,8 +5,6 @@
 
 <vector
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_volume_on_white_24dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_volume_on_white_24dp.xml
index 7548449a..b038b5d 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_volume_on_white_24dp.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_volume_on_white_24dp.xml
@@ -5,8 +5,6 @@
 
 <vector
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_vpn_key_grey.xml b/components/browser_ui/styles/android/java/res/drawable/ic_vpn_key_grey.xml
index 58bf849f..6cae7f2 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_vpn_key_grey.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_vpn_key_grey.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--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.-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:targetApi="21" android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0">
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0">
     <path android:pathData="M7.5 6c2.796 0 5.145 1.912 5.811 4.5H22.5v3h-2V18H17v-4.5h-3.69C12.646 16.089 10.297 18 7.5 18c-3.314 0-6-2.686-6-6s2.686-6 6-6zm0 3.5C6.12 9.5 5 10.62 5 12s1.12 2.5 2.5 2.5S10 13.38 10 12 8.88 9.5 7.5 9.5z" android:strokeWidth="1" android:fillColor="@android:color/white" android:fillType="evenOdd"/>
 </vector>
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_warning_red_16dp.xml b/components/browser_ui/styles/android/java/res/drawable/ic_warning_red_16dp.xml
index 35b9bcd..200b57e8 100644
--- a/components/browser_ui/styles/android/java/res/drawable/ic_warning_red_16dp.xml
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_warning_red_16dp.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="16dp"
     android:height="16dp"
     android:viewportWidth="16"
diff --git a/components/browser_ui/styles/android/java/res/values-night/dimens.xml b/components/browser_ui/styles/android/java/res/values-night/dimens.xml
new file mode 100644
index 0000000..c83ce0a
--- /dev/null
+++ b/components/browser_ui/styles/android/java/res/values-night/dimens.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 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>
+    <!-- Surface color elevations -->
+    <dimen name="dialog_bg_color_elev">@dimen/default_elevation_3</dimen>
+    <dimen name="sheet_bg_color_elev">@dimen/default_elevation_4</dimen>
+    <dimen name="snackbar_background_color_elev">@dimen/default_elevation_4</dimen>
+</resources>
diff --git a/components/browser_ui/styles/android/java/res/values-night/themes.xml b/components/browser_ui/styles/android/java/res/values-night/themes.xml
index 8a2e85e9..d601bf4 100644
--- a/components/browser_ui/styles/android/java/res/values-night/themes.xml
+++ b/components/browser_ui/styles/android/java/res/values-night/themes.xml
@@ -39,8 +39,6 @@
 
         <!-- Custom semantic names -->
         <!-- Common icon colors for drawables. -->
-        <item name="default_icon_color_secondary">@color/default_icon_color_secondary_light</item>
         <item name="default_icon_color_disabled">@color/default_icon_color_disabled_light</item>
-        <item name="default_icon_color_disabled_inverse">@color/default_icon_color_disabled_dark</item>
     </style>
 </resources>
diff --git a/components/browser_ui/styles/android/java/res/values/dimens.xml b/components/browser_ui/styles/android/java/res/values/dimens.xml
index 6806760..c55ea4d 100644
--- a/components/browser_ui/styles/android/java/res/values/dimens.xml
+++ b/components/browser_ui/styles/android/java/res/values/dimens.xml
@@ -20,4 +20,9 @@
 
     <item name="text_highlight_alpha" format="float" type="dimen">0.2</item>
     <item name="progress_bar_bg_alpha" format="float" type="dimen">0.2</item>
+
+    <!-- Surface color elevations -->
+    <dimen name="dialog_bg_color_elev">@dimen/default_elevation_0</dimen>
+    <dimen name="sheet_bg_color_elev">@dimen/default_elevation_0</dimen>
+    <dimen name="snackbar_background_color_elev">@dimen/default_elevation_0</dimen>
 </resources>
diff --git a/components/browser_ui/styles/android/java/res/values/semantic_colors_dynamic.xml b/components/browser_ui/styles/android/java/res/values/semantic_colors_dynamic.xml
index 15cc99e..e7457af9 100644
--- a/components/browser_ui/styles/android/java/res/values/semantic_colors_dynamic.xml
+++ b/components/browser_ui/styles/android/java/res/values/semantic_colors_dynamic.xml
@@ -13,6 +13,7 @@
     <macro name="default_icon_color_accent1">?attr/colorPrimary</macro>
     <macro name="default_icon_color_inverse">?attr/colorOnSurfaceInverse</macro>
     <macro name="default_icon_color_on_accent1">?attr/colorOnPrimary</macro>
+    <macro name="default_icon_color_secondary">?attr/colorOnSurfaceVariant</macro>
     <macro name="default_text_color">?attr/colorOnSurface</macro>
     <macro name="default_text_color_accent1">?attr/colorPrimary</macro>
     <macro name="default_text_color_on_accent1">?attr/colorOnPrimary</macro>
@@ -29,6 +30,7 @@
     <macro name="default_icon_color_accent1">@color/default_icon_color_accent1_baseline</macro>
     <macro name="default_icon_color_inverse">@color/default_icon_color_inverse_baseline</macro>
     <macro name="default_icon_color_on_accent1">@color/default_icon_color_on_accent1_baseline</macro>
+    <macro name="default_icon_color_secondary">@color/default_icon_color_secondary_baseline</macro>
     <macro name="default_text_color">@color/default_text_color_baseline</macro>
     <macro name="default_text_color_accent1">@color/default_text_color_blue_baseline</macro>
     <macro name="default_text_color_on_accent1">@color/default_text_color_on_accent1_baseline</macro>
diff --git a/components/browser_ui/styles/android/java/res/values/themes.xml b/components/browser_ui/styles/android/java/res/values/themes.xml
index a3378fc1..086c113d 100644
--- a/components/browser_ui/styles/android/java/res/values/themes.xml
+++ b/components/browser_ui/styles/android/java/res/values/themes.xml
@@ -59,9 +59,7 @@
         <item name="default_bg_color_dynamic">?attr/colorSurface</item>
         <item name="divider_line_bg_color_dynamic">?attr/colorSurfaceVariant</item>
         <!-- Common icon colors for drawables. -->
-        <item name="default_icon_color_secondary">@color/default_icon_color_secondary_dark</item>
         <item name="default_icon_color_disabled">@color/default_icon_color_disabled_dark</item>
-        <item name="default_icon_color_disabled_inverse">@color/default_icon_color_disabled_light</item>
     </style>
     <style name="Base.V31.Theme.BrowserUI" parent="Base.V21.Theme.BrowserUI" />
     <style name="Base.Theme.BrowserUI" parent="Base.V31.Theme.BrowserUI" />
@@ -121,9 +119,7 @@
         <item name="default_bg_color_dynamic">?attr/colorSurface</item>
         <item name="divider_line_bg_color_dynamic">?attr/colorSurfaceVariant</item>
         <!-- Common icon colors for drawables. -->
-        <item name="default_icon_color_secondary">@color/default_icon_color_secondary_dark</item>
         <item name="default_icon_color_disabled">@color/default_icon_color_disabled_dark</item>
-        <item name="default_icon_color_disabled_inverse">@color/default_icon_color_disabled_light</item>
     </style>
     <style name="Theme.BrowserUI.DialogWhenLarge" parent="Base.Theme.BrowserUI.DialogWhenLarge"/>
 
diff --git a/components/browser_ui/styles/android/java/src/org/chromium/components/browser_ui/styles/SemanticColorUtils.java b/components/browser_ui/styles/android/java/src/org/chromium/components/browser_ui/styles/SemanticColorUtils.java
index e50f5418..ef76e4b 100644
--- a/components/browser_ui/styles/android/java/src/org/chromium/components/browser_ui/styles/SemanticColorUtils.java
+++ b/components/browser_ui/styles/android/java/src/org/chromium/components/browser_ui/styles/SemanticColorUtils.java
@@ -15,7 +15,7 @@
 
 /**
  * Provides semantic color values, typically in place of <macro>s which currently cannot be used in
- * Java code.
+ * Java code, or for surface colors that must be calculated to Java code.
  */
 public class SemanticColorUtils {
     private static final String TAG = "SemanticColorUtils";
@@ -72,6 +72,12 @@
         return resolve(R.attr.colorPrimary, R.color.default_icon_color_accent1_baseline, context);
     }
 
+    /** Returns the semantic color value that corresponds to default_icon_color_secondary. */
+    public static @ColorInt int getDefaultIconColorSecondary(Context context) {
+        return resolve(R.attr.colorOnSurfaceVariant, R.color.default_icon_color_secondary_baseline,
+                context);
+    }
+
     /** Returns the semantic color value that corresponds to divider_line_bg_color. */
     public static @ColorInt int getDividerLineBgColor(Context context) {
         return resolve(R.attr.colorSurfaceVariant, R.color.divider_line_bg_color_baseline, context);
@@ -113,6 +119,24 @@
         return getDefaultBgColorElev2(context);
     }
 
+    /** Returns the surface color value of the conceptual dialog_bg_color. */
+    public static @ColorInt int getDialogBgColor(Context context) {
+        return resolveSurfaceColorElev(
+                R.dimen.dialog_bg_color_elev, R.color.dialog_bg_color, context);
+    }
+
+    /** Returns the surface color value of the conceptual sheet_bg_color. */
+    public static @ColorInt int getSheetBgColor(Context context) {
+        return resolveSurfaceColorElev(
+                R.dimen.sheet_bg_color_elev, R.color.sheet_bg_color, context);
+    }
+
+    /** Returns the surface color value of the conceptual snackbar_background_color_baseline. */
+    public static @ColorInt int getSnackbarBackgroundColor(Context context) {
+        return resolveSurfaceColorElev(R.dimen.snackbar_background_color_elev,
+                R.color.snackbar_background_color_baseline, context);
+    }
+
     // Colors that will be experimented with. This is independent of |IS_FULL_DYNAMIC_COLORS|.
     /** Returns the semantic color value that corresponds to default_text_color_link. */
     public static @ColorInt int getDefaultTextColorLink(Context context) {
diff --git a/components/browser_ui/widget/android/BUILD.gn b/components/browser_ui/widget/android/BUILD.gn
index 6a647bd..a6d1d055 100644
--- a/components/browser_ui/widget/android/BUILD.gn
+++ b/components/browser_ui/widget/android/BUILD.gn
@@ -162,6 +162,7 @@
     "java/res/drawable-v24/oval_surface_1.xml",
     "java/res/drawable-v24/rectangle_surface_1.xml",
     "java/res/drawable-v24/rounded_rectangle_surface_1.xml",
+    "java/res/drawable-v24/sheet_background.xml",
     "java/res/drawable-xhdpi/btn_delete_24dp.png",
     "java/res/drawable-xhdpi/btn_info.png",
     "java/res/drawable-xhdpi/ic_arrow_back_white_24dp.png",
@@ -202,6 +203,7 @@
     "java/res/drawable/rectangle_surface_1.xml",
     "java/res/drawable/rounded_rectangle_surface_1.xml",
     "java/res/drawable/search_toolbar_modern_bg.xml",
+    "java/res/drawable/sheet_background.xml",
     "java/res/drawable/tile_view_highlight.xml",
     "java/res/drawable/tile_view_highlight_mask.xml",
     "java/res/drawable/tile_view_highlight_plain.xml",
@@ -235,6 +237,7 @@
     "java/res/layout/tile_view_modern.xml",
     "java/res/layout/tile_view_modern_condensed.xml",
     "java/res/values-ldrtl/values.xml",
+    "java/res/values-night/dimens.xml",
     "java/res/values-night/drawables.xml",
     "java/res/values-sw600dp/dimens.xml",
     "java/res/values/attrs.xml",
diff --git a/components/browser_ui/widget/android/java/res/drawable-v24/sheet_background.xml b/components/browser_ui/widget/android/java/res/drawable-v24/sheet_background.xml
new file mode 100644
index 0000000..52fe2e42
--- /dev/null
+++ b/components/browser_ui/widget/android/java/res/drawable-v24/sheet_background.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 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. -->
+
+<org.chromium.components.browser_ui.widget.SurfaceColorDrawable
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:shape="rectangle"
+    app:surfaceElevation="@dimen/sheet_background_elev">
+</org.chromium.components.browser_ui.widget.SurfaceColorDrawable>
\ No newline at end of file
diff --git a/components/browser_ui/widget/android/java/res/drawable/async_image_view_unavailable.xml b/components/browser_ui/widget/android/java/res/drawable/async_image_view_unavailable.xml
index 17d7157..34b5be6 100644
--- a/components/browser_ui/widget/android/java/res/drawable/async_image_view_unavailable.xml
+++ b/components/browser_ui/widget/android/java/res/drawable/async_image_view_unavailable.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="100dp"
     android:height="100dp"
     android:viewportWidth="100.0"
diff --git a/components/browser_ui/widget/android/java/res/drawable/ic_arrow_back_24dp.xml b/components/browser_ui/widget/android/java/res/drawable/ic_arrow_back_24dp.xml
index 8f9174b..1501787 100644
--- a/components/browser_ui/widget/android/java/res/drawable/ic_arrow_back_24dp.xml
+++ b/components/browser_ui/widget/android/java/res/drawable/ic_arrow_back_24dp.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        tools:targetApi="21"
         android:width="24dp"
         android:height="24dp"
         android:viewportWidth="24"
diff --git a/components/browser_ui/widget/android/java/res/drawable/ic_check_googblue_24dp_animated.xml b/components/browser_ui/widget/android/java/res/drawable/ic_check_googblue_24dp_animated.xml
index 7845100e..0950b6f 100644
--- a/components/browser_ui/widget/android/java/res/drawable/ic_check_googblue_24dp_animated.xml
+++ b/components/browser_ui/widget/android/java/res/drawable/ic_check_googblue_24dp_animated.xml
@@ -6,8 +6,7 @@
 <animated-vector
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:aapt="http://schemas.android.com/aapt"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21">
+    xmlns:tools="http://schemas.android.com/tools">
 
     <aapt:attr name="android:drawable">
         <vector
@@ -35,4 +34,4 @@
                 tools:valueFrom="1"/>
         </aapt:attr>
     </target>
-</animated-vector>
\ No newline at end of file
+</animated-vector>
diff --git a/components/browser_ui/widget/android/java/res/drawable/ic_settings_gear_24dp.xml b/components/browser_ui/widget/android/java/res/drawable/ic_settings_gear_24dp.xml
index ab7d337..9f64a0ed 100644
--- a/components/browser_ui/widget/android/java/res/drawable/ic_settings_gear_24dp.xml
+++ b/components/browser_ui/widget/android/java/res/drawable/ic_settings_gear_24dp.xml
@@ -7,12 +7,13 @@
         android:width="24dp"
         android:height="24dp"
         android:viewportWidth="24"
-        android:viewportHeight="24">
+        android:viewportHeight="24"
+        android:tint="@macro/default_icon_color_secondary">
     
     <path
         android:pathData="M0,0h24v24H0V0z" />
     <path
-        android:fillColor="@color/default_icon_color_secondary"
+        android:fillColor="@android:color/white"
         android:pathData="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61
 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41
 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87
diff --git a/components/browser_ui/widget/android/java/res/drawable/sheet_background.xml b/components/browser_ui/widget/android/java/res/drawable/sheet_background.xml
new file mode 100644
index 0000000..2eee173
--- /dev/null
+++ b/components/browser_ui/widget/android/java/res/drawable/sheet_background.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 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. -->
+
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+  <solid android:color="@color/sheet_bg_color" />
+</shape>
\ No newline at end of file
diff --git a/components/browser_ui/widget/android/java/res/values-night/dimens.xml b/components/browser_ui/widget/android/java/res/values-night/dimens.xml
new file mode 100644
index 0000000..73d5e205
--- /dev/null
+++ b/components/browser_ui/widget/android/java/res/values-night/dimens.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 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>
+    <dimen name="sheet_background_elev">@dimen/default_elevation_4</dimen>
+</resources>
diff --git a/components/browser_ui/widget/android/java/res/values/dimens.xml b/components/browser_ui/widget/android/java/res/values/dimens.xml
index edb5ee3..bedf8c8b 100644
--- a/components/browser_ui/widget/android/java/res/values/dimens.xml
+++ b/components/browser_ui/widget/android/java/res/values/dimens.xml
@@ -10,6 +10,7 @@
     <dimen name="clear_text_button_end_padding">10dp</dimen>
     <dimen name="default_rounded_corner_radius">8dp</dimen>
     <dimen name="card_rounded_corner_radius">16dp</dimen>
+    <dimen name="sheet_background_elev">@dimen/default_elevation_0</dimen>
 
     <!-- DualControlLayout -->
     <dimen name="dual_control_margin_between_items">8dp</dimen>
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ContextMenuDialog.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ContextMenuDialog.java
index bc707af..1d828a5 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ContextMenuDialog.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ContextMenuDialog.java
@@ -63,6 +63,12 @@
     private Integer mDesiredPopupContentWidth;
 
     /**
+     * View that is showing behind the context menu. If menu is shown as a popup without scrim, this
+     * view will be used to dispatch touch events other than ACTION_DOWN.
+     */
+    private @Nullable View mTouchEventDelegateView;
+
+    /**
      * Creates an instance of the ContextMenuDialog.
      * @param ownerActivity The activity in which the dialog should run
      * @param theme A style resource describing the theme to use for the window, or {@code 0} to use
@@ -81,11 +87,15 @@
      *         visually.
      * @param popupMargin The margin for the context menu.
      * @param desiredPopupContentWidth The desired width for the content of the context menu.
+     * @param touchEventDelegateView View View that is showing behind the context menu. If menu is
+     *         shown as a popup without scrim, and this view is provided, the context menu will
+     *         dispatch touch events other than ACTION_DOWN.
      */
     public ContextMenuDialog(Activity ownerActivity, int theme, float touchPointXPx,
             float touchPointYPx, float topContentOffsetPx, int topMarginPx, int bottomMarginPx,
             View layout, View contentView, boolean isPopup, boolean shouldRemoveScrim,
-            @Nullable Integer popupMargin, @Nullable Integer desiredPopupContentWidth) {
+            @Nullable Integer popupMargin, @Nullable Integer desiredPopupContentWidth,
+            @Nullable View touchEventDelegateView) {
         super(ownerActivity, theme);
         mActivity = ownerActivity;
         mTouchPointXPx = touchPointXPx;
@@ -99,6 +109,7 @@
         mShouldRemoveScrim = shouldRemoveScrim;
         mPopupMargin = popupMargin;
         mDesiredPopupContentWidth = desiredPopupContentWidth;
+        mTouchEventDelegateView = touchEventDelegateView;
     }
 
     @Override
@@ -258,6 +269,9 @@
             dismiss();
             return true;
         }
+        if (mIsPopup && mShouldRemoveScrim && mTouchEventDelegateView != null) {
+            return mTouchEventDelegateView.dispatchTouchEvent(event);
+        }
         return false;
     }
 
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ContextMenuDialogUnitTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ContextMenuDialogUnitTest.java
index a57f27999..bd00a81 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ContextMenuDialogUnitTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ContextMenuDialogUnitTest.java
@@ -11,6 +11,7 @@
 import android.app.ActionBar.LayoutParams;
 import android.app.Activity;
 import android.view.Gravity;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
@@ -53,6 +54,8 @@
     UiWidgetFactory mMockUiWidgetFactory;
     @Spy
     PopupWindow mSpyPopupWindow;
+    @Mock
+    View mMockTouchEventDelegateView;
 
     @Before
     public void setup() {
@@ -112,9 +115,7 @@
     public void testShowPopupWindow() {
         mDialog = createContextMenuDialog(/*isPopup=*/true, /*shouldRemoveScrim=*/false);
         mDialog.show();
-        // Change layout params and request layout so #onLayoutChange is triggered.
-        mRootView.setRight(mRootView.getRight() + 1);
-        mRootView.requestLayout();
+        requestLayoutForRootView();
 
         final ArgumentCaptor<Integer> gravityCaptor = ArgumentCaptor.forClass(Integer.class);
         Mockito.verify(mSpyPopupWindow)
@@ -135,17 +136,14 @@
         mDialog = createContextMenuDialog(/*isPopup=*/true, /*shouldRemoveScrim=*/false);
         mDialog.show();
         // Change layout params and request layout so #onLayoutChange is triggered.
-        mRootView.setRight(mRootView.getRight() + 1);
-        mRootView.requestLayout();
+        requestLayoutForRootView();
         Mockito.verify(mSpyPopupWindow)
                 .showAtLocation(eq(mRootView.getRootView()), anyInt(), anyInt(), anyInt());
 
         // Mock up popup window is showing.
         Mockito.doReturn(true).when(mSpyPopupWindow).isShowing();
 
-        // Change layout params and request layout so #onLayoutChange is triggered.
-        mRootView.setRight(mRootView.getRight() - 1);
-        mRootView.requestLayout();
+        requestLayoutForRootView();
         Mockito.verify(mSpyPopupWindow).dismiss();
     }
 
@@ -163,9 +161,50 @@
         Mockito.verify(mSpyPopupWindow, Mockito.times(0)).dismiss();
     }
 
+    @Test
+    public void testDispatchTouchToDelegate() {
+        mDialog = createContextMenuDialog(/*isPopup=*/true, /*shouldRemoveScrim=*/true);
+        mDialog.show();
+        requestLayoutForRootView();
+        Mockito.verify(mSpyPopupWindow)
+                .showAtLocation(eq(mRootView.getRootView()), anyInt(), anyInt(), anyInt());
+
+        // common motion events other than ACTION_DOWN should be forwarded to touch event delegate.
+        int[] motionEvenActions = new int[] {MotionEvent.ACTION_CANCEL,
+                MotionEvent.ACTION_HOVER_ENTER, MotionEvent.ACTION_HOVER_EXIT,
+                MotionEvent.ACTION_HOVER_MOVE, MotionEvent.ACTION_MOVE, MotionEvent.ACTION_OUTSIDE,
+                MotionEvent.ACTION_POINTER_DOWN, MotionEvent.ACTION_POINTER_UP,
+                MotionEvent.ACTION_SCROLL, MotionEvent.ACTION_UP};
+        for (int actionType : motionEvenActions) {
+            MotionEvent event = createMockMotionEventWithActionType(actionType);
+            mDialog.onTouchEvent(event);
+            Mockito.verify(mMockTouchEventDelegateView).dispatchTouchEvent(eq(event));
+        }
+
+        // ACTION_DOWN should dismiss the dialog and the popup window.
+        MotionEvent downEvent = createMockMotionEventWithActionType(MotionEvent.ACTION_DOWN);
+        mDialog.onTouchEvent(downEvent);
+        Mockito.verify(mMockTouchEventDelegateView, Mockito.times(0))
+                .dispatchTouchEvent(eq(downEvent));
+        Mockito.verify(mSpyPopupWindow).dismiss();
+    }
+
     private ContextMenuDialog createContextMenuDialog(boolean isPopup, boolean shouldRemoveScrim) {
         return new ContextMenuDialog(mActivity, 0, 0, 0, 0, ContextMenuDialog.NO_CUSTOM_MARGIN,
                 ContextMenuDialog.NO_CUSTOM_MARGIN, mRootView, mMenuContentView, isPopup,
-                shouldRemoveScrim, 0, 0);
+                shouldRemoveScrim, 0, 0, mMockTouchEventDelegateView);
+    }
+
+    private void requestLayoutForRootView() {
+        // Change layout params and request layout so #onLayoutChange is triggered.
+        mRootView.setRight(mRootView.getRight() + 1);
+        mRootView.requestLayout();
+    }
+
+    private MotionEvent createMockMotionEventWithActionType(int actionType) {
+        MotionEvent motionEvent = Mockito.mock(MotionEvent.class);
+        Mockito.doReturn(actionType).when(motionEvent).getAction();
+
+        return motionEvent;
     }
 }
diff --git a/components/browser_ui/widget/android/test/java/res/drawable/promo_dialog_test_vector.xml b/components/browser_ui/widget/android/test/java/res/drawable/promo_dialog_test_vector.xml
index 1f3fa1f6..728cc661 100644
--- a/components/browser_ui/widget/android/test/java/res/drawable/promo_dialog_test_vector.xml
+++ b/components/browser_ui/widget/android/test/java/res/drawable/promo_dialog_test_vector.xml
@@ -7,7 +7,6 @@
      be greater than 200. -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     tools:ignore="VectorRaster"
     android:width="224dp"
     android:height="107dp"
diff --git a/components/browser_ui/widget/android/test/java/res/drawable/test_logo_avatar_anonymous.xml b/components/browser_ui/widget/android/test/java/res/drawable/test_logo_avatar_anonymous.xml
index b6534a5..df25d4e 100644
--- a/components/browser_ui/widget/android/test/java/res/drawable/test_logo_avatar_anonymous.xml
+++ b/components/browser_ui/widget/android/test/java/res/drawable/test_logo_avatar_anonymous.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="40dp"
     android:height="40dp"
     android:viewportWidth="192"
diff --git a/components/cdm/browser/media_drm_storage_impl.cc b/components/cdm/browser/media_drm_storage_impl.cc
index 94dde34..f59e2b55 100644
--- a/components/cdm/browser/media_drm_storage_impl.cc
+++ b/components/cdm/browser/media_drm_storage_impl.cc
@@ -573,7 +573,8 @@
 
     // Save the origin ID in the preference as long as it is not null.
     if (origin_id) {
-      DictionaryPrefUpdate update(pref_service, prefs::kMediaDrmStorage);
+      DictionaryPrefUpdateDeprecated update(pref_service,
+                                            prefs::kMediaDrmStorage);
       CreateOriginDictAndReturnSessionsDict(update.Get(), origin,
                                             origin_id.value());
     }
@@ -692,7 +693,7 @@
     base::OnceClosure complete_cb) {
   DVLOG(1) << __func__ << ": Clear licenses [" << start << ", " << end << "]";
 
-  DictionaryPrefUpdate update(pref_service, prefs::kMediaDrmStorage);
+  DictionaryPrefUpdateDeprecated update(pref_service, prefs::kMediaDrmStorage);
 
   std::vector<base::UnguessableToken> no_license_origin_ids =
       ClearMatchingLicenseData(update.Get(), start, end, filter);
@@ -814,7 +815,7 @@
     return;
   }
 
-  DictionaryPrefUpdate update(pref_service_, prefs::kMediaDrmStorage);
+  DictionaryPrefUpdateDeprecated update(pref_service_, prefs::kMediaDrmStorage);
   base::DictionaryValue* storage_dict = update.Get();
   DCHECK(storage_dict);
 
@@ -846,7 +847,7 @@
     return;
   }
 
-  DictionaryPrefUpdate update(pref_service_, prefs::kMediaDrmStorage);
+  DictionaryPrefUpdateDeprecated update(pref_service_, prefs::kMediaDrmStorage);
   base::DictionaryValue* storage_dict = update.Get();
   DCHECK(storage_dict);
 
@@ -947,7 +948,7 @@
     return;
   }
 
-  DictionaryPrefUpdate update(pref_service_, prefs::kMediaDrmStorage);
+  DictionaryPrefUpdateDeprecated update(pref_service_, prefs::kMediaDrmStorage);
 
   base::Value* sessions_dict = GetSessionsDictFromStorageDict<base::Value>(
       update.Get(), origin().Serialize());
diff --git a/components/components_chromium_strings.grd b/components/components_chromium_strings.grd
index 0d7eddf..8ebae85 100644
--- a/components/components_chromium_strings.grd
+++ b/components/components_chromium_strings.grd
@@ -37,7 +37,9 @@
       <output filename="components_chromium_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="components_chromium_strings_af.pak" type="data_package" lang="af" />
       <output filename="components_chromium_strings_is.pak" type="data_package" lang="is" />
+      <output filename="components_chromium_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="components_chromium_strings_am.pak" type="data_package" lang="am" />
     <output filename="components_chromium_strings_ar.pak" type="data_package" lang="ar" />
diff --git a/components/components_google_chrome_strings.grd b/components/components_google_chrome_strings.grd
index 71dcb21..5e43b72 100644
--- a/components/components_google_chrome_strings.grd
+++ b/components/components_google_chrome_strings.grd
@@ -37,7 +37,9 @@
       <output filename="components_google_chrome_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="components_google_chrome_strings_af.pak" type="data_package" lang="af" />
       <output filename="components_google_chrome_strings_is.pak" type="data_package" lang="is" />
+      <output filename="components_google_chrome_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="components_google_chrome_strings_am.pak" type="data_package" lang="am" />
     <output filename="components_google_chrome_strings_ar.pak" type="data_package" lang="ar" />
diff --git a/components/components_locale_settings.grd b/components/components_locale_settings.grd
index ba55129..7b483b09 100644
--- a/components/components_locale_settings.grd
+++ b/components/components_locale_settings.grd
@@ -35,7 +35,9 @@
       <output filename="components_locale_settings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="components_locale_settings_af.pak" type="data_package" lang="af" />
       <output filename="components_locale_settings_is.pak" type="data_package" lang="is" />
+      <output filename="components_locale_settings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="components_locale_settings_am.pak" type="data_package" lang="am" />
     <output filename="components_locale_settings_ar.pak" type="data_package" lang="ar" />
diff --git a/components/components_strings.grd b/components/components_strings.grd
index 5dc186a..61ceeb8e 100644
--- a/components/components_strings.grd
+++ b/components/components_strings.grd
@@ -37,7 +37,9 @@
       <output filename="components_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="components_strings_af.pak" type="data_package" lang="af" />
       <output filename="components_strings_is.pak" type="data_package" lang="is" />
+      <output filename="components_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="components_strings_am.pak" type="data_package" lang="am" />
     <output filename="components_strings_ar.pak" type="data_package" lang="ar" />
diff --git a/components/consent_auditor/consent_auditor_impl.cc b/components/consent_auditor/consent_auditor_impl.cc
index 3148dac..650ed2b 100644
--- a/components/consent_auditor/consent_auditor_impl.cc
+++ b/components/consent_auditor/consent_auditor_impl.cc
@@ -145,8 +145,8 @@
     const std::string& feature,
     const std::string& description_text,
     const std::string& confirmation_text) {
-  DictionaryPrefUpdate consents_update(pref_service_,
-                                       prefs::kLocalConsentsDictionary);
+  DictionaryPrefUpdateDeprecated consents_update(
+      pref_service_, prefs::kLocalConsentsDictionary);
   base::DictionaryValue* consents = consents_update.Get();
   DCHECK(consents);
 
diff --git a/components/content_settings/browser/content_settings_manager_impl.cc b/components/content_settings/browser/content_settings_manager_impl.cc
index bb210d17..4b061b40 100644
--- a/components/content_settings/browser/content_settings_manager_impl.cc
+++ b/components/content_settings/browser/content_settings_manager_impl.cc
@@ -154,9 +154,14 @@
       std::move(delegate));
   if (base::FeatureList::IsEnabled(
           features::kNavigationThreadingOptimizations)) {
-    base::ThreadPool::CreateSingleThreadTaskRunner(
-        {base::TaskPriority::USER_VISIBLE})
-        ->PostTask(FROM_HERE, std::move(create));
+    if (base::FeatureList::IsEnabled(features::kThreadingOptimizationsOnIO)) {
+      content::GetIOThreadTaskRunner({})->PostTask(FROM_HERE,
+                                                   std::move(create));
+    } else {
+      base::ThreadPool::CreateSingleThreadTaskRunner(
+          {base::TaskPriority::USER_BLOCKING})
+          ->PostTask(FROM_HERE, std::move(create));
+    }
   } else {
     std::move(create).Run();
   }
diff --git a/components/content_settings/core/browser/content_settings_policy_provider.cc b/components/content_settings/core/browser/content_settings_policy_provider.cc
index 4beba1869..6f4a4cb 100644
--- a/components/content_settings/core/browser/content_settings_policy_provider.cc
+++ b/components/content_settings/core/browser/content_settings_policy_provider.cc
@@ -361,7 +361,8 @@
 
     const std::string& pattern_str = pattern->GetString();
     if (filters_map.find(pattern_str) == filters_map.end())
-      filters_map[pattern_str].SetKey("filters", base::ListValue());
+      filters_map[pattern_str].SetKey("filters",
+                                      base::Value(base::Value::Type::LIST));
 
     // Don't pass removed values from `pattern_filter`, because base::Values
     // read with JSONReader use a shared string buffer. Instead, Clone() here.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
index 1699ed7..c6bb57e 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.cc
@@ -111,7 +111,7 @@
                          const std::string& pref,
                          int key,
                          int value) {
-  DictionaryPrefUpdate pref_update(pref_service, pref);
+  DictionaryPrefUpdateDeprecated pref_update(pref_service, pref);
   base::Value* pref_dict = pref_update.Get();
   const std::string key_str = base::NumberToString(key);
   base::Value* dict_value = pref_dict->FindKey(key_str);
@@ -125,9 +125,9 @@
 void MoveAndClearDictionaryPrefs(PrefService* pref_service,
                                  const std::string& pref_dst,
                                  const std::string& pref_src) {
-  DictionaryPrefUpdate pref_update_dst(pref_service, pref_dst);
+  DictionaryPrefUpdateDeprecated pref_update_dst(pref_service, pref_dst);
   base::DictionaryValue* pref_dict_dst = pref_update_dst.Get();
-  DictionaryPrefUpdate pref_update_src(pref_service, pref_src);
+  DictionaryPrefUpdateDeprecated pref_update_src(pref_service, pref_src);
   base::DictionaryValue* pref_dict_src = pref_update_src.Get();
   pref_dict_dst->DictClear();
   pref_dict_dst->Swap(pref_dict_src);
@@ -428,7 +428,7 @@
 base::Value* DataReductionProxyCompressionStats::GetList(
     const char* pref_path) {
   if (delay_.is_zero())
-    return ListPrefUpdate(pref_service_, pref_path).Get();
+    return ListPrefUpdateDeprecated(pref_service_, pref_path).Get();
 
   DelayedWritePrefs();
   auto it = list_pref_map_.find(pref_path);
@@ -449,7 +449,7 @@
   for (auto iter = list_pref_map_.begin(); iter != list_pref_map_.end();
        ++iter) {
     TransferList(iter->second,
-                 ListPrefUpdate(pref_service_, iter->first).Get());
+                 ListPrefUpdateDeprecated(pref_service_, iter->first).Get());
   }
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs_unittest.cc
index b749cf6..d4b856d 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs_unittest.cc
@@ -37,7 +37,7 @@
   void InitializeList(const char* pref_name,
                       int64_t starting_value,
                       PrefService* pref_service) {
-    ListPrefUpdate list(local_state_prefs(), pref_name);
+    ListPrefUpdateDeprecated list(local_state_prefs(), pref_name);
     for (int64_t i = 0; i < 10L; ++i) {
       list->Append(base::NumberToString(i + starting_value));
     }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
index 4180ef9..8a18c13 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
@@ -52,10 +52,10 @@
 
   ResetSettings(nullptr);
 
-  ListPrefUpdate original_update(test_context_->pref_service(),
-                                 prefs::kDailyHttpOriginalContentLength);
-  ListPrefUpdate received_update(test_context_->pref_service(),
-                                 prefs::kDailyHttpReceivedContentLength);
+  ListPrefUpdateDeprecated original_update(
+      test_context_->pref_service(), prefs::kDailyHttpOriginalContentLength);
+  ListPrefUpdateDeprecated received_update(
+      test_context_->pref_service(), prefs::kDailyHttpReceivedContentLength);
   for (int64_t i = 0; i < kNumDaysInHistory; i++) {
     original_update->Insert(original_update->GetList().begin(),
                             base::Value(base::NumberToString(2 * i)));
diff --git a/components/data_use_measurement/core/data_use_tracker_prefs.cc b/components/data_use_measurement/core/data_use_tracker_prefs.cc
index af6c42169..72b8c67 100644
--- a/components/data_use_measurement/core/data_use_tracker_prefs.cc
+++ b/components/data_use_measurement/core/data_use_tracker_prefs.cc
@@ -103,7 +103,7 @@
   if (!pref_service_)
     return;
 
-  DictionaryPrefUpdate pref_updater(pref_service_, pref_name);
+  DictionaryPrefUpdateDeprecated pref_updater(pref_service_, pref_name);
   std::string todays_key = GetCurrentMeasurementDateAsString();
 
   const base::Value* user_pref_dict = pref_service_->GetDictionary(pref_name);
diff --git a/components/embedder_support/origin_trials/component_updater_utils.cc b/components/embedder_support/origin_trials/component_updater_utils.cc
index 941af27..160be448 100644
--- a/components/embedder_support/origin_trials/component_updater_utils.cc
+++ b/components/embedder_support/origin_trials/component_updater_utils.cc
@@ -37,7 +37,7 @@
   }
 
   // TODO(crbug.com/1187062): Modernize use of base::ListValue once
-  // ListPrefUpdate is converted.
+  // ListPrefUpdateDeprecated is converted.
   base::ListValue* override_disabled_feature_list = nullptr;
   if (base::Value* raw_override_disabled_feature_list =
           manifest.FindListPath(kManifestDisabledFeaturesPath)) {
@@ -46,21 +46,23 @@
   }
   if (override_disabled_feature_list &&
       !override_disabled_feature_list->GetList().empty()) {
-    ListPrefUpdate update(local_state, prefs::kOriginTrialDisabledFeatures);
+    ListPrefUpdateDeprecated update(local_state,
+                                    prefs::kOriginTrialDisabledFeatures);
     update->Swap(override_disabled_feature_list);
   } else {
     local_state->ClearPref(prefs::kOriginTrialDisabledFeatures);
   }
 
   // TODO(crbug.com/1187062): Modernize use of base::ListValue once
-  // ListPrefUpdate is converted.
+  // ListPrefUpdateDeprecated is converted.
   base::ListValue* disabled_tokens_list = nullptr;
   if (base::Value* raw_disabled_tokens_list =
           manifest.FindListPath(kManifestDisabledTokenSignaturesPath)) {
     raw_disabled_tokens_list->GetAsList(&disabled_tokens_list);
   }
   if (disabled_tokens_list && !disabled_tokens_list->GetList().empty()) {
-    ListPrefUpdate update(local_state, prefs::kOriginTrialDisabledTokens);
+    ListPrefUpdateDeprecated update(local_state,
+                                    prefs::kOriginTrialDisabledTokens);
     update->Swap(disabled_tokens_list);
   } else {
     local_state->ClearPref(prefs::kOriginTrialDisabledTokens);
diff --git a/components/embedder_support/origin_trials/component_updater_utils_unittest.cc b/components/embedder_support/origin_trials/component_updater_utils_unittest.cc
index 0843dc1..4267446 100644
--- a/components/embedder_support/origin_trials/component_updater_utils_unittest.cc
+++ b/components/embedder_support/origin_trials/component_updater_utils_unittest.cc
@@ -85,7 +85,7 @@
     for (const std::string& feature : features) {
       disabled_feature_list.Append(feature);
     }
-    ListPrefUpdate update(
+    ListPrefUpdateDeprecated update(
         local_state(), embedder_support::prefs::kOriginTrialDisabledFeatures);
     update->Swap(&disabled_feature_list);
   }
@@ -119,8 +119,8 @@
     for (const std::string& token : tokens) {
       disabled_token_list.Append(token);
     }
-    ListPrefUpdate update(local_state(),
-                          embedder_support::prefs::kOriginTrialDisabledTokens);
+    ListPrefUpdateDeprecated update(
+        local_state(), embedder_support::prefs::kOriginTrialDisabledTokens);
     update->Swap(&disabled_token_list);
   }
 
diff --git a/components/embedder_support/user_agent_utils.cc b/components/embedder_support/user_agent_utils.cc
index 8fa2d7f..581b65cd 100644
--- a/components/embedder_support/user_agent_utils.cc
+++ b/components/embedder_support/user_agent_utils.cc
@@ -439,17 +439,22 @@
          greasey_chars[(seed + 1) % greasey_chars.size()], "A",
          greasey_chars[(seed + 2) % greasey_chars.size()], "Brand"});
     greasey_version = greased_versions[seed % greased_versions.size()];
+
+    return GetProcessedGreasedBrandVersion(
+        maybe_greasey_brand.value_or(greasey_brand),
+        maybe_greasey_version.value_or(greasey_version), output_version_type);
   } else {
     const std::vector<std::string> greasey_chars = {" ", " ", ";"};
     greasey_brand = base::StrCat({greasey_chars[permuted_order[0]], "Not",
                                   greasey_chars[permuted_order[1]], "A",
                                   greasey_chars[permuted_order[2]], "Brand"});
     greasey_version = "99";
-  }
 
-  return GetProcessedGreasedBrandVersion(
-      maybe_greasey_brand.value_or(greasey_brand),
-      maybe_greasey_version.value_or(greasey_version), output_version_type);
+    // The old algorithm is held constant; it does not respond to experiment
+    // overrides.
+    return GetProcessedGreasedBrandVersion(greasey_brand, greasey_version,
+                                           output_version_type);
+  }
 }
 // TODO(crbug.com/1103047): This can be removed/re-refactored once we use
 // "macOS" by default
diff --git a/components/embedder_support/user_agent_utils_unittest.cc b/components/embedder_support/user_agent_utils_unittest.cc
index 8d2e7d8..9feca1e 100644
--- a/components/embedder_support/user_agent_utils_unittest.cc
+++ b/components/embedder_support/user_agent_utils_unittest.cc
@@ -531,6 +531,9 @@
                           "\"Chromium\";v=\"84.0.0.0\", ",
                           "\"Totally A Brand\";v=\"84.0.0.0\""}),
             brand_list_w_brand_fv);
+
+  // The old GREASE generation algorithm should not respond to experiment
+  // overrides.
   metadata.brand_version_list = GenerateBrandVersionList(
       84, absl::nullopt, "84", "Clean GREASE", absl::nullopt, true,
       blink::UserAgentBrandVersionType::kMajorVersion);
@@ -540,15 +543,13 @@
   // 1. verify major version
   std::string brand_list_grease_override =
       metadata.SerializeBrandMajorVersionList();
-  EXPECT_EQ(R"("Clean GREASE";v="99", "Chromium";v="84")",
+  EXPECT_EQ(R"(" Not A;Brand";v="99", "Chromium";v="84")",
             brand_list_grease_override);
-  EXPECT_NE(brand_list, brand_list_grease_override);
   // 2. verify full version
   std::string brand_list_grease_override_fv =
       metadata.SerializeBrandFullVersionList();
-  EXPECT_EQ(R"("Clean GREASE";v="99.0.0.0", "Chromium";v="84.0.0.0")",
+  EXPECT_EQ(R"(" Not A;Brand";v="99.0.0.0", "Chromium";v="84.0.0.0")",
             brand_list_grease_override_fv);
-  EXPECT_NE(brand_list_w_fv, brand_list_grease_override_fv);
 
   metadata.brand_version_list = GenerateBrandVersionList(
       84, absl::nullopt, "84", "Clean GREASE", "1024", true,
@@ -559,15 +560,13 @@
   // 1. verify major version
   std::string brand_list_and_version_grease_override =
       metadata.SerializeBrandMajorVersionList();
-  EXPECT_EQ(R"("Clean GREASE";v="1024", "Chromium";v="84")",
+  EXPECT_EQ(R"(" Not A;Brand";v="99", "Chromium";v="84")",
             brand_list_and_version_grease_override);
-  EXPECT_NE(brand_list, brand_list_and_version_grease_override);
   // 2. verify full version
   std::string brand_list_and_version_grease_override_fv =
       metadata.SerializeBrandFullVersionList();
-  EXPECT_EQ(R"("Clean GREASE";v="1024.0.0.0", "Chromium";v="84.0.0.0")",
+  EXPECT_EQ(R"(" Not A;Brand";v="99.0.0.0", "Chromium";v="84.0.0.0")",
             brand_list_and_version_grease_override_fv);
-  EXPECT_NE(brand_list_w_fv, brand_list_and_version_grease_override_fv);
 
   metadata.brand_version_list = GenerateBrandVersionList(
       84, absl::nullopt, "84", absl::nullopt, "1024", true,
@@ -578,15 +577,13 @@
   // 1. verify major version
   std::string brand_version_grease_override =
       metadata.SerializeBrandMajorVersionList();
-  EXPECT_EQ(R"(" Not A;Brand";v="1024", "Chromium";v="84")",
+  EXPECT_EQ(R"(" Not A;Brand";v="99", "Chromium";v="84")",
             brand_version_grease_override);
-  EXPECT_NE(brand_list, brand_version_grease_override);
   // 2. verify full version
   std::string brand_version_grease_override_fv =
       metadata.SerializeBrandFullVersionList();
-  EXPECT_EQ(R"(" Not A;Brand";v="1024.0.0.0", "Chromium";v="84.0.0.0")",
+  EXPECT_EQ(R"(" Not A;Brand";v="99.0.0.0", "Chromium";v="84.0.0.0")",
             brand_version_grease_override_fv);
-  EXPECT_NE(brand_list_w_fv, brand_version_grease_override_fv);
 
   // Should DCHECK on negative numbers
   EXPECT_DCHECK_DEATH(GenerateBrandVersionList(
@@ -616,41 +613,43 @@
   EXPECT_EQ(greased_bv.brand, " Not A;Brand");
   EXPECT_EQ(greased_bv.version, "99.0.0.0");
 
+  // With the new algorithm disabled, we want to avoid experiment params
+  // ("WhatIsGrease", 1024) from taking an effect.
   greased_bv = GetGreasedUserAgentBrandVersion(
       permuted_order, 84, "WhatIsGrease", absl::nullopt, true,
       blink::UserAgentBrandVersionType::kMajorVersion);
-  EXPECT_EQ(greased_bv.brand, "WhatIsGrease");
+  EXPECT_EQ(greased_bv.brand, " Not A;Brand");
   EXPECT_EQ(greased_bv.version, "99");
 
   greased_bv = GetGreasedUserAgentBrandVersion(
       permuted_order, 84, "WhatIsGrease", absl::nullopt, true,
       blink::UserAgentBrandVersionType::kFullVersion);
-  EXPECT_EQ(greased_bv.brand, "WhatIsGrease");
+  EXPECT_EQ(greased_bv.brand, " Not A;Brand");
   EXPECT_EQ(greased_bv.version, "99.0.0.0");
 
   greased_bv = GetGreasedUserAgentBrandVersion(
       permuted_order, 84, absl::nullopt, "1024", true,
       blink::UserAgentBrandVersionType::kMajorVersion);
   EXPECT_EQ(greased_bv.brand, " Not A;Brand");
-  EXPECT_EQ(greased_bv.version, "1024");
+  EXPECT_EQ(greased_bv.version, "99");
 
   greased_bv = GetGreasedUserAgentBrandVersion(
       permuted_order, 84, absl::nullopt, "1024", true,
       blink::UserAgentBrandVersionType::kFullVersion);
   EXPECT_EQ(greased_bv.brand, " Not A;Brand");
-  EXPECT_EQ(greased_bv.version, "1024.0.0.0");
+  EXPECT_EQ(greased_bv.version, "99.0.0.0");
 
   greased_bv = GetGreasedUserAgentBrandVersion(
       permuted_order, 84, "WhatIsGrease", "1024", true,
       blink::UserAgentBrandVersionType::kMajorVersion);
-  EXPECT_EQ(greased_bv.brand, "WhatIsGrease");
-  EXPECT_EQ(greased_bv.version, "1024");
+  EXPECT_EQ(greased_bv.brand, " Not A;Brand");
+  EXPECT_EQ(greased_bv.version, "99");
 
   greased_bv = GetGreasedUserAgentBrandVersion(
       permuted_order, 84, "WhatIsGrease", "1024", true,
       blink::UserAgentBrandVersionType::kFullVersion);
-  EXPECT_EQ(greased_bv.brand, "WhatIsGrease");
-  EXPECT_EQ(greased_bv.version, "1024.0.0.0");
+  EXPECT_EQ(greased_bv.brand, " Not A;Brand");
+  EXPECT_EQ(greased_bv.version, "99.0.0.0");
 
   // Test to ensure the new algorithm works and is still overridable.
   scoped_feature_list.Reset();
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index 4a6b7a4b..1d05287 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -385,7 +385,7 @@
                      1, display_, bind_zwp_idle_inhibit_manager);
   }
 
-  weston_test_holder_ = std::make_unique<WestonTest>(wl_display_.get());
+  weston_test_holder_ = std::make_unique<WestonTest>(this);
 
   zcr_keyboard_extension_data_ =
       std::make_unique<WaylandKeyboardExtension>(serial_tracker_.get());
diff --git a/components/exo/wayland/server.h b/components/exo/wayland/server.h
index 354ba35..47c3cadd 100644
--- a/components/exo/wayland/server.h
+++ b/components/exo/wayland/server.h
@@ -13,11 +13,10 @@
 #include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/time/time.h"
-#include "components/exo/wayland/scoped_wl.h"
-#include "ui/display/display_observer.h"
-
 #include "build/chromeos_buildflags.h"
 #include "components/exo/buildflags.h"
+#include "components/exo/wayland/scoped_wl.h"
+#include "ui/display/display_observer.h"
 
 struct wl_resource;
 struct wl_client;
@@ -109,11 +108,14 @@
   const base::FilePath& socket_path() const { return socket_path_; }
 
  protected:
+  friend class WestonTest;
   void AddWaylandOutput(int64_t id,
                         std::unique_ptr<WaylandDisplayOutput> output);
   wl_display* GetWaylandDisplay() const { return wl_display_.get(); }
 
  private:
+  friend class ScopedEventDispatchDisabler;
+
   // This has the server's socket inside it, so it must be deleted last.
   base::ScopedTempDir socket_dir_;
   Display* const display_;
diff --git a/components/exo/wayland/wayland_watcher.cc b/components/exo/wayland/wayland_watcher.cc
index 48a32ad..44333b84 100644
--- a/components/exo/wayland/wayland_watcher.cc
+++ b/components/exo/wayland/wayland_watcher.cc
@@ -12,16 +12,28 @@
 
 WaylandWatcher::WaylandWatcher(wayland::Server* server)
     : controller_(FROM_HERE), server_(server) {
-  base::CurrentUIThread::Get()->WatchFileDescriptor(
-      server_->GetFileDescriptor(),
-      true,  // persistent
-      base::MessagePumpForUI::WATCH_READ, &controller_, this);
+  Start();
 }
 
 WaylandWatcher::~WaylandWatcher() {
   controller_.StopWatchingFileDescriptor();
 }
 
+void WaylandWatcher::StartForTesting() {
+  Start();
+}
+
+void WaylandWatcher::StopForTesting() {
+  controller_.StopWatchingFileDescriptor();
+}
+
+void WaylandWatcher::Start() {
+  base::CurrentUIThread::Get()->WatchFileDescriptor(
+      server_->GetFileDescriptor(),
+      true,  // persistent
+      base::MessagePumpForUI::WATCH_READ, &controller_, this);
+}
+
 void WaylandWatcher::OnFileCanReadWithoutBlocking(int fd) {
   server_->Dispatch(base::TimeDelta());
   server_->Flush();
diff --git a/components/exo/wayland/wayland_watcher.h b/components/exo/wayland/wayland_watcher.h
index 74bf4ca..13ccbc2 100644
--- a/components/exo/wayland/wayland_watcher.h
+++ b/components/exo/wayland/wayland_watcher.h
@@ -22,7 +22,13 @@
 
   ~WaylandWatcher() override;
 
+  // Start/Stop watching the fd for testing.
+  void StartForTesting();
+  void StopForTesting();
+
  private:
+  void Start();
+
   // base::MessagePumpForUI::FdWatcher:
   void OnFileCanReadWithoutBlocking(int fd) override;
 
diff --git a/components/exo/wayland/weston_test.cc b/components/exo/wayland/weston_test.cc
index 6b575510..4647545 100644
--- a/components/exo/wayland/weston_test.cc
+++ b/components/exo/wayland/weston_test.cc
@@ -13,7 +13,9 @@
 #include "base/notreached.h"
 #include "base/run_loop.h"
 #include "components/exo/surface.h"
+#include "components/exo/wayland/server.h"
 #include "components/exo/wayland/server_util.h"
+#include "components/exo/wayland/wayland_watcher.h"
 #include "components/exo/wm_helper.h"
 #include "components/exo/xkb_tracker.h"
 #include "ui/base/test/ui_controls.h"
@@ -27,11 +29,13 @@
 
 // Tracks button and mouse states for testing.
 struct WestonTest::WestonTestState {
-  WestonTestState() {}
+  explicit WestonTestState(Server* s) : server(s) {}
 
   WestonTestState(const WestonTestState&) = delete;
   WestonTestState& operator=(const WestonTestState&) = delete;
 
+  Server* server;
+
   bool left_button_pressed = false;
   bool middle_button_pressed = false;
   bool right_button_pressed = false;
@@ -42,6 +46,22 @@
   bool command_pressed = false;
 };
 
+class ScopedEventDispatchDisabler {
+ public:
+  ScopedEventDispatchDisabler(Server* server) : server_(server) {
+    server_->wayland_watcher_->StopForTesting();
+  }
+  ScopedEventDispatchDisabler(const ScopedEventDispatchDisabler&) = delete;
+  ScopedEventDispatchDisabler& operator=(const ScopedEventDispatchDisabler&) =
+      delete;
+  ~ScopedEventDispatchDisabler() {
+    server_->wayland_watcher_->StartForTesting();
+  }
+
+ private:
+  Server* server_;
+};
+
 namespace {
 
 using WestonTestState = WestonTest::WestonTestState;
@@ -64,6 +84,8 @@
                                      uint32_t tv_nsec,
                                      int32_t x,
                                      int32_t y) {
+  auto* weston_test = GetUserDataAs<WestonTestState>(resource);
+
   // Convert cursor point from window space to root space
   gfx::Point point_in_root(x, y);
   if (surface_resource) {
@@ -74,11 +96,14 @@
   base::RunLoop run_loop;
   ui_controls::SendMouseMoveNotifyWhenDone(point_in_root.x(), point_in_root.y(),
                                            run_loop.QuitClosure());
-  run_loop.Run();
+  {
+    // Do not process incoming wayland events which may destroy resources.
+    ScopedEventDispatchDisabler disable(weston_test->server);
+    run_loop.Run();
+  }
+
   // TODO(https://crbug.com/1284726): This should not be necessary.
-  // At this point the resource could have been destroyed.
-  if (GetUserDataAs<WestonTestState>(resource))
-    weston_test_send_pointer_position(resource, x, y);
+  weston_test_send_pointer_position(resource, x, y);
 }
 
 static void weston_test_send_button(struct wl_client* client,
@@ -93,8 +118,8 @@
   DCHECK(button != BTN_BACK);
 
   // Track mouse click state
-  auto* weston_test = GetUserDataAs<WestonTestState>(resource);
   ui_controls::MouseButton mouse_button = ui_controls::LEFT;
+  auto* weston_test = GetUserDataAs<WestonTestState>(resource);
   switch (button) {
     case BTN_LEFT:
       mouse_button = ui_controls::LEFT;
@@ -118,17 +143,20 @@
   base::RunLoop run_loop;
   ui_controls::SendMouseEventsNotifyWhenDone(mouse_button, mouse_state,
                                              run_loop.QuitClosure());
-  run_loop.Run();
-
+  {
+    // Do not process incoming wayland events which may destroy resources.
+    ScopedEventDispatchDisabler disable(weston_test->server);
+    run_loop.Run();
+  }
   // TODO(https://crbug.com/1284726): This should not be necessary.
-  // At this point the resource could have been destroyed.
-  if (GetUserDataAs<WestonTestState>(resource))
-    weston_test_send_pointer_button(resource, button, state);
+  weston_test_send_pointer_button(resource, button, state);
 }
 
 static void weston_test_reset_pointer(struct wl_client* client,
                                       struct wl_resource* resource) {
   auto* weston_test = GetUserDataAs<WestonTestState>(resource);
+  ScopedEventDispatchDisabler disable(weston_test->server);
+
   if (weston_test->left_button_pressed) {
     weston_test->left_button_pressed = false;
     base::RunLoop run_loop;
@@ -136,11 +164,8 @@
         ui_controls::LEFT, ui_controls::UP, run_loop.QuitClosure());
     run_loop.Run();
     // TODO(https://crbug.com/1284726): This should not be necessary.
-    // At this point the resource could have been destroyed.
-    if (GetUserDataAs<WestonTestState>(resource)) {
-      weston_test_send_pointer_button(resource, BTN_LEFT,
-                                      WL_POINTER_BUTTON_STATE_RELEASED);
-    }
+    weston_test_send_pointer_button(resource, BTN_LEFT,
+                                    WL_POINTER_BUTTON_STATE_RELEASED);
   }
   if (weston_test->middle_button_pressed) {
     weston_test->middle_button_pressed = false;
@@ -149,11 +174,8 @@
         ui_controls::MIDDLE, ui_controls::UP, run_loop.QuitClosure());
     run_loop.Run();
     // TODO(https://crbug.com/1284726): This should not be necessary.
-    // At this point the resource could have been destroyed.
-    if (GetUserDataAs<WestonTestState>(resource)) {
-      weston_test_send_pointer_button(resource, BTN_MIDDLE,
-                                      WL_POINTER_BUTTON_STATE_RELEASED);
-    }
+    weston_test_send_pointer_button(resource, BTN_MIDDLE,
+                                    WL_POINTER_BUTTON_STATE_RELEASED);
   }
   if (weston_test->right_button_pressed) {
     weston_test->right_button_pressed = false;
@@ -162,11 +184,8 @@
         ui_controls::RIGHT, ui_controls::UP, run_loop.QuitClosure());
     run_loop.Run();
     // TODO(https://crbug.com/1284726): This should not be necessary.
-    // At this point the resource could have been destroyed.
-    if (GetUserDataAs<WestonTestState>(resource)) {
-      weston_test_send_pointer_button(resource, BTN_RIGHT,
-                                      WL_POINTER_BUTTON_STATE_RELEASED);
-    }
+    weston_test_send_pointer_button(resource, BTN_RIGHT,
+                                    WL_POINTER_BUTTON_STATE_RELEASED);
   }
 }
 
@@ -195,9 +214,10 @@
                                  uint32_t tv_nsec,
                                  uint32_t key,
                                  uint32_t state) {
-  ui::DomCode dom_code = ui::KeycodeConverter::EvdevCodeToDomCode(key);
   auto* weston_test = GetUserDataAs<WestonTestState>(resource);
 
+  ui::DomCode dom_code = ui::KeycodeConverter::EvdevCodeToDomCode(key);
+
   // Get keyboard modifiers
   switch (dom_code) {
     case ui::DomCode::CONTROL_LEFT:
@@ -239,11 +259,13 @@
       weston_test->shift_pressed, weston_test->alt_pressed,
       weston_test->command_pressed,
       run_loop.QuitClosure());
-  run_loop.Run();
-  // TODO(https://crbug.com/1284726): This should not be necessary.
-  // At this point the resource could have been destroyed.
-  if (GetUserDataAs<WestonTestState>(resource))
-    weston_test_send_keyboard_key(resource, key, state);
+  {
+    // Do not process incoming wayland events which may destroy resources.
+    ScopedEventDispatchDisabler disable(weston_test->server);
+    run_loop.Run();
+  }
+
+  weston_test_send_keyboard_key(resource, key, state);
 }
 
 static void weston_test_device_release(struct wl_client* client,
@@ -298,10 +320,10 @@
 
 }  // namespace
 
-WestonTest::WestonTest(wl_display* display)
-    : data_(std::make_unique<WestonTestState>()) {
-  wl_global_create(display, &weston_test_interface, kWestonTestVersion,
-                   data_.get(), bind_weston_test);
+WestonTest::WestonTest(Server* server)
+    : data_(std::make_unique<WestonTestState>(server)) {
+  wl_global_create(server->GetWaylandDisplay(), &weston_test_interface,
+                   kWestonTestVersion, data_.get(), bind_weston_test);
 }
 
 WestonTest::~WestonTest() = default;
diff --git a/components/exo/wayland/weston_test.h b/components/exo/wayland/weston_test.h
index 3c9b7bc..8d93fcb 100644
--- a/components/exo/wayland/weston_test.h
+++ b/components/exo/wayland/weston_test.h
@@ -9,13 +9,13 @@
 
 #include "base/component_export.h"
 
-struct wl_display;
 namespace exo {
 namespace wayland {
+class Server;
 
 class COMPONENT_EXPORT(WESTON_TEST) WestonTest {
  public:
-  explicit WestonTest(wl_display* display);
+  explicit WestonTest(Server* server);
   WestonTest(const WestonTest&) = delete;
   WestonTest& operator=(const WestonTest&) = delete;
   ~WestonTest();
diff --git a/components/exo/wayland/weston_test_stub.cc b/components/exo/wayland/weston_test_stub.cc
index 0bbaa95..a69a9e3 100644
--- a/components/exo/wayland/weston_test_stub.cc
+++ b/components/exo/wayland/weston_test_stub.cc
@@ -6,10 +6,11 @@
 
 namespace exo {
 namespace wayland {
+class Server;
 
 struct WestonTest::WestonTestState {};
 
-WestonTest::WestonTest(wl_display* display) {}
+WestonTest::WestonTest(Server* server) {}
 
 WestonTest::~WestonTest() = default;
 
diff --git a/components/exo/wayland/zwp_pointer_constraints.cc b/components/exo/wayland/zwp_pointer_constraints.cc
index 2bddf4a..452b5d6 100644
--- a/components/exo/wayland/zwp_pointer_constraints.cc
+++ b/components/exo/wayland/zwp_pointer_constraints.cc
@@ -143,8 +143,8 @@
   // Confined pointer is not currently supported.
   wl_resource* confined_pointer_resource =
       wl_resource_create(client, &zwp_confined_pointer_v1_interface, 1, id);
-  SetImplementation<WaylandPointerConstraintDelegate>(
-      confined_pointer_resource, &confined_pointer_implementation, nullptr);
+  SetImplementation<int>(confined_pointer_resource,
+                         &confined_pointer_implementation, nullptr);
 }
 
 const struct zwp_pointer_constraints_v1_interface
diff --git a/components/feedback/redaction_tool.cc b/components/feedback/redaction_tool.cc
index 6a0ea26..cc0cfdd 100644
--- a/components/feedback/redaction_tool.cc
+++ b/components/feedback/redaction_tool.cc
@@ -768,6 +768,11 @@
   if (url.contains("?"))
     return false;
 
+  // Last part of an SELinux context is misdetected as a URL.
+  // e.g. "u:object_r:system_data_file:s0:c512,c768"
+  if (url.starts_with("file:s0"))
+    return true;
+
   // Check for chrome:// URLs that are exempt.
   if (url.starts_with("chrome://")) {
     // We allow everything in chrome://resources/.
diff --git a/components/feedback/redaction_tool_unittest.cc b/components/feedback/redaction_tool_unittest.cc
index d6977f3..50cce14 100644
--- a/components/feedback/redaction_tool_unittest.cc
+++ b/components/feedback/redaction_tool_unittest.cc
@@ -36,6 +36,8 @@
      "aaaaaaaa [SSID=<SSID: 1>]aaaaa", PIIType::kSSID},
     {"aaaaaaaahttp://tets.comaaaaaaa",  // URL.
      "aaaaaaaa<URL: 1>", PIIType::kURL},
+    {"u:object_r:system_data_file:s0:c512,c768",  // No PII, it is an SELinux context.
+     "u:object_r:system_data_file:s0:c512,c768", PIIType::kNone},
     {"aaaaaemail@example.comaaa",  // Email address.
      "<email: 1>", PIIType::kEmail},
     {"example@@1234",  // No PII, it is not a valid email address.
diff --git a/components/flags_ui/pref_service_flags_storage.cc b/components/flags_ui/pref_service_flags_storage.cc
index 8b79ade..c22acd0d 100644
--- a/components/flags_ui/pref_service_flags_storage.cc
+++ b/components/flags_ui/pref_service_flags_storage.cc
@@ -36,7 +36,7 @@
 }
 
 bool PrefServiceFlagsStorage::SetFlags(const std::set<std::string>& flags) {
-  ListPrefUpdate update(prefs_, prefs::kAboutFlagsEntries);
+  ListPrefUpdateDeprecated update(prefs_, prefs::kAboutFlagsEntries);
   base::Value* experiments_list = update.Get();
   DCHECK(experiments_list->is_list());
 
@@ -61,7 +61,7 @@
 void PrefServiceFlagsStorage::SetOriginListFlag(
     const std::string& internal_entry_name,
     const std::string& origin_list_value) {
-  DictionaryPrefUpdate update(prefs_, prefs::kAboutFlagsOriginLists);
+  DictionaryPrefUpdateDeprecated update(prefs_, prefs::kAboutFlagsOriginLists);
   update->SetString(internal_entry_name, origin_list_value);
 }
 
diff --git a/components/history/core/browser/BUILD.gn b/components/history/core/browser/BUILD.gn
index f8b31a4..4ca233c5 100644
--- a/components/history/core/browser/BUILD.gn
+++ b/components/history/core/browser/BUILD.gn
@@ -213,6 +213,7 @@
     "top_sites_database_unittest.cc",
     "top_sites_impl_unittest.cc",
     "url_database_unittest.cc",
+    "url_row_unittest.cc",
     "url_utils_unittest.cc",
     "visit_annotations_database_unittest.cc",
     "visit_annotations_test_utils.cc",
diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc
index c7b842b6..5f8116d 100644
--- a/components/history/core/browser/history_backend.cc
+++ b/components/history/core/browser/history_backend.cc
@@ -1325,7 +1325,7 @@
       std::min(number_of_days_to_report, kDomainDiversityMaxBacktrackedDays);
 
   base::Time current_midnight = report_time.LocalMidnight();
-  SCOPED_UMA_HISTOGRAM_TIMER("History.DomainCountQueryTime");
+  SCOPED_UMA_HISTOGRAM_TIMER("History.DomainCountQueryTime_V2");
 
   for (int days_back = 0; days_back < number_of_days_to_report; ++days_back) {
     DomainMetricSet single_metric_set;
diff --git a/components/history/core/browser/top_sites_impl.cc b/components/history/core/browser/top_sites_impl.cc
index d921aa5f..1bad31c8 100644
--- a/components/history/core/browser/top_sites_impl.cc
+++ b/components/history/core/browser/top_sites_impl.cc
@@ -151,7 +151,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   {
-    DictionaryPrefUpdate update(pref_service_, kBlockedUrlsPrefsKey);
+    DictionaryPrefUpdateDeprecated update(pref_service_, kBlockedUrlsPrefsKey);
     base::Value* blocked_urls = update.Get();
     blocked_urls->SetKey(GetURLHash(url), base::Value());
   }
@@ -163,7 +163,7 @@
 void TopSitesImpl::RemoveBlockedUrl(const GURL& url) {
   DCHECK(thread_checker_.CalledOnValidThread());
   {
-    DictionaryPrefUpdate update(pref_service_, kBlockedUrlsPrefsKey);
+    DictionaryPrefUpdateDeprecated update(pref_service_, kBlockedUrlsPrefsKey);
     base::Value* blocked_urls = update.Get();
     blocked_urls->RemoveKey(GetURLHash(url));
   }
@@ -181,7 +181,7 @@
 void TopSitesImpl::ClearBlockedUrls() {
   DCHECK(thread_checker_.CalledOnValidThread());
   {
-    DictionaryPrefUpdate update(pref_service_, kBlockedUrlsPrefsKey);
+    DictionaryPrefUpdateDeprecated update(pref_service_, kBlockedUrlsPrefsKey);
     base::Value* blocked_urls = update.Get();
     blocked_urls->DictClear();
   }
diff --git a/components/history/core/browser/url_row.cc b/components/history/core/browser/url_row.cc
index c66a298..f8dd7c8 100644
--- a/components/history/core/browser/url_row.cc
+++ b/components/history/core/browser/url_row.cc
@@ -94,6 +94,40 @@
     const VisitContentModelAnnotations&) = default;
 VisitContentModelAnnotations::~VisitContentModelAnnotations() = default;
 
+// static
+void VisitContentModelAnnotations::MergeCategoryIntoVector(
+    const Category& category,
+    std::vector<Category>* categories) {
+  DCHECK(categories);
+  for (auto& this_category : *categories) {
+    // If this visit already has the category, upgrade the weight.
+    if (category.id == this_category.id) {
+      this_category.weight = std::max(this_category.weight, category.weight);
+      return;
+    }
+  }
+
+  // Append the category since it wasn't found in our existing `categories`.
+  categories->push_back(category);
+}
+
+void VisitContentModelAnnotations::MergeFrom(
+    const VisitContentModelAnnotations& other) {
+  // To be conservative, we use the lesser of the two visibility scores, but
+  // ignore sentinel values (which are negative).
+  if (other.visibility_score >= 0 &&
+      other.visibility_score < visibility_score) {
+    visibility_score = other.visibility_score;
+  }
+
+  for (auto& other_category : other.categories) {
+    MergeCategoryIntoVector(other_category, &categories);
+  }
+  for (auto& other_entity : other.entities) {
+    MergeCategoryIntoVector(other_entity, &entities);
+  }
+}
+
 VisitContentAnnotations::VisitContentAnnotations(
     VisitContentAnnotationFlags annotation_flags,
     VisitContentModelAnnotations model_annotations,
diff --git a/components/history/core/browser/url_row.h b/components/history/core/browser/url_row.h
index 2b4ad90..d431b91 100644
--- a/components/history/core/browser/url_row.h
+++ b/components/history/core/browser/url_row.h
@@ -201,6 +201,15 @@
   VisitContentModelAnnotations(const VisitContentModelAnnotations& other);
   ~VisitContentModelAnnotations();
 
+  // Merges `category` into `categories`. It upgrades the weight if it already
+  // exists, and appends it if it doesn't.
+  static void MergeCategoryIntoVector(const Category& category,
+                                      std::vector<Category>* categories);
+
+  // Merges the max-score, categories, and entities from `other`, which is the
+  // content model annotations of a duplicate visit.
+  void MergeFrom(const VisitContentModelAnnotations& other);
+
   // A value from 0 to 1 that represents how prominent, or visible, the page
   // might be considered on UI surfaces.
   float visibility_score = kDefaultVisibilityScore;
diff --git a/components/history/core/browser/url_row_unittest.cc b/components/history/core/browser/url_row_unittest.cc
new file mode 100644
index 0000000..49ed3fe
--- /dev/null
+++ b/components/history/core/browser/url_row_unittest.cc
@@ -0,0 +1,39 @@
+// Copyright 2022 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/history/core/browser/url_row.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::ElementsAre;
+
+namespace history {
+
+namespace {
+
+TEST(HistoryUrlRowTest, MergeCategoryIntoVector) {
+  std::vector<VisitContentModelAnnotations::Category> categories;
+  categories.emplace_back("category1", 40);
+  categories.emplace_back("category2", 20);
+
+  VisitContentModelAnnotations::MergeCategoryIntoVector({"category1", 50},
+                                                        &categories);
+  EXPECT_THAT(
+      categories,
+      ElementsAre(VisitContentModelAnnotations::Category("category1", 50),
+                  VisitContentModelAnnotations::Category("category2", 20)));
+
+  VisitContentModelAnnotations::MergeCategoryIntoVector({"category3", 30},
+                                                        &categories);
+  EXPECT_THAT(
+      categories,
+      ElementsAre(VisitContentModelAnnotations::Category("category1", 50),
+                  VisitContentModelAnnotations::Category("category2", 20),
+                  VisitContentModelAnnotations::Category("category3", 30)));
+}
+
+}  // namespace
+
+}  // namespace history
diff --git a/components/history/metrics/domain_diversity_reporter.cc b/components/history/metrics/domain_diversity_reporter.cc
index 6de974f5..9ede2b3b 100644
--- a/components/history/metrics/domain_diversity_reporter.cc
+++ b/components/history/metrics/domain_diversity_reporter.cc
@@ -4,7 +4,7 @@
 
 #include "components/history/metrics/domain_diversity_reporter.h"
 
-#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -139,12 +139,12 @@
     return;
 
   for (auto& result_one_day : result) {
-    UMA_HISTOGRAM_COUNTS_1000("History.DomainCount1Day",
-                              result_one_day.one_day_metric.value().count);
-    UMA_HISTOGRAM_COUNTS_1000("History.DomainCount7Day",
-                              result_one_day.seven_day_metric.value().count);
-    UMA_HISTOGRAM_COUNTS_1000(
-        "History.DomainCount28Day",
+    base::UmaHistogramCounts1000("History.DomainCount1Day_V2",
+                                 result_one_day.one_day_metric.value().count);
+    base::UmaHistogramCounts1000("History.DomainCount7Day_V2",
+                                 result_one_day.seven_day_metric.value().count);
+    base::UmaHistogramCounts1000(
+        "History.DomainCount28Day_V2",
         result_one_day.twenty_eight_day_metric.value().count);
   }
 
diff --git a/components/history/metrics/domain_diversity_reporter_unittest.cc b/components/history/metrics/domain_diversity_reporter_unittest.cc
index 5ca1e5c..f4f6c06 100644
--- a/components/history/metrics/domain_diversity_reporter_unittest.cc
+++ b/components/history/metrics/domain_diversity_reporter_unittest.cc
@@ -135,22 +135,22 @@
   task_environment_.RunUntilIdle();
 
   // Since History is not yet loaded, there should be no histograms.
-  histograms().ExpectTotalCount("History.DomainCountQueryTime", 0);
-  histograms().ExpectTotalCount("History.DomainCount1Day", 0);
-  histograms().ExpectTotalCount("History.DomainCount7Day", 0);
-  histograms().ExpectTotalCount("History.DomainCount28Day", 0);
+  histograms().ExpectTotalCount("History.DomainCountQueryTime_V2", 0);
+  histograms().ExpectTotalCount("History.DomainCount1Day_V2", 0);
+  histograms().ExpectTotalCount("History.DomainCount7Day_V2", 0);
+  histograms().ExpectTotalCount("History.DomainCount28Day_V2", 0);
 
   // Load history. This should trigger reporter, via HistoryService observer.
   ASSERT_TRUE(LoadHistory());
   Wait();
 
-  histograms().ExpectTotalCount("History.DomainCountQueryTime", 1);
+  histograms().ExpectTotalCount("History.DomainCountQueryTime_V2", 1);
 
   // No domains were visited, but there should be 7 samples. The last
   // reporting date, since it has never been set, was defaulted to epoch.
-  histograms().ExpectUniqueSample("History.DomainCount1Day", 0, 7);
-  histograms().ExpectUniqueSample("History.DomainCount7Day", 0, 7);
-  histograms().ExpectUniqueSample("History.DomainCount28Day", 0, 7);
+  histograms().ExpectUniqueSample("History.DomainCount1Day_V2", 0, 7);
+  histograms().ExpectUniqueSample("History.DomainCount7Day_V2", 0, 7);
+  histograms().ExpectUniqueSample("History.DomainCount28Day_V2", 0, 7);
 }
 
 TEST_F(DomainDiversityReporterTest, HistoryLoaded) {
@@ -165,9 +165,9 @@
   task_environment_.RunUntilIdle();
 
   // Since History is already loaded, there should be a sample reported.
-  histograms().ExpectUniqueSample("History.DomainCount1Day", 0, 1);
-  histograms().ExpectUniqueSample("History.DomainCount7Day", 0, 1);
-  histograms().ExpectUniqueSample("History.DomainCount28Day", 0, 1);
+  histograms().ExpectUniqueSample("History.DomainCount1Day_V2", 0, 1);
+  histograms().ExpectUniqueSample("History.DomainCount7Day_V2", 0, 1);
+  histograms().ExpectUniqueSample("History.DomainCount28Day_V2", 0, 1);
 }
 
 TEST_F(DomainDiversityReporterTest, HostAddedSimple) {
@@ -182,24 +182,24 @@
 
   history_service()->AddPage(GURL("http://www.google.com"), two_days_ago,
                              history::VisitSource::SOURCE_BROWSED);
-  histograms().ExpectTotalCount("History.DomainCountQueryTime", 0);
+  histograms().ExpectTotalCount("History.DomainCountQueryTime_V2", 0);
 
   CreateDomainDiversityReporter();
   task_environment_.RunUntilIdle();
 
-  histograms().ExpectTotalCount("History.DomainCountQueryTime", 1);
+  histograms().ExpectTotalCount("History.DomainCountQueryTime_V2", 1);
 
   // There are 3 samples for each histogram. One sample of DomainCount1Day,
   // two samples of DomainCount7Day and two samples of DomainCount28Day
   // should have a visit count of 1.
-  histograms().ExpectBucketCount("History.DomainCount1Day", 1, 1);
-  histograms().ExpectBucketCount("History.DomainCount1Day", 0, 2);
+  histograms().ExpectBucketCount("History.DomainCount1Day_V2", 1, 1);
+  histograms().ExpectBucketCount("History.DomainCount1Day_V2", 0, 2);
 
-  histograms().ExpectBucketCount("History.DomainCount7Day", 1, 2);
-  histograms().ExpectBucketCount("History.DomainCount7Day", 0, 1);
+  histograms().ExpectBucketCount("History.DomainCount7Day_V2", 1, 2);
+  histograms().ExpectBucketCount("History.DomainCount7Day_V2", 0, 1);
 
-  histograms().ExpectBucketCount("History.DomainCount28Day", 1, 2);
-  histograms().ExpectBucketCount("History.DomainCount28Day", 0, 1);
+  histograms().ExpectBucketCount("History.DomainCount28Day_V2", 1, 2);
+  histograms().ExpectBucketCount("History.DomainCount28Day_V2", 0, 1);
 }
 
 TEST_F(DomainDiversityReporterTest, HostAddedLongAgo) {
@@ -235,15 +235,15 @@
   CreateDomainDiversityReporter();
   task_environment_.RunUntilIdle();
 
-  histograms().ExpectTotalCount("History.DomainCountQueryTime", 1);
+  histograms().ExpectTotalCount("History.DomainCountQueryTime_V2", 1);
 
-  histograms().ExpectUniqueSample("History.DomainCount1Day", 0, 3);
-  histograms().ExpectUniqueSample("History.DomainCount7Day", 0, 3);
+  histograms().ExpectUniqueSample("History.DomainCount1Day_V2", 0, 3);
+  histograms().ExpectUniqueSample("History.DomainCount7Day_V2", 0, 3);
 
   // Two of the three DomainCount28Day samples should reflect the
   // 4 domain visits 29 days ago.
-  histograms().ExpectBucketCount("History.DomainCount28Day", 4, 2);
-  histograms().ExpectBucketCount("History.DomainCount28Day", 0, 1);
+  histograms().ExpectBucketCount("History.DomainCount28Day_V2", 4, 2);
+  histograms().ExpectBucketCount("History.DomainCount28Day_V2", 0, 1);
 }
 
 TEST_F(DomainDiversityReporterTest, ScheduleNextDay) {
@@ -288,13 +288,13 @@
   task_environment_.RunUntilIdle();
 
   // Two domains visited two days ago.
-  histograms().ExpectTotalCount("History.DomainCountQueryTime", 1);
-  histograms().ExpectBucketCount("History.DomainCount1Day", 2, 1);
-  histograms().ExpectBucketCount("History.DomainCount1Day", 0, 3);
-  histograms().ExpectBucketCount("History.DomainCount7Day", 1, 2);
-  histograms().ExpectBucketCount("History.DomainCount7Day", 3, 2);
-  histograms().ExpectBucketCount("History.DomainCount28Day", 2, 2);
-  histograms().ExpectBucketCount("History.DomainCount28Day", 4, 2);
+  histograms().ExpectTotalCount("History.DomainCountQueryTime_V2", 1);
+  histograms().ExpectBucketCount("History.DomainCount1Day_V2", 2, 1);
+  histograms().ExpectBucketCount("History.DomainCount1Day_V2", 0, 3);
+  histograms().ExpectBucketCount("History.DomainCount7Day_V2", 1, 2);
+  histograms().ExpectBucketCount("History.DomainCount7Day_V2", 3, 2);
+  histograms().ExpectBucketCount("History.DomainCount28Day_V2", 2, 2);
+  histograms().ExpectBucketCount("History.DomainCount28Day_V2", 4, 2);
 
   test_clock().SetTime(MidnightNDaysLater(test_clock().Now(), 1) +
                        base::Hours(10));
@@ -302,18 +302,18 @@
 
   // The new report will include the four domain visits on the last
   // repoting date.
-  histograms().ExpectTotalCount("History.DomainCountQueryTime", 2);
-  histograms().ExpectBucketCount("History.DomainCount1Day", 4, 1);
-  histograms().ExpectBucketCount("History.DomainCount1Day", 2, 1);
-  histograms().ExpectBucketCount("History.DomainCount1Day", 0, 3);
+  histograms().ExpectTotalCount("History.DomainCountQueryTime_V2", 2);
+  histograms().ExpectBucketCount("History.DomainCount1Day_V2", 4, 1);
+  histograms().ExpectBucketCount("History.DomainCount1Day_V2", 2, 1);
+  histograms().ExpectBucketCount("History.DomainCount1Day_V2", 0, 3);
 
-  histograms().ExpectBucketCount("History.DomainCount7Day", 5, 1);
-  histograms().ExpectBucketCount("History.DomainCount7Day", 1, 2);
-  histograms().ExpectBucketCount("History.DomainCount7Day", 3, 2);
+  histograms().ExpectBucketCount("History.DomainCount7Day_V2", 5, 1);
+  histograms().ExpectBucketCount("History.DomainCount7Day_V2", 1, 2);
+  histograms().ExpectBucketCount("History.DomainCount7Day_V2", 3, 2);
 
-  histograms().ExpectBucketCount("History.DomainCount28Day", 6, 1);
-  histograms().ExpectBucketCount("History.DomainCount28Day", 2, 2);
-  histograms().ExpectBucketCount("History.DomainCount28Day", 4, 2);
+  histograms().ExpectBucketCount("History.DomainCount28Day_V2", 6, 1);
+  histograms().ExpectBucketCount("History.DomainCount28Day_V2", 2, 2);
+  histograms().ExpectBucketCount("History.DomainCount28Day_V2", 4, 2);
 }
 
 TEST_F(DomainDiversityReporterTest, SaveTimestampInPreference) {
@@ -326,7 +326,7 @@
   CreateDomainDiversityReporter();
   task_environment_.RunUntilIdle();
 
-  histograms().ExpectTotalCount("History.DomainCountQueryTime", 1);
+  histograms().ExpectTotalCount("History.DomainCountQueryTime_V2", 1);
 
   // Reporter should have updated the pref to the time of the request.
   EXPECT_EQ(test_clock().Now(),
@@ -343,10 +343,10 @@
   CreateDomainDiversityReporter();
   task_environment_.RunUntilIdle();
 
-  histograms().ExpectTotalCount("History.DomainCountQueryTime", 1);
-  histograms().ExpectUniqueSample("History.DomainCount1Day", 0, 1);
-  histograms().ExpectUniqueSample("History.DomainCount7Day", 0, 1);
-  histograms().ExpectUniqueSample("History.DomainCount28Day", 0, 1);
+  histograms().ExpectTotalCount("History.DomainCountQueryTime_V2", 1);
+  histograms().ExpectUniqueSample("History.DomainCount1Day_V2", 0, 1);
+  histograms().ExpectUniqueSample("History.DomainCount7Day_V2", 0, 1);
+  histograms().ExpectUniqueSample("History.DomainCount28Day_V2", 0, 1);
 
   history_service()->AddPage(GURL("http://www.google.com"), test_clock().Now(),
                              history::VisitSource::SOURCE_BROWSED);
@@ -363,9 +363,9 @@
   // This could happen when the last report occurred very early
   // on a day longer than 24 hours (e.g. the day on which daylight saving
   // time ends).
-  histograms().ExpectTotalCount("History.DomainCountQueryTime", 1);
-  histograms().ExpectUniqueSample("History.DomainCount1Day", 0, 1);
-  histograms().ExpectUniqueSample("History.DomainCount7Day", 0, 1);
-  histograms().ExpectUniqueSample("History.DomainCount28Day", 0, 1);
+  histograms().ExpectTotalCount("History.DomainCountQueryTime_V2", 1);
+  histograms().ExpectUniqueSample("History.DomainCount1Day_V2", 0, 1);
+  histograms().ExpectUniqueSample("History.DomainCount7Day_V2", 0, 1);
+  histograms().ExpectUniqueSample("History.DomainCount28Day_V2", 0, 1);
 }
 }  // namespace history
diff --git a/components/history_clusters/core/BUILD.gn b/components/history_clusters/core/BUILD.gn
index 01e2e92..cb9aa49 100644
--- a/components/history_clusters/core/BUILD.gn
+++ b/components/history_clusters/core/BUILD.gn
@@ -34,6 +34,7 @@
   if (build_with_on_device_clustering_backend) {
     sources += [
       "cluster_finalizer.h",
+      "cluster_metrics_utils.h",
       "cluster_processor.h",
       "clusterer.cc",
       "clusterer.h",
diff --git a/components/history_clusters/core/cluster_metrics_utils.h b/components/history_clusters/core/cluster_metrics_utils.h
new file mode 100644
index 0000000..9cb2e94
--- /dev/null
+++ b/components/history_clusters/core/cluster_metrics_utils.h
@@ -0,0 +1,40 @@
+// Copyright 2022 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_HISTORY_CLUSTERS_CORE_CLUSTER_METRICS_UTILS_H_
+#define COMPONENTS_HISTORY_CLUSTERS_CORE_CLUSTER_METRICS_UTILS_H_
+
+#include "base/metrics/histogram_functions.h"
+
+namespace history_clusters {
+
+// A helper object for recording metrics about whether a cluster was filtered
+// for a specified reason. The metric is emitted when the object falls out of
+// scope.
+class ScopedFilterClusterMetricsRecorder {
+ public:
+  explicit ScopedFilterClusterMetricsRecorder(
+      const std::string& filtered_reason)
+      : filtered_reason_(filtered_reason) {}
+  ~ScopedFilterClusterMetricsRecorder() {
+    base::UmaHistogramBoolean(
+        "History.Clusters.Backend.WasClusterFiltered." + filtered_reason_,
+        was_filtered_);
+  }
+
+  void set_was_filtered(bool was_filtered) { was_filtered_ = was_filtered; }
+
+ private:
+  // Whether the cluster associated with this metrics recordered was filtered or
+  // not.
+  bool was_filtered_ = false;
+  // The reason for why the cluster was filtered. Most be one of the items
+  // specified in the patterned histogram in
+  // tools/metrics/histograms/metadata/history/histograms.xml.
+  const std::string filtered_reason_;
+};
+
+}  // namespace history_clusters
+
+#endif  // COMPONENTS_HISTORY_CLUSTERS_CORE_CLUSTER_METRICS_UTILS_H_
diff --git a/components/history_clusters/core/content_visibility_cluster_finalizer.cc b/components/history_clusters/core/content_visibility_cluster_finalizer.cc
index b25c186..b5bb47a 100644
--- a/components/history_clusters/core/content_visibility_cluster_finalizer.cc
+++ b/components/history_clusters/core/content_visibility_cluster_finalizer.cc
@@ -4,7 +4,9 @@
 
 #include "components/history_clusters/core/content_visibility_cluster_finalizer.h"
 
+#include "components/history_clusters/core/cluster_metrics_utils.h"
 #include "components/history_clusters/core/on_device_clustering_features.h"
+#include "components/history_clusters/core/on_device_clustering_util.h"
 
 namespace history_clusters {
 
@@ -15,6 +17,7 @@
 
 void ContentVisibilityClusterFinalizer::FinalizeCluster(
     history::Cluster& cluster) {
+  ScopedFilterClusterMetricsRecorder metrics_recorder("VisibilityScore");
   for (const auto& visit : cluster.visits) {
     float visibility_score = visit.annotated_visit.content_annotations
                                  .model_annotations.visibility_score;
@@ -25,7 +28,7 @@
     }
     if (visibility_score < features::ContentVisibilityThreshold()) {
       cluster.should_show_on_prominent_ui_surfaces = false;
-      return;
+      metrics_recorder.set_was_filtered(true);
     }
   }
   // If we get here, this is a visible cluster from our point of view. If the
diff --git a/components/history_clusters/core/content_visibility_cluster_finalizer_unittest.cc b/components/history_clusters/core/content_visibility_cluster_finalizer_unittest.cc
index 7c70363..2b24997 100644
--- a/components/history_clusters/core/content_visibility_cluster_finalizer_unittest.cc
+++ b/components/history_clusters/core/content_visibility_cluster_finalizer_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/history_clusters/core/content_visibility_cluster_finalizer.h"
 
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "components/history_clusters/core/clustering_test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -54,6 +55,7 @@
 }
 
 TEST_F(ContentVisibilityClusterFinalizerTest, MultipleVisitsOneBelowThreshold) {
+  base::HistogramTester histogram_tester;
   history::ClusterVisit visit = testing::CreateClusterVisit(
       testing::CreateDefaultAnnotatedVisit(1, GURL("https://google.com/")));
   visit.annotated_visit.content_annotations.model_annotations.visibility_score =
@@ -68,9 +70,12 @@
   cluster.visits = {visit, visit2};
   FinalizeCluster(cluster);
   EXPECT_FALSE(cluster.should_show_on_prominent_ui_surfaces);
+  histogram_tester.ExpectUniqueSample(
+      "History.Clusters.Backend.WasClusterFiltered.VisibilityScore", true, 1);
 }
 
 TEST_F(ContentVisibilityClusterFinalizerTest, AllVisitsAboveThreshold) {
+  base::HistogramTester histogram_tester;
   history::ClusterVisit visit = testing::CreateClusterVisit(
       testing::CreateDefaultAnnotatedVisit(1, GURL("https://google.com/")));
   visit.annotated_visit.content_annotations.model_annotations.visibility_score =
@@ -85,6 +90,8 @@
   cluster.visits = {visit, visit2};
   FinalizeCluster(cluster);
   EXPECT_TRUE(cluster.should_show_on_prominent_ui_surfaces);
+  histogram_tester.ExpectUniqueSample(
+      "History.Clusters.Backend.WasClusterFiltered.VisibilityScore", false, 1);
 }
 
 }  // namespace
diff --git a/components/history_clusters/core/noisy_cluster_finalizer.cc b/components/history_clusters/core/noisy_cluster_finalizer.cc
index 3bc2dae..1940e0c 100644
--- a/components/history_clusters/core/noisy_cluster_finalizer.cc
+++ b/components/history_clusters/core/noisy_cluster_finalizer.cc
@@ -4,6 +4,7 @@
 
 #include "components/history_clusters/core/noisy_cluster_finalizer.h"
 
+#include "components/history_clusters/core/cluster_metrics_utils.h"
 #include "components/history_clusters/core/on_device_clustering_features.h"
 #include "components/history_clusters/core/on_device_clustering_util.h"
 
@@ -14,6 +15,7 @@
 
 void NoisyClusterFinalizer::FinalizeCluster(history::Cluster& cluster) {
   size_t interesting_visit_cnt = 0;
+  ScopedFilterClusterMetricsRecorder metrics_recorder("NoisyCluster");
   for (const auto& visit : cluster.visits) {
     if (!IsNoisyVisit(visit)) {
       interesting_visit_cnt += 1;
@@ -27,6 +29,7 @@
   // If we check all the visits in the cluster and all have high engagement
   // scores, then its probably not interesting so we can hide it.
   cluster.should_show_on_prominent_ui_surfaces = false;
+  metrics_recorder.set_was_filtered(true);
 }
 
 }  // namespace history_clusters
diff --git a/components/history_clusters/core/noisy_cluster_finalizer_unittest.cc b/components/history_clusters/core/noisy_cluster_finalizer_unittest.cc
index c0a387c2e..7adbbc17f 100644
--- a/components/history_clusters/core/noisy_cluster_finalizer_unittest.cc
+++ b/components/history_clusters/core/noisy_cluster_finalizer_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/history_clusters/core/noisy_cluster_finalizer.h"
 
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "components/history_clusters/core/clustering_test_utils.h"
@@ -54,6 +55,7 @@
 }
 
 TEST_F(NoisyClusterFinalizerTest, HideClusterWithOnlyOneInterestingVisit) {
+  base::HistogramTester histogram_tester;
   history::ClusterVisit visit = testing::CreateClusterVisit(
       testing::CreateDefaultAnnotatedVisit(1, GURL("https://bar.com/")));
   visit.engagement_score = 5.0;
@@ -67,9 +69,12 @@
   cluster.visits = {visit, visit2};
   FinalizeCluster(cluster);
   EXPECT_FALSE(cluster.should_show_on_prominent_ui_surfaces);
+  histogram_tester.ExpectUniqueSample(
+      "History.Clusters.Backend.WasClusterFiltered.NoisyCluster", true, 1);
 }
 
 TEST_F(NoisyClusterFinalizerTest, KeepClusterWitihAtLeastTwoInterestingVisits) {
+  base::HistogramTester histogram_tester;
   history::ClusterVisit visit = testing::CreateClusterVisit(
       testing::CreateDefaultAnnotatedVisit(1, GURL("https://bar.com/")));
   visit.engagement_score = 5.0;
@@ -87,6 +92,8 @@
   cluster.visits = {visit, visit2, visit3};
   FinalizeCluster(cluster);
   EXPECT_TRUE(cluster.should_show_on_prominent_ui_surfaces);
+  histogram_tester.ExpectUniqueSample(
+      "History.Clusters.Backend.WasClusterFiltered.NoisyCluster", false, 1);
 }
 
 }  // namespace
diff --git a/components/history_clusters/core/on_device_clustering_util.cc b/components/history_clusters/core/on_device_clustering_util.cc
index 6621c0b..6f2d9ef 100644
--- a/components/history_clusters/core/on_device_clustering_util.cc
+++ b/components/history_clusters/core/on_device_clustering_util.cc
@@ -43,6 +43,11 @@
     }
   }
 
+  // Merge over the model annotations (categories and entities) too.
+  canonical_visit.annotated_visit.content_annotations.model_annotations
+      .MergeFrom(duplicate_visit.annotated_visit.content_annotations
+                     .model_annotations);
+
   // Roll up the visit duration from the duplicate visit into the canonical
   // visit.
   canonical_visit.annotated_visit.visit_row.visit_duration +=
diff --git a/components/history_clusters/core/on_device_clustering_util_unittest.cc b/components/history_clusters/core/on_device_clustering_util_unittest.cc
index 00d7bc26..4cc8034b 100644
--- a/components/history_clusters/core/on_device_clustering_util_unittest.cc
+++ b/components/history_clusters/core/on_device_clustering_util_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/history_clusters/core/on_device_clustering_util.h"
 
 #include "base/test/task_environment.h"
+#include "components/history/core/browser/url_row.h"
 #include "components/history_clusters/core/clustering_test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -36,6 +37,13 @@
   duplicate_visit.annotated_visit.context_annotations
       .total_foreground_duration = base::Seconds(20);
 
+  duplicate_visit.annotated_visit.content_annotations.model_annotations
+      .visibility_score = 0.6;
+  duplicate_visit.annotated_visit.content_annotations.model_annotations
+      .categories.emplace_back("category1", 40);
+  duplicate_visit.annotated_visit.content_annotations.model_annotations.entities
+      .emplace_back("entity1", 20);
+
   history::ClusterVisit canonical_visit =
       testing::CreateClusterVisit(testing::CreateDefaultAnnotatedVisit(
           2, GURL("https://example.com/normalized")));
@@ -54,6 +62,10 @@
       false;
   canonical_visit.annotated_visit.context_annotations
       .total_foreground_duration = base::Seconds(20);
+  canonical_visit.annotated_visit.content_annotations.model_annotations
+      .visibility_score = 0.5;
+  canonical_visit.annotated_visit.content_annotations.model_annotations
+      .categories.emplace_back("category1", 20);
 
   MergeDuplicateVisitIntoCanonicalVisit(duplicate_visit, canonical_visit);
   EXPECT_TRUE(
@@ -76,6 +88,38 @@
   EXPECT_EQ(canonical_visit.annotated_visit.context_annotations
                 .total_foreground_duration,
             base::Seconds(20 * 2));
+
+  EXPECT_FLOAT_EQ(canonical_visit.annotated_visit.content_annotations
+                      .model_annotations.visibility_score,
+                  0.5);
+
+  ASSERT_EQ(canonical_visit.annotated_visit.content_annotations
+                .model_annotations.categories.size(),
+            1U);
+  EXPECT_EQ(
+      canonical_visit.annotated_visit.content_annotations.model_annotations
+          .categories[0]
+          .id,
+      "category1");
+  EXPECT_EQ(
+      canonical_visit.annotated_visit.content_annotations.model_annotations
+          .categories[0]
+          .weight,
+      40);
+
+  ASSERT_EQ(canonical_visit.annotated_visit.content_annotations
+                .model_annotations.entities.size(),
+            1U);
+  EXPECT_EQ(
+      canonical_visit.annotated_visit.content_annotations.model_annotations
+          .entities[0]
+          .id,
+      "entity1");
+  EXPECT_EQ(
+      canonical_visit.annotated_visit.content_annotations.model_annotations
+          .entities[0]
+          .weight,
+      20);
 }
 
 TEST_F(OnDeviceClusteringUtilTest, CalculateAllDuplicateVisitsForCluster) {
diff --git a/components/history_clusters/core/single_visit_cluster_finalizer.cc b/components/history_clusters/core/single_visit_cluster_finalizer.cc
index f7a8e8e..f40849e6 100644
--- a/components/history_clusters/core/single_visit_cluster_finalizer.cc
+++ b/components/history_clusters/core/single_visit_cluster_finalizer.cc
@@ -4,6 +4,7 @@
 
 #include "components/history_clusters/core/single_visit_cluster_finalizer.h"
 
+#include "components/history_clusters/core/cluster_metrics_utils.h"
 #include "components/history_clusters/core/on_device_clustering_util.h"
 
 namespace history_clusters {
@@ -12,8 +13,10 @@
 SingleVisitClusterFinalizer::~SingleVisitClusterFinalizer() = default;
 
 void SingleVisitClusterFinalizer::FinalizeCluster(history::Cluster& cluster) {
+  ScopedFilterClusterMetricsRecorder metrics_recorder("SingleVisit");
   if (cluster.visits.size() <= 1) {
     cluster.should_show_on_prominent_ui_surfaces = false;
+    metrics_recorder.set_was_filtered(true);
     return;
   }
 
@@ -35,6 +38,7 @@
   // If we get here, then we have only seen at most 1 canonical visit. Do not
   // show this cluster on prominent UI surfaces.
   cluster.should_show_on_prominent_ui_surfaces = false;
+  metrics_recorder.set_was_filtered(true);
 }
 
 }  // namespace history_clusters
diff --git a/components/history_clusters/core/single_visit_cluster_finalizer_unittest.cc b/components/history_clusters/core/single_visit_cluster_finalizer_unittest.cc
index 252008b..ee42c58 100644
--- a/components/history_clusters/core/single_visit_cluster_finalizer_unittest.cc
+++ b/components/history_clusters/core/single_visit_cluster_finalizer_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/history_clusters/core/single_visit_cluster_finalizer.h"
 
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "components/history_clusters/core/clustering_test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -55,6 +56,7 @@
 }
 
 TEST_F(SingleVisitClusterFinalizerTest, MultipleVisits) {
+  base::HistogramTester histogram_tester;
   history::ClusterVisit visit = testing::CreateClusterVisit(
       testing::CreateDefaultAnnotatedVisit(1, GURL("https://google.com/")));
   history::ClusterVisit visit2 = testing::CreateClusterVisit(
@@ -64,10 +66,13 @@
   cluster.visits = {visit, visit2};
   FinalizeCluster(cluster);
   EXPECT_TRUE(cluster.should_show_on_prominent_ui_surfaces);
+  histogram_tester.ExpectUniqueSample(
+      "History.Clusters.Backend.WasClusterFiltered.SingleVisit", false, 1);
 }
 
 TEST_F(SingleVisitClusterFinalizerTest,
        MultipleVisitsButDuplicatesOfCanonical) {
+  base::HistogramTester histogram_tester;
   history::ClusterVisit visit = testing::CreateClusterVisit(
       testing::CreateDefaultAnnotatedVisit(1, GURL("https://google.com/")));
   history::ClusterVisit visit2 = testing::CreateClusterVisit(
@@ -78,6 +83,8 @@
   cluster.visits = {visit, visit2};
   FinalizeCluster(cluster);
   EXPECT_FALSE(cluster.should_show_on_prominent_ui_surfaces);
+  histogram_tester.ExpectUniqueSample(
+      "History.Clusters.Backend.WasClusterFiltered.SingleVisit", true, 1);
 }
 
 }  // namespace
diff --git a/components/invalidation/impl/fcm_invalidation_service_base.cc b/components/invalidation/impl/fcm_invalidation_service_base.cc
index c5626d0..911ba06 100644
--- a/components/invalidation/impl/fcm_invalidation_service_base.cc
+++ b/components/invalidation/impl/fcm_invalidation_service_base.cc
@@ -29,7 +29,8 @@
   if (!prefs->HasPrefPath(prefs::kFCMInvalidationClientIDCacheDeprecated)) {
     return;
   }
-  DictionaryPrefUpdate update(prefs, prefs::kInvalidationClientIDCache);
+  DictionaryPrefUpdateDeprecated update(prefs,
+                                        prefs::kInvalidationClientIDCache);
   update->SetString(
       sender_id,
       prefs->GetString(prefs::kFCMInvalidationClientIDCacheDeprecated));
@@ -275,7 +276,8 @@
   // source of truth, and are responsible for ensuring that the deletion
   // actually happens.
   client_id_.clear();
-  DictionaryPrefUpdate update(pref_service_, prefs::kInvalidationClientIDCache);
+  DictionaryPrefUpdateDeprecated update(pref_service_,
+                                        prefs::kInvalidationClientIDCache);
   update->RemoveKey(sender_id_);
 
   // Also let the registrar (and its observers) know that the instance ID is
@@ -294,8 +296,8 @@
   diagnostic_info_.instance_id_received = base::Time::Now();
   if (client_id_ != instance_id) {
     client_id_ = instance_id;
-    DictionaryPrefUpdate update(pref_service_,
-                                prefs::kInvalidationClientIDCache);
+    DictionaryPrefUpdateDeprecated update(pref_service_,
+                                          prefs::kInvalidationClientIDCache);
     update->SetStringKey(sender_id_, instance_id);
     invalidator_registrar_.UpdateInvalidatorInstanceId(instance_id);
   }
diff --git a/components/invalidation/impl/fcm_invalidation_service_unittest.cc b/components/invalidation/impl/fcm_invalidation_service_unittest.cc
index 1f53441c..982a755 100644
--- a/components/invalidation/impl/fcm_invalidation_service_unittest.cc
+++ b/components/invalidation/impl/fcm_invalidation_service_unittest.cc
@@ -209,8 +209,8 @@
 
   // Set up a cached InstanceID aka client ID stored in prefs.
   {
-    DictionaryPrefUpdate update(&delegate->pref_service_,
-                                prefs::kInvalidationClientIDCache);
+    DictionaryPrefUpdateDeprecated update(&delegate->pref_service_,
+                                          prefs::kInvalidationClientIDCache);
     update->SetStringKey(kSenderId, "InstanceIDFromPrefs");
   }
 
diff --git a/components/invalidation/impl/invalidator_registrar_with_memory.cc b/components/invalidation/impl/invalidator_registrar_with_memory.cc
index 4637780..cb98270 100644
--- a/components/invalidation/impl/invalidator_registrar_with_memory.cc
+++ b/components/invalidation/impl/invalidator_registrar_with_memory.cc
@@ -36,7 +36,7 @@
     return;
   }
   {
-    DictionaryPrefUpdate update(prefs, kTopicsToHandler);
+    DictionaryPrefUpdateDeprecated update(prefs, kTopicsToHandler);
     update->SetKey(sender_id, old_prefs->Clone());
   }
   prefs->ClearPref(kTopicsToHandlerDeprecated);
@@ -82,7 +82,7 @@
   const base::Value* pref_data =
       prefs_->Get(kTopicsToHandler)->FindDictKey(sender_id_);
   if (!pref_data) {
-    DictionaryPrefUpdate update(prefs_, kTopicsToHandler);
+    DictionaryPrefUpdateDeprecated update(prefs_, kTopicsToHandler);
     update->SetKey(sender_id_, base::DictionaryValue());
     return;
   }
@@ -148,7 +148,7 @@
     registered_handler_to_topics_map_[handler] = topics;
   }
 
-  DictionaryPrefUpdate update(prefs_, kTopicsToHandler);
+  DictionaryPrefUpdateDeprecated update(prefs_, kTopicsToHandler);
   base::Value* pref_data = update->FindDictKey(sender_id_);
   // TODO(crbug.com/1020117): This does currently *not* remove subscribed
   // topics which are not registered, but it almost certainly should. It
diff --git a/components/invalidation/impl/per_user_topic_subscription_manager.cc b/components/invalidation/impl/per_user_topic_subscription_manager.cc
index 04a562d3..9dcf7cf 100644
--- a/components/invalidation/impl/per_user_topic_subscription_manager.cc
+++ b/components/invalidation/impl/per_user_topic_subscription_manager.cc
@@ -84,10 +84,11 @@
     false,
 };
 
-class PerProjectDictionaryPrefUpdate {
+class PerProjectDictionaryPrefUpdateDeprecated {
  public:
-  explicit PerProjectDictionaryPrefUpdate(PrefService* prefs,
-                                          const std::string& project_id)
+  explicit PerProjectDictionaryPrefUpdateDeprecated(
+      PrefService* prefs,
+      const std::string& project_id)
       : update_(prefs, kTypeSubscribedForInvalidations) {
     per_sender_pref_ = update_->FindDictKey(project_id);
     if (!per_sender_pref_) {
@@ -102,7 +103,7 @@
   base::Value* operator->() { return per_sender_pref_; }
 
  private:
-  DictionaryPrefUpdate update_;
+  DictionaryPrefUpdateDeprecated update_;
   raw_ptr<base::Value> per_sender_pref_;
 };
 
@@ -112,7 +113,8 @@
     return;
   }
   {
-    DictionaryPrefUpdate token_update(prefs, kActiveRegistrationTokens);
+    DictionaryPrefUpdateDeprecated token_update(prefs,
+                                                kActiveRegistrationTokens);
     token_update->SetString(
         project_id, prefs->GetString(kActiveRegistrationTokenDeprecated));
   }
@@ -120,7 +122,7 @@
   auto* old_subscriptions =
       prefs->GetDictionary(kTypeSubscribedForInvalidationsDeprecated);
   {
-    PerProjectDictionaryPrefUpdate update(prefs, project_id);
+    PerProjectDictionaryPrefUpdateDeprecated update(prefs, project_id);
     *update = old_subscriptions->Clone();
   }
   prefs->ClearPref(kActiveRegistrationTokenDeprecated);
@@ -240,7 +242,7 @@
   if (migrate_prefs_) {
     MigratePrefs(pref_service_, project_id_);
   }
-  PerProjectDictionaryPrefUpdate update(pref_service_, project_id_);
+  PerProjectDictionaryPrefUpdateDeprecated update(pref_service_, project_id_);
   if (update->DictEmpty()) {
     return;
   }
@@ -320,7 +322,8 @@
       it = topic_to_private_topic_.erase(it);
       // The decision to unsubscribe from invalidations for |topic| was
       // made, the preferences should be cleaned up immediately.
-      PerProjectDictionaryPrefUpdate update(pref_service_, project_id_);
+      PerProjectDictionaryPrefUpdateDeprecated update(pref_service_,
+                                                      project_id_);
       update->RemoveKey(topic);
     } else {
       // Topic is still wanted, nothing to do.
@@ -403,7 +406,8 @@
     // unsubscription, we've already updated the prefs when scheduling the
     // request).
     {
-      PerProjectDictionaryPrefUpdate update(pref_service_, project_id_);
+      PerProjectDictionaryPrefUpdateDeprecated update(pref_service_,
+                                                      project_id_);
       update->SetKey(topic, base::Value(private_topic_name));
       topic_to_private_topic_[topic] = private_topic_name;
       private_topic_to_topic_[private_topic_name] = topic;
@@ -568,7 +572,8 @@
 PerUserTopicSubscriptionManager::TokenStateOnSubscriptionRequest
 PerUserTopicSubscriptionManager::DropAllSavedSubscriptionsOnTokenChangeImpl() {
   {
-    DictionaryPrefUpdate token_update(pref_service_, kActiveRegistrationTokens);
+    DictionaryPrefUpdateDeprecated token_update(pref_service_,
+                                                kActiveRegistrationTokens);
     std::string previous_token;
     token_update->GetString(project_id_, &previous_token);
     if (previous_token == instance_id_token_) {
@@ -588,7 +593,7 @@
   // subscriptions since they won't be valid anymore. (No need to send
   // unsubscribe requests - if the token was revoked, the server will drop the
   // subscriptions anyway.)
-  PerProjectDictionaryPrefUpdate update(pref_service_, project_id_);
+  PerProjectDictionaryPrefUpdateDeprecated update(pref_service_, project_id_);
   *update = base::Value(base::Value::Type::DICTIONARY);
   topic_to_private_topic_.clear();
   private_topic_to_topic_.clear();
diff --git a/components/language/content/browser/ulp_language_code_locator/ulp_language_code_locator.cc b/components/language/content/browser/ulp_language_code_locator/ulp_language_code_locator.cc
index 73d78261..b0ed1b6 100644
--- a/components/language/content/browser/ulp_language_code_locator/ulp_language_code_locator.cc
+++ b/components/language/content/browser/ulp_language_code_locator/ulp_language_code_locator.cc
@@ -54,7 +54,7 @@
   S2CellId cell(S2LatLng::FromDegrees(latitude, longitude));
   std::vector<std::string> languages;
 
-  ListPrefUpdate update(prefs_, kCachedGeoLanguagesPref);
+  ListPrefUpdateDeprecated update(prefs_, kCachedGeoLanguagesPref);
   base::ListValue* celllangs_cached = update.Get();
   for (size_t index = 0; index < serialized_langtrees_.size(); index++) {
     std::string language;
diff --git a/components/language/core/browser/url_language_histogram.cc b/components/language/core/browser/url_language_histogram.cc
index 7663776..ad732b1 100644
--- a/components/language/core/browser/url_language_histogram.cc
+++ b/components/language/core/browser/url_language_histogram.cc
@@ -117,7 +117,8 @@
 }
 
 void UrlLanguageHistogram::OnPageVisited(const std::string& language_code) {
-  DictionaryPrefUpdate update(pref_service_, kUrlLanguageHistogramCounters);
+  DictionaryPrefUpdateDeprecated update(pref_service_,
+                                        kUrlLanguageHistogramCounters);
   base::Value* dict = update.Get();
   // If the key |language_code| does not exist, |counter_value| stays 0.
   int counter_value = dict->FindIntKey(language_code).value_or(0);
diff --git a/components/language/core/common/language_experiments.cc b/components/language/core/common/language_experiments.cc
index 2da0269..db8028c 100644
--- a/components/language/core/common/language_experiments.cc
+++ b/components/language/core/common/language_experiments.cc
@@ -27,8 +27,8 @@
                                             base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kNotifySyncOnLanguageDetermined{
     "NotifySyncOnLanguageDetermined", base::FEATURE_ENABLED_BY_DEFAULT};
-const base::Feature kDetailedLanguageSettings{
-    "DetailedLanguageSettings", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kDetailedLanguageSettings{"DetailedLanguageSettings",
+                                              base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kDesktopRestructuredLanguageSettings{
     "DesktopRestructuredLanguageSettings", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kDesktopDetailedLanguageSettings{
diff --git a/components/language/core/common/locale_util_unittest.cc b/components/language/core/common/locale_util_unittest.cc
index c9fb3cb..4658cca 100644
--- a/components/language/core/common/locale_util_unittest.cc
+++ b/components/language/core/common/locale_util_unittest.cc
@@ -126,10 +126,6 @@
   is_ui = ConvertToActualUILocale(&locale);
   EXPECT_FALSE(is_ui);
 
-  locale = "af";  // Afrikaans
-  is_ui = ConvertToActualUILocale(&locale);
-  EXPECT_FALSE(is_ui);
-
   locale = "ga";  // Irish
   is_ui = ConvertToActualUILocale(&locale);
   EXPECT_FALSE(is_ui);
@@ -137,10 +133,6 @@
   locale = "ky";  // Kyrgyz
   is_ui = ConvertToActualUILocale(&locale);
   EXPECT_FALSE(is_ui);
-
-  locale = "zu";  // Zulu
-  is_ui = ConvertToActualUILocale(&locale);
-  EXPECT_FALSE(is_ui);
 #endif
 }
 
diff --git a/components/messages/android/internal/BUILD.gn b/components/messages/android/internal/BUILD.gn
index 3d04eb0..c6c7b83d 100644
--- a/components/messages/android/internal/BUILD.gn
+++ b/components/messages/android/internal/BUILD.gn
@@ -95,8 +95,10 @@
 
 android_resources("java_resources") {
   sources = [
+    "java/res/drawable-v24/message_bg_tinted.xml",
     "java/res/drawable/message_bg_tinted.xml",
     "java/res/layout/message_banner_view.xml",
+    "java/res/values-night/dimens.xml",
     "java/res/values/dimens.xml",
   ]
   deps = [
diff --git a/components/messages/android/internal/java/res/drawable-v24/message_bg_tinted.xml b/components/messages/android/internal/java/res/drawable-v24/message_bg_tinted.xml
new file mode 100644
index 0000000..8601c314
--- /dev/null
+++ b/components/messages/android/internal/java/res/drawable-v24/message_bg_tinted.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 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. -->
+
+<org.chromium.components.browser_ui.widget.SurfaceColorDrawable
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:shape="rectangle"
+    app:surfaceElevation="@dimen/message_bg_tinted_elev">
+  <corners android:radius="@dimen/message_banner_radius"/>
+</org.chromium.components.browser_ui.widget.SurfaceColorDrawable>
diff --git a/components/messages/android/internal/java/res/layout/message_banner_view.xml b/components/messages/android/internal/java/res/layout/message_banner_view.xml
index b168483..189f45a3 100644
--- a/components/messages/android/internal/java/res/layout/message_banner_view.xml
+++ b/components/messages/android/internal/java/res/layout/message_banner_view.xml
@@ -59,7 +59,7 @@
     <!--  Content description is set programmatically according to secondary button icon. -->
     <org.chromium.components.browser_ui.widget.listmenu.ListMenuButton
         android:id="@+id/message_secondary_button"
-        app:tint="@color/default_icon_color_secondary"
+        app:tint="@macro/default_icon_color_secondary"
         app:menuVerticalOverlapAnchor="false"
         android:visibility="gone"
         android:contentDescription="@null"
diff --git a/components/messages/android/internal/java/res/values-night/dimens.xml b/components/messages/android/internal/java/res/values-night/dimens.xml
new file mode 100644
index 0000000..e18fbcd1
--- /dev/null
+++ b/components/messages/android/internal/java/res/values-night/dimens.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 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>
+  <dimen name="message_bg_tinted_elev">@dimen/default_elevation_3</dimen>
+</resources>
diff --git a/components/messages/android/internal/java/res/values/dimens.xml b/components/messages/android/internal/java/res/values/dimens.xml
index 5b748f5e9..19b6dd3 100644
--- a/components/messages/android/internal/java/res/values/dimens.xml
+++ b/components/messages/android/internal/java/res/values/dimens.xml
@@ -22,5 +22,6 @@
     <dimen name="message_horizontal_hide_threshold">24dp</dimen>
     <dimen name="message_max_horizontal_translation">360dp</dimen>
     <dimen name="message_max_width">380dp</dimen>
+    <dimen name="message_bg_tinted_elev">@dimen/default_elevation_0</dimen>
 
 </resources>
diff --git a/components/metrics/clean_exit_beacon.cc b/components/metrics/clean_exit_beacon.cc
index 51839edc..2aae373 100644
--- a/components/metrics/clean_exit_beacon.cc
+++ b/components/metrics/clean_exit_beacon.cc
@@ -214,12 +214,14 @@
 // only some channels. If assigned to an experiment group, returns the name of
 // the group name, e.g. "Control"; otherwise, returns the empty string.
 std::string SetUpExtendedSafeModeTrial(version_info::Channel channel) {
+#if !defined(OS_IOS)
   if (channel != version_info::Channel::UNKNOWN &&
       channel != version_info::Channel::CANARY &&
       channel != version_info::Channel::DEV &&
       channel != version_info::Channel::BETA) {
     return std::string();
   }
+#endif  // !defined(OS_IOS)
 
   int default_group;
   scoped_refptr<base::FieldTrial> trial(
@@ -227,8 +229,9 @@
           kExtendedSafeModeTrial, 100, kDefaultGroup,
           base::FieldTrial::ONE_TIME_RANDOMIZED, &default_group));
 
-  trial->AppendGroup(kControlGroup, 50);
-  trial->AppendGroup(kSignalAndWriteViaFileUtilGroup, 50);
+  int group_probability = channel == version_info::Channel::STABLE ? 1 : 50;
+  trial->AppendGroup(kControlGroup, group_probability);
+  trial->AppendGroup(kSignalAndWriteViaFileUtilGroup, group_probability);
   return trial->group_name();
 }
 
diff --git a/components/metrics/data_use_tracker.cc b/components/metrics/data_use_tracker.cc
index 11f2041b..488704b 100644
--- a/components/metrics/data_use_tracker.cc
+++ b/components/metrics/data_use_tracker.cc
@@ -110,7 +110,7 @@
                                      int message_size) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  DictionaryPrefUpdate pref_updater(local_state_, pref_name);
+  DictionaryPrefUpdateDeprecated pref_updater(local_state_, pref_name);
   std::string todays_key = GetCurrentMeasurementDateAsString();
 
   const base::Value* user_pref_dict = local_state_->GetDictionary(pref_name);
diff --git a/components/metrics/file_metrics_provider.cc b/components/metrics/file_metrics_provider.cc
index a153ba6..095490bb 100644
--- a/components/metrics/file_metrics_provider.cc
+++ b/components/metrics/file_metrics_provider.cc
@@ -683,8 +683,8 @@
 }
 
 void FileMetricsProvider::AppendToSamplesCountPref(size_t samples_count) {
-  ListPrefUpdate update(pref_service_,
-                        metrics::prefs::kMetricsFileMetricsMetadata);
+  ListPrefUpdateDeprecated update(pref_service_,
+                                  metrics::prefs::kMetricsFileMetricsMetadata);
   update->Append(static_cast<int>(samples_count));
 }
 
@@ -924,8 +924,8 @@
     return false;
   }
 
-  ListPrefUpdate list_value(pref_service_,
-                            metrics::prefs::kMetricsFileMetricsMetadata);
+  ListPrefUpdateDeprecated list_value(
+      pref_service_, metrics::prefs::kMetricsFileMetricsMetadata);
   if (list_value->GetList().empty())
     return false;
 
diff --git a/components/metrics/metrics_state_manager.h b/components/metrics/metrics_state_manager.h
index bc23145..1d23edca 100644
--- a/components/metrics/metrics_state_manager.h
+++ b/components/metrics/metrics_state_manager.h
@@ -37,10 +37,14 @@
 // which the browser process starts; does some work, e.g. servicing a sync; and
 // ends without ever becoming visible. Note that the point in startup at which
 // this value is determined is likely before the UI is visible.
+//
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
 enum class StartupVisibility {
   kUnknown = 0,
   kBackground = 1,
   kForeground = 2,
+  kMaxValue = kForeground,
 };
 
 // Denotes the type of EntropyProvider to use for one-time randomization.
diff --git a/components/metrics/unsent_log_store.cc b/components/metrics/unsent_log_store.cc
index 0ae02d1..6d5c1078 100644
--- a/components/metrics/unsent_log_store.cc
+++ b/components/metrics/unsent_log_store.cc
@@ -178,7 +178,7 @@
 }
 
 void UnsentLogStore::TrimAndPersistUnsentLogs() {
-  ListPrefUpdate update(local_state_, log_data_pref_name_);
+  ListPrefUpdateDeprecated update(local_state_, log_data_pref_name_);
   TrimLogs();
   WriteLogsToPrefList(update.Get());
 }
@@ -373,7 +373,7 @@
   if (metadata_pref_name_ == nullptr)
     return;
 
-  DictionaryPrefUpdate update(local_state_, metadata_pref_name_);
+  DictionaryPrefUpdateDeprecated update(local_state_, metadata_pref_name_);
   base::DictionaryValue* pref_data = update.Get();
   pref_data->SetKey(kLogUnsentCountKey, base::Value(unsent_samples_count));
   pref_data->SetKey(kLogSentCountKey, base::Value(sent_samples_count));
diff --git a/components/offline_pages/core/offline_page_feature.cc b/components/offline_pages/core/offline_page_feature.cc
index d1a9a4fc..21b6898 100644
--- a/components/offline_pages/core/offline_page_feature.cc
+++ b/components/offline_pages/core/offline_page_feature.cc
@@ -27,7 +27,7 @@
     "OfflinePagesLivePageSharing", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kPrefetchingOfflinePagesFeature{
-    "OfflinePagesPrefetching", base::FEATURE_ENABLED_BY_DEFAULT};
+    "OfflinePagesPrefetching", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kOfflinePagesCTV2Feature{"OfflinePagesCTV2",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/omnibox/browser/omnibox_prefs.cc b/components/omnibox/browser/omnibox_prefs.cc
index f2fb007b0..a1fda49 100644
--- a/components/omnibox/browser/omnibox_prefs.cc
+++ b/components/omnibox/browser/omnibox_prefs.cc
@@ -82,7 +82,7 @@
                                   SuggestionGroupVisibility new_value) {
   DCHECK(prefs);
 
-  DictionaryPrefUpdate update(prefs, kSuggestionGroupVisibility);
+  DictionaryPrefUpdateDeprecated update(prefs, kSuggestionGroupVisibility);
   update->SetIntKey(base::NumberToString(suggestion_group_id), new_value);
 
   base::SparseHistogram::FactoryGet(
diff --git a/components/omnibox/resources/omnibox_pedal_synonyms.grd b/components/omnibox/resources/omnibox_pedal_synonyms.grd
index 7dfc458..ee724ea 100644
--- a/components/omnibox/resources/omnibox_pedal_synonyms.grd
+++ b/components/omnibox/resources/omnibox_pedal_synonyms.grd
@@ -38,7 +38,9 @@
       <output filename="omnibox_pedal_synonyms_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="omnibox_pedal_synonyms_af.pak" type="data_package" lang="af" />
       <output filename="omnibox_pedal_synonyms_is.pak" type="data_package" lang="is" />
+      <output filename="omnibox_pedal_synonyms_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="omnibox_pedal_synonyms_am.pak" type="data_package" lang="am" />
     <output filename="omnibox_pedal_synonyms_ar.pak" type="data_package" lang="ar" />
diff --git a/components/omnibox/resources/omnibox_resources.grd b/components/omnibox/resources/omnibox_resources.grd
index dfb8eb33..86bf4e1 100644
--- a/components/omnibox/resources/omnibox_resources.grd
+++ b/components/omnibox/resources/omnibox_resources.grd
@@ -39,7 +39,9 @@
       <output filename="omnibox_resources_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="omnibox_resources_af.pak" type="data_package" lang="af" />
       <output filename="omnibox_resources_is.pak" type="data_package" lang="is" />
+      <output filename="omnibox_resources_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="omnibox_resources_am.pak" type="data_package" lang="am" />
     <output filename="omnibox_resources_ar.pak" type="data_package" lang="ar" />
diff --git a/components/optimization_guide/content/browser/BUILD.gn b/components/optimization_guide/content/browser/BUILD.gn
index d7138ce..0d26cc4 100644
--- a/components/optimization_guide/content/browser/BUILD.gn
+++ b/components/optimization_guide/content/browser/BUILD.gn
@@ -83,8 +83,7 @@
     "page_text_dump_result_unittest.cc",
     "page_text_observer_unittest.cc",
   ]
-  # crbug.com/1279884 Flaky on CrOS
-  if (!is_chromeos && build_with_tflite_lib) {
+  if (build_with_tflite_lib) {
     sources += [ "page_content_annotations_model_manager_unittest.cc" ]
   }
   deps = [
diff --git a/components/optimization_guide/core/hints_fetcher.cc b/components/optimization_guide/core/hints_fetcher.cc
index 273e1ebf..108aa97e 100644
--- a/components/optimization_guide/core/hints_fetcher.cc
+++ b/components/optimization_guide/core/hints_fetcher.cc
@@ -109,7 +109,7 @@
 
 // static
 void HintsFetcher::ClearHostsSuccessfullyFetched(PrefService* pref_service) {
-  DictionaryPrefUpdate hosts_fetched_list(
+  DictionaryPrefUpdateDeprecated hosts_fetched_list(
       pref_service, prefs::kHintsFetcherHostsSuccessfullyFetched);
   hosts_fetched_list->DictClear();
 }
@@ -134,7 +134,7 @@
     return false;
   }
 
-  DictionaryPrefUpdate hosts_fetched(
+  DictionaryPrefUpdateDeprecated hosts_fetched(
       pref_service, prefs::kHintsFetcherHostsSuccessfullyFetched);
   absl::optional<double> value =
       hosts_fetched->FindDoubleKey(HashHostForDictionary(host));
@@ -149,7 +149,7 @@
 // static
 void HintsFetcher::ClearSingleFetchedHost(PrefService* pref_service,
                                           const std::string& host) {
-  DictionaryPrefUpdate hosts_fetched_list(
+  DictionaryPrefUpdateDeprecated hosts_fetched_list(
       pref_service, prefs::kHintsFetcherHostsSuccessfullyFetched);
   hosts_fetched_list->RemovePath(HashHostForDictionary(host));
 }
@@ -158,7 +158,7 @@
 void HintsFetcher::AddFetchedHostForTesting(PrefService* pref_service,
                                             const std::string& host,
                                             base::Time time) {
-  DictionaryPrefUpdate hosts_fetched_list(
+  DictionaryPrefUpdateDeprecated hosts_fetched_list(
       pref_service, prefs::kHintsFetcherHostsSuccessfullyFetched);
   hosts_fetched_list->SetDoubleKey(
       HashHostForDictionary(host),
@@ -363,7 +363,7 @@
     return;
   }
 
-  DictionaryPrefUpdate hosts_fetched_list(
+  DictionaryPrefUpdateDeprecated hosts_fetched_list(
       pref_service_, prefs::kHintsFetcherHostsSuccessfullyFetched);
 
   // Remove any expired hosts.
@@ -454,7 +454,7 @@
     const std::vector<std::string>& hosts) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  DictionaryPrefUpdate hosts_fetched(
+  DictionaryPrefUpdateDeprecated hosts_fetched(
       pref_service_, prefs::kHintsFetcherHostsSuccessfullyFetched);
 
   std::vector<std::string> target_hosts;
diff --git a/components/optimization_guide/core/hints_fetcher_unittest.cc b/components/optimization_guide/core/hints_fetcher_unittest.cc
index 7605284..42da1d70 100644
--- a/components/optimization_guide/core/hints_fetcher_unittest.cc
+++ b/components/optimization_guide/core/hints_fetcher_unittest.cc
@@ -90,7 +90,7 @@
   // expire at |host_invalid_time|.
   void SeedCoveredHosts(const std::vector<std::string>& hosts,
                         base::Time host_invalid_time) {
-    DictionaryPrefUpdate hosts_fetched(
+    DictionaryPrefUpdateDeprecated hosts_fetched(
         pref_service(), prefs::kHintsFetcherHostsSuccessfullyFetched);
 
     for (const std::string& host : hosts) {
@@ -520,7 +520,7 @@
 
   // The first pair of hosts should be removed from the dictionary
   // pref as they have expired.
-  DictionaryPrefUpdate hosts_fetched(
+  DictionaryPrefUpdateDeprecated hosts_fetched(
       pref_service(), prefs::kHintsFetcherHostsSuccessfullyFetched);
   EXPECT_EQ(2u, hosts_fetched->DictSize());
 
@@ -535,7 +535,7 @@
   base::Time host_invalid_time = base::Time::Now() + base::Hours(1);
 
   SeedCoveredHosts(hosts, host_invalid_time);
-  DictionaryPrefUpdate hosts_fetched(
+  DictionaryPrefUpdateDeprecated hosts_fetched(
       pref_service(), prefs::kHintsFetcherHostsSuccessfullyFetched);
   EXPECT_EQ(2u, hosts_fetched->DictSize());
 
@@ -563,7 +563,7 @@
 
   // The two expired hosts should be removed from the dictionary pref as they
   // have expired.
-  DictionaryPrefUpdate hosts_fetched(
+  DictionaryPrefUpdateDeprecated hosts_fetched(
       pref_service(), prefs::kHintsFetcherHostsSuccessfullyFetched);
   EXPECT_EQ(2u, hosts_fetched->DictSize());
 
@@ -597,7 +597,7 @@
   EXPECT_TRUE(hints_fetched());
 
   // Navigations to both the extra hosts should be recorded.
-  DictionaryPrefUpdate hosts_fetched(
+  DictionaryPrefUpdateDeprecated hosts_fetched(
       pref_service(), prefs::kHintsFetcherHostsSuccessfullyFetched);
   EXPECT_EQ(200u, hosts_fetched->DictSize());
 
@@ -633,7 +633,7 @@
   if (!ShouldPersistHintsToDisk())
     return;
 
-  DictionaryPrefUpdate hosts_fetched(
+  DictionaryPrefUpdateDeprecated hosts_fetched(
       pref_service(), prefs::kHintsFetcherHostsSuccessfullyFetched);
   EXPECT_EQ(max_hosts_in_fetch_request, hosts_fetched->DictSize());
   EXPECT_EQ(all_hosts.size(), max_hosts_in_fetch_request + 5);
diff --git a/components/optimization_guide/core/hints_manager.cc b/components/optimization_guide/core/hints_manager.cc
index 7ae4f3a..786a5c4 100644
--- a/components/optimization_guide/core/hints_manager.cc
+++ b/components/optimization_guide/core/hints_manager.cc
@@ -885,7 +885,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   bool should_load_new_optimization_filter = false;
 
-  DictionaryPrefUpdate previously_registered_opt_types(
+  DictionaryPrefUpdateDeprecated previously_registered_opt_types(
       pref_service_, prefs::kPreviouslyRegisteredOptimizationTypes);
   for (const auto optimization_type : optimization_types) {
     if (optimization_type == proto::TYPE_UNSPECIFIED)
diff --git a/components/page_info/android/BUILD.gn b/components/page_info/android/BUILD.gn
index ed3d20d..d24c868 100644
--- a/components/page_info/android/BUILD.gn
+++ b/components/page_info/android/BUILD.gn
@@ -33,6 +33,7 @@
 
 android_resources("java_resources") {
   sources = [
+    "java/res/drawable-v24/page_info_bg.xml",
     "java/res/drawable/page_info_bg.xml",
     "java/res/layout/connection_info.xml",
     "java/res/layout/page_info.xml",
@@ -40,6 +41,7 @@
     "java/res/layout/page_info_row.xml",
     "java/res/layout/page_info_summary.xml",
     "java/res/layout/page_zoom_view.xml",
+    "java/res/values-night/dimens.xml",
     "java/res/values/colors.xml",
     "java/res/values/dimens.xml",
     "java/res/values/ids.xml",
diff --git a/components/page_info/android/java/res/drawable-v24/page_info_bg.xml b/components/page_info/android/java/res/drawable-v24/page_info_bg.xml
new file mode 100644
index 0000000..f37e0c3
--- /dev/null
+++ b/components/page_info/android/java/res/drawable-v24/page_info_bg.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 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. -->
+
+<org.chromium.components.browser_ui.widget.SurfaceColorDrawable
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:shape="rectangle"
+    app:surfaceElevation="@dimen/page_info_bg_elev">
+  <corners
+      android:bottomLeftRadius="16dp"
+      android:bottomRightRadius="16dp"
+      android:topLeftRadius="0dp"
+      android:topRightRadius="0dp" />
+</org.chromium.components.browser_ui.widget.SurfaceColorDrawable>
+
diff --git a/components/page_info/android/java/res/layout/page_info.xml b/components/page_info/android/java/res/layout/page_info.xml
index 51b25ca..82b9a4cc 100644
--- a/components/page_info/android/java/res/layout/page_info.xml
+++ b/components/page_info/android/java/res/layout/page_info.xml
@@ -10,7 +10,7 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@color/sheet_bg_color"
+    android:background="@drawable/sheet_background"
     android:orientation="vertical">
 
     <LinearLayout
diff --git a/components/page_info/android/java/res/layout/page_info_container.xml b/components/page_info/android/java/res/layout/page_info_container.xml
index 18f4bef..5c793741 100644
--- a/components/page_info/android/java/res/layout/page_info_container.xml
+++ b/components/page_info/android/java/res/layout/page_info_container.xml
@@ -11,7 +11,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@color/sheet_bg_color"
+    android:background="@drawable/sheet_background"
     android:orientation="vertical">
 
     <LinearLayout
diff --git a/components/page_info/android/java/res/values-night/dimens.xml b/components/page_info/android/java/res/values-night/dimens.xml
new file mode 100644
index 0000000..8f945135ab
--- /dev/null
+++ b/components/page_info/android/java/res/values-night/dimens.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 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>
+  <!-- Page Info Background Elevation -->
+  <dimen name="page_info_bg_elev">@dimen/default_elevation_4</dimen>
+</resources>
\ No newline at end of file
diff --git a/components/page_info/android/java/res/values/dimens.xml b/components/page_info/android/java/res/values/dimens.xml
index dc9ea83..fa17bbb 100644
--- a/components/page_info/android/java/res/values/dimens.xml
+++ b/components/page_info/android/java/res/values/dimens.xml
@@ -11,6 +11,9 @@
     <dimen name="page_info_popup_corners_radius">16dp</dimen>
     <dimen name="page_info_popup_button_padding_sides">8dp</dimen>
 
+    <!-- Page Info Background Elevation -->
+    <dimen name="page_info_bg_elev">@dimen/default_elevation_0</dimen>
+
     <!-- Page Zoom dimensions -->
     <dimen name="page_zoom_min_touch_target_size">48dp</dimen>
     <dimen name="page_zoom_control_spacing">10dp</dimen>
diff --git a/components/page_info_strings.grdp b/components/page_info_strings.grdp
index 7e7be27..081ffc8 100644
--- a/components/page_info_strings.grdp
+++ b/components/page_info_strings.grdp
@@ -447,8 +447,8 @@
     <message name="IDS_PAGE_INFO_STATE_TEXT_IDLE_DETECTION_ASK" desc="The Page Info permission subpage and the main page info page contain a label which shows the state of the site permission. This is the text shown if the idle detection permission is in the ask state and the site can prompt the user to ask if they allow the site to know when you're actively using this device.">
       Can ask to know when you're actively using this device
     </message>
-    <message name="IDS_PAGE_INFO_STATE_TEXT_WINDOW_PLACEMENT_ASK" desc="The Page Info permission subpage and the main page info page contain a label which shows the state of the site permission. This is the text shown if the window placement permission is in the ask state and the site can prompt the user to ask if they allow the site to open and place windows on your screens.">
-      Can ask to open and place windows on your screens
+    <message name="IDS_PAGE_INFO_STATE_TEXT_WINDOW_PLACEMENT_ASK" desc="The Page Info permission subpage and the main page info page contain a label which shows the state of the site permission. This is the text shown if the window placement permission is in the ask state and the site can prompt the user to ask if they allow the site to use info about your screens to open and place windows.">
+      Can ask to use info about your screens
     </message>
     <message name="IDS_PAGE_INFO_STATE_TEXT_BLUETOOTH_SCANNING_ASK" desc="The Page Info permission subpage and the main page info page contain a label which shows the state of the site permission. This is the text shown if the bluetooth scanning permission is in the ask state and the site can prompt the user to ask if they allow the site to discover nearby Bluetooth devices.">
       Can ask to discover nearby Bluetooth devices
diff --git a/components/page_info_strings_grdp/IDS_PAGE_INFO_STATE_TEXT_WINDOW_PLACEMENT_ASK.png.sha1 b/components/page_info_strings_grdp/IDS_PAGE_INFO_STATE_TEXT_WINDOW_PLACEMENT_ASK.png.sha1
index e4f8234..fc2bdfd 100644
--- a/components/page_info_strings_grdp/IDS_PAGE_INFO_STATE_TEXT_WINDOW_PLACEMENT_ASK.png.sha1
+++ b/components/page_info_strings_grdp/IDS_PAGE_INFO_STATE_TEXT_WINDOW_PLACEMENT_ASK.png.sha1
@@ -1 +1 @@
-dea1b631a1b45af9457cf237f9bbe7a8c887ca1c
\ No newline at end of file
+e4a5d5c55aac37fffcfa64e3fbfbe7e14aff46b0
\ No newline at end of file
diff --git a/components/page_load_metrics/browser/metrics_web_contents_observer.cc b/components/page_load_metrics/browser/metrics_web_contents_observer.cc
index 48c9a25..526541a2 100644
--- a/components/page_load_metrics/browser/metrics_web_contents_observer.cc
+++ b/components/page_load_metrics/browser/metrics_web_contents_observer.cc
@@ -255,18 +255,6 @@
       NotifyAbortedProvisionalLoadsNewNavigation(navigation_handle,
                                                  user_initiated_info);
 
-  int chain_size_same_url = 0;
-  int chain_size = 0;
-  if (last_aborted) {
-    if (last_aborted->MatchesOriginalNavigation(navigation_handle)) {
-      chain_size_same_url = last_aborted->aborted_chain_size_same_url() + 1;
-    } else if (last_aborted->aborted_chain_size_same_url() > 0) {
-      LogAbortChainSameURLHistogram(
-          last_aborted->aborted_chain_size_same_url());
-    }
-    chain_size = last_aborted->aborted_chain_size() + 1;
-  }
-
   if (!ShouldTrackMainFrameNavigation(navigation_handle))
     return;
 
@@ -294,8 +282,7 @@
       navigation_handle,
       std::make_unique<PageLoadTracker>(
           in_foreground, embedder_interface_.get(), currently_committed_url,
-          !has_navigated_, navigation_handle, user_initiated_info, chain_size,
-          chain_size_same_url)));
+          !has_navigated_, navigation_handle, user_initiated_info)));
   DCHECK(insertion_result.second)
       << "provisional_loads_ already contains NavigationHandle.";
   for (auto& observer : testing_observers_)
diff --git a/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc b/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc
index edd349d..77e4b29 100644
--- a/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc
+++ b/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc
@@ -561,72 +561,6 @@
   CheckTotalErrorEvents();
 }
 
-TEST_F(MetricsWebContentsObserverTest, DontLogAbortChains) {
-  NavigateAndCommit(GURL(kDefaultTestUrl));
-  NavigateAndCommit(GURL(kDefaultTestUrl2));
-  NavigateAndCommit(GURL(kDefaultTestUrl));
-  histogram_tester_.ExpectTotalCount(internal::kAbortChainSizeNewNavigation, 0);
-  CheckErrorNoIPCsReceivedIfNeeded(2);
-  CheckTotalErrorEvents();
-}
-
-TEST_F(MetricsWebContentsObserverTest, LogAbortChains) {
-  // Start and abort three loads before one finally commits.
-  NavigationSimulator::NavigateAndFailFromBrowser(
-      web_contents(), GURL(kDefaultTestUrl), net::ERR_ABORTED);
-
-  NavigationSimulator::NavigateAndFailFromBrowser(
-      web_contents(), GURL(kDefaultTestUrl2), net::ERR_ABORTED);
-
-  NavigationSimulator::NavigateAndFailFromBrowser(
-      web_contents(), GURL(kDefaultTestUrl), net::ERR_ABORTED);
-
-  NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(),
-                                                    GURL(kDefaultTestUrl2));
-
-  histogram_tester_.ExpectTotalCount(internal::kAbortChainSizeNewNavigation, 1);
-  histogram_tester_.ExpectBucketCount(internal::kAbortChainSizeNewNavigation, 3,
-                                      1);
-  CheckNoErrorEvents();
-}
-
-TEST_F(MetricsWebContentsObserverTest, LogAbortChainsSameURL) {
-  // Start and abort three loads before one finally commits.
-  NavigationSimulator::NavigateAndFailFromBrowser(
-      web_contents(), GURL(kDefaultTestUrl), net::ERR_ABORTED);
-
-  NavigationSimulator::NavigateAndFailFromBrowser(
-      web_contents(), GURL(kDefaultTestUrl), net::ERR_ABORTED);
-
-  NavigationSimulator::NavigateAndFailFromBrowser(
-      web_contents(), GURL(kDefaultTestUrl), net::ERR_ABORTED);
-
-  NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(),
-                                                    GURL(kDefaultTestUrl));
-  histogram_tester_.ExpectTotalCount(internal::kAbortChainSizeNewNavigation, 1);
-  histogram_tester_.ExpectBucketCount(internal::kAbortChainSizeNewNavigation, 3,
-                                      1);
-  histogram_tester_.ExpectTotalCount(internal::kAbortChainSizeSameURL, 1);
-  histogram_tester_.ExpectBucketCount(internal::kAbortChainSizeSameURL, 3, 1);
-}
-
-TEST_F(MetricsWebContentsObserverTest, LogAbortChainsNoCommit) {
-  // Start and abort three loads before one finally commits.
-  NavigationSimulator::NavigateAndFailFromBrowser(
-      web_contents(), GURL(kDefaultTestUrl), net::ERR_ABORTED);
-
-  NavigationSimulator::NavigateAndFailFromBrowser(
-      web_contents(), GURL(kDefaultTestUrl2), net::ERR_ABORTED);
-
-  NavigationSimulator::NavigateAndFailFromBrowser(
-      web_contents(), GURL(kDefaultTestUrl), net::ERR_ABORTED);
-
-  web_contents()->Stop();
-
-  histogram_tester_.ExpectTotalCount(internal::kAbortChainSizeNoCommit, 1);
-  histogram_tester_.ExpectBucketCount(internal::kAbortChainSizeNoCommit, 3, 1);
-}
-
 TEST_F(MetricsWebContentsObserverTest, FlushMetricsOnAppEnterBackground) {
   content::NavigationSimulator::NavigateAndCommitFromBrowser(
       web_contents(), GURL(kDefaultTestUrl));
diff --git a/components/page_load_metrics/browser/page_load_tracker.cc b/components/page_load_metrics/browser/page_load_tracker.cc
index a96e788..57918aa 100644
--- a/components/page_load_metrics/browser/page_load_tracker.cc
+++ b/components/page_load_metrics/browser/page_load_tracker.cc
@@ -52,16 +52,6 @@
 namespace internal {
 
 const char kErrorEvents[] = "PageLoad.Internal.ErrorCode";
-const char kAbortChainSizeReload[] =
-    "PageLoad.Internal.ProvisionalAbortChainSize.Reload";
-const char kAbortChainSizeForwardBack[] =
-    "PageLoad.Internal.ProvisionalAbortChainSize.ForwardBack";
-const char kAbortChainSizeNewNavigation[] =
-    "PageLoad.Internal.ProvisionalAbortChainSize.NewNavigation";
-const char kAbortChainSizeSameURL[] =
-    "PageLoad.Internal.ProvisionalAbortChainSize.SameURL";
-const char kAbortChainSizeNoCommit[] =
-    "PageLoad.Internal.ProvisionalAbortChainSize.NoCommit";
 const char kPageLoadCompletedAfterAppBackground[] =
     "PageLoad.Internal.PageLoadCompleted.AfterAppBackground";
 const char kPageLoadStartedInForeground[] =
@@ -95,13 +85,6 @@
   return END_OTHER;
 }
 
-void LogAbortChainSameURLHistogram(int aborted_chain_size_same_url) {
-  if (aborted_chain_size_same_url > 0) {
-    UMA_HISTOGRAM_COUNTS_1M(internal::kAbortChainSizeSameURL,
-                            aborted_chain_size_same_url);
-  }
-}
-
 bool IsNavigationUserInitiated(content::NavigationHandle* handle) {
   // TODO(crbug.com/617904): Browser initiated navigations should have
   // HasUserGesture() set to true. In the meantime, we consider all
@@ -225,9 +208,7 @@
     const GURL& currently_committed_url,
     bool is_first_navigation_in_web_contents,
     content::NavigationHandle* navigation_handle,
-    UserInitiatedInfo user_initiated_info,
-    int aborted_chain_size,
-    int aborted_chain_size_same_url)
+    UserInitiatedInfo user_initiated_info)
     : did_stop_tracking_(false),
       app_entered_background_(false),
       navigation_start_(navigation_handle->NavigationStart()),
@@ -240,8 +221,6 @@
       started_in_foreground_(in_foreground),
       last_dispatched_merged_page_timing_(CreatePageLoadTiming()),
       user_initiated_info_(user_initiated_info),
-      aborted_chain_size_(aborted_chain_size),
-      aborted_chain_size_same_url_(aborted_chain_size_same_url),
       embedder_interface_(embedder_interface),
       metrics_update_dispatcher_(this, navigation_handle, embedder_interface),
       web_contents_(navigation_handle->GetWebContents()),
@@ -289,14 +268,6 @@
   if (!did_commit_) {
     if (!failed_provisional_load_info_)
       RecordInternalError(ERR_NO_COMMIT_OR_FAILED_PROVISIONAL_LOAD);
-
-    // Don't include any aborts that resulted in a new navigation, as the chain
-    // length will be included in the aborter PageLoadTracker.
-    if (page_end_reason_ != END_RELOAD &&
-        page_end_reason_ != END_FORWARD_BACK &&
-        page_end_reason_ != END_NEW_NAVIGATION) {
-      LogAbortChainHistograms(nullptr);
-    }
   } else if (page_load_metrics::IsEmpty(metrics_update_dispatcher_.timing())) {
     RecordInternalError(ERR_NO_IPCS_RECEIVED);
   }
@@ -310,57 +281,6 @@
   }
 }
 
-void PageLoadTracker::LogAbortChainHistograms(
-    content::NavigationHandle* final_navigation) {
-  if (aborted_chain_size_ == 0)
-    return;
-  // Note that this could be broken out by this navigation's abort type, if more
-  // granularity is needed. Add one to the chain size to count the current
-  // navigation. In the other cases, the current navigation is the final
-  // navigation (which commits).
-  if (!final_navigation) {
-    UMA_HISTOGRAM_COUNTS_1M(internal::kAbortChainSizeNoCommit,
-                            aborted_chain_size_ + 1);
-    LogAbortChainSameURLHistogram(aborted_chain_size_same_url_ + 1);
-    return;
-  }
-
-  // The following is only executed for committing trackers.
-  DCHECK(did_commit_);
-
-  // Note that histograms could be separated out by this commit's transition
-  // type, but for simplicity they will all be bucketed together.
-  LogAbortChainSameURLHistogram(aborted_chain_size_same_url_);
-
-  ui::PageTransition committed_transition =
-      final_navigation->GetPageTransition();
-  switch (EndReasonForPageTransition(committed_transition)) {
-    case END_RELOAD:
-      UMA_HISTOGRAM_COUNTS_1M(internal::kAbortChainSizeReload,
-                              aborted_chain_size_);
-      return;
-    case END_FORWARD_BACK:
-      UMA_HISTOGRAM_COUNTS_1M(internal::kAbortChainSizeForwardBack,
-                              aborted_chain_size_);
-      return;
-    // TODO(csharrison): Refactor this code so it is based on the WillStart*
-    // code path instead of the committed load code path. Then, for every abort
-    // chain, log a histogram of the counts of each of these metrics. For now,
-    // merge client redirects with new navigations, which was (basically) the
-    // previous behavior.
-    case END_CLIENT_REDIRECT:
-    case END_NEW_NAVIGATION:
-      UMA_HISTOGRAM_COUNTS_1M(internal::kAbortChainSizeNewNavigation,
-                              aborted_chain_size_);
-      return;
-    default:
-      NOTREACHED()
-          << "LogAbortChainHistograms received unexpected ui::PageTransition: "
-          << committed_transition;
-      return;
-  }
-}
-
 void PageLoadTracker::PageHidden() {
   // Only log the first time we background in a given page load.
   if (!first_background_time_.has_value() ||
@@ -451,7 +371,6 @@
   INVOKE_AND_PRUNE_OBSERVERS(observers_, ShouldObserveMimeType, mime_type);
   INVOKE_AND_PRUNE_OBSERVERS(observers_, OnCommit, navigation_handle,
                              source_id_);
-  LogAbortChainHistograms(navigation_handle);
 }
 
 void PageLoadTracker::DidActivatePrerenderedPage(
@@ -679,14 +598,6 @@
          (abort_cause_time - page_end_time_).InMilliseconds() < 100;
 }
 
-bool PageLoadTracker::MatchesOriginalNavigation(
-    content::NavigationHandle* navigation_handle) {
-  // Neither navigation should have committed.
-  DCHECK(!navigation_handle->HasCommitted());
-  DCHECK(!did_commit_);
-  return navigation_handle->GetURL() == start_url_;
-}
-
 void PageLoadTracker::UpdatePageEndInternal(
     PageEndReason page_end_reason,
     UserInitiatedInfo user_initiated_info,
diff --git a/components/page_load_metrics/browser/page_load_tracker.h b/components/page_load_metrics/browser/page_load_tracker.h
index aa08b5c0..267421d 100644
--- a/components/page_load_metrics/browser/page_load_tracker.h
+++ b/components/page_load_metrics/browser/page_load_tracker.h
@@ -52,11 +52,6 @@
 };
 
 extern const char kErrorEvents[];
-extern const char kAbortChainSizeReload[];
-extern const char kAbortChainSizeForwardBack[];
-extern const char kAbortChainSizeNewNavigation[];
-extern const char kAbortChainSizeNoCommit[];
-extern const char kAbortChainSizeSameURL[];
 extern const char kPageLoadCompletedAfterAppBackground[];
 extern const char kPageLoadStartedInForeground[];
 extern const char kPageLoadPrerender2Event[];
@@ -166,7 +161,6 @@
 // to access them.
 void RecordInternalError(InternalErrorLoadEvent event);
 PageEndReason EndReasonForPageTransition(ui::PageTransition transition);
-void LogAbortChainSameURLHistogram(int aborted_chain_size_same_url);
 bool IsNavigationUserInitiated(content::NavigationHandle* handle);
 
 // This class tracks a given page load, starting from navigation start /
@@ -185,9 +179,7 @@
                   const GURL& currently_committed_url,
                   bool is_first_navigation_in_web_contents,
                   content::NavigationHandle* navigation_handle,
-                  UserInitiatedInfo user_initiated_info,
-                  int aborted_chain_size,
-                  int aborted_chain_size_same_url);
+                  UserInitiatedInfo user_initiated_info);
 
   PageLoadTracker(const PageLoadTracker&) = delete;
   PageLoadTracker& operator=(const PageLoadTracker&) = delete;
@@ -322,11 +314,6 @@
   // tracking metrics in DidFinishNavigation.
   void StopTracking();
 
-  int aborted_chain_size() const { return aborted_chain_size_; }
-  int aborted_chain_size_same_url() const {
-    return aborted_chain_size_same_url_;
-  }
-
   PageEndReason page_end_reason() const { return page_end_reason_; }
   base::TimeTicks page_end_time() const { return page_end_time_; }
 
@@ -355,8 +342,6 @@
   // and is simpler than other feasible methods. See https://goo.gl/WKRG98.
   bool IsLikelyProvisionalAbort(base::TimeTicks abort_cause_time) const;
 
-  bool MatchesOriginalNavigation(content::NavigationHandle* navigation_handle);
-
   bool did_commit() const { return did_commit_; }
   const GURL& url() const { return url_; }
 
@@ -410,10 +395,6 @@
                              UserInitiatedInfo user_initiated_info,
                              base::TimeTicks timestamp,
                              bool is_certainly_browser_timestamp);
-  // If |final_navigation| is null, then this is an "unparented" abort chain,
-  // and represents a sequence of provisional aborts that never ends with a
-  // committed load.
-  void LogAbortChainHistograms(content::NavigationHandle* final_navigation);
 
   // Given a |time|, returns the duration between |navigation_start_| and
   // |time|. |time| must be greater than or equal to |navigation_start_|.
@@ -480,18 +461,6 @@
   // Whether this page load was user initiated.
   UserInitiatedInfo user_initiated_info_;
 
-  // This is a subtle member. If a provisional load A gets aborted by
-  // provisional load B, which gets aborted by C that eventually commits, then
-  // there exists an abort chain of length 2, starting at A's navigation_start.
-  // This is useful because it allows histograming abort chain lengths based on
-  // what the last load's transition type is. i.e. holding down F-5 to spam
-  // reload will produce a long chain with the RELOAD transition.
-  const int aborted_chain_size_;
-
-  // This member counts consecutive provisional aborts that share a url. It will
-  // always be less than or equal to |aborted_chain_size_|.
-  const int aborted_chain_size_same_url_;
-
   // Keeps track of actively loading resources on the page.
   ResourceTracker resource_tracker_;
 
diff --git a/components/password_manager/core/browser/hash_password_manager.cc b/components/password_manager/core/browser/hash_password_manager.cc
index 19aa516..6247de1 100644
--- a/components/password_manager/core/browser/hash_password_manager.cc
+++ b/components/password_manager/core/browser/hash_password_manager.cc
@@ -135,7 +135,7 @@
   // If we've already saved password hash for |username|, and the |password| is
   // unchanged, no need to save password hash again. Instead we update the last
   // sign in timestamp.
-  ListPrefUpdate update(prefs_, prefs::kPasswordHashDataList);
+  ListPrefUpdateDeprecated update(prefs_, prefs::kPasswordHashDataList);
   for (base::Value& password_hash_data : update.Get()->GetList()) {
     if (AreUsernamesSame(
             GetAndDecryptField(password_hash_data, kUsernameFieldKey),
@@ -183,7 +183,7 @@
   if (!prefs_)
     return;
 
-  ListPrefUpdate update(prefs_, prefs::kPasswordHashDataList);
+  ListPrefUpdateDeprecated update(prefs_, prefs::kPasswordHashDataList);
   update->EraseListValueIf([&](const auto& dict) {
     return AreUsernamesSame(GetAndDecryptField(dict, kUsernameFieldKey),
                             IsGaiaPassword(dict), username, is_gaia_password);
@@ -194,7 +194,7 @@
   if (!prefs_)
     return;
 
-  ListPrefUpdate update(prefs_, prefs::kPasswordHashDataList);
+  ListPrefUpdateDeprecated update(prefs_, prefs::kPasswordHashDataList);
   update->EraseListValueIf([&](const auto& dict) {
     return GetAndDecryptField(dict, kIsGaiaFieldKey) ==
            BooleanToString(is_gaia_password);
@@ -205,7 +205,7 @@
   if (!prefs_)
     return;
 
-  ListPrefUpdate update(prefs_, prefs::kPasswordHashDataList);
+  ListPrefUpdateDeprecated update(prefs_, prefs::kPasswordHashDataList);
   update->EraseListValueIf([](const base::Value& data) {
     if (GetAndDecryptField(data, kIsGaiaFieldKey) == "false") {
       return false;
@@ -313,7 +313,7 @@
                                        base::Value(encrypted_is_gaia_value));
   encrypted_password_hash_entry.SetKey(
       kLastSignInTimeFieldKey, base::Value(base::Time::Now().ToDoubleT()));
-  ListPrefUpdate update(prefs_, prefs::kPasswordHashDataList);
+  ListPrefUpdateDeprecated update(prefs_, prefs::kPasswordHashDataList);
   size_t num_erased = update->EraseListValueIf([&](const auto& dict) {
     return AreUsernamesSame(GetAndDecryptField(dict, kUsernameFieldKey),
                             IsGaiaPassword(dict), password_hash_data.username,
diff --git a/components/password_manager/core/browser/password_manager_features_util.cc b/components/password_manager/core/browser/password_manager_features_util.cc
index e651733..c7e65de3 100644
--- a/components/password_manager/core/browser/password_manager_features_util.cc
+++ b/components/password_manager/core/browser/password_manager_features_util.cc
@@ -136,8 +136,8 @@
 };
 
 // Helper class for updating account storage settings for a given account. Like
-// with DictionaryPrefUpdate, updates are only published once the instance gets
-// destroyed.
+// with DictionaryPrefUpdateDeprecated, updates are only published once the
+// instance gets destroyed.
 class ScopedAccountStorageSettingsUpdate {
  public:
   ScopedAccountStorageSettingsUpdate(PrefService* prefs,
@@ -178,7 +178,7 @@
   void ClearAllSettings() { update_->RemoveKey(account_hash_); }
 
  private:
-  DictionaryPrefUpdate update_;
+  DictionaryPrefUpdateDeprecated update_;
   const std::string account_hash_;
 };
 }  // namespace
@@ -410,8 +410,8 @@
   // Now remove any settings for account that are *not* in the set of hashes.
   // DictionaryValue doesn't allow removing elements while iterating, so first
   // collect all the keys to remove, then actually remove them in a second pass.
-  DictionaryPrefUpdate update(pref_service,
-                              prefs::kAccountStoragePerAccountSettings);
+  DictionaryPrefUpdateDeprecated update(
+      pref_service, prefs::kAccountStoragePerAccountSettings);
   std::vector<std::string> keys_to_remove;
   for (auto kv : update->DictItems()) {
     if (!hashes_to_keep.contains(kv.first))
diff --git a/components/payments/content/android/minimal_java_res/drawable/google_pay.xml b/components/payments/content/android/minimal_java_res/drawable/google_pay.xml
index 7076701..b48dc4a 100644
--- a/components/payments/content/android/minimal_java_res/drawable/google_pay.xml
+++ b/components/payments/content/android/minimal_java_res/drawable/google_pay.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:viewportWidth="49"
     android:viewportHeight="20"
     android:width="49dp"
diff --git a/components/permissions/android/res/layout/bluetooth_scanning_permission_dialog.xml b/components/permissions/android/res/layout/bluetooth_scanning_permission_dialog.xml
index 778e2e10..d104fbe 100644
--- a/components/permissions/android/res/layout/bluetooth_scanning_permission_dialog.xml
+++ b/components/permissions/android/res/layout/bluetooth_scanning_permission_dialog.xml
@@ -11,7 +11,7 @@
     android:orientation="vertical"
     android:paddingBottom="12dp"
     android:paddingTop="20dp"
-    android:background="@color/sheet_bg_color">
+    android:background="@drawable/sheet_background">
 
     <include layout="@layout/device_item_list" />
 
diff --git a/components/permissions/android/res/layout/item_chooser_dialog.xml b/components/permissions/android/res/layout/item_chooser_dialog.xml
index 78e5819..a63af7b 100644
--- a/components/permissions/android/res/layout/item_chooser_dialog.xml
+++ b/components/permissions/android/res/layout/item_chooser_dialog.xml
@@ -10,7 +10,7 @@
     android:orientation="vertical"
     android:paddingBottom="12dp"
     android:paddingTop="20dp"
-    android:background="@color/sheet_bg_color">
+    android:background="@drawable/sheet_background">
 
     <include layout="@layout/device_item_list" />
 
diff --git a/components/permissions/permission_actions_history.cc b/components/permissions/permission_actions_history.cc
index b78cb48e..660eddb 100644
--- a/components/permissions/permission_actions_history.cc
+++ b/components/permissions/permission_actions_history.cc
@@ -83,7 +83,8 @@
     PermissionAction action,
     RequestType type,
     PermissionPromptDisposition prompt_disposition) {
-  DictionaryPrefUpdate update(pref_service_, prefs::kPermissionActions);
+  DictionaryPrefUpdateDeprecated update(pref_service_,
+                                        prefs::kPermissionActions);
 
   const base::StringPiece permission_path(PermissionKeyForRequestType(type));
 
@@ -122,7 +123,8 @@
     return;
   }
 
-  DictionaryPrefUpdate update(pref_service_, prefs::kPermissionActions);
+  DictionaryPrefUpdateDeprecated update(pref_service_,
+                                        prefs::kPermissionActions);
 
   for (auto permission_entry : update->DictItems()) {
     permission_entry.second.EraseListValueIf([delete_begin,
diff --git a/components/permissions_strings.grdp b/components/permissions_strings.grdp
index 0bc7ebd..c5dfd9b9 100644
--- a/components/permissions_strings.grdp
+++ b/components/permissions_strings.grdp
@@ -133,7 +133,7 @@
 This will otherwise be blocked by your privacy settings. This will allow the content you interacted with to work correctly, but may allow <ph name="EMBEDDED_URL">$1<ex>news.site</ex></ph> to track your activity.
   </message>
   <message name="IDS_WINDOW_PLACEMENT_PERMISSION_FRAGMENT" desc="Permission request shown if the user is visiting a site that wants to place windows. Follows a prompt: 'This site would like to:'">
-    Open and place windows on your screens
+    Use info about your screens to open and place windows
   </message>
   <message name="IDS_FONT_ACCESS_PERMISSION_FRAGMENT" desc="Permission request shown if the user is visiting a site that wants to access locally installed font data. Follows a prompt: 'This site would like to:'">
     Use the fonts on your computer so you can create high-fidelity content
diff --git a/components/permissions_strings_grdp/IDS_WINDOW_PLACEMENT_PERMISSION_FRAGMENT.png.sha1 b/components/permissions_strings_grdp/IDS_WINDOW_PLACEMENT_PERMISSION_FRAGMENT.png.sha1
index cd2ace2..766865d 100644
--- a/components/permissions_strings_grdp/IDS_WINDOW_PLACEMENT_PERMISSION_FRAGMENT.png.sha1
+++ b/components/permissions_strings_grdp/IDS_WINDOW_PLACEMENT_PERMISSION_FRAGMENT.png.sha1
@@ -1 +1 @@
-81a78351acc34e6f8413a82488253e97c0b907fc
\ No newline at end of file
+84dc3e5387507dd3904aa9c365683f1d5bd7450a
\ No newline at end of file
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn
index 51b741a..9aa66afa 100644
--- a/components/policy/core/common/BUILD.gn
+++ b/components/policy/core/common/BUILD.gn
@@ -280,6 +280,8 @@
   if (is_chromeos_ash) {
     deps += [ "//chromeos/system" ]
     sources += [
+      "default_chrome_apps_migrator.cc",
+      "default_chrome_apps_migrator.h",
       "policy_scheduler.cc",
       "policy_scheduler.h",
     ]
@@ -474,6 +476,7 @@
   }
   if (is_chromeos_ash) {
     sources += [
+      "default_chrome_apps_migrator_unittest.cc",
       "policy_scheduler_unittest.cc",
       "proxy_policy_provider_unittest.cc",
     ]
diff --git a/components/policy/core/common/default_chrome_apps_migrator.cc b/components/policy/core/common/default_chrome_apps_migrator.cc
new file mode 100644
index 0000000..2a914f6
--- /dev/null
+++ b/components/policy/core/common/default_chrome_apps_migrator.cc
@@ -0,0 +1,112 @@
+// Copyright 2022 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/policy/core/common/default_chrome_apps_migrator.h"
+
+#include "components/policy/policy_constants.h"
+#include "components/strings/grit/components_strings.h"
+
+namespace policy {
+
+namespace {
+
+std::map<std::string, std::string> GetChromeAppToWebAppMapping() {
+  return std::map<std::string, std::string>();
+}
+
+}  // namespace
+
+DefaultChromeAppsMigrator::DefaultChromeAppsMigrator()
+    : DefaultChromeAppsMigrator(GetChromeAppToWebAppMapping()) {}
+
+DefaultChromeAppsMigrator::DefaultChromeAppsMigrator(
+    std::map<std::string, std::string> chrome_app_to_web_app)
+    : chrome_app_to_web_app_(std::move(chrome_app_to_web_app)) {}
+
+DefaultChromeAppsMigrator::DefaultChromeAppsMigrator(
+    DefaultChromeAppsMigrator&&) noexcept = default;
+DefaultChromeAppsMigrator& DefaultChromeAppsMigrator::operator=(
+    DefaultChromeAppsMigrator&&) noexcept = default;
+
+DefaultChromeAppsMigrator::~DefaultChromeAppsMigrator() = default;
+
+void DefaultChromeAppsMigrator::Migrate(PolicyMap* policies) const {
+  PolicyMap::Entry* forcelist_entry =
+      policies->GetMutable(key::kExtensionInstallForcelist);
+  if (!forcelist_entry)
+    return;
+
+  const base::Value* forcelist_value = forcelist_entry->value();
+  if (!forcelist_value || !forcelist_value->is_list())
+    return;
+
+  base::Value new_forcelist_value(base::Value::Type::LIST);
+  std::vector<std::string> chrome_app_ids;
+  std::vector<std::string> web_app_urls;
+  // Remove Chrome apps listed in `chrome_app_to_web_app_` from new
+  // ExtensionInstallForcelist value. Add the Chrome app ids that need to be
+  // blocked to 'chrome_app_ids'. Add the URLs of Web Apps that need to be
+  // installed to `web_app_urls`.
+  for (const auto& list_entry : forcelist_value->GetList()) {
+    if (!list_entry.is_string()) {
+      new_forcelist_value.Append(list_entry.Clone());
+      continue;
+    }
+
+    const std::string entry = list_entry.GetString();
+    const size_t pos = entry.find(';');
+    const std::string extension_id = entry.substr(0, pos);
+
+    const auto iter = chrome_app_to_web_app_.find(extension_id);
+    if (iter != chrome_app_to_web_app_.end()) {
+      chrome_app_ids.push_back(iter->first);
+      web_app_urls.push_back(iter->second);
+    } else {
+      new_forcelist_value.Append(entry);
+    }
+  }
+
+  // If no chrome apps need to be replaced, we have nothing to do.
+  if (chrome_app_ids.empty())
+    return;
+
+  forcelist_entry->set_value(std::move(new_forcelist_value));
+
+  EnsurePolicyValueIsList(policies, key::kExtensionInstallBlocklist);
+  base::Value* blocklist_value =
+      policies->GetMutableValue(key::kExtensionInstallBlocklist);
+  for (const std::string& chrome_app_id : chrome_app_ids) {
+    blocklist_value->Append(chrome_app_id);
+  }
+
+  EnsurePolicyValueIsList(policies, key::kWebAppInstallForceList);
+  base::Value* web_app_policy_value =
+      policies->GetMutableValue(key::kWebAppInstallForceList);
+  for (const std::string& web_app_url : web_app_urls) {
+    base::Value web_app(base::Value::Type::DICTIONARY);
+    web_app.SetStringKey("url", web_app_url);
+    web_app_policy_value->Append(std::move(web_app));
+  }
+}
+
+void DefaultChromeAppsMigrator::EnsurePolicyValueIsList(
+    PolicyMap* policies,
+    const std::string& policy_name) const {
+  const base::Value* policy_value = policies->GetValue(policy_name);
+  if (!policy_value || !policy_value->is_list()) {
+    const PolicyMap::Entry* forcelist_entry =
+        policies->Get(key::kExtensionInstallForcelist);
+    PolicyMap::Entry policy_entry(
+        forcelist_entry->level, forcelist_entry->scope, forcelist_entry->source,
+        base::Value(base::Value::Type::LIST), nullptr);
+    // If `policy_value` has wrong type, add message before overriding value.
+    if (policy_value) {
+      policy_entry.AddMessage(PolicyMap::MessageType::kError,
+                              IDS_POLICY_TYPE_ERROR);
+    }
+    policies->Set(policy_name, std::move(policy_entry));
+  }
+}
+
+}  // namespace policy
\ No newline at end of file
diff --git a/components/policy/core/common/default_chrome_apps_migrator.h b/components/policy/core/common/default_chrome_apps_migrator.h
new file mode 100644
index 0000000..8bc6706
--- /dev/null
+++ b/components/policy/core/common/default_chrome_apps_migrator.h
@@ -0,0 +1,56 @@
+// Copyright 2022 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_POLICY_CORE_COMMON_DEFAULT_CHROME_APPS_MIGRATOR_H_
+#define COMPONENTS_POLICY_CORE_COMMON_DEFAULT_CHROME_APPS_MIGRATOR_H_
+
+#include <map>
+#include <string>
+
+#include "base/values.h"
+#include "components/policy/core/common/policy_map.h"
+
+namespace policy {
+
+// This class is used as a temporary solution to handle force install policies
+// for deprecated Chrome apps. It replaces ExtensionInstallForcelist policy
+// for Chrome app with ExtensionInstallBlocklist for Chrome app and
+// WebAppInstallForceList policy for the corresponding Web App.
+// This code will be removed when the following steps are done:
+// 1. Build discoverability for default apps in Admin panel (Dpanel).
+// 2. Build new control logic for blocking installation (but not blocking use
+// of) PWAs.
+// 3. Migrate policy from Chrome Apps to PWAs in Admin surface (DMServer).
+class POLICY_EXPORT DefaultChromeAppsMigrator {
+ public:
+  DefaultChromeAppsMigrator();
+  explicit DefaultChromeAppsMigrator(
+      std::map<std::string, std::string> chrome_app_to_web_app);
+
+  DefaultChromeAppsMigrator(const DefaultChromeAppsMigrator&) = delete;
+  DefaultChromeAppsMigrator& operator=(const DefaultChromeAppsMigrator&) =
+      delete;
+
+  DefaultChromeAppsMigrator(DefaultChromeAppsMigrator&&) noexcept;
+  DefaultChromeAppsMigrator& operator=(DefaultChromeAppsMigrator&&) noexcept;
+
+  ~DefaultChromeAppsMigrator();
+
+  // Replaces ExtensionInstallForcelist policy for Chrome apps listed in
+  // `chrome_app_to_web_app_`.
+  void Migrate(PolicyMap* policies) const;
+
+ private:
+  // Checks that policy value type is list. If not, adds error message to the
+  // policy entry and overrides policy value with an empty list.
+  void EnsurePolicyValueIsList(PolicyMap* policies,
+                               const std::string& policy_name) const;
+
+  // Maps from ids of Chrome apps that need to be replaced to Web App urls.
+  std::map<std::string, std::string> chrome_app_to_web_app_;
+};
+
+}  // namespace policy
+
+#endif  // COMPONENTS_POLICY_CORE_COMMON_DEFAULT_CHROME_APPS_MIGRATOR_H_
diff --git a/components/policy/core/common/default_chrome_apps_migrator_unittest.cc b/components/policy/core/common/default_chrome_apps_migrator_unittest.cc
new file mode 100644
index 0000000..b7bdcdb
--- /dev/null
+++ b/components/policy/core/common/default_chrome_apps_migrator_unittest.cc
@@ -0,0 +1,199 @@
+// Copyright 2022 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/policy/core/common/default_chrome_apps_migrator.h"
+
+#include <map>
+#include <string>
+
+#include "base/logging.h"
+#include "components/policy/policy_constants.h"
+#include "components/strings/grit/components_strings.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+constexpr char kAppId1[] = "aaaa";
+constexpr char kAppId2[] = "bbbb";
+constexpr char kWebAppUrl1[] = "https://gmail.com";
+constexpr char kWebAppUrl2[] = "https://google.com";
+}  // namespace
+
+class DefaultChromeAppsMigratorTest : public testing::Test {
+  void SetUp() override {
+    migrator_ = DefaultChromeAppsMigrator(std::map<std::string, std::string>{
+        {kAppId1, kWebAppUrl1}, {kAppId2, kWebAppUrl2}});
+
+    // Set initial values for ExtensionInstallForcelist,
+    // ExtensionInstallBlocklist and WebAppInstallForceList policies.
+    policy_map_.Set(key::kExtensionInstallForcelist, POLICY_LEVEL_MANDATORY,
+                    POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+                    base::Value(base::Value::Type::LIST), nullptr);
+
+    base::Value blocklist_value(base::Value::Type::LIST);
+    blocklist_value.Append("eeee");
+    policy_map_.Set(key::kExtensionInstallBlocklist, POLICY_LEVEL_MANDATORY,
+                    POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+                    std::move(blocklist_value), nullptr);
+
+    base::Value web_app_value(base::Value::Type::LIST);
+    base::Value maps_web_app(base::Value::Type::DICTIONARY);
+    maps_web_app.SetStringKey("url", "https://google.com/maps");
+    web_app_value.Append(std::move(maps_web_app));
+    policy_map_.Set(key::kWebAppInstallForceList, POLICY_LEVEL_MANDATORY,
+                    POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+                    std::move(web_app_value), nullptr);
+  }
+
+ protected:
+  DefaultChromeAppsMigrator migrator_;
+  PolicyMap policy_map_;
+};
+
+TEST_F(DefaultChromeAppsMigratorTest, NoChromeApps) {
+  PolicyMap expected_map(policy_map_.Clone());
+  migrator_.Migrate(&policy_map_);
+
+  // No chrome apps in ExtensionInstallForcelist policy, policy map should not
+  // change.
+  EXPECT_TRUE(policy_map_.Equals(expected_map));
+}
+
+TEST_F(DefaultChromeAppsMigratorTest, ChromeAppWithUpdateUrl) {
+  PolicyMap expected_map(policy_map_.Clone());
+
+  // Add force installed chrome app that should be migrated.
+  base::Value* forcelist_value =
+      policy_map_.GetMutableValue(key::kExtensionInstallForcelist);
+  forcelist_value->Append(std::string(kAppId1) + ";https://example.com");
+
+  // Chrome app should be blocked after migration.
+  base::Value* blocklist_value =
+      expected_map.GetMutableValue(key::kExtensionInstallBlocklist);
+  blocklist_value->Append(std::string(kAppId1));
+
+  // Corresponding web app should be force installed after migration.
+  base::Value first_app(base::Value::Type::DICTIONARY);
+  first_app.SetStringKey("url", kWebAppUrl1);
+  base::Value* web_app_value =
+      expected_map.GetMutableValue(key::kWebAppInstallForceList);
+  web_app_value->Append(std::move(first_app));
+
+  migrator_.Migrate(&policy_map_);
+
+  EXPECT_TRUE(policy_map_.Equals(expected_map));
+}
+
+TEST_F(DefaultChromeAppsMigratorTest, ChromeAppsAndExtensions) {
+  PolicyMap expected_map(policy_map_.Clone());
+
+  // Add two force installed chrome apps and two extensions.
+  base::Value* forcelist_value =
+      policy_map_.GetMutableValue(key::kExtensionInstallForcelist);
+  forcelist_value->Append("extension1");
+  forcelist_value->Append(kAppId1);
+  forcelist_value->Append("extension2");
+  forcelist_value->Append(kAppId2);
+
+  // Only extensions should be left now.
+  base::Value* expected_forcelist =
+      expected_map.GetMutableValue(key::kExtensionInstallForcelist);
+  expected_forcelist->Append("extension1");
+  expected_forcelist->Append("extension2");
+
+  // Chrome apps should be blocked after migration.
+  base::Value* blocklist_value =
+      expected_map.GetMutableValue(key::kExtensionInstallBlocklist);
+  blocklist_value->Append(kAppId1);
+  blocklist_value->Append(kAppId2);
+
+  // Corresponding web apps should be force installed after migration.
+  base::Value first_app(base::Value::Type::DICTIONARY);
+  first_app.SetStringKey("url", kWebAppUrl1);
+  base::Value second_app(base::Value::Type::DICTIONARY);
+  second_app.SetStringKey("url", kWebAppUrl2);
+  base::Value* web_app_value =
+      expected_map.GetMutableValue(key::kWebAppInstallForceList);
+  web_app_value->Append(std::move(first_app));
+  web_app_value->Append(std::move(second_app));
+
+  migrator_.Migrate(&policy_map_);
+
+  EXPECT_TRUE(policy_map_.Equals(expected_map));
+}
+
+// Tests the case when ExtensionInstallBlocklist is initially set to wrong type
+// and we have to append chrome app id to it. The value should be overridden and
+// error message should be added.
+TEST_F(DefaultChromeAppsMigratorTest, ExtensionBlocklistPolicyWrongType) {
+  PolicyMap expected_map(policy_map_.Clone());
+
+  // Add force installed chrome app.
+  base::Value* forcelist_value =
+      policy_map_.GetMutableValue(key::kExtensionInstallForcelist);
+  forcelist_value->Append(kAppId1);
+
+  // Set ExtensionInstallBlocklist to non-list type.
+  base::Value blocklist_value(base::Value::Type::DICTIONARY);
+  policy_map_.GetMutable(key::kExtensionInstallBlocklist)
+      ->set_value(std::move(blocklist_value));
+
+  base::Value blocklist_expected_value(base::Value::Type::LIST);
+  blocklist_expected_value.Append(kAppId1);
+  PolicyMap::Entry* blocklist_expected_entry =
+      expected_map.GetMutable(key::kExtensionInstallBlocklist);
+  blocklist_expected_entry->set_value(std::move(blocklist_expected_value));
+  blocklist_expected_entry->AddMessage(PolicyMap::MessageType::kError,
+                                       IDS_POLICY_TYPE_ERROR);
+
+  // Corresponding web app should be force installed after migration.
+  base::Value first_app(base::Value::Type::DICTIONARY);
+  first_app.SetStringKey("url", kWebAppUrl1);
+  base::Value* web_app_value =
+      expected_map.GetMutableValue(key::kWebAppInstallForceList);
+  web_app_value->Append(std::move(first_app));
+
+  migrator_.Migrate(&policy_map_);
+
+  EXPECT_TRUE(policy_map_.Equals(expected_map));
+}
+
+// Tests the case when WebAppInstallForceList is initially set to wrong type and
+// we have to append a web app to it. The value should be overridden and error
+// message should be added.
+TEST_F(DefaultChromeAppsMigratorTest, WebAppPolicyWrongType) {
+  PolicyMap expected_map(policy_map_.Clone());
+
+  // Add force installed chrome app.
+  base::Value* forcelist_value =
+      policy_map_.GetMutableValue(key::kExtensionInstallForcelist);
+  forcelist_value->Append(kAppId1);
+
+  // Set WebAppInstallForceList to non-list type.
+  base::Value web_app_value(base::Value::Type::DICTIONARY);
+  policy_map_.GetMutable(key::kWebAppInstallForceList)
+      ->set_value(std::move(web_app_value));
+
+  // Chrome app should be blocked after migration.
+  base::Value* blocklist_value =
+      expected_map.GetMutableValue(key::kExtensionInstallBlocklist);
+  blocklist_value->Append(kAppId1);
+
+  base::Value web_app_expected_value(base::Value::Type::LIST);
+  base::Value first_app(base::Value::Type::DICTIONARY);
+  first_app.SetStringKey("url", kWebAppUrl1);
+  web_app_expected_value.Append(std::move(first_app));
+  PolicyMap::Entry* web_app_expected_entry =
+      expected_map.GetMutable(key::kWebAppInstallForceList);
+  web_app_expected_entry->set_value(std::move(web_app_expected_value));
+  web_app_expected_entry->AddMessage(PolicyMap::MessageType::kError,
+                                     IDS_POLICY_TYPE_ERROR);
+
+  migrator_.Migrate(&policy_map_);
+
+  EXPECT_TRUE(policy_map_.Equals(expected_map));
+}
+
+}  // namespace policy
\ No newline at end of file
diff --git a/components/policy/core/common/features.cc b/components/policy/core/common/features.cc
index 4d9c4a1..7151069f 100644
--- a/components/policy/core/common/features.cc
+++ b/components/policy/core/common/features.cc
@@ -8,6 +8,9 @@
 
 namespace features {
 
+const base::Feature kDefaultChromeAppsMigration{
+    "EnableDefaultAppsMigration", base::FEATURE_ENABLED_BY_DEFAULT};
+
 const base::Feature kPolicyBlocklistThrottleRequiresPoliciesLoaded{
     "PolicyBlocklistThrottleRequiresPoliciesLoaded",
     base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/policy/core/common/features.h b/components/policy/core/common/features.h
index c30f1a2..a8242cc 100644
--- a/components/policy/core/common/features.h
+++ b/components/policy/core/common/features.h
@@ -16,6 +16,9 @@
 // Enable chrome://management page on Android.
 POLICY_EXPORT extern const base::Feature kChromeManagementPageAndroid;
 
+// Enable force installed Chrome apps policy migration.
+POLICY_EXPORT extern const base::Feature kDefaultChromeAppsMigration;
+
 // PolicyBlocklistThrottle defers navigations until policies are loaded.
 POLICY_EXPORT extern const base::Feature
     kPolicyBlocklistThrottleRequiresPoliciesLoaded;
diff --git a/components/policy/core/common/policy_service_impl.cc b/components/policy/core/common/policy_service_impl.cc
index 716aeb8f..fb92395d 100644
--- a/components/policy/core/common/policy_service_impl.cc
+++ b/components/policy/core/common/policy_service_impl.cc
@@ -35,6 +35,10 @@
 #include "components/policy/core/common/android/policy_service_android.h"
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "components/policy/core/common/default_chrome_apps_migrator.h"
+#endif
+
 namespace policy {
 
 namespace {
@@ -349,6 +353,9 @@
   // Merge from each provider in their order of priority.
   const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
   PolicyBundle bundle;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  DefaultChromeAppsMigrator chrome_apps_migrator;
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   for (auto* provider : providers_) {
     PolicyBundle provided_bundle;
     provided_bundle.CopyFrom(provider->policies());
@@ -356,6 +363,12 @@
     IgnoreUserCloudPrecedencePolicies(&provided_bundle.Get(chrome_namespace));
     DowngradeMetricsReportingToRecommendedPolicy(
         &provided_bundle.Get(chrome_namespace));
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    if (base::FeatureList::IsEnabled(
+            policy::features::kDefaultChromeAppsMigration)) {
+      chrome_apps_migrator.Migrate(&provided_bundle.Get(chrome_namespace));
+    }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
     bundle.MergeFrom(provided_bundle);
   }
 
diff --git a/components/policy/proto/chrome_device_policy.proto b/components/policy/proto/chrome_device_policy.proto
index 857f3f9..5c64110f 100644
--- a/components/policy/proto/chrome_device_policy.proto
+++ b/components/policy/proto/chrome_device_policy.proto
@@ -166,9 +166,9 @@
 
   // Network telemetry policies.
   optional int64 report_network_telemetry_collection_rate_ms = 33
-      [default = 600000];
+      [default = 3600000];
   optional int64 report_network_telemetry_event_checking_rate_ms = 34
-      [default = 60000];
+      [default = 600000];
 
   // Audio telemetry policy
   optional int64 report_device_audio_status_checking_rate_ms = 35
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index f4ca2bd2..ae7bb13 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -4019,7 +4019,6 @@
 //     * enterprise_psm_check
 //     * chrome_desktop_report
 //     * chrome_os_user_report
-//     * ping
 //     * policy
 //     * register
 //     * status_upload
@@ -4067,102 +4066,93 @@
 //     token is passed in the "oauth" parameter.
 //
 // DeviceManagementRequest should only contain one request which matches the
-// HTTP query parameter - request, as listed below. Other requests within the
-// container will be ignored.
-//   chrome_desktop_report: chrome_desktop_report_request
-//   chrome_os_user_report: chrome_os_user_report_request
-//   cert_upload: cert_upload_request
-//   check_device_pairing: check_device_pairing_request
-//   device_pairing: device_pairing_request
-//   device_state_retrieval: device_state_retrieval_request
-//   enterprise_check: auto_enrollment_request
-//   ping: policy_request
-//   policy: policy_request
-//   register: register_request
-//   status: device_status_report_request or session_status_report_request or
-//       child_status_report_request
-//   unregister: unregister_request
-//   remote_commands: remote_command_request
-//   attribute_update_permission: device_attribute_update_permission_request
-//   attribute_update: device_attribute_update_request
-//   gcm_id_update: gcm_id_update_request
-//   check_android_management: check_android_management_request
-//   certificate_based_register: certificate_based_register_request
-//   active_directory_enroll_play_user:
-//       active_directory_enroll_play_user_request
-//   active_directory_play_activity: active_directory_play_activity_request
-//   active_directory_user_signin: active_directory_user_signin_request
-//   register_browser: register_browser_request
-//   app_install_report: app_install_report_request
-//   policy_validation_report: policy_validation_report_request
-//   device_initial_enrollment_state: device_initial_enrollment_state_request
-//   refresh_account: refresh_account_request
-//   client_cert_provisioning: client_certificate_provisioning_request
-//   upload_euicc_info: upload_euicc_info_request
+// HTTP query parameter - request, as mentioned in comments on the fields of the
+// message. Other requests within the container will be ignored.
 message DeviceManagementRequest {
   reserved 24;  // unused previous version of chrome_desktop_report_request.
 
   // Register request.
+  // HTTP query parameter 'request': 'register'
   optional DeviceRegisterRequest register_request = 1;
 
   // Unregister request.
+  // HTTP query parameter 'request': 'unregister'
   optional DeviceUnregisterRequest unregister_request = 2;
 
   // Policy request.
+  // HTTP query parameter 'request': 'policy'
   optional DevicePolicyRequest policy_request = 3;
 
   // Update status.
+  // HTTP query parameter 'request': 'status'
+  // (this applies to all of the following three fields, and they can all be
+  // present in the same request).
   optional DeviceStatusReportRequest device_status_report_request = 4;
   optional SessionStatusReportRequest session_status_report_request = 5;
   optional ChildStatusReportRequest child_status_report_request = 30;
 
   // Auto-enrollment detection.
+  // HTTP query parameter 'request': 'enterprise_check'
   optional DeviceAutoEnrollmentRequest auto_enrollment_request = 6;
 
   // EMCert upload (for remote attestation)
+  // HTTP query parameter 'request': 'cert_upload'
   optional DeviceCertUploadRequest cert_upload_request = 7;
 
   // Request for OAuth2 authorization codes to access Google services.
+  // HTTP query parameter 'request': 'api_authorization'
   optional DeviceServiceApiAccessRequest service_api_access_request = 8;
 
   // Device-state retrieval.
+  // HTTP query parameter 'request': 'device_state_retrieval'
   optional DeviceStateRetrievalRequest device_state_retrieval_request = 9;
 
   // Device state key update.
+  // HTTP query parameter 'request': 'policy'
   optional DeviceStateKeyUpdateRequest device_state_key_update_request = 10;
 
   // Pair two devices.
+  // HTTP query parameter 'request': 'device_pairing'
   optional DevicePairingRequest device_pairing_request = 11;
 
   // Check if two devices are paired.
+  // HTTP query parameter 'request': 'check_device_pairing'
   optional CheckDevicePairingRequest check_device_pairing_request = 12;
 
   // Remote command fetching.
+  // HTTP query parameter 'request': 'remote_commands'
   optional DeviceRemoteCommandRequest remote_command_request = 13;
 
   // Check permission for updating device attribute.
+  // HTTP query parameter 'request': 'attribute_update_permission'
   optional DeviceAttributeUpdatePermissionRequest
       device_attribute_update_permission_request = 14;
 
   // Update device attribute.
+  // HTTP query parameter 'request': 'attribute_update'
   optional DeviceAttributeUpdateRequest device_attribute_update_request = 15;
 
   // Update the GCM id to device_id mapping.
+  // HTTP query parameter 'request': 'gcm_id_update'
   optional GcmIdUpdateRequest gcm_id_update_request = 16;
 
   // Check if user is a managed Android-for-Work user with DPC enforcement.
+  // HTTP query parameter 'request': 'check_android_management'
   optional CheckAndroidManagementRequest check_android_management_request = 17;
 
   // Request to register with a registration certificate.
+  // HTTP query parameter 'request': 'certificate_based_register'
   optional CertificateBasedDeviceRegisterRequest
       certificate_based_register_request = 18;
 
   // Gets an enrollment token to a Managed Google Play Account for using it with
   // Active Directory.
+  // HTTP query parameter 'request': 'active_directory_enroll_play_user'
   optional ActiveDirectoryEnrollPlayUserRequest
       active_directory_enroll_play_user_request = 19;
 
   // Reports that a Play account is used.
+  // HTTP query parameter 'request': 'active_directory_play_activity'
   optional ActiveDirectoryPlayActivityRequest
       active_directory_play_activity_request = 20;
 
@@ -4171,58 +4161,75 @@
       21 [deprecated = true];
 
   // Initiate an Active Directory user signin.
+  // HTTP query parameter 'request': 'active_directory_user_signin'
   optional ActiveDirectoryUserSigninRequest
       active_directory_user_signin_request = 22;
 
   // Request to register a browser independently of its users.
+  // HTTP query parameter 'request': 'register_browser'
   optional RegisterBrowserRequest register_browser_request = 23;
 
   // A report on the status of app push-installs.
+  // HTTP query parameter 'request': 'app_install_report'
   optional AppInstallReportRequest app_install_report_request = 25;
 
   // A Chrome desktop report request.
+  // HTTP query parameter 'request': 'chrome_desktop_report'
   optional ChromeDesktopReportRequest chrome_desktop_report_request = 26;
 
   // Result of validating fetched policy on the client.
+  // HTTP query parameter 'request': 'policy_validation_report'
   optional PolicyValidationReportRequest policy_validation_report_request = 27;
 
   // Query for initial enrollment details.
+  // HTTP query parameter 'request': 'device_initial_enrollment_state'
   optional DeviceInitialEnrollmentStateRequest
       device_initial_enrollment_state_request = 28;
 
   // Request from device to wipe an old account and get a new account.
+  // HTTP query parameter 'request': 'refresh_account'
   optional RefreshAccountRequest refresh_account_request = 29;
 
   // Request from device to upload RSU lookup key.
+  // TODO(crbug/1284656): seems unused
   optional RsuLookupKeyUploadRequest rsu_lookup_key_upload_request = 31;
 
   // Request from device for SAML IdP URL address.
+  // HTTP query parameter 'request': 'public_saml_user_request'
   optional PublicSamlUserRequest public_saml_user_request = 32;
 
   // A ChromeOS user report request.
+  // HTTP query parameter 'request': 'chrome_os_user_report'
   optional ChromeOsUserReportRequest chrome_os_user_report_request = 33;
 
   // Request to start / continue client certificate provisioning process.
+  // HTTP query parameter 'request': 'client_cert_provisioning'
   optional ClientCertificateProvisioningRequest
       client_certificate_provisioning_request = 34;
 
   // A report on the status of extension install process.
+  // TODO(crbug/1284656): seems unused.
   optional ExtensionInstallReportRequest extension_install_report_request = 35;
 
   // Request to check user account for smart enrollment.
+  // HTTP query parameter 'request': 'check_user_account'
   optional CheckUserAccountRequest check_user_account_request = 36;
 
   // Request from device to check the state stored in PSM. Currently, it is used
   // for ZTE/LP device initial enrollment state check.
+  // HTTP query parameter 'request': 'enterprise_psm_check'
   optional PrivateSetMembershipRequest private_set_membership_request = 37;
 
   // Request from browser to upload public key.
+  // HTTP query parameter 'request': 'browser_public_key_upload'
   optional BrowserPublicKeyUploadRequest browser_public_key_upload_request = 38;
 
   // Request to upload info about eSIM provisioning.
+  // HTTP query parameter 'request': 'upload_euicc_info'
   optional UploadEuiccInfoRequest upload_euicc_info_request = 39;
 
   // Request to upload profile usage information.
+  // HTTP query parameter 'request': 'chrome_profile_report'
   optional ChromeProfileReportRequest chrome_profile_report_request = 40;
 
   // Next id: 41.
@@ -4235,25 +4242,31 @@
 // to perform log analysis and customer support since HTTP Status Code is easily
 // visible in the logs.
 //
-// The following list defines the error code returned by this API:
-//
+// Possible HTTP status codes:
 // 200 OK: valid response is returned to client.
-// 400 Bad Request: invalid argument.
-// 401 Unauthorized: invalid auth cookie or DM token.
-// 402 Missing licenses.
-// 403 Forbidden: device management is not allowed.
+// 400 Bad Request: invalid argument or payload.
+// 401 Unauthorized: invalid auth cookie/OAuth token or DM token.
+// 402 Missing license.
+// 403 Forbidden: device management is not allowed (e.g. user may not enroll).
 // 404 Not Found: the request URL is invalid.
 // 405 Invalid serial number.
+// 406 Domain mismatch (e.g. device is enrolling into the wrong domain, should
+//     force-re-enroll into another domain).
 // 409 Device id conflict.
 // 410 Device Not Found: the device id is not found.
-// 412 Pending approval.
-// 417 Consumer account with packaged license.
-// 491 Request Pending: the request is pending approval.
+// 412 Pending approval. Used with ClientCertificateProvisioningRequest to
+//     indicate that a certificate has not been issued yet.
+// 413 Request payload too large.
+// 417 Consumer (non-dasher) account can not enroll with packaged license.
+// 429 Too many requests.
 // 500 Internal Server Error: most likely a bug in DM server.
 // 503 Service Unavailable: most likely a backend error.
 // 902 Policy Not Found: the policy is not found.
 // 903 Deprovisioned: the device has been deprovisioned.
 // 904 Arc Disabled: ARC is not enabled on the domain.
+// 905 Can not enroll packaged license device to domainless customer.
+// 906 TOS has not been accepted by customer.
+// 907 Illegal account for packaged EDU license.
 message DeviceManagementResponse {
   reserved 1, 24;
 
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 582d28fb..d8cfe33 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -5872,7 +5872,9 @@
       'tags': [],
       'desc': '''Setting the policy to 0 (the default) means you can access the developer tools and the JavaScript console, but not in the context of extensions installed by enterprise policy. Setting the policy to 1 means you can access the developer tools and the JavaScript console in all contexts, including that of extensions installed by enterprise policy. Setting the policy to 2 means you can't acess developer tools, and you can't inspect website elements.
 
-      This setting also turns off keyboard shortcuts and menu or context menu entries to open developer tools or the JavaScript console.''',
+      This setting also turns off keyboard shortcuts and menu or context menu entries to open developer tools or the JavaScript console.
+
+      As of <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> version 99, this setting also controls entry points for the 'View page source' feature. If you set this policy to 'DeveloperToolsDisallowed' (value 2), users cannot access source viewing via keyboard shortcut or the context menu. To fully block source viewing, you must also add 'view-source:*' to the <ph name="URL_BLOCKLIST_POLICY_NAME">URLBlocklist</ph> policy.''',
       'arc_support': '''This policy also controls access to Android Developer Options. If you set this policy to 'DeveloperToolsDisallowed' (value 2), users cannot access Developer Options. If you set this policy to another value or leave it unset, users can access Developer Options by tapping seven times on the build number in the Android settings app.''',
     },
     {
@@ -10462,7 +10464,7 @@
       'owners': ['cros-reporting-team@google.com', 'tylergarrett@google.com'],
       'type': 'main',
       'schema': { 'type': 'boolean' },
-      'future_on': ['chrome_os'],
+      'supported_on': ['chrome_os:96-'],
       'supported_chrome_os_management': ['google_cloud'],
       'device_only': True,
       'features': {
@@ -10647,7 +10649,7 @@
         'dynamic_refresh': True,
       },
       'example_value': 600000,
-      'default': 600000,
+      'default': 3600000,
       'id': 914,
       'caption': '''Network telemetry collection rate in milliseconds.''',
       'tags': ['admin-sharing'],
@@ -10668,7 +10670,7 @@
         'dynamic_refresh': True,
       },
       'example_value': 60000,
-      'default': 60000,
+      'default': 600000,
       'id': 915,
       'caption': '''Network events checking rate in milliseconds.''',
       'tags': ['admin-sharing'],
diff --git a/components/policy/test_support/policy_storage.cc b/components/policy/test_support/policy_storage.cc
index 5fd2615..0d8381e 100644
--- a/components/policy/test_support/policy_storage.cc
+++ b/components/policy/test_support/policy_storage.cc
@@ -4,25 +4,10 @@
 
 #include "components/policy/test_support/policy_storage.h"
 #include "base/big_endian.h"
-#include "base/strings/strcat.h"
-#include "base/strings/string_util.h"
 #include "crypto/sha2.h"
 
 namespace policy {
 
-namespace {
-
-const char kPolicyKeySeparator[] = "/";
-
-std::string GetPolicyKey(const std::string& policy_type,
-                         const std::string& entity_id) {
-  if (entity_id.empty())
-    return policy_type;
-  return base::StrCat({policy_type, kPolicyKeySeparator, entity_id});
-}
-
-}  // namespace
-
 PolicyStorage::PolicyStorage()
     : signature_provider_(std::make_unique<SignatureProvider>()) {}
 
@@ -34,33 +19,14 @@
 PolicyStorage::~PolicyStorage() = default;
 
 std::string PolicyStorage::GetPolicyPayload(
-    const std::string& policy_type,
-    const std::string& entity_id) const {
-  auto it = policy_payloads_.find(GetPolicyKey(policy_type, entity_id));
+    const std::string& policy_type) const {
+  auto it = policy_payloads_.find(policy_type);
   return it == policy_payloads_.end() ? std::string() : it->second;
 }
 
-std::vector<std::string> PolicyStorage::GetEntityIdsForType(
-    const std::string& policy_type) {
-  std::string prefix = base::StrCat({policy_type, kPolicyKeySeparator});
-  std::vector<std::string> ids;
-  const size_t prefix_length = prefix.length();
-  for (const auto& [policy_key, payload] : policy_payloads_) {
-    if (base::StartsWith(policy_key, prefix))
-      ids.push_back(policy_key.substr(prefix_length));
-  }
-  return ids;
-}
-
 void PolicyStorage::SetPolicyPayload(const std::string& policy_type,
                                      const std::string& policy_payload) {
-  SetPolicyPayload(policy_type, std::string(), policy_payload);
-}
-
-void PolicyStorage::SetPolicyPayload(const std::string& policy_type,
-                                     const std::string& entity_id,
-                                     const std::string& policy_payload) {
-  policy_payloads_[GetPolicyKey(policy_type, entity_id)] = policy_payload;
+  policy_payloads_[policy_type] = policy_payload;
 }
 
 void PolicyStorage::SetPsmEntry(const std::string& brand_serial_id,
diff --git a/components/policy/test_support/policy_storage.h b/components/policy/test_support/policy_storage.h
index 952363d..e8a840a6 100644
--- a/components/policy/test_support/policy_storage.h
+++ b/components/policy/test_support/policy_storage.h
@@ -29,20 +29,13 @@
   PolicyStorage& operator=(PolicyStorage&& policy_storage);
   virtual ~PolicyStorage();
 
-  // Returns the serialized proto associated with |policy_type| and optional
-  // |entity_id|. Returns empty string if there is no such association.
-  std::string GetPolicyPayload(
-      const std::string& policy_type,
-      const std::string& entity_id = std::string()) const;
-  std::vector<std::string> GetEntityIdsForType(const std::string& policy_type);
-
+  // Returns the serialized proto associated with |policy_type|. Returns empty
+  // string if there is no such association.
+  std::string GetPolicyPayload(const std::string& policy_type) const;
   // Associates the serialized proto stored in |policy_payload| with
-  // |policy_type| and optional |entity_id|.
+  // |policy_type|.
   void SetPolicyPayload(const std::string& policy_type,
                         const std::string& policy_payload);
-  void SetPolicyPayload(const std::string& policy_type,
-                        const std::string& entity_id,
-                        const std::string& policy_payload);
 
   SignatureProvider* signature_provider() const {
     return signature_provider_.get();
diff --git a/components/policy/test_support/request_handler_for_policy.cc b/components/policy/test_support/request_handler_for_policy.cc
index ec2ff1a29..bfbe346 100644
--- a/components/policy/test_support/request_handler_for_policy.cc
+++ b/components/policy/test_support/request_handler_for_policy.cc
@@ -4,12 +4,9 @@
 
 #include "components/policy/test_support/request_handler_for_policy.h"
 
-#include "base/containers/contains.h"
-#include "base/containers/flat_set.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
-#include "components/policy/proto/device_management_backend.pb.h"
 #include "components/policy/test_support/policy_storage.h"
 #include "components/policy/test_support/signature_provider.h"
 #include "components/policy/test_support/test_server_helpers.h"
@@ -39,7 +36,7 @@
 
 std::unique_ptr<HttpResponse> RequestHandlerForPolicy::HandleRequest(
     const HttpRequest& request) {
-  const base::flat_set<std::string> kCloudPolicyTypes{
+  const std::set<std::string> kCloudPolicyTypes{
       dm_protocol::kChromeDevicePolicyType,
       dm_protocol::kChromeExtensionPolicyType,
       dm_protocol::kChromeMachineLevelUserCloudPolicyType,
@@ -49,11 +46,6 @@
       dm_protocol::kChromeSigninExtensionPolicyType,
       dm_protocol::kChromeUserPolicyType,
   };
-  const base::flat_set<std::string> kExtensionPolicyTypes{
-      dm_protocol::kChromeExtensionPolicyType,
-      dm_protocol::kChromeMachineLevelExtensionCloudPolicyType,
-      dm_protocol::kChromeSigninExtensionPolicyType,
-  };
 
   std::string request_device_token;
   if (!GetDeviceTokenFromRequest(request, &request_device_token))
@@ -73,25 +65,18 @@
        device_management_request.policy_request().requests()) {
     const std::string& policy_type = fetch_request.policy_type();
     // TODO(crbug.com/1221328): Add other policy types as needed.
-    if (!base::Contains(kCloudPolicyTypes, policy_type)) {
+    if (kCloudPolicyTypes.find(policy_type) == kCloudPolicyTypes.end()) {
       return CreateHttpResponse(
           net::HTTP_BAD_REQUEST,
           base::StringPrintf("Invalid policy_type: %s", policy_type.c_str()));
     }
 
     std::string error_msg;
-    if (base::Contains(kExtensionPolicyTypes, policy_type)) {
-      if (!ProcessCloudPolicyForExtensions(
-              fetch_request, *client_info,
-              device_management_response.mutable_policy_response(),
-              &error_msg)) {
-        return CreateHttpResponse(net::HTTP_BAD_REQUEST, error_msg);
-      }
-    } else if (!ProcessCloudPolicy(
-                   fetch_request, *client_info,
-                   device_management_response.mutable_policy_response()
-                       ->add_responses(),
-                   &error_msg)) {
+    if (!ProcessCloudPolicy(
+            fetch_request, *client_info,
+            device_management_response.mutable_policy_response()
+                ->add_responses(),
+            &error_msg)) {
       return CreateHttpResponse(net::HTTP_BAD_REQUEST, error_msg);
     }
   }
@@ -115,11 +100,10 @@
 
   // Determine the current key on the client.
   const SignatureProvider::SigningKey* client_key = nullptr;
-  const SignatureProvider* signature_provider =
-      policy_storage()->signature_provider();
-  int public_key_version = fetch_request.public_key_version();
   if (fetch_request.has_public_key_version()) {
-    client_key = signature_provider->GetKeyByVersion(public_key_version);
+    int public_key_version = fetch_request.public_key_version();
+    client_key = policy_storage()->signature_provider()->GetKeyByVersion(
+        public_key_version);
     if (!client_key) {
       error_msg->assign(base::StringPrintf("Invalid public key version: %d",
                                            public_key_version));
@@ -128,16 +112,12 @@
   }
 
   // Choose the key for signing the policy.
-  int signing_key_version = signature_provider->current_key_version();
-  if (fetch_request.has_public_key_version() &&
-      signature_provider->rotate_keys()) {
-    signing_key_version = public_key_version + 1;
-  }
   const SignatureProvider::SigningKey* signing_key =
-      signature_provider->GetKeyByVersion(signing_key_version);
+      policy_storage()->signature_provider()->GetCurrentKey();
   if (!signing_key) {
     error_msg->assign(base::StringPrintf(
-        "Can't find signin key for version: %d", signing_key_version));
+        "Can't find signin key for version: %d",
+        policy_storage()->signature_provider()->current_key_version()));
     return false;
   }
 
@@ -147,9 +127,7 @@
                                 ? base::Time::Now().ToJavaTime()
                                 : policy_storage()->timestamp().ToJavaTime());
   policy_data.set_request_token(client_info.device_token);
-  policy_data.set_policy_value(policy_storage()->GetPolicyPayload(
-      policy_type, fetch_request.settings_entity_id()));
-  policy_data.set_settings_entity_id(fetch_request.settings_entity_id());
+  policy_data.set_policy_value(policy_storage()->GetPolicyPayload(policy_type));
   policy_data.set_machine_name(client_info.machine_name);
   policy_data.set_service_account_identity(
       policy_storage()->service_account_identity().empty()
@@ -163,8 +141,10 @@
   policy_data.set_policy_invalidation_topic(
       policy_storage()->policy_invalidation_topic());
 
-  if (fetch_request.signature_type() != em::PolicyFetchRequest::NONE)
-    policy_data.set_public_key_version(signing_key_version);
+  if (fetch_request.signature_type() != em::PolicyFetchRequest::NONE) {
+    policy_data.set_public_key_version(
+        policy_storage()->signature_provider()->current_key_version());
+  }
 
   policy_data.SerializeToString(fetch_response->mutable_policy_data());
 
@@ -176,8 +156,8 @@
       return false;
     }
 
-    if (!fetch_request.has_public_key_version() ||
-        public_key_version != signing_key_version) {
+    if (fetch_request.public_key_version() !=
+        policy_storage()->signature_provider()->current_key_version()) {
       fetch_response->set_new_public_key(signing_key->public_key());
     }
 
@@ -205,27 +185,4 @@
   return true;
 }
 
-bool RequestHandlerForPolicy::ProcessCloudPolicyForExtensions(
-    const em::PolicyFetchRequest& fetch_request,
-    const ClientStorage::ClientInfo& client_info,
-    em::DevicePolicyResponse* response,
-    std::string* error_msg) {
-  // Send one PolicyFetchResponse for each extension configured on the server as
-  // the client does not actually tell us which extensions it has installed to
-  // protect user privacy.
-  std::vector<std::string> ids =
-      policy_storage()->GetEntityIdsForType(fetch_request.policy_type());
-  for (const std::string& id : ids) {
-    em::PolicyFetchRequest fetch_request_with_id;
-    fetch_request_with_id.CopyFrom(fetch_request);
-    fetch_request_with_id.set_settings_entity_id(id);
-    if (!ProcessCloudPolicy(fetch_request_with_id, client_info,
-                            response->add_responses(), error_msg)) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
 }  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_policy.h b/components/policy/test_support/request_handler_for_policy.h
index e8b2594..beabb9d 100644
--- a/components/policy/test_support/request_handler_for_policy.h
+++ b/components/policy/test_support/request_handler_for_policy.h
@@ -5,14 +5,13 @@
 #ifndef COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_POLICY_H_
 #define COMPONENTS_POLICY_TEST_SUPPORT_REQUEST_HANDLER_FOR_POLICY_H_
 
+#include "components/policy/test_support/embedded_policy_test_server.h"
+
 #include <string>
 
 #include "components/policy/test_support/client_storage.h"
-#include "components/policy/test_support/embedded_policy_test_server.h"
 
 namespace enterprise_management {
-class DevicePolicyResponse;
-class PolicyFetchRequest;
 class PolicyFetchResponse;
 }  // namespace enterprise_management
 
@@ -43,16 +42,6 @@
       const ClientStorage::ClientInfo& client,
       enterprise_management::PolicyFetchResponse* fetch_response,
       std::string* error_msg);
-
-  // Add to |response| the policies associated with |client_info| for extension
-  // policy type in |fetch_request|. Returns true is request is well-formed, or
-  // false otherwise (in which case, |error_msg| is set with the corresponding
-  // error message).
-  bool ProcessCloudPolicyForExtensions(
-      const enterprise_management::PolicyFetchRequest& fetch_request,
-      const ClientStorage::ClientInfo& client_info,
-      enterprise_management::DevicePolicyResponse* response,
-      std::string* error_msg);
 };
 
 }  // namespace policy
diff --git a/components/policy/test_support/request_handler_for_policy_unittest.cc b/components/policy/test_support/request_handler_for_policy_unittest.cc
index aff00a9dc..bdbf9f35 100644
--- a/components/policy/test_support/request_handler_for_policy_unittest.cc
+++ b/components/policy/test_support/request_handler_for_policy_unittest.cc
@@ -25,9 +25,6 @@
 constexpr char kMachineName[] = "machine_name";
 constexpr char kPolicyInvalidationTopic[] = "policy_invalidation_topic";
 constexpr char kUsername[] = "user-for-policy@example.com";
-#if !BUILDFLAG(IS_ANDROID)
-constexpr char kExtensionId[] = "extension_id";
-#endif  // !BUILDFLAG(IS_ANDROID)
 
 }  // namespace
 
@@ -99,13 +96,15 @@
   client_info.device_token = kDeviceToken;
   client_info.device_id = kDeviceId;
   client_info.machine_name = kMachineName;
-  client_info.allowed_policy_types.insert(dm_protocol::kChromeDevicePolicyType);
+  client_info.allowed_policy_types.insert(
+      dm_protocol::kChromeMachineLevelUserCloudPolicyType);
   client_storage()->RegisterClient(std::move(client_info));
 
   em::DeviceManagementRequest device_management_request;
   em::PolicyFetchRequest* fetch_request =
       device_management_request.mutable_policy_request()->add_requests();
-  fetch_request->set_policy_type(dm_protocol::kChromeRemoteCommandPolicyType);
+  fetch_request->set_policy_type(
+      dm_protocol::kChromeMachineLevelExtensionCloudPolicyType);
 
   SetDeviceTokenHeader(kDeviceToken);
   SetPayload(device_management_request);
@@ -287,81 +286,4 @@
   EXPECT_FALSE(fetch_response.new_public_key_signature().empty());
 }
 
-#if !BUILDFLAG(IS_ANDROID)
-TEST_F(RequestHandlerForPolicyTest, HandleRequest_Success_ExtensionPolicies) {
-  ClientStorage::ClientInfo client_info;
-  client_info.device_token = kDeviceToken;
-  client_info.device_id = kDeviceId;
-  client_info.username = kUsername;
-  client_info.allowed_policy_types.insert(
-      dm_protocol::kChromeExtensionPolicyType);
-  client_storage()->RegisterClient(client_info);
-
-  em::CloudPolicySettings settings;
-  settings.mutable_extensionsettings()->mutable_value()->assign(
-      "extension-policy");
-  policy_storage()->SetPolicyPayload(dm_protocol::kChromeExtensionPolicyType,
-                                     kExtensionId,
-                                     settings.SerializeAsString());
-
-  em::DeviceManagementRequest device_management_request;
-  device_management_request.mutable_policy_request()
-      ->add_requests()
-      ->set_policy_type(dm_protocol::kChromeExtensionPolicyType);
-
-  SetDeviceTokenHeader(kDeviceToken);
-  SetPayload(device_management_request);
-
-  StartRequestAndWait();
-
-  EXPECT_EQ(GetResponseCode(), net::HTTP_OK);
-
-  ASSERT_TRUE(HasResponseBody());
-  em::DeviceManagementResponse device_management_response =
-      GetDeviceManagementResponse();
-
-  ASSERT_EQ(device_management_response.policy_response().responses_size(), 1);
-  const em::PolicyFetchResponse& fetch_response =
-      device_management_response.policy_response().responses(0);
-  em::PolicyData policy_data;
-  policy_data.ParseFromString(fetch_response.policy_data());
-  EXPECT_EQ(policy_data.policy_type(), dm_protocol::kChromeExtensionPolicyType);
-  EXPECT_EQ(policy_data.policy_value(),
-            policy_storage()->GetPolicyPayload(
-                dm_protocol::kChromeExtensionPolicyType, kExtensionId));
-}
-
-TEST_F(RequestHandlerForPolicyTest,
-       HandleRequest_ExtensionPoliciesWithMissingSigningKey) {
-  ClientStorage::ClientInfo client_info;
-  client_info.device_token = kDeviceToken;
-  client_info.device_id = kDeviceId;
-  client_info.username = kUsername;
-  client_info.allowed_policy_types.insert(
-      dm_protocol::kChromeMachineLevelExtensionCloudPolicyType);
-  client_storage()->RegisterClient(client_info);
-
-  em::CloudPolicySettings settings;
-  settings.mutable_extensionsettings()->mutable_value()->assign(
-      "extension-policy");
-  policy_storage()->SetPolicyPayload(
-      dm_protocol::kChromeMachineLevelExtensionCloudPolicyType, kExtensionId,
-      settings.SerializeAsString());
-  policy_storage()->signature_provider()->set_current_key_version(-1);
-
-  em::DeviceManagementRequest device_management_request;
-  device_management_request.mutable_policy_request()
-      ->add_requests()
-      ->set_policy_type(
-          dm_protocol::kChromeMachineLevelExtensionCloudPolicyType);
-
-  SetDeviceTokenHeader(kDeviceToken);
-  SetPayload(device_management_request);
-
-  StartRequestAndWait();
-
-  EXPECT_EQ(GetResponseCode(), net::HTTP_BAD_REQUEST);
-}
-#endif  // !BUILDFLAG(IS_ANDROID)
-
 }  // namespace policy
diff --git a/components/policy/test_support/signature_provider.cc b/components/policy/test_support/signature_provider.cc
index 7cd6787..31cf722 100644
--- a/components/policy/test_support/signature_provider.cc
+++ b/components/policy/test_support/signature_provider.cc
@@ -199,15 +199,10 @@
 const SignatureProvider::SigningKey* SignatureProvider::GetKeyByVersion(
     int key_version) const {
   // |key_version| is 1-based.
-  if (key_version < 1)
-    return nullptr;
-  size_t key_index = static_cast<size_t>(key_version) - 1;
-  if (key_index >= signing_keys_.size()) {
-    if (!rotate_keys())
-      return nullptr;
-    key_index %= signing_keys_.size();
-  }
-  return &signing_keys_[key_index];
+  return key_version > 0 &&
+                 static_cast<size_t>(key_version) <= signing_keys_.size()
+             ? &signing_keys_[key_version - 1]
+             : nullptr;
 }
 
 const SignatureProvider::SigningKey* SignatureProvider::GetCurrentKey() const {
diff --git a/components/policy/test_support/signature_provider.h b/components/policy/test_support/signature_provider.h
index 327cb705..4d595db 100644
--- a/components/policy/test_support/signature_provider.h
+++ b/components/policy/test_support/signature_provider.h
@@ -79,20 +79,11 @@
     current_key_version_ = current_key_version;
   }
 
-  bool rotate_keys() const { return rotate_keys_; }
-  void set_rotate_keys(bool rotate_keys) { rotate_keys_ = rotate_keys; }
-
  private:
   std::vector<SigningKey> signing_keys_;
 
   // The key version to be used if no key version is defined by the client.
   int current_key_version_ = 1;
-
-  // Whether to rotate signing keys or to fail when last key is reached. The
-  // policy keys will be rotated in a round-robin fashion for each policy
-  // request (by default, the |current_key_version_| will be used for all
-  // requests).
-  bool rotate_keys_ = false;
 };
 
 }  // namespace policy
diff --git a/components/prefs/scoped_user_pref_update.h b/components/prefs/scoped_user_pref_update.h
index e2d894f..805e146e 100644
--- a/components/prefs/scoped_user_pref_update.h
+++ b/components/prefs/scoped_user_pref_update.h
@@ -62,11 +62,14 @@
 
 }  // namespace subtle
 
-// Class to support modifications to DictionaryValues and ListValues while
-// guaranteeing that PrefObservers are notified of changed values.
+// Class to support modifications to Values, DictionaryValues and ListValues
+// while guaranteeing that PrefObservers are notified of changed values.
 //
 // This class may only be used on the UI thread as it requires access to the
 // PrefService.
+//
+// TODO(crbug.com/1285745): Replace T with base::Value once the deprecated
+// ListValue and DictionaryValue options are removed.
 template <typename T, base::Value::Type type_enum_value>
 class ScopedUserPrefUpdate : public subtle::ScopedUserPrefUpdateBase {
  public:
@@ -103,10 +106,20 @@
   }
 };
 
+// DictionaryPrefUpdateDeprecated and ListPrefUpdateDeprecated rely on the
+// deprecated base::DictionaryValue and base::ListValue. Use
+// DictionaryPrefUpdate and ListPrefUpdate going forward to modify dictionaries
+// and lists.
+// TODO(crbug.com/1285745): Remove deprecated options when no longer used.
 typedef ScopedUserPrefUpdate<base::DictionaryValue,
                              base::Value::Type::DICTIONARY>
-    DictionaryPrefUpdate;
+    DictionaryPrefUpdateDeprecated;
 typedef ScopedUserPrefUpdate<base::ListValue, base::Value::Type::LIST>
+    ListPrefUpdateDeprecated;
+
+typedef ScopedUserPrefUpdate<base::Value, base::Value::Type::DICTIONARY>
+    DictionaryPrefUpdate;
+typedef ScopedUserPrefUpdate<base::Value, base::Value::Type::LIST>
     ListPrefUpdate;
 
 #endif  // COMPONENTS_PREFS_SCOPED_USER_PREF_UPDATE_H_
diff --git a/components/prefs/scoped_user_pref_update_unittest.cc b/components/prefs/scoped_user_pref_update_unittest.cc
index 050606d5..551c80d 100644
--- a/components/prefs/scoped_user_pref_update_unittest.cc
+++ b/components/prefs/scoped_user_pref_update_unittest.cc
@@ -40,15 +40,15 @@
 
 TEST_F(ScopedUserPrefUpdateTest, RegularUse) {
   // Dictionary that will be expected to be set at the end.
-  base::DictionaryValue expected_dictionary;
-  expected_dictionary.SetString(kKey, kValue);
+  base::Value expected_dictionary(base::Value::Type::DICTIONARY);
+  expected_dictionary.SetStringKey(kKey, kValue);
 
   {
     EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
     DictionaryPrefUpdate update(&prefs_, kPref);
-    base::DictionaryValue* value = update.Get();
+    base::Value* value = update.Get();
     ASSERT_TRUE(value);
-    value->SetString(kKey, kValue);
+    value->SetStringKey(kKey, kValue);
 
     // The dictionary was created for us but the creation should have happened
     // silently without notifications.
@@ -72,9 +72,7 @@
 TEST_F(ScopedUserPrefUpdateTest, NeverTouchAnything) {
   const base::Value* old_value = prefs_.GetDictionary(kPref);
   EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
-  {
-    DictionaryPrefUpdate update(&prefs_, kPref);
-  }
+  { DictionaryPrefUpdate update(&prefs_, kPref); }
   const base::Value* new_value = prefs_.GetDictionary(kPref);
   EXPECT_EQ(old_value, new_value);
   Mock::VerifyAndClearExpectations(&observer_);
@@ -97,6 +95,75 @@
 
 TEST_F(ScopedUserPrefUpdateTest, UpdatingDictionaryPrefWithDefaults) {
   base::Value defaults(base::Value::Type::DICTIONARY);
+  defaults.SetStringKey("firstkey", "value");
+  defaults.SetStringKey("secondkey", "value");
+
+  std::string pref_name = "mypref";
+  prefs_.registry()->RegisterDictionaryPref(pref_name, std::move(defaults));
+  EXPECT_EQ(2u, prefs_.GetDictionary(pref_name)->DictSize());
+
+  DictionaryPrefUpdate update(&prefs_, pref_name);
+  update->SetStringKey("thirdkey", "value");
+  EXPECT_EQ(3u, prefs_.GetDictionary(pref_name)->DictSize());
+}
+
+TEST_F(ScopedUserPrefUpdateTest, RegularUseDeprecated) {
+  // Dictionary that will be expected to be set at the end.
+  base::DictionaryValue expected_dictionary;
+  expected_dictionary.SetString(kKey, kValue);
+
+  {
+    EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+    DictionaryPrefUpdateDeprecated update(&prefs_, kPref);
+    base::DictionaryValue* value = update.Get();
+    ASSERT_TRUE(value);
+    value->SetString(kKey, kValue);
+
+    // The dictionary was created for us but the creation should have happened
+    // silently without notifications.
+    Mock::VerifyAndClearExpectations(&observer_);
+
+    // Modifications happen online and are instantly visible, though.
+    const base::Value* current_value = prefs_.GetDictionary(kPref);
+    ASSERT_TRUE(current_value);
+    EXPECT_TRUE(expected_dictionary.Equals(current_value));
+
+    // Now we are leaving the scope of the update so we should be notified.
+    observer_.Expect(kPref, &expected_dictionary);
+  }
+  Mock::VerifyAndClearExpectations(&observer_);
+
+  const base::Value* current_value = prefs_.GetDictionary(kPref);
+  ASSERT_TRUE(current_value);
+  EXPECT_TRUE(expected_dictionary.Equals(current_value));
+}
+
+TEST_F(ScopedUserPrefUpdateTest, NeverTouchAnythingDeprecated) {
+  const base::Value* old_value = prefs_.GetDictionary(kPref);
+  EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+  { DictionaryPrefUpdateDeprecated update(&prefs_, kPref); }
+  const base::Value* new_value = prefs_.GetDictionary(kPref);
+  EXPECT_EQ(old_value, new_value);
+  Mock::VerifyAndClearExpectations(&observer_);
+}
+
+TEST_F(ScopedUserPrefUpdateTest, UpdatingListPrefWithDefaultsDeprecated) {
+  base::Value::ListStorage defaults;
+  defaults.emplace_back("firstvalue");
+  defaults.emplace_back("secondvalue");
+
+  std::string pref_name = "mypref";
+  prefs_.registry()->RegisterListPref(pref_name,
+                                      base::Value(std::move(defaults)));
+  EXPECT_EQ(2u, prefs_.GetList(pref_name)->GetList().size());
+
+  ListPrefUpdateDeprecated update(&prefs_, pref_name);
+  update->Append("thirdvalue");
+  EXPECT_EQ(3u, prefs_.GetList(pref_name)->GetList().size());
+}
+
+TEST_F(ScopedUserPrefUpdateTest, UpdatingDictionaryPrefWithDefaultsDeprecated) {
+  base::Value defaults(base::Value::Type::DICTIONARY);
   defaults.SetKey("firstkey", base::Value("value"));
   defaults.SetKey("secondkey", base::Value("value"));
 
@@ -104,7 +171,7 @@
   prefs_.registry()->RegisterDictionaryPref(pref_name, std::move(defaults));
   EXPECT_EQ(2u, prefs_.GetDictionary(pref_name)->DictSize());
 
-  DictionaryPrefUpdate update(&prefs_, pref_name);
+  DictionaryPrefUpdateDeprecated update(&prefs_, pref_name);
   update->SetKey("thirdkey", base::Value("value"));
   EXPECT_EQ(3u, prefs_.GetDictionary(pref_name)->DictSize());
 }
diff --git a/components/quirks/quirks_manager.cc b/components/quirks/quirks_manager.cc
index f3601d8..57acd27 100644
--- a/components/quirks/quirks_manager.cc
+++ b/components/quirks/quirks_manager.cc
@@ -206,7 +206,8 @@
 void QuirksManager::SetLastServerCheck(int64_t product_id,
                                        const base::Time& last_check) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DictionaryPrefUpdate dict(local_state_, prefs::kQuirksClientLastServerCheck);
+  DictionaryPrefUpdateDeprecated dict(local_state_,
+                                      prefs::kQuirksClientLastServerCheck);
   dict->SetDouble(IdToHexString(product_id), last_check.ToDoubleT());
 }
 
diff --git a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
index ced9006c6..a7e12a4 100644
--- a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
+++ b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.cc
@@ -2031,34 +2031,35 @@
   WebUIInfoSingleton::GetInstance()->UnregisterWebUIInstance(this);
 }
 
-void SafeBrowsingUIHandler::GetExperiments(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetExperiments(
+    const base::Value::ConstListView args) {
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), GetFeatureStatusList());
 }
 
-void SafeBrowsingUIHandler::GetPrefs(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetPrefs(const base::Value::ConstListView args) {
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id),
                             safe_browsing::GetSafeBrowsingPreferencesList(
                                 user_prefs::UserPrefs::Get(browser_context_)));
 }
 
-void SafeBrowsingUIHandler::GetPolicies(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetPolicies(const base::Value::ConstListView args) {
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id),
                             safe_browsing::GetSafeBrowsingPoliciesList(
                                 user_prefs::UserPrefs::Get(browser_context_)));
 }
 
-void SafeBrowsingUIHandler::GetCookie(const base::ListValue* args) {
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+void SafeBrowsingUIHandler::GetCookie(const base::Value::ConstListView args) {
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
 
   cookie_manager_remote_ =
       WebUIInfoSingleton::GetInstance()->GetCookieManager(browser_context_);
@@ -2088,7 +2089,8 @@
                             base::Value(std::move(response)));
 }
 
-void SafeBrowsingUIHandler::GetSavedPasswords(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetSavedPasswords(
+    const base::Value::ConstListView args) {
   password_manager::HashPasswordManager hash_manager(
       user_prefs::UserPrefs::Get(browser_context_));
 
@@ -2100,13 +2102,13 @@
   }
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), saved_passwords);
 }
 
 void SafeBrowsingUIHandler::GetDatabaseManagerInfo(
-    const base::ListValue* args) {
+    const base::Value::ConstListView args) {
   base::ListValue database_manager_info;
 
 #if BUILDFLAG(SAFE_BROWSING_DB_LOCAL)
@@ -2134,8 +2136,8 @@
 #endif
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
 
   ResolveJavascriptCallback(base::Value(callback_id), database_manager_info);
 }
@@ -2208,7 +2210,7 @@
 }
 
 void SafeBrowsingUIHandler::GetDownloadUrlsChecked(
-    const base::ListValue* args) {
+    const base::Value::ConstListView args) {
   const std::vector<std::pair<std::vector<GURL>, DownloadCheckResult>>&
       urls_checked = WebUIInfoSingleton::GetInstance()->download_urls_checked();
 
@@ -2220,13 +2222,13 @@
   }
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), urls_checked_value);
 }
 
 void SafeBrowsingUIHandler::GetSentClientDownloadRequests(
-    const base::ListValue* args) {
+    const base::Value::ConstListView args) {
   const std::vector<std::unique_ptr<ClientDownloadRequest>>& cdrs =
       WebUIInfoSingleton::GetInstance()->client_download_requests_sent();
 
@@ -2237,13 +2239,13 @@
   }
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), cdrs_sent);
 }
 
 void SafeBrowsingUIHandler::GetReceivedClientDownloadResponses(
-    const base::ListValue* args) {
+    const base::Value::ConstListView args) {
   const std::vector<std::unique_ptr<ClientDownloadResponse>>& cdrs =
       WebUIInfoSingleton::GetInstance()->client_download_responses_received();
 
@@ -2254,13 +2256,13 @@
   }
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), cdrs_received);
 }
 
 void SafeBrowsingUIHandler::GetSentClientPhishingRequests(
-    const base::ListValue* args) {
+    const base::Value::ConstListView args) {
   const std::vector<ClientPhishingRequestAndToken>& cprs =
       WebUIInfoSingleton::GetInstance()->client_phishing_requests_sent();
 
@@ -2271,13 +2273,13 @@
   }
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), cprs_sent);
 }
 
 void SafeBrowsingUIHandler::GetReceivedClientPhishingResponses(
-    const base::ListValue* args) {
+    const base::Value::ConstListView args) {
   const std::vector<std::unique_ptr<ClientPhishingResponse>>& cprs =
       WebUIInfoSingleton::GetInstance()->client_phishing_responses_received();
 
@@ -2288,12 +2290,13 @@
   }
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), cprs_received);
 }
 
-void SafeBrowsingUIHandler::GetSentCSBRRs(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetSentCSBRRs(
+    const base::Value::ConstListView args) {
   const std::vector<std::unique_ptr<ClientSafeBrowsingReportRequest>>& reports =
       WebUIInfoSingleton::GetInstance()->csbrrs_sent();
 
@@ -2304,12 +2307,12 @@
   }
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), sent_reports);
 }
 
-void SafeBrowsingUIHandler::GetPGEvents(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetPGEvents(const base::Value::ConstListView args) {
   const std::vector<sync_pb::UserEventSpecifics>& events =
       WebUIInfoSingleton::GetInstance()->pg_event_log();
 
@@ -2319,12 +2322,13 @@
     events_sent.Append(SerializePGEvent(event));
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), events_sent);
 }
 
-void SafeBrowsingUIHandler::GetSecurityEvents(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetSecurityEvents(
+    const base::Value::ConstListView args) {
   const std::vector<sync_pb::GaiaPasswordReuse>& events =
       WebUIInfoSingleton::GetInstance()->security_event_log();
 
@@ -2334,12 +2338,12 @@
     events_sent.Append(SerializeSecurityEvent(event));
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), events_sent);
 }
 
-void SafeBrowsingUIHandler::GetPGPings(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetPGPings(const base::Value::ConstListView args) {
   const std::vector<LoginReputationClientRequestAndToken> requests =
       WebUIInfoSingleton::GetInstance()->pg_pings();
 
@@ -2353,12 +2357,13 @@
   }
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), pings_sent);
 }
 
-void SafeBrowsingUIHandler::GetPGResponses(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetPGResponses(
+    const base::Value::ConstListView args) {
   const std::map<int, LoginReputationClientResponse> responses =
       WebUIInfoSingleton::GetInstance()->pg_responses();
 
@@ -2372,12 +2377,13 @@
   }
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), responses_sent);
 }
 
-void SafeBrowsingUIHandler::GetRTLookupPings(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetRTLookupPings(
+    const base::Value::ConstListView args) {
   const std::vector<RTLookupRequestAndToken> requests =
       WebUIInfoSingleton::GetInstance()->rt_lookup_pings();
 
@@ -2392,12 +2398,13 @@
   }
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), pings_sent);
 }
 
-void SafeBrowsingUIHandler::GetRTLookupResponses(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetRTLookupResponses(
+    const base::Value::ConstListView args) {
   const std::map<int, RTLookupResponse> responses =
       WebUIInfoSingleton::GetInstance()->rt_lookup_responses();
 
@@ -2411,20 +2418,21 @@
   }
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), responses_sent);
 }
 
-void SafeBrowsingUIHandler::GetReferrerChain(const base::ListValue* args) {
-  DCHECK_GE(args->GetList().size(), 2U);
-  std::string url_string = args->GetList()[1].GetString();
+void SafeBrowsingUIHandler::GetReferrerChain(
+    const base::Value::ConstListView args) {
+  DCHECK_GE(args.size(), 2U);
+  std::string url_string = args[1].GetString();
 
   ReferrerChainProvider* provider =
       WebUIInfoSingleton::GetInstance()->GetReferrerChainProvider(
           browser_context_);
 
-  std::string callback_id = args->GetList()[0].GetString();
+  std::string callback_id = args[0].GetString();
 
   if (!provider) {
     AllowJavascript();
@@ -2451,7 +2459,8 @@
                             base::Value(referrer_chain_serialized));
 }
 
-void SafeBrowsingUIHandler::GetReferringAppInfo(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetReferringAppInfo(
+    const base::Value::ConstListView args) {
   base::Value referring_app_value;
 #if defined(OS_ANDROID)
   LoginReputationClientRequest::ReferringAppInfo info =
@@ -2465,13 +2474,14 @@
   serializer.Serialize(referring_app_value);
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id),
                             base::Value(referring_app_serialized));
 }
 
-void SafeBrowsingUIHandler::GetReportingEvents(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetReportingEvents(
+    const base::Value::ConstListView args) {
   base::ListValue reporting_events;
   for (const auto& reporting_event :
        WebUIInfoSingleton::GetInstance()->reporting_events()) {
@@ -2479,12 +2489,13 @@
   }
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), reporting_events);
 }
 
-void SafeBrowsingUIHandler::GetLogMessages(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetLogMessages(
+    const base::Value::ConstListView args) {
   const std::vector<std::pair<base::Time, std::string>>& log_messages =
       WebUIInfoSingleton::GetInstance()->log_messages();
 
@@ -2495,12 +2506,13 @@
   }
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), messages_received);
 }
 
-void SafeBrowsingUIHandler::GetDeepScans(const base::ListValue* args) {
+void SafeBrowsingUIHandler::GetDeepScans(
+    const base::Value::ConstListView args) {
   base::ListValue pings_sent;
 #if BUILDFLAG(FULL_SAFE_BROWSING)
   for (const auto& token_and_data :
@@ -2511,8 +2523,8 @@
 #endif
 
   AllowJavascript();
-  DCHECK(!args->GetList().empty());
-  std::string callback_id = args->GetList()[0].GetString();
+  DCHECK(!args.empty());
+  std::string callback_id = args[0].GetString();
   ResolveJavascriptCallback(base::Value(callback_id), pings_sent);
 }
 
@@ -2643,92 +2655,92 @@
 #endif
 
 void SafeBrowsingUIHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getExperiments",
       base::BindRepeating(&SafeBrowsingUIHandler::GetExperiments,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getPolicies", base::BindRepeating(&SafeBrowsingUIHandler::GetPolicies,
                                          base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getPrefs", base::BindRepeating(&SafeBrowsingUIHandler::GetPrefs,
                                       base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getCookie", base::BindRepeating(&SafeBrowsingUIHandler::GetCookie,
                                        base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getSavedPasswords",
       base::BindRepeating(&SafeBrowsingUIHandler::GetSavedPasswords,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getDatabaseManagerInfo",
       base::BindRepeating(&SafeBrowsingUIHandler::GetDatabaseManagerInfo,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getDownloadUrlsChecked",
       base::BindRepeating(&SafeBrowsingUIHandler::GetDownloadUrlsChecked,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getSentClientDownloadRequests",
       base::BindRepeating(&SafeBrowsingUIHandler::GetSentClientDownloadRequests,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getReceivedClientDownloadResponses",
       base::BindRepeating(
           &SafeBrowsingUIHandler::GetReceivedClientDownloadResponses,
           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getSentClientPhishingRequests",
       base::BindRepeating(&SafeBrowsingUIHandler::GetSentClientPhishingRequests,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getReceivedClientPhishingResponses",
       base::BindRepeating(
           &SafeBrowsingUIHandler::GetReceivedClientPhishingResponses,
           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getSentCSBRRs",
       base::BindRepeating(&SafeBrowsingUIHandler::GetSentCSBRRs,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getPGEvents", base::BindRepeating(&SafeBrowsingUIHandler::GetPGEvents,
                                          base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getSecurityEvents",
       base::BindRepeating(&SafeBrowsingUIHandler::GetSecurityEvents,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getPGPings", base::BindRepeating(&SafeBrowsingUIHandler::GetPGPings,
                                         base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getPGResponses",
       base::BindRepeating(&SafeBrowsingUIHandler::GetPGResponses,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getRTLookupPings",
       base::BindRepeating(&SafeBrowsingUIHandler::GetRTLookupPings,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getRTLookupResponses",
       base::BindRepeating(&SafeBrowsingUIHandler::GetRTLookupResponses,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getLogMessages",
       base::BindRepeating(&SafeBrowsingUIHandler::GetLogMessages,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getReferrerChain",
       base::BindRepeating(&SafeBrowsingUIHandler::GetReferrerChain,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getReferringAppInfo",
       base::BindRepeating(&SafeBrowsingUIHandler::GetReferringAppInfo,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getReportingEvents",
       base::BindRepeating(&SafeBrowsingUIHandler::GetReportingEvents,
                           base::Unretained(this)));
-  web_ui()->RegisterDeprecatedMessageCallback(
+  web_ui()->RegisterMessageCallback(
       "getDeepScans", base::BindRepeating(&SafeBrowsingUIHandler::GetDeepScans,
                                           base::Unretained(this)));
 }
diff --git a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h
index 86931f2b..da46661 100644
--- a/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h
+++ b/components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h
@@ -97,90 +97,92 @@
   void OnJavascriptDisallowed() override;
 
   // Get the experiments that are currently enabled per Chrome instance.
-  void GetExperiments(const base::ListValue* args);
+  void GetExperiments(const base::Value::ConstListView args);
 
   // Get the Safe Browsing related preferences for the current user.
-  void GetPrefs(const base::ListValue* args);
+  void GetPrefs(const base::Value::ConstListView args);
 
   // Get the Safe Browsing related policies for the current user.
-  void GetPolicies(const base::ListValue* args);
+  void GetPolicies(const base::Value::ConstListView args);
 
   // Get the Safe Browsing cookie.
-  void GetCookie(const base::ListValue* args);
+  void GetCookie(const base::Value::ConstListView args);
 
   // Get the current captured passwords.
-  void GetSavedPasswords(const base::ListValue* args);
+  void GetSavedPasswords(const base::Value::ConstListView args);
 
   // Get the information related to the Safe Browsing database and full hash
   // cache.
-  void GetDatabaseManagerInfo(const base::ListValue* args);
+  void GetDatabaseManagerInfo(const base::Value::ConstListView args);
 
   // Get the download URLs that have been checked since the oldest currently
   // open chrome://safe-browsing tab was opened.
-  void GetDownloadUrlsChecked(const base::ListValue* args);
+  void GetDownloadUrlsChecked(const base::Value::ConstListView args);
 
   // Get the ClientDownloadRequests that have been collected since the oldest
   // currently open chrome://safe-browsing tab was opened.
-  void GetSentClientDownloadRequests(const base::ListValue* args);
+  void GetSentClientDownloadRequests(const base::Value::ConstListView args);
 
   // Get the ClientDownloadReponses that have been collected since the oldest
   // currently open chrome://safe-browsing tab was opened.
-  void GetReceivedClientDownloadResponses(const base::ListValue* args);
+  void GetReceivedClientDownloadResponses(
+      const base::Value::ConstListView args);
 
   // Get the ClientPhishingRequests that have been collected since the oldest
   // currently open chrome://safe-browsing tab was opened.
-  void GetSentClientPhishingRequests(const base::ListValue* args);
+  void GetSentClientPhishingRequests(const base::Value::ConstListView args);
 
   // Get the ClientPhishingResponses that have been collected since the oldest
   // currently open chrome://safe-browsing tab was opened.
-  void GetReceivedClientPhishingResponses(const base::ListValue* args);
+  void GetReceivedClientPhishingResponses(
+      const base::Value::ConstListView args);
 
   // Get the ThreatDetails that have been collected since the oldest currently
   // open chrome://safe-browsing tab was opened.
-  void GetSentCSBRRs(const base::ListValue* args);
+  void GetSentCSBRRs(const base::Value::ConstListView args);
 
   // Get the PhishGuard events that have been collected since the oldest
   // currently open chrome://safe-browsing tab was opened.
-  void GetPGEvents(const base::ListValue* args);
+  void GetPGEvents(const base::Value::ConstListView args);
 
   // Get the Security events that have been collected since the oldest
   // currently open chrome://safe-browsing tab was opened.
-  void GetSecurityEvents(const base::ListValue* args);
+  void GetSecurityEvents(const base::Value::ConstListView args);
 
   // Get the PhishGuard pings that have been sent since the oldest currently
   // open chrome://safe-browsing tab was opened.
-  void GetPGPings(const base::ListValue* args);
+  void GetPGPings(const base::Value::ConstListView args);
 
   // Get the PhishGuard responses that have been received since the oldest
   // currently open chrome://safe-browsing tab was opened.
-  void GetPGResponses(const base::ListValue* args);
+  void GetPGResponses(const base::Value::ConstListView args);
 
   // Get the real time lookup pings that have been sent since the oldest
   // currently open chrome://safe-browsing tab was opened.
-  void GetRTLookupPings(const base::ListValue* args);
+  void GetRTLookupPings(const base::Value::ConstListView args);
 
   // Get the real time lookup responses that have been received since the oldest
   // currently open chrome://safe-browsing tab was opened.
-  void GetRTLookupResponses(const base::ListValue* args);
+  void GetRTLookupResponses(const base::Value::ConstListView args);
 
   // Get the current referrer chain for a given URL.
-  void GetReferrerChain(const base::ListValue* args);
+  void GetReferrerChain(const base::Value::ConstListView args);
 
   // Get the referring app info that launches Chrome on Android. Always set to
   // null if it's called from platforms other than Android.
-  void GetReferringAppInfo(const base::ListValue* args);
+  void GetReferringAppInfo(const base::Value::ConstListView args);
 
   // Get the list of log messages that have been received since the oldest
   // currently open chrome://safe-browsing tab was opened.
-  void GetLogMessages(const base::ListValue* args);
+  void GetLogMessages(const base::Value::ConstListView args);
 
   // Get the reporting events that have been collected since the oldest
   // currently open chrome://safe-browsing tab was opened.
-  void GetReportingEvents(const base::ListValue* args);
+  void GetReportingEvents(const base::Value::ConstListView args);
 
   // Get the deep scanning requests that have been collected since the oldest
   // currently open chrome://safe-browsing tab was opened.
-  void GetDeepScans(const base::ListValue* args);
+  void GetDeepScans(const base::Value::ConstListView args);
 
   // Register callbacks for WebUI messages.
   void RegisterMessages() override;
diff --git a/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc b/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc
index b99ae6f..bf265a9 100644
--- a/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc
+++ b/components/safe_browsing/core/browser/safe_browsing_metrics_collector.cc
@@ -154,8 +154,8 @@
 }
 
 void SafeBrowsingMetricsCollector::RemoveOldEventsFromPref() {
-  DictionaryPrefUpdate update(pref_service_,
-                              prefs::kSafeBrowsingEventTimestamps);
+  DictionaryPrefUpdateDeprecated update(pref_service_,
+                                        prefs::kSafeBrowsingEventTimestamps);
   base::DictionaryValue* mutable_state_dict = update.Get();
   bool is_pref_valid = mutable_state_dict->is_dict();
   base::UmaHistogramBoolean("SafeBrowsing.MetricsCollector.IsPrefValid",
@@ -218,8 +218,8 @@
 void SafeBrowsingMetricsCollector::AddSafeBrowsingEventAndUserStateToPref(
     UserState user_state,
     EventType event_type) {
-  DictionaryPrefUpdate update(pref_service_,
-                              prefs::kSafeBrowsingEventTimestamps);
+  DictionaryPrefUpdateDeprecated update(pref_service_,
+                                        prefs::kSafeBrowsingEventTimestamps);
   base::DictionaryValue* mutable_state_dict = update.Get();
 
   base::Value* event_dict =
diff --git a/components/search_provider_logos/google_logo_api.cc b/components/search_provider_logos/google_logo_api.cc
index f9d61c9..65abb1d 100644
--- a/components/search_provider_logos/google_logo_api.cc
+++ b/components/search_provider_logos/google_logo_api.cc
@@ -12,6 +12,7 @@
 #include "base/base64.h"
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/check.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/json/json_reader.h"
@@ -76,14 +77,15 @@
 namespace {
 const char kResponsePreamble[] = ")]}'";
 
-GURL ParseUrl(const base::DictionaryValue& parent_dict,
+GURL ParseUrl(const base::Value& parent_dict,
               const std::string& key,
               const GURL& base_url) {
-  std::string url_str;
-  if (!parent_dict.GetString(key, &url_str) || url_str.empty()) {
+  DCHECK(parent_dict.is_dict());
+  const std::string* url_str = parent_dict.FindStringKey(key);
+  if (!url_str || url_str->empty()) {
     return GURL();
   }
-  GURL result = base_url.Resolve(url_str);
+  GURL result = base_url.Resolve(*url_str);
   // If the base URL is https:// (which should almost always be the case, see
   // above), then we require all other URLs to be https:// too.
   if (base_url.SchemeIs(url::kHttpsScheme) &&
@@ -159,13 +161,11 @@
     return nullptr;
   }
 
-  std::unique_ptr<base::DictionaryValue> config = base::DictionaryValue::From(
-      base::Value::ToUniquePtrValue(std::move(*parsed_json.value)));
-  if (!config)
+  if (!parsed_json.value->is_dict())
     return nullptr;
 
-  const base::DictionaryValue* ddljson = nullptr;
-  if (!config->GetDictionary("ddljson", &ddljson))
+  const base::Value* ddljson = parsed_json.value->FindDictKey("ddljson");
+  if (!ddljson)
     return nullptr;
 
   // If there is no logo today, the "ddljson" dictionary will be empty.
@@ -176,14 +176,14 @@
 
   auto logo = std::make_unique<EncodedLogo>();
 
-  std::string doodle_type;
+  const std::string* doodle_type = ddljson->FindStringKey("doodle_type");
   logo->metadata.type = LogoType::SIMPLE;
-  if (ddljson->GetString("doodle_type", &doodle_type)) {
-    if (doodle_type == "ANIMATED") {
+  if (doodle_type) {
+    if (*doodle_type == "ANIMATED") {
       logo->metadata.type = LogoType::ANIMATED;
-    } else if (doodle_type == "INTERACTIVE") {
+    } else if (*doodle_type == "INTERACTIVE") {
       logo->metadata.type = LogoType::INTERACTIVE;
-    } else if (doodle_type == "VIDEO") {
+    } else if (*doodle_type == "VIDEO") {
       logo->metadata.type = LogoType::INTERACTIVE;
     }
   }
@@ -195,30 +195,37 @@
   // Check if the main image is animated.
   if (is_animated) {
     // If animated, get the URL for the animated image.
-    const base::DictionaryValue* image = nullptr;
-    if (!ddljson->GetDictionary("large_image", &image))
+    const base::Value* image = ddljson->FindDictKey("large_image");
+    if (!image)
       return nullptr;
     logo->metadata.animated_url = ParseUrl(*image, "url", base_url);
     if (!logo->metadata.animated_url.is_valid())
       return nullptr;
 
-    const base::DictionaryValue* dark_image = nullptr;
-    if (ddljson->GetDictionary("dark_large_image", &dark_image))
+    const base::Value* dark_image = ddljson->FindDictKey("dark_large_image");
+    if (dark_image)
       logo->metadata.dark_animated_url = ParseUrl(*dark_image, "url", base_url);
   }
 
   if (is_simple || is_animated) {
-    const base::DictionaryValue* image = nullptr;
-    if (ddljson->GetDictionary("large_image", &image)) {
-      image->GetInteger("width", &logo->metadata.width_px);
-      image->GetInteger("height", &logo->metadata.height_px);
+    const base::Value* image = ddljson->FindDictKey("large_image");
+    if (image) {
+      if (absl::optional<int> width_px = image->FindIntKey("width"))
+        logo->metadata.width_px = *width_px;
+      if (absl::optional<int> height_px = image->FindIntKey("height"))
+        logo->metadata.height_px = *height_px;
     }
-    const base::DictionaryValue* dark_image = nullptr;
-    if (ddljson->GetDictionary("dark_large_image", &dark_image)) {
-      dark_image->GetString("background_color",
-                            &logo->metadata.dark_background_color);
-      dark_image->GetInteger("width", &logo->metadata.dark_width_px);
-      dark_image->GetInteger("height", &logo->metadata.dark_height_px);
+
+    const base::Value* dark_image = ddljson->FindDictKey("dark_large_image");
+    if (dark_image) {
+      if (const std::string* background_color =
+              dark_image->FindStringKey("background_color")) {
+        logo->metadata.dark_background_color = *background_color;
+      }
+      if (absl::optional<int> width_px = dark_image->FindIntKey("width"))
+        logo->metadata.dark_width_px = *width_px;
+      if (absl::optional<int> height_px = dark_image->FindIntKey("height"))
+        logo->metadata.dark_height_px = *height_px;
     }
   }
 
@@ -227,44 +234,56 @@
        logo->metadata.type == LogoType::SIMPLE);
 
   if (is_eligible_for_share_button) {
-    const base::DictionaryValue* share_button = nullptr;
-    std::string short_link_str;
+    const base::Value* share_button = ddljson->FindDictKey("share_button");
+    const std::string* short_link_ptr = ddljson->FindStringKey("short_link");
     // The short link in the doodle proto is an incomplete URL with the format
     // //g.co/*, //doodle.gle/* or //google.com?doodle=*.
     // Complete the URL if possible.
-    if (ddljson->GetDictionary("share_button", &share_button) &&
-        ddljson->GetString("short_link", &short_link_str) &&
-        short_link_str.find("//") == 0) {
+    if (share_button && short_link_ptr && short_link_ptr->find("//") == 0) {
+      std::string short_link_str = *short_link_ptr;
       short_link_str.insert(0, "https:");
-      logo->metadata.short_link = GURL(short_link_str);
+      logo->metadata.short_link = GURL(std::move(short_link_str));
       if (logo->metadata.short_link.is_valid()) {
-        share_button->GetInteger("offset_x", &logo->metadata.share_button_x);
-        share_button->GetInteger("offset_y", &logo->metadata.share_button_y);
+        if (absl::optional<int> offset_x = share_button->FindIntKey("offset_x"))
+          logo->metadata.share_button_x = *offset_x;
+        if (absl::optional<int> offset_y = share_button->FindIntKey("offset_y"))
+          logo->metadata.share_button_y = *offset_y;
         if (absl::optional<double> opacity =
                 share_button->FindDoubleKey("opacity")) {
           logo->metadata.share_button_opacity = *opacity;
         }
-        share_button->GetString("icon_image",
-                                &logo->metadata.share_button_icon);
-        share_button->GetString("background_color",
-                                &logo->metadata.share_button_bg);
+        if (const std::string* icon = share_button->FindStringKey("icon_image"))
+          logo->metadata.share_button_icon = *icon;
+        if (const std::string* bg_color =
+                share_button->FindStringKey("background_color")) {
+          logo->metadata.share_button_bg = *bg_color;
+        }
       }
     }
-    const base::DictionaryValue* dark_share_button = nullptr;
-    if (ddljson->GetDictionary("dark_share_button", &dark_share_button)) {
+    const base::Value* dark_share_button =
+        ddljson->FindDictKey("dark_share_button");
+    if (dark_share_button) {
       if (logo->metadata.short_link.is_valid()) {
-        dark_share_button->GetInteger("offset_x",
-                                      &logo->metadata.dark_share_button_x);
-        dark_share_button->GetInteger("offset_y",
-                                      &logo->metadata.dark_share_button_y);
+        if (absl::optional<int> offset_x =
+                dark_share_button->FindIntKey("offset_x")) {
+          logo->metadata.dark_share_button_x = *offset_x;
+        }
+        if (absl::optional<int> offset_y =
+                dark_share_button->FindIntKey("offset_y")) {
+          logo->metadata.dark_share_button_y = *offset_y;
+        }
         if (absl::optional<double> opacity =
                 dark_share_button->FindDoubleKey("opacity")) {
           logo->metadata.dark_share_button_opacity = *opacity;
         }
-        dark_share_button->GetString("icon_image",
-                                     &logo->metadata.dark_share_button_icon);
-        dark_share_button->GetString("background_color",
-                                     &logo->metadata.dark_share_button_bg);
+        if (const std::string* icon =
+                dark_share_button->FindStringKey("icon_image")) {
+          logo->metadata.dark_share_button_icon = *icon;
+        }
+        if (const std::string* bg_color =
+                dark_share_button->FindStringKey("background_color")) {
+          logo->metadata.dark_share_button_bg = *bg_color;
+        }
       }
     }
   }
@@ -274,24 +293,28 @@
 
   // Data is optional, since we may be revalidating a cached logo.
   // If there is a CTA image, get that; otherwise use the regular image.
-  std::string encoded_image_data;
-  if (ddljson->GetString("cta_data_uri", &encoded_image_data) ||
-      ddljson->GetString("data_uri", &encoded_image_data)) {
+  const std::string* encoded_image_data =
+      ddljson->FindStringKey("cta_data_uri");
+  if (!encoded_image_data)
+    encoded_image_data = ddljson->FindStringKey("data_uri");
+  if (encoded_image_data) {
     std::string mime_type;
     scoped_refptr<base::RefCountedString> data;
-    std::tie(mime_type, data) = ParseEncodedImageData(encoded_image_data);
+    std::tie(mime_type, data) = ParseEncodedImageData(*encoded_image_data);
     if (!data)
       return nullptr;
     logo->metadata.mime_type = mime_type;
     logo->encoded_image = data;
   }
 
-  std::string dark_encoded_image_data;
-  if (ddljson->GetString("dark_cta_data_uri", &dark_encoded_image_data) ||
-      ddljson->GetString("dark_data_uri", &dark_encoded_image_data)) {
+  const std::string* dark_encoded_image_data =
+      ddljson->FindStringKey("dark_cta_data_uri");
+  if (!dark_encoded_image_data)
+    dark_encoded_image_data = ddljson->FindStringKey("dark_data_uri");
+  if (dark_encoded_image_data) {
     std::string mime_type;
     scoped_refptr<base::RefCountedString> data;
-    std::tie(mime_type, data) = ParseEncodedImageData(dark_encoded_image_data);
+    std::tie(mime_type, data) = ParseEncodedImageData(*dark_encoded_image_data);
 
     if (data)
       logo->metadata.dark_mime_type = mime_type;
@@ -299,7 +322,8 @@
   }
 
   logo->metadata.on_click_url = ParseUrl(*ddljson, "target_url", base_url);
-  ddljson->GetString("alt_text", &logo->metadata.alt_text);
+  if (const std::string* alt_text = ddljson->FindStringKey("alt_text"))
+    logo->metadata.alt_text = *alt_text;
 
   logo->metadata.cta_log_url = ParseUrl(*ddljson, "cta_log_url", base_url);
   logo->metadata.dark_cta_log_url =
@@ -307,12 +331,13 @@
   logo->metadata.log_url = ParseUrl(*ddljson, "log_url", base_url);
   logo->metadata.dark_log_url = ParseUrl(*ddljson, "dark_log_url", base_url);
 
-  ddljson->GetString("fingerprint", &logo->metadata.fingerprint);
+  if (const std::string* fingerprint = ddljson->FindStringKey("fingerprint"))
+    logo->metadata.fingerprint = *fingerprint;
 
   if (is_interactive) {
-    std::string behavior;
-    if (ddljson->GetString("launch_interactive_behavior", &behavior) &&
-        (behavior == "NEW_WINDOW")) {
+    const std::string* behavior =
+        ddljson->FindStringKey("launch_interactive_behavior");
+    if (behavior && (*behavior == "NEW_WINDOW")) {
       logo->metadata.type = LogoType::SIMPLE;
       logo->metadata.on_click_url = logo->metadata.full_page_url;
       is_interactive = false;
@@ -322,12 +347,10 @@
   logo->metadata.iframe_width_px = 0;
   logo->metadata.iframe_height_px = 0;
   if (is_interactive) {
-    if (!ddljson->GetInteger("iframe_width_px",
-                             &logo->metadata.iframe_width_px))
-      logo->metadata.iframe_width_px = kDefaultIframeWidthPx;
-    if (!ddljson->GetInteger("iframe_height_px",
-                             &logo->metadata.iframe_height_px))
-      logo->metadata.iframe_height_px = kDefaultIframeHeightPx;
+    logo->metadata.iframe_width_px =
+        ddljson->FindIntKey("iframe_width_px").value_or(kDefaultIframeWidthPx);
+    logo->metadata.iframe_height_px = ddljson->FindIntKey("iframe_height_px")
+                                          .value_or(kDefaultIframeHeightPx);
   }
 
   base::TimeDelta time_to_live;
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 186363e..8a4c9d5 100644
--- a/components/security_interstitials/content/stateful_ssl_host_state_delegate.cc
+++ b/components/security_interstitials/content/stateful_ssl_host_state_delegate.cc
@@ -99,8 +99,8 @@
                                      int threshold) {
   double now = clock->Now().ToJsTime();
 
-  DictionaryPrefUpdate pref_update(pref_service,
-                                   prefs::kRecurrentSSLInterstitial);
+  DictionaryPrefUpdateDeprecated pref_update(pref_service,
+                                             prefs::kRecurrentSSLInterstitial);
   base::Value* list_value =
       pref_update->FindListKey(net::ErrorToShortString(error));
   if (list_value) {
@@ -525,8 +525,8 @@
 
 void StatefulSSLHostStateDelegate::ResetRecurrentErrorCountForTesting() {
   recurrent_errors_.clear();
-  DictionaryPrefUpdate pref_update(pref_service_,
-                                   prefs::kRecurrentSSLInterstitial);
+  DictionaryPrefUpdateDeprecated pref_update(pref_service_,
+                                             prefs::kRecurrentSSLInterstitial);
   pref_update->DictClear();
 }
 
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc
index 9c8e9c6..e1183f3 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl_unittest.cc
@@ -155,7 +155,8 @@
   }
 
   virtual void SetUpPrefs() {
-    DictionaryPrefUpdate update(&pref_service_, kSegmentationResultPref);
+    DictionaryPrefUpdateDeprecated update(&pref_service_,
+                                          kSegmentationResultPref);
     base::DictionaryValue* dictionary = update.Get();
 
     base::Value segmentation_result(base::Value::Type::DICTIONARY);
@@ -372,7 +373,8 @@
 class SegmentationPlatformServiceImplMultiClientTest
     : public SegmentationPlatformServiceImplTest {
   void SetUpPrefs() override {
-    DictionaryPrefUpdate update(&pref_service_, kSegmentationResultPref);
+    DictionaryPrefUpdateDeprecated update(&pref_service_,
+                                          kSegmentationResultPref);
     base::DictionaryValue* dictionary = update.Get();
 
     base::Value segmentation_result(base::Value::Type::DICTIONARY);
diff --git a/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc b/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc
index 877a9e3..73b7b8a 100644
--- a/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc
+++ b/components/segmentation_platform/internal/selection/segmentation_result_prefs.cc
@@ -23,7 +23,7 @@
 void SegmentationResultPrefs::SaveSegmentationResultToPref(
     const std::string& result_key,
     const absl::optional<SelectedSegment>& selected_segment) {
-  DictionaryPrefUpdate update(prefs_, kSegmentationResultPref);
+  DictionaryPrefUpdateDeprecated update(prefs_, kSegmentationResultPref);
   base::DictionaryValue* dictionary = update.Get();
   if (!selected_segment.has_value()) {
     dictionary->RemoveKey(result_key);
diff --git a/components/services/app_service/public/cpp/BUILD.gn b/components/services/app_service/public/cpp/BUILD.gn
index 504e757..73567925 100644
--- a/components/services/app_service/public/cpp/BUILD.gn
+++ b/components/services/app_service/public/cpp/BUILD.gn
@@ -73,6 +73,7 @@
     "app_update.h",
     "capability_access_update.cc",
     "capability_access_update.h",
+    "macros.h",
   ]
 
   defines = [ "IS_APP_UPDATE_IMPL" ]
diff --git a/components/services/app_service/public/cpp/app_types.cc b/components/services/app_service/public/cpp/app_types.cc
index 4a6d3ed..c330230 100644
--- a/components/services/app_service/public/cpp/app_types.cc
+++ b/components/services/app_service/public/cpp/app_types.cc
@@ -25,6 +25,10 @@
     app->icon_key = apps::IconKey(icon_key->timeline, icon_key->resource_id,
                                   icon_key->icon_effects);
   }
+
+  app->install_reason = install_reason;
+  app->install_source = install_source;
+
   return app;
 }
 
@@ -85,6 +89,46 @@
   }
 }
 
+InstallReason ConvertMojomInstallReasonToInstallReason(
+    apps::mojom::InstallReason mojom_install_reason) {
+  switch (mojom_install_reason) {
+    case apps::mojom::InstallReason::kUnknown:
+      return InstallReason::kUnknown;
+    case apps::mojom::InstallReason::kSystem:
+      return InstallReason::kSystem;
+    case apps::mojom::InstallReason::kPolicy:
+      return InstallReason::kPolicy;
+    case apps::mojom::InstallReason::kOem:
+      return InstallReason::kOem;
+    case apps::mojom::InstallReason::kDefault:
+      return InstallReason::kDefault;
+    case apps::mojom::InstallReason::kSync:
+      return InstallReason::kSync;
+    case apps::mojom::InstallReason::kUser:
+      return InstallReason::kUser;
+    case apps::mojom::InstallReason::kSubApp:
+      return InstallReason::kSubApp;
+  }
+}
+
+InstallSource ConvertMojomInstallSourceToInstallSource(
+    apps::mojom::InstallSource mojom_install_source) {
+  switch (mojom_install_source) {
+    case apps::mojom::InstallSource::kUnknown:
+      return InstallSource::kUnknown;
+    case apps::mojom::InstallSource::kSystem:
+      return InstallSource::kSystem;
+    case apps::mojom::InstallSource::kSync:
+      return InstallSource::kSync;
+    case apps::mojom::InstallSource::kPlayStore:
+      return InstallSource::kPlayStore;
+    case apps::mojom::InstallSource::kChromeWebStore:
+      return InstallSource::kChromeWebStore;
+    case apps::mojom::InstallSource::kBrowser:
+      return InstallSource::kBrowser;
+  }
+}
+
 std::unique_ptr<App> ConvertMojomAppToApp(
     const apps::mojom::AppPtr& mojom_app) {
   DCHECK(mojom_app);
@@ -103,6 +147,12 @@
                                   mojom_app->icon_key->resource_id,
                                   mojom_app->icon_key->icon_effects);
   }
+
+  app->install_reason =
+      ConvertMojomInstallReasonToInstallReason(mojom_app->install_reason);
+  app->install_source =
+      ConvertMojomInstallSourceToInstallSource(mojom_app->install_source);
+
   return app;
 }
 
diff --git a/components/services/app_service/public/cpp/app_types.h b/components/services/app_service/public/cpp/app_types.h
index d22de50..c77d76a 100644
--- a/components/services/app_service/public/cpp/app_types.h
+++ b/components/services/app_service/public/cpp/app_types.h
@@ -54,6 +54,44 @@
   kMaxValue = kUninstalledByMigration,
 };
 
+// How the app was installed.
+// This should be kept in sync with histograms.xml, and InstallReason in
+// enums.xml.
+// Note the enumeration is used in UMA histogram so entries should not be
+// re-ordered or removed. New entries should be added at the bottom.
+enum class InstallReason {
+  kUnknown = 0,
+  kSystem,   // Installed with the system and is considered a part of the OS.
+  kPolicy,   // Installed by policy.
+  kOem,      // Installed by an OEM.
+  kDefault,  // Preinstalled by default, but is not considered a system app.
+  kSync,     // Installed by sync.
+  kUser,     // Installed by user action.
+  kSubApp,   // Installed by the SubApp API call.
+
+  // Add any new values above this one, and update kMaxValue to the highest
+  // enumerator value.
+  kMaxValue = kSubApp,
+};
+
+// Where the app was installed from.
+// This should be kept in sync with histograms.xml, and InstallSource in
+// enums.xml.
+// Note the enumeration is used in UMA histogram so entries should not be
+// re-ordered or removed. New entries should be added at the bottom.
+enum class InstallSource {
+  kUnknown = 0,
+  kSystem,          // Installed as part of Chrome OS.
+  kSync,            // Installed from sync.
+  kPlayStore,       // Installed from Play store.
+  kChromeWebStore,  // Installed from Chrome web store.
+  kBrowser,         // Installed from browser.
+
+  // Add any new values above this one, and update kMaxValue to the highest
+  // enumerator value.
+  kMaxValue = kBrowser,
+};
+
 struct COMPONENT_EXPORT(APP_TYPES) App {
   App(AppType app_type, const std::string& app_id);
 
@@ -81,6 +119,13 @@
 
   absl::optional<IconKey> icon_key;
 
+  // Whether the app was installed by sync, policy or as a default app.
+  InstallReason install_reason = InstallReason::kUnknown;
+
+  // Where the app was installed from, e.g. from Play Store, from Chrome Web
+  // Store, etc.
+  InstallSource install_source = InstallSource::kUnknown;
+
   // TODO(crbug.com/1253250): Add other App struct fields.
 
   // When adding new fields to the App type, the `Clone` function and the
@@ -97,6 +142,14 @@
     apps::mojom::Readiness mojom_readiness);
 
 COMPONENT_EXPORT(APP_TYPES)
+InstallReason ConvertMojomInstallReasonToInstallReason(
+    apps::mojom::InstallReason mojom_install_reason);
+
+COMPONENT_EXPORT(APP_TYPES)
+InstallSource ConvertMojomInstallSourceToInstallSource(
+    apps::mojom::InstallSource mojom_install_source);
+
+COMPONENT_EXPORT(APP_TYPES)
 std::unique_ptr<App> ConvertMojomAppToApp(const apps::mojom::AppPtr& mojom_app);
 
 }  // namespace apps
diff --git a/components/services/app_service/public/cpp/app_update.cc b/components/services/app_service/public/cpp/app_update.cc
index 71a696f..4f480f35 100644
--- a/components/services/app_service/public/cpp/app_update.cc
+++ b/components/services/app_service/public/cpp/app_update.cc
@@ -9,6 +9,7 @@
 #include "base/time/time.h"
 #include "components/services/app_service/public/cpp/icon_types.h"
 #include "components/services/app_service/public/cpp/intent_filter_util.h"
+#include "components/services/app_service/public/cpp/macros.h"
 
 namespace {
 
@@ -515,6 +516,10 @@
   return apps::mojom::InstallReason::kUnknown;
 }
 
+apps::InstallReason AppUpdate::GetInstallReason() const {
+  GET_VALUE_WITH_DEFAULT_VALUE(install_reason, InstallReason::kUnknown)
+}
+
 bool AppUpdate::InstallReasonChanged() const {
   return mojom_delta_ &&
          (mojom_delta_->install_reason !=
@@ -534,6 +539,10 @@
   return apps::mojom::InstallSource::kUnknown;
 }
 
+apps::InstallSource AppUpdate::GetInstallSource() const {
+  GET_VALUE_WITH_DEFAULT_VALUE(install_source, InstallSource::kUnknown)
+}
+
 bool AppUpdate::InstallSourceChanged() const {
   return mojom_delta_ &&
          (mojom_delta_->install_source !=
diff --git a/components/services/app_service/public/cpp/app_update.h b/components/services/app_service/public/cpp/app_update.h
index 9070c6b..988b443 100644
--- a/components/services/app_service/public/cpp/app_update.h
+++ b/components/services/app_service/public/cpp/app_update.h
@@ -124,9 +124,11 @@
   bool PermissionsChanged() const;
 
   apps::mojom::InstallReason InstallReason() const;
+  apps::InstallReason GetInstallReason() const;
   bool InstallReasonChanged() const;
 
   apps::mojom::InstallSource InstallSource() const;
+  apps::InstallSource GetInstallSource() const;
   bool InstallSourceChanged() const;
 
   // An optional ID used for policy to identify the app.
diff --git a/components/services/app_service/public/cpp/browser_app_instance_update.h b/components/services/app_service/public/cpp/browser_app_instance_update.h
index 37f7c25..dfaa7dd37 100644
--- a/components/services/app_service/public/cpp/browser_app_instance_update.h
+++ b/components/services/app_service/public/cpp/browser_app_instance_update.h
@@ -27,8 +27,10 @@
   std::string app_id;
   std::string window_id;
   std::string title;
-  bool is_browser_active;
-  bool is_web_contents_active;
+  bool is_browser_active = false;
+  bool is_web_contents_active = false;
+  uint32_t browser_session_id = 0;
+  uint32_t restored_browser_session_id = 0;
 };
 
 }  // namespace apps
diff --git a/components/services/app_service/public/cpp/browser_window_instance_update.h b/components/services/app_service/public/cpp/browser_window_instance_update.h
index 4f0d3c1b..83402c0 100644
--- a/components/services/app_service/public/cpp/browser_window_instance_update.h
+++ b/components/services/app_service/public/cpp/browser_window_instance_update.h
@@ -12,7 +12,9 @@
 struct BrowserWindowInstanceUpdate {
   base::UnguessableToken id;
   std::string window_id;
-  bool is_active;
+  bool is_active = false;
+  uint32_t browser_session_id = 0;
+  uint32_t restored_browser_session_id = 0;
 };
 
 }  // namespace apps
diff --git a/components/services/app_service/public/cpp/instance_update.cc b/components/services/app_service/public/cpp/instance_update.cc
index 028b19c..6a776c0 100644
--- a/components/services/app_service/public/cpp/instance_update.cc
+++ b/components/services/app_service/public/cpp/instance_update.cc
@@ -133,7 +133,7 @@
 }
 
 InstanceState InstanceUpdate::State() const {
-  GET_VALUE_WITH_DEFAULT_VALUE(State, InstanceState::kUnknown);
+  GET_VALUE_WITH_DEFAULT_VALUE(State(), InstanceState::kUnknown);
 }
 
 bool InstanceUpdate::StateChanged() const {
diff --git a/components/services/app_service/public/cpp/macros.h b/components/services/app_service/public/cpp/macros.h
index 9cef7b39..714cb75 100644
--- a/components/services/app_service/public/cpp/macros.h
+++ b/components/services/app_service/public/cpp/macros.h
@@ -21,11 +21,11 @@
          (!state_ || (delta_->VALUE() != state_->VALUE()));
 
 #define GET_VALUE_WITH_DEFAULT_VALUE(VALUE, DEFAULT_VALUE) \
-  if (delta_ && delta_->VALUE() != (DEFAULT_VALUE)) {      \
-    return delta_->VALUE();                                \
+  if (delta_ && delta_->VALUE != (DEFAULT_VALUE)) {        \
+    return delta_->VALUE;                                  \
   }                                                        \
   if (state_) {                                            \
-    return state_->VALUE();                                \
+    return state_->VALUE;                                  \
   }                                                        \
   return DEFAULT_VALUE;
 
diff --git a/components/services/storage/service_worker/service_worker_storage_control_impl.cc b/components/services/storage/service_worker/service_worker_storage_control_impl.cc
index d5d1ccb..9001b00 100644
--- a/components/services/storage/service_worker/service_worker_storage_control_impl.cc
+++ b/components/services/storage/service_worker/service_worker_storage_control_impl.cc
@@ -5,6 +5,7 @@
 #include "components/services/storage/service_worker/service_worker_storage_control_impl.h"
 
 #include "base/containers/contains.h"
+#include "base/debug/alias.h"
 #include "components/services/storage/service_worker/service_worker_resource_ops.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
@@ -483,6 +484,10 @@
     reference->Add(remote_reference.InitWithNewPipeAndPassReceiver());
     live_versions_[version_id] = std::move(reference);
   } else {
+    // TODO(https://crbug.com/1277263): Remove the following CHECK() once the
+    // cause is identified.
+    base::debug::Alias(&version_id);
+    CHECK(it->second.get()) << "Invalid version id: " << version_id;
     it->second->Add(remote_reference.InitWithNewPipeAndPassReceiver());
   }
   return remote_reference;
diff --git a/components/signin/internal/identity_manager/account_tracker_service.cc b/components/signin/internal/identity_manager/account_tracker_service.cc
index 8192c065..57f7eea6 100644
--- a/components/signin/internal/identity_manager/account_tracker_service.cc
+++ b/components/signin/internal/identity_manager/account_tracker_service.cc
@@ -523,7 +523,7 @@
     return;
 
   base::DictionaryValue* dict = nullptr;
-  ListPrefUpdate update(pref_service_, prefs::kAccountInfo);
+  ListPrefUpdateDeprecated update(pref_service_, prefs::kAccountInfo);
   for (size_t i = 0; i < update->GetList().size(); ++i, dict = nullptr) {
     base::Value& dict_value = update->GetList()[i];
     if (dict_value.is_dict()) {
@@ -594,7 +594,7 @@
                                               ? signin::Tribool::kTrue
                                               : signin::Tribool::kFalse;
           // Migrate to kAccountChildAttributePath.
-          ListPrefUpdate update(pref_service_, prefs::kAccountInfo);
+          ListPrefUpdateDeprecated update(pref_service_, prefs::kAccountInfo);
           base::Value* update_dict = &update->GetList()[i];
           DCHECK(update_dict->is_dict());
           SetAccountCapabilityPath(update_dict, kAccountChildAttributePath,
@@ -662,7 +662,7 @@
     return;
 
   base::DictionaryValue* dict = nullptr;
-  ListPrefUpdate update(pref_service_, prefs::kAccountInfo);
+  ListPrefUpdateDeprecated update(pref_service_, prefs::kAccountInfo);
   for (size_t i = 0; i < update->GetList().size(); ++i, dict = nullptr) {
     base::Value& dict_value = update->GetList()[i];
     if (dict_value.is_dict()) {
@@ -708,7 +708,7 @@
   if (!pref_service_)
     return;
 
-  ListPrefUpdate update(pref_service_, prefs::kAccountInfo);
+  ListPrefUpdateDeprecated update(pref_service_, prefs::kAccountInfo);
   const std::string account_id = account_info.account_id.ToString();
   update->EraseListValueIf([&account_id](const base::Value& value) {
     if (!value.is_dict())
diff --git a/components/signin/internal/identity_manager/account_tracker_service_unittest.cc b/components/signin/internal/identity_manager/account_tracker_service_unittest.cc
index 037837a..631b2be 100644
--- a/components/signin/internal/identity_manager/account_tracker_service_unittest.cc
+++ b/components/signin/internal/identity_manager/account_tracker_service_unittest.cc
@@ -1027,7 +1027,7 @@
             account_tracker()
                 ->GetAccountInfo(AccountKeyToAccountId(kAccountKeyAlpha))
                 .is_child_account);
-  ListPrefUpdate update(prefs(), prefs::kAccountInfo);
+  ListPrefUpdateDeprecated update(prefs(), prefs::kAccountInfo);
   base::Value* dict = nullptr;
   update->Get(0, &dict);
   ASSERT_TRUE(dict && dict->is_dict());
@@ -1283,7 +1283,7 @@
   const std::string email_beta = AccountKeyToEmail(kAccountKeyBeta);
   const std::string gaia_beta = AccountKeyToGaiaId(kAccountKeyBeta);
 
-  ListPrefUpdate update(prefs(), prefs::kAccountInfo);
+  ListPrefUpdateDeprecated update(prefs(), prefs::kAccountInfo);
 
   base::Value dict(base::Value::Type::DICTIONARY);
   dict.SetStringKey("account_id", email_alpha);
@@ -1331,7 +1331,7 @@
   const std::string gaia_alpha = AccountKeyToGaiaId(kAccountKeyAlpha);
   const std::string email_beta = AccountKeyToEmail(kAccountKeyBeta);
 
-  ListPrefUpdate update(prefs(), prefs::kAccountInfo);
+  ListPrefUpdateDeprecated update(prefs(), prefs::kAccountInfo);
 
   base::Value dict(base::Value::Type::DICTIONARY);
   dict.SetStringKey("account_id", email_alpha);
@@ -1380,7 +1380,7 @@
   const std::string email_beta = AccountKeyToEmail(kAccountKeyBeta);
   const std::string gaia_beta = AccountKeyToGaiaId(kAccountKeyBeta);
 
-  ListPrefUpdate update(prefs(), prefs::kAccountInfo);
+  ListPrefUpdateDeprecated update(prefs(), prefs::kAccountInfo);
 
   base::Value dict(base::Value::Type::DICTIONARY);
   dict.SetStringKey("account_id", email_alpha);
@@ -1713,7 +1713,7 @@
   const std::string email_beta = AccountKeyToEmail(kAccountKeyBeta);
   const std::string gaia_beta = AccountKeyToGaiaId(kAccountKeyBeta);
 
-  ListPrefUpdate update(prefs(), prefs::kAccountInfo);
+  ListPrefUpdateDeprecated update(prefs(), prefs::kAccountInfo);
 
   base::Value dict(base::Value::Type::DICTIONARY);
   dict.SetStringKey("account_id", email_alpha);
@@ -1741,7 +1741,7 @@
   const std::string email_foobar = AccountKeyToEmail(kAccountKeyFooDotBar);
   const std::string gaia_foobar = AccountKeyToGaiaId(kAccountKeyFooDotBar);
 
-  ListPrefUpdate update(prefs(), prefs::kAccountInfo);
+  ListPrefUpdateDeprecated update(prefs(), prefs::kAccountInfo);
 
   base::Value dict(base::Value::Type::DICTIONARY);
   dict.SetStringKey("account_id", email_alpha);
diff --git a/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc b/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc
index 6791237e..0040ef3 100644
--- a/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc
+++ b/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc
@@ -1100,7 +1100,7 @@
     pref_service_.SetInteger(prefs::kAccountIdMigrationState,
                              AccountTrackerService::MIGRATION_NOT_STARTED);
 
-    ListPrefUpdate update(&pref_service_, prefs::kAccountInfo);
+    ListPrefUpdateDeprecated update(&pref_service_, prefs::kAccountInfo);
     update->ClearList();
     auto dict = std::make_unique<base::DictionaryValue>();
     dict->SetString("account_id", email);
@@ -1163,7 +1163,7 @@
     pref_service_.SetInteger(prefs::kAccountIdMigrationState,
                              AccountTrackerService::MIGRATION_NOT_STARTED);
 
-    ListPrefUpdate update(&pref_service_, prefs::kAccountInfo);
+    ListPrefUpdateDeprecated update(&pref_service_, prefs::kAccountInfo);
     update->ClearList();
     auto dict = std::make_unique<base::DictionaryValue>();
     dict->SetString("account_id", email1);
diff --git a/components/signin/internal/identity_manager/primary_account_manager_unittest.cc b/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
index d63af1fd92..ae30689 100644
--- a/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
+++ b/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
@@ -365,7 +365,7 @@
   PrefService* client_prefs = signin_client()->GetPrefs();
   client_prefs->SetInteger(prefs::kAccountIdMigrationState,
                            AccountTrackerService::MIGRATION_NOT_STARTED);
-  ListPrefUpdate update(client_prefs, prefs::kAccountInfo);
+  ListPrefUpdateDeprecated update(client_prefs, prefs::kAccountInfo);
   update->ClearList();
   auto dict = std::make_unique<base::DictionaryValue>();
   dict->SetString("account_id", email);
@@ -398,7 +398,7 @@
   PrefService* client_prefs = signin_client()->GetPrefs();
   client_prefs->SetInteger(prefs::kAccountIdMigrationState,
                            AccountTrackerService::MIGRATION_NOT_STARTED);
-  ListPrefUpdate update(client_prefs, prefs::kAccountInfo);
+  ListPrefUpdateDeprecated update(client_prefs, prefs::kAccountInfo);
   update->ClearList();
   auto dict = std::make_unique<base::DictionaryValue>();
   dict->SetString("account_id", email);
@@ -431,7 +431,7 @@
   PrefService* client_prefs = signin_client()->GetPrefs();
   client_prefs->SetInteger(prefs::kAccountIdMigrationState,
                            AccountTrackerService::MIGRATION_NOT_STARTED);
-  ListPrefUpdate update(client_prefs, prefs::kAccountInfo);
+  ListPrefUpdateDeprecated update(client_prefs, prefs::kAccountInfo);
   update->ClearList();
   auto dict = std::make_unique<base::DictionaryValue>();
   dict->SetString("account_id", email);
diff --git a/components/signin/public/android/java/res/drawable/test_profile_picture.xml b/components/signin/public/android/java/res/drawable/test_profile_picture.xml
index 1ac46f0a..86a4e236 100644
--- a/components/signin/public/android/java/res/drawable/test_profile_picture.xml
+++ b/components/signin/public/android/java/res/drawable/test_profile_picture.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
     android:width="40dp"
     android:height="40dp"
     android:viewportWidth="192"
diff --git a/components/site_isolation/site_isolation_policy.cc b/components/site_isolation/site_isolation_policy.cc
index 773fbfc..0a7b186b 100644
--- a/components/site_isolation/site_isolation_policy.cc
+++ b/components/site_isolation/site_isolation_policy.cc
@@ -178,8 +178,9 @@
   // unlimited size.
   // TODO(alexmos): Cap the maximum number of entries and evict older entries.
   // See https://crbug.com/1172407.
-  ListPrefUpdate update(user_prefs::UserPrefs::Get(context),
-                        site_isolation::prefs::kUserTriggeredIsolatedOrigins);
+  ListPrefUpdateDeprecated update(
+      user_prefs::UserPrefs::Get(context),
+      site_isolation::prefs::kUserTriggeredIsolatedOrigins);
   base::ListValue* list = update.Get();
   base::Value value(origin.Serialize());
   if (!base::Contains(list->GetList(), value))
@@ -193,7 +194,7 @@
   // Web-triggered isolated origins are stored in a dictionary of (origin,
   // timestamp) pairs.  The number of entries is capped by a field trial param,
   // and older entries are evicted.
-  DictionaryPrefUpdate update(
+  DictionaryPrefUpdateDeprecated update(
       user_prefs::UserPrefs::Get(context),
       site_isolation::prefs::kWebTriggeredIsolatedOrigins);
   base::DictionaryValue* dict = update.Get();
@@ -276,8 +277,8 @@
       }
       // Remove expired entries (as well as those with an invalid timestamp).
       if (!expired_entries.empty()) {
-        DictionaryPrefUpdate update(pref_service,
-                                    prefs::kWebTriggeredIsolatedOrigins);
+        DictionaryPrefUpdateDeprecated update(
+            pref_service, prefs::kWebTriggeredIsolatedOrigins);
         base::DictionaryValue* updated_dict = update.Get();
         for (const auto& entry : expired_entries)
           updated_dict->RemoveKey(entry);
diff --git a/components/site_isolation/site_isolation_policy_unittest.cc b/components/site_isolation/site_isolation_policy_unittest.cc
index b800782..934ef724 100644
--- a/components/site_isolation/site_isolation_policy_unittest.cc
+++ b/components/site_isolation/site_isolation_policy_unittest.cc
@@ -242,7 +242,7 @@
 TEST_F(WebTriggeredIsolatedOriginsPolicyTest, UpdatedMaxSize) {
   // Populate the pref manually with more entries than the 3 allowed by the
   // field trial param.
-  DictionaryPrefUpdate update(
+  DictionaryPrefUpdateDeprecated update(
       user_prefs::UserPrefs::Get(browser_context()),
       site_isolation::prefs::kWebTriggeredIsolatedOrigins);
   base::DictionaryValue* dict = update.Get();
@@ -345,7 +345,8 @@
 
   // Add foo.com and bar.com to stored isolated origins.
   {
-    ListPrefUpdate update(prefs(), prefs::kUserTriggeredIsolatedOrigins);
+    ListPrefUpdateDeprecated update(prefs(),
+                                    prefs::kUserTriggeredIsolatedOrigins);
     base::ListValue* list = update.Get();
     list->Append("http://foo.com");
     list->Append("https://bar.com");
@@ -426,7 +427,8 @@
 
   // Add foo.com to stored isolated origins.
   {
-    ListPrefUpdate update(prefs(), prefs::kUserTriggeredIsolatedOrigins);
+    ListPrefUpdateDeprecated update(prefs(),
+                                    prefs::kUserTriggeredIsolatedOrigins);
     base::ListValue* list = update.Get();
     list->Append("http://foo.com");
   }
diff --git a/components/soda/soda_installer.cc b/components/soda/soda_installer.cc
index 07bebcf..76162aa 100644
--- a/components/soda/soda_installer.cc
+++ b/components/soda/soda_installer.cc
@@ -261,14 +261,16 @@
 
 void SodaInstaller::RegisterLanguage(const std::string& language,
                                      PrefService* global_prefs) {
-  ListPrefUpdate update(global_prefs, prefs::kSodaRegisteredLanguagePacks);
+  ListPrefUpdateDeprecated update(global_prefs,
+                                  prefs::kSodaRegisteredLanguagePacks);
   if (!base::Contains(update->GetList(), base::Value(language))) {
     update->Append(language);
   }
 }
 
 void SodaInstaller::UnregisterLanguages(PrefService* global_prefs) {
-  ListPrefUpdate update(global_prefs, prefs::kSodaRegisteredLanguagePacks);
+  ListPrefUpdateDeprecated update(global_prefs,
+                                  prefs::kSodaRegisteredLanguagePacks);
   update->ClearList();
 }
 
diff --git a/components/spellcheck/browser/spell_check_host_impl.cc b/components/spellcheck/browser/spell_check_host_impl.cc
index 581a48a..e5b84bc 100644
--- a/components/spellcheck/browser/spell_check_host_impl.cc
+++ b/components/spellcheck/browser/spell_check_host_impl.cc
@@ -32,8 +32,10 @@
     CallSpellingServiceCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  if (text.empty())
+  if (text.empty()) {
     mojo::ReportBadMessage("Requested spelling service with empty text");
+    return;
+  }
 
   // This API requires Chrome-only features.
   std::move(callback).Run(false, std::vector<SpellCheckResult>());
@@ -46,8 +48,10 @@
                                           RequestTextCheckCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  if (text.empty())
+  if (text.empty()) {
     mojo::ReportBadMessage("Requested text check with empty text");
+    return;
+  }
 
   session_bridge_.RequestTextCheck(text, std::move(callback));
 }
diff --git a/components/spellcheck/browser/spelling_service_client.cc b/components/spellcheck/browser/spelling_service_client.cc
index 27f76cc..d49d96d 100644
--- a/components/spellcheck/browser/spelling_service_client.cc
+++ b/components/spellcheck/browser/spelling_service_client.cc
@@ -252,57 +252,48 @@
   //    }
   //  }
 
-  std::unique_ptr<base::DictionaryValue> value(
-      static_cast<base::DictionaryValue*>(
-          base::JSONReader::ReadDeprecated(data,
-                                           base::JSON_ALLOW_TRAILING_COMMAS)
-              .release()));
+  absl::optional<base::Value> value(
+      base::JSONReader::Read(data, base::JSON_ALLOW_TRAILING_COMMAS));
   if (!value || !value->is_dict())
     return false;
 
   // Check for errors from spelling service.
-  base::DictionaryValue* error = nullptr;
-  if (value->GetDictionary(kErrorPath, &error))
+  const base::Value* error = value->FindDictPath(kErrorPath);
+  if (error)
     return false;
 
   // Retrieve the array of Misspelling objects. When the input text does not
   // have misspelled words, it returns an empty JSON. (In this case, its HTTP
   // status is 200.) We just return true for this case.
-  base::ListValue* misspellings = nullptr;
+  const base::Value* misspellings = value->FindListPath(kMisspellingsRestPath);
 
-  if (!value->GetList(kMisspellingsRestPath, &misspellings))
+  if (!misspellings)
     return true;
 
-  for (const base::Value& misspelling_value : misspellings->GetList()) {
+  for (const base::Value& misspelling : misspellings->GetList()) {
     // Retrieve the i-th misspelling region and put it to the given vector. When
     // the Spelling service sends two or more suggestions, we read only the
     // first one because SpellCheckResult can store only one suggestion.
-    if (!misspelling_value.is_dict())
+    if (!misspelling.is_dict())
       return false;
 
-    const base::DictionaryValue& misspelling =
-        base::Value::AsDictionaryValue(misspelling_value);
-
-    int start = 0;
-    int length = 0;
-    const base::ListValue* suggestions = nullptr;
-    if (!misspelling.GetInteger("charStart", &start) ||
-        !misspelling.GetInteger("charLength", &length) ||
-        !misspelling.GetList("suggestions", &suggestions)) {
+    absl::optional<int> start = misspelling.FindIntKey("charStart");
+    absl::optional<int> length = misspelling.FindIntKey("charLength");
+    const base::Value* suggestions = misspelling.FindListKey("suggestions");
+    if (!start || !length || !suggestions) {
       return false;
     }
 
-    const base::Value& suggestion_value = suggestions->GetList()[0];
-    const base::DictionaryValue* suggestion = nullptr;
-    if (suggestion_value.is_dict())
-      suggestion = &base::Value::AsDictionaryValue(suggestion_value);
+    const base::Value& suggestion = suggestions->GetList()[0];
+    if (!suggestion.is_dict())
+      return false;
 
-    std::u16string replacement;
-    if (!suggestion || !suggestion->GetString("suggestion", &replacement)) {
+    const std::string* replacement = suggestion.FindStringKey("suggestion");
+    if (!replacement) {
       return false;
     }
-    SpellCheckResult result(SpellCheckResult::SPELLING, start, length,
-                            replacement);
+    SpellCheckResult result(SpellCheckResult::SPELLING, *start, *length,
+                            base::UTF8ToUTF16(*replacement));
     results->push_back(result);
   }
   return true;
diff --git a/components/subresource_redirect/BUILD.gn b/components/subresource_redirect/BUILD.gn
deleted file mode 100644
index c4629b5..0000000
--- a/components/subresource_redirect/BUILD.gn
+++ /dev/null
@@ -1,29 +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.
-
-static_library("test_support") {
-  testonly = true
-
-  sources = [
-    "subresource_redirect_browser_test_util.cc",
-    "subresource_redirect_browser_test_util.h",
-    "subresource_redirect_test_util.cc",
-    "subresource_redirect_test_util.h",
-  ]
-
-  deps = [
-    "//base/test:test_support",
-    "//chrome/common:constants",
-    "//components/metrics:content",
-    "//components/subresource_redirect/proto:proto",
-    "//content/test:test_support",
-    "//net:test_support",
-    "//testing/gtest:gtest",
-    "//url:url",
-  ]
-
-  if (!is_android) {
-    deps += [ "//chrome/test:test_support_ui" ]
-  }
-}
diff --git a/components/subresource_redirect/DEPS b/components/subresource_redirect/DEPS
deleted file mode 100644
index 2b1c72b..0000000
--- a/components/subresource_redirect/DEPS
+++ /dev/null
@@ -1,9 +0,0 @@
-include_rules = [
-  "+chrome/common",
-  "+components/data_reduction_proxy",
-  "+components/metrics",
-  "+content/public/test",
-  "+net/base",
-  "+net/test/embedded_test_server",
-  "+third_party/blink/public/common/features.h",
-]
diff --git a/components/subresource_redirect/DIR_METADATA b/components/subresource_redirect/DIR_METADATA
deleted file mode 100644
index 3d3ad077..0000000
--- a/components/subresource_redirect/DIR_METADATA
+++ /dev/null
@@ -1,3 +0,0 @@
-monorail {
-  component: "Internals>Network>DataUse"
-}
diff --git a/components/subresource_redirect/OWNERS b/components/subresource_redirect/OWNERS
deleted file mode 100644
index 2783dea..0000000
--- a/components/subresource_redirect/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://components/data_reduction_proxy/OWNERS
diff --git a/components/subresource_redirect/common/BUILD.gn b/components/subresource_redirect/common/BUILD.gn
deleted file mode 100644
index abef8611..0000000
--- a/components/subresource_redirect/common/BUILD.gn
+++ /dev/null
@@ -1,13 +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.
-
-source_set("common") {
-  sources = [
-    "subresource_redirect_features.cc",
-    "subresource_redirect_features.h",
-    "subresource_redirect_result.h",
-  ]
-
-  deps = [ "//third_party/blink/public/common" ]
-}
diff --git a/components/subresource_redirect/common/subresource_redirect_features.cc b/components/subresource_redirect/common/subresource_redirect_features.cc
deleted file mode 100644
index f94f28e..0000000
--- a/components/subresource_redirect/common/subresource_redirect_features.cc
+++ /dev/null
@@ -1,92 +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 "components/subresource_redirect/common/subresource_redirect_features.h"
-
-#include "base/metrics/field_trial_params.h"
-#include "base/system/sys_info.h"
-#include "third_party/blink/public/common/features.h"
-#include "url/gurl.h"
-
-namespace subresource_redirect {
-
-namespace {
-
-// This default low memory threshold is chosen as the default threshold for
-// Android in
-// SiteIsolationPolicy::ShouldDisableSiteIsolationDueToMemoryThreshold(). This
-// is the threshold for enabling the site-isolation preloaded list and
-// populating the password entered sites. So, the subresource redirect feature
-// should not compress the devices below this threshold.
-constexpr int kDefaultLowMemoryThresholdMb = 1900;
-
-// The default origin for the LitePages.
-constexpr char kDefaultLitePageOrigin[] = "https://litepages.googlezip.net/";
-
-bool IsSubresourceRedirectEnabled() {
-  return base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect);
-}
-
-}  // namespace
-
-bool ShouldEnablePublicImageHintsBasedCompression() {
-  bool is_enabled = IsSubresourceRedirectEnabled() &&
-                    base::GetFieldTrialParamByFeatureAsBool(
-                        blink::features::kSubresourceRedirect,
-                        "enable_public_image_hints_based_compression", true);
-  // Only one of the public image hints or login and robots based image
-  // compression should be active.
-  DCHECK(!is_enabled || !ShouldEnableLoginRobotsCheckedImageCompression());
-  return is_enabled;
-}
-
-bool ShouldEnableLoginRobotsCheckedImageCompression() {
-  bool is_enabled = IsSubresourceRedirectEnabled() &&
-                    base::GetFieldTrialParamByFeatureAsBool(
-                        blink::features::kSubresourceRedirect,
-                        "enable_login_robots_based_compression", false);
-  // Only one of the public image hints or login and robots based image
-  // compression should be active.
-  DCHECK(!is_enabled || !ShouldEnablePublicImageHintsBasedCompression());
-  if (is_enabled && !base::GetFieldTrialParamByFeatureAsBool(
-                        blink::features::kSubresourceRedirect,
-                        "enable_login_robots_for_low_memory", false)) {
-    return base::SysInfo::AmountOfPhysicalMemoryMB() >
-           base::GetFieldTrialParamByFeatureAsInt(
-               blink::features::kSubresourceRedirect,
-               "login_robots_low_memory_threshold_mb",
-               kDefaultLowMemoryThresholdMb);
-  }
-  return is_enabled;
-}
-
-bool ShouldRecordLoginRobotsCheckedSrcVideoMetrics() {
-  return base::FeatureList::IsEnabled(
-      blink::features::kSubresourceRedirectSrcVideo);
-}
-
-// Should the subresource be redirected to its compressed version. This returns
-// false if only coverage metrics need to be recorded and actual redirection
-// should not happen.
-bool ShouldCompressRedirectSubresource() {
-  return base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect) &&
-         base::GetFieldTrialParamByFeatureAsBool(
-             blink::features::kSubresourceRedirect,
-             "enable_subresource_server_redirect", true);
-}
-
-bool ShouldEnableRobotsRulesFetching() {
-  return ShouldEnableLoginRobotsCheckedImageCompression() ||
-         ShouldRecordLoginRobotsCheckedSrcVideoMetrics();
-}
-
-url::Origin GetSubresourceRedirectOrigin() {
-  auto lite_page_subresource_origin = base::GetFieldTrialParamValueByFeature(
-      blink::features::kSubresourceRedirect, "lite_page_subresource_origin");
-  if (lite_page_subresource_origin.empty())
-    return url::Origin::Create(GURL(kDefaultLitePageOrigin));
-  return url::Origin::Create(GURL(lite_page_subresource_origin));
-}
-
-}  // namespace subresource_redirect
diff --git a/components/subresource_redirect/common/subresource_redirect_features.h b/components/subresource_redirect/common/subresource_redirect_features.h
deleted file mode 100644
index 68ea7038..0000000
--- a/components/subresource_redirect/common/subresource_redirect_features.h
+++ /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.
-
-#ifndef COMPONENTS_SUBRESOURCE_REDIRECT_COMMON_SUBRESOURCE_REDIRECT_FEATURES_H_
-#define COMPONENTS_SUBRESOURCE_REDIRECT_COMMON_SUBRESOURCE_REDIRECT_FEATURES_H_
-
-#include "url/origin.h"
-
-namespace subresource_redirect {
-
-// Returns if the public image hints based subresource compression is enabled.
-bool ShouldEnablePublicImageHintsBasedCompression();
-
-// Returns if the login and robots checks based image compression is enabled.
-// This compresses images in non logged-in pages allowed by robots.txt rules.
-bool ShouldEnableLoginRobotsCheckedImageCompression();
-
-// Returns if the login and robots checks based src-video metrics recording is
-// enabled. This only records data use and coverage metrics for src videos on
-// non logged-in pages allowed by robots.txt rules.
-bool ShouldRecordLoginRobotsCheckedSrcVideoMetrics();
-
-// Should the subresource be redirected to its compressed version. This returns
-// false if only coverage metrics need to be recorded and actual redirection
-// should not happen.
-bool ShouldCompressRedirectSubresource();
-
-// Returns whether robots rules can be fetched. Robots rules fetching is enabled
-// when certain features are active, such as robots and login checked image
-// and src-video compression.
-bool ShouldEnableRobotsRulesFetching();
-
-// Returns the origin to use for subresource redirect from fieldtrial or the
-// default.
-url::Origin GetSubresourceRedirectOrigin();
-
-}  // namespace subresource_redirect
-
-#endif  // COMPONENTS_SUBRESOURCE_REDIRECT_COMMON_SUBRESOURCE_REDIRECT_FEATURES_H_
diff --git a/components/subresource_redirect/common/subresource_redirect_result.h b/components/subresource_redirect/common/subresource_redirect_result.h
deleted file mode 100644
index dae817b..0000000
--- a/components/subresource_redirect/common/subresource_redirect_result.h
+++ /dev/null
@@ -1,77 +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 COMPONENTS_SUBRESOURCE_REDIRECT_COMMON_SUBRESOURCE_REDIRECT_RESULT_H_
-#define COMPONENTS_SUBRESOURCE_REDIRECT_COMMON_SUBRESOURCE_REDIRECT_RESULT_H_
-
-namespace subresource_redirect {
-
-// Enumerates the different results possible for subresource redirection, such
-// as redirectable or different reasons of ineligibility. This enum should be in
-// sync with SubresourceRedirectRedirectResult in enums.xml
-enum class SubresourceRedirectResult {
-  kUnknown = 0,
-
-  // The image was determined as public and is eligible to be redirected
-  // to a compressed version.
-  kRedirectable = 1,
-
-  // Possible reasons for ineligibility:
-
-  // Because of reasons Blink could disallow compression such as non <img>
-  // element, CSP/CORS security restrictions, javascript initiated image, etc.
-  kIneligibleBlinkDisallowed = 2,
-
-  // Because the resource is fetched for a subframe.
-  kIneligibleSubframeResource = 3,
-
-  // Because the compressed subresource fetch failed, and then the original
-  // subresource was loaded.
-  kIneligibleRedirectFailed = 4,
-
-  // Possible reasons for ineligibility due to public image hints approach:
-
-  // Because the image hint list was not retrieved within certain time limit
-  // of navigation start,
-  kIneligibleImageHintsUnavailable = 5,
-
-  // Because the image hint list was not retrieved at the time of image fetch,
-  // but the image URL was found in the hint list, which finished fetching
-  // later.
-  kIneligibleImageHintsUnavailableButRedirectable = 6,
-
-  // Because the image hint list was not retrieved at the time of image fetch,
-  // and the image URL was not in the hint list as well, which finished
-  // fetching later.
-  kIneligibleImageHintsUnavailableAndMissingInHints = 7,
-
-  // Because the image URL was not found in the image hints.
-  kIneligibleMissingInImageHints = 8,
-
-  // Possible reasons for ineligibility due to login and robots rules
-  // based approach:
-
-  // Because the image was disallowed by robots rules of the image origin.
-  kIneligibleRobotsDisallowed = 9,
-
-  // Because the robots rules fetch timedout.
-  kIneligibleRobotsTimeout = 10,
-
-  // Because the page was detected to be logged-in.
-  kIneligibleLoginDetected = 11,
-
-  // Because the subresource was within the first k subresources on the page and
-  // got disabled.
-  kIneligibleFirstKDisableSubresourceRedirect = 12,
-
-  // Because the subresource redirection was disabled, where only metrics are
-  // recorded and the actual subresource redirection does not happen.
-  kIneligibleCompressionDisabled = 13,
-
-  kMaxValue = SubresourceRedirectResult::kIneligibleCompressionDisabled
-};
-
-}  // namespace subresource_redirect
-
-#endif  // COMPONENTS_SUBRESOURCE_REDIRECT_COMMON_SUBRESOURCE_REDIRECT_RESULT_H_
diff --git a/components/subresource_redirect/proto/BUILD.gn b/components/subresource_redirect/proto/BUILD.gn
deleted file mode 100644
index b54caebd..0000000
--- a/components/subresource_redirect/proto/BUILD.gn
+++ /dev/null
@@ -1,9 +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.
-
-import("//third_party/protobuf/proto_library.gni")
-
-proto_library("proto") {
-  sources = [ "robots_rules.proto" ]
-}
diff --git a/components/subresource_redirect/proto/robots_rules.proto b/components/subresource_redirect/proto/robots_rules.proto
deleted file mode 100644
index 72173ba..0000000
--- a/components/subresource_redirect/proto/robots_rules.proto
+++ /dev/null
@@ -1,26 +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.
-
-syntax = "proto2";
-
-option optimize_for = LITE_RUNTIME;
-
-package subresource_redirect.proto;
-
-message RobotsRules {
-  // A single page-pattern rule, either allowed or disallowed.
-  message Rule {
-    oneof rule_field {
-      // The allowed path-patterns.
-      string allowed_pattern = 1;
-      // The disallowed path-patterns.
-      string disallowed_pattern = 2;
-    }
-  }
-
-  // Rules for image resources, ordered by length (longest first).
-  repeated Rule image_ordered_rules = 1;
-  // Rules for video resources, ordered by length (longest first).
-  repeated Rule video_ordered_rules = 2;
-}
\ No newline at end of file
diff --git a/components/subresource_redirect/subresource_redirect_browser_test_util.cc b/components/subresource_redirect/subresource_redirect_browser_test_util.cc
deleted file mode 100644
index b76205f..0000000
--- a/components/subresource_redirect/subresource_redirect_browser_test_util.cc
+++ /dev/null
@@ -1,177 +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 "components/subresource_redirect/subresource_redirect_browser_test_util.h"
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/path_service.h"
-#include "chrome/common/chrome_paths.h"
-#include "components/metrics/content/subprocess_metrics_provider.h"
-#include "components/subresource_redirect/proto/robots_rules.pb.h"
-#include "content/public/test/browser_test_utils.h"
-#include "net/base/url_util.h"
-#include "net/test/embedded_test_server/http_request.h"
-#include "net/test/embedded_test_server/http_response.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace subresource_redirect {
-
-void RetryForHistogramUntilCountReached(base::HistogramTester* histogram_tester,
-                                        const std::string& histogram_name,
-                                        size_t count) {
-  while (true) {
-    FetchHistogramsFromChildProcesses();
-
-    const std::vector<base::Bucket> buckets =
-        histogram_tester->GetAllSamples(histogram_name);
-    size_t total_count = 0;
-    for (const auto& bucket : buckets) {
-      total_count += bucket.count;
-    }
-    if (total_count >= count) {
-      break;
-    }
-  }
-}
-
-void FetchHistogramsFromChildProcesses() {
-  content::FetchHistogramsFromChildProcesses();
-  metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-}
-
-RobotsRulesTestServer::RobotsRulesTestServer()
-    : server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
-
-RobotsRulesTestServer::~RobotsRulesTestServer() = default;
-
-bool RobotsRulesTestServer::Start() {
-  server_.ServeFilesFromSourceDirectory("chrome/test/data");
-  server_.RegisterRequestHandler(base::BindRepeating(
-      &RobotsRulesTestServer::OnServerRequest, base::Unretained(this)));
-  server_.RegisterRequestMonitor(base::BindRepeating(
-      &RobotsRulesTestServer::OnRequestMonitor, base::Unretained(this)));
-  return server_.Start();
-}
-
-void RobotsRulesTestServer::AddRobotsRules(
-    const GURL& origin,
-    const std::vector<RobotsRule>& robots_rules) {
-  robots_rules_proto_[origin.spec()] = GetRobotsRulesProtoString(robots_rules);
-}
-
-void RobotsRulesTestServer::VerifyRequestedOrigins(
-    const std::set<std::string>& requests) {
-  EXPECT_EQ(received_requests_, requests);
-}
-
-std::unique_ptr<net::test_server::HttpResponse>
-RobotsRulesTestServer::OnServerRequest(
-    const net::test_server::HttpRequest& request) {
-  std::unique_ptr<net::test_server::BasicHttpResponse> response =
-      std::make_unique<net::test_server::BasicHttpResponse>();
-  std::string robots_url_str;
-  EXPECT_EQ("/robots", request.GetURL().path());
-  EXPECT_TRUE(
-      net::GetValueForKeyInQuery(request.GetURL(), "u", &robots_url_str));
-  GURL robots_url(robots_url_str);
-  EXPECT_EQ("/robots.txt", GURL(robots_url).path());
-
-  switch (failure_mode_) {
-    case FailureMode::kLoadshed503RetryAfterResponse:
-      response->set_code(net::HTTP_SERVICE_UNAVAILABLE);
-      response->AddCustomHeader("Retry-After", "5");
-      return response;
-    case FailureMode::kTimeout:
-      response = std::make_unique<net::test_server::DelayedHttpResponse>(
-          base::Seconds(4));
-      break;
-    case FailureMode::kNone:
-      break;
-  }
-
-  auto it =
-      robots_rules_proto_.find(robots_url.DeprecatedGetOriginAsURL().spec());
-  if (it != robots_rules_proto_.end())
-    response->set_content(it->second);
-  return std::move(response);
-}
-
-void RobotsRulesTestServer::OnRequestMonitor(
-    const net::test_server::HttpRequest& request) {
-  std::string robots_url_str;
-  EXPECT_EQ("/robots", request.GetURL().path());
-  EXPECT_TRUE(
-      net::GetValueForKeyInQuery(request.GetURL(), "u", &robots_url_str));
-  std::string robots_origin =
-      GURL(robots_url_str).DeprecatedGetOriginAsURL().spec();
-  EXPECT_TRUE(received_requests_.find(robots_origin) ==
-              received_requests_.end());
-  received_requests_.insert(robots_origin);
-}
-
-ImageCompressionTestServer::ImageCompressionTestServer()
-    : server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
-
-ImageCompressionTestServer::~ImageCompressionTestServer() = default;
-
-bool ImageCompressionTestServer::Start() {
-  server_.ServeFilesFromSourceDirectory("chrome/test/data");
-  server_.RegisterRequestHandler(base::BindRepeating(
-      &ImageCompressionTestServer::OnServerRequest, base::Unretained(this)));
-  server_.RegisterRequestMonitor(base::BindRepeating(
-      &ImageCompressionTestServer::OnRequestMonitor, base::Unretained(this)));
-  return server_.Start();
-}
-
-void ImageCompressionTestServer::VerifyRequestedImagePaths(
-    const std::set<std::string>& paths) {
-  EXPECT_EQ(received_request_paths_, paths);
-}
-
-std::unique_ptr<net::test_server::HttpResponse>
-ImageCompressionTestServer::OnServerRequest(
-    const net::test_server::HttpRequest& request) {
-  std::unique_ptr<net::test_server::BasicHttpResponse> response =
-      std::make_unique<net::test_server::BasicHttpResponse>();
-
-  switch (failure_mode_) {
-    case FailureMode::kLoadshed503RetryAfterResponse:
-      response->set_code(net::HTTP_SERVICE_UNAVAILABLE);
-      response->AddCustomHeader("Retry-After", "5");
-      return response;
-    case FailureMode::kNone:
-      break;
-  }
-
-  // Serve the correct image file.
-  std::string img_url;
-  std::string file_contents;
-  base::FilePath test_data_directory;
-  EXPECT_EQ("/i", request.GetURL().path());
-  EXPECT_TRUE(net::GetValueForKeyInQuery(request.GetURL(), "u", &img_url));
-  base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory);
-  if (base::ReadFileToString(
-          test_data_directory.AppendASCII(GURL(img_url).path().substr(1)),
-          &file_contents)) {
-    response->AddCustomHeader("Chrome-Proxy", "ofcl=10000");
-    response->set_content(file_contents);
-    response->set_code(net::HTTP_OK);
-  }
-  return std::move(response);
-}
-
-// Called on every subresource request
-void ImageCompressionTestServer::OnRequestMonitor(
-    const net::test_server::HttpRequest& request) {
-  std::string img_url;
-  EXPECT_EQ("/i", request.GetURL().path());
-  EXPECT_TRUE(net::GetValueForKeyInQuery(request.GetURL(), "u", &img_url));
-  img_url = GURL(img_url).PathForRequest();
-  EXPECT_TRUE(received_request_paths_.find(img_url) ==
-              received_request_paths_.end());
-  received_request_paths_.insert(img_url);
-}
-
-}  // namespace subresource_redirect
diff --git a/components/subresource_redirect/subresource_redirect_browser_test_util.h b/components/subresource_redirect/subresource_redirect_browser_test_util.h
deleted file mode 100644
index 969abdbb..0000000
--- a/components/subresource_redirect/subresource_redirect_browser_test_util.h
+++ /dev/null
@@ -1,119 +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 COMPONENTS_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_BROWSER_TEST_UTIL_H_
-#define COMPONENTS_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_BROWSER_TEST_UTIL_H_
-
-#include <map>
-#include <set>
-#include <string>
-
-#include "base/test/metrics/histogram_tester.h"
-#include "components/subresource_redirect/subresource_redirect_test_util.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "url/gurl.h"
-
-namespace subresource_redirect {
-
-// Retries fetching |histogram_name| until it contains at least |count| samples.
-void RetryForHistogramUntilCountReached(base::HistogramTester* histogram_tester,
-                                        const std::string& histogram_name,
-                                        size_t count);
-
-// Fetches histograms from renderer child processes.
-void FetchHistogramsFromChildProcesses();
-
-// Embedded test server for the robots rules.
-class RobotsRulesTestServer {
- public:
-  // Different failures modes the robots server should return.
-  enum FailureMode {
-    kNone = 0,
-    kLoadshed503RetryAfterResponse,
-    kTimeout,
-  };
-
-  RobotsRulesTestServer();
-  ~RobotsRulesTestServer();
-
-  // Start the server.
-  bool Start();
-
-  std::string GetURL() const {
-    return server_.GetURL("robotsrules.com", "/").spec();
-  }
-
-  void AddRobotsRules(const GURL& origin,
-                      const std::vector<RobotsRule>& robots_rules);
-
-  void VerifyRequestedOrigins(const std::set<std::string>& requests);
-
-  std::set<std::string> received_requests() const { return received_requests_; }
-
-  void set_failure_mode(FailureMode failure_mode) {
-    failure_mode_ = failure_mode;
-  }
-
- private:
-  std::unique_ptr<net::test_server::HttpResponse> OnServerRequest(
-      const net::test_server::HttpRequest& request);
-
-  // Called on every robots request.
-  void OnRequestMonitor(const net::test_server::HttpRequest& request);
-
-  // Robots rules proto keyed by origin.
-  std::map<std::string, std::string> robots_rules_proto_;
-
-  // Whether the robots server should return failure.
-  FailureMode failure_mode_ = FailureMode::kNone;
-
-  // All the origins the robots rules are requested for.
-  std::set<std::string> received_requests_;
-
-  net::EmbeddedTestServer server_;
-};
-
-// Embedded test server to serve the image resources.
-class ImageCompressionTestServer {
- public:
-  // Different failures modes the image server should return
-  enum FailureMode {
-    kNone = 0,
-    kLoadshed503RetryAfterResponse,
-  };
-  ImageCompressionTestServer();
-  ~ImageCompressionTestServer();
-
-  // Start the server.
-  bool Start();
-
-  std::string GetURL() const {
-    return server_.GetURL("imagecompression.com", "/").spec();
-  }
-
-  void VerifyRequestedImagePaths(const std::set<std::string>& paths);
-
-  void set_failure_mode(FailureMode failure_mode) {
-    failure_mode_ = failure_mode;
-  }
-
- private:
-  std::unique_ptr<net::test_server::HttpResponse> OnServerRequest(
-      const net::test_server::HttpRequest& request);
-
-  // Called on every subresource request.
-  void OnRequestMonitor(const net::test_server::HttpRequest& request);
-
-  // All the URL paths of the requested images.
-  std::set<std::string> received_request_paths_;
-
-  // Whether the subresource server should return failure.
-  FailureMode failure_mode_ = FailureMode::kNone;
-
-  net::EmbeddedTestServer server_;
-};
-
-}  // namespace subresource_redirect
-
-#endif  // COMPONENTS_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_BROWSER_TEST_UTIL_H_
diff --git a/components/subresource_redirect/subresource_redirect_test_util.cc b/components/subresource_redirect/subresource_redirect_test_util.cc
deleted file mode 100644
index 0ba4a48d..0000000
--- a/components/subresource_redirect/subresource_redirect_test_util.cc
+++ /dev/null
@@ -1,24 +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 "components/subresource_redirect/subresource_redirect_test_util.h"
-
-#include "components/subresource_redirect/proto/robots_rules.pb.h"
-
-namespace subresource_redirect {
-
-std::string GetRobotsRulesProtoString(const std::vector<RobotsRule>& patterns) {
-  proto::RobotsRules robots_rules;
-  for (const auto& pattern : patterns) {
-    auto* new_rule = robots_rules.add_image_ordered_rules();
-    if (pattern.rule_type == kRuleTypeAllow) {
-      new_rule->set_allowed_pattern(pattern.pattern);
-    } else {
-      new_rule->set_disallowed_pattern(pattern.pattern);
-    }
-  }
-  return robots_rules.SerializeAsString();
-}
-
-}  // namespace subresource_redirect
diff --git a/components/subresource_redirect/subresource_redirect_test_util.h b/components/subresource_redirect/subresource_redirect_test_util.h
deleted file mode 100644
index 8a36b11..0000000
--- a/components/subresource_redirect/subresource_redirect_test_util.h
+++ /dev/null
@@ -1,30 +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 COMPONENTS_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_TEST_UTIL_H_
-#define COMPONENTS_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_TEST_UTIL_H_
-
-#include <string>
-#include <vector>
-
-namespace subresource_redirect {
-
-const bool kRuleTypeAllow = true;
-const bool kRuleTypeDisallow = false;
-
-// Holds one allow or disallow robots rule
-struct RobotsRule {
-  RobotsRule(bool rule_type, const std::string& pattern)
-      : rule_type(rule_type), pattern(pattern) {}
-
-  bool rule_type;
-  std::string pattern;
-};
-
-// Convert robots rules to its proto.
-std::string GetRobotsRulesProtoString(const std::vector<RobotsRule>& patterns);
-
-}  // namespace subresource_redirect
-
-#endif  // COMPONENTS_SUBRESOURCE_REDIRECT_SUBRESOURCE_REDIRECT_TEST_UTIL_H_
diff --git a/components/sync/driver/model_load_manager.cc b/components/sync/driver/model_load_manager.cc
index dcb7f78..72ebb5f8 100644
--- a/components/sync/driver/model_load_manager.cc
+++ b/components/sync/driver/model_load_manager.cc
@@ -27,7 +27,9 @@
                                   ModelTypeSet preferred_types,
                                   const ConfigureContext& context) {
   // |desired_types| must be a subset of |preferred_types|.
-  DCHECK(preferred_types.HasAll(desired_types));
+  DCHECK(preferred_types.HasAll(desired_types))
+      << " desired: " << ModelTypeSetToString(desired_types)
+      << ", preferred: " << ModelTypeSetToString(preferred_types);
 
   bool sync_mode_changed = configure_context_.sync_mode != context.sync_mode;
 
diff --git a/components/sync/engine/entity_data.cc b/components/sync/engine/entity_data.cc
index ca64ac64..39033914 100644
--- a/components/sync/engine/entity_data.cc
+++ b/components/sync/engine/entity_data.cc
@@ -4,12 +4,10 @@
 
 #include "components/sync/engine/entity_data.h"
 
-#include <algorithm>
 #include <ostream>
 #include <utility>
 
 #include "base/json/json_writer.h"
-#include "base/strings/string_util.h"
 #include "base/trace_event/memory_usage_estimator.h"
 #include "base/values.h"
 #include "components/sync/base/time.h"
@@ -26,41 +24,30 @@
 
 EntityData& EntityData::operator=(EntityData&& other) = default;
 
-#define ADD_TO_DICT(dict, value) \
-  dict->SetString(base::ToUpperASCII(#value), value);
-
-#define ADD_TO_DICT_WITH_TRANSFORM(dict, value, transform) \
-  dict->SetString(base::ToUpperASCII(#value), transform(value));
-
 std::unique_ptr<base::DictionaryValue> EntityData::ToDictionaryValue() {
   // This is used when debugging at sync-internals page. The code in
   // sync_node_browser.js is expecting certain fields names. e.g. CTIME, MTIME,
   // and IS_DIR.
-  base::Time ctime = creation_time;
-  base::Time mtime = modification_time;
   std::unique_ptr<base::DictionaryValue> dict =
       std::make_unique<base::DictionaryValue>();
   dict->SetKey("SPECIFICS", base::Value::FromUniquePtrValue(
                                 EntitySpecificsToValue(specifics)));
-  ADD_TO_DICT(dict, id);
-  ADD_TO_DICT(dict, client_tag_hash.value());
-  ADD_TO_DICT(dict, originator_cache_guid);
-  ADD_TO_DICT(dict, originator_client_item_id);
-  ADD_TO_DICT(dict, server_defined_unique_tag);
+  dict->SetString("ID", id);
+  dict->SetString("CLIENT_TAG_HASH", client_tag_hash.value());
+  dict->SetString("ORIGINATOR_CACHE_GUID", originator_cache_guid);
+  dict->SetString("ORIGINATOR_CLIENT_ITEM_ID", originator_client_item_id);
+  dict->SetString("SERVER_DEFINED_UNIQUE_TAG", server_defined_unique_tag);
   // The string "NON_UNIQUE_NAME" is used in sync-internals to identify the node
   // title.
   dict->SetString("NON_UNIQUE_NAME", name);
-  ADD_TO_DICT(dict, name);
+  dict->SetString("NAME", name);
   // The string "PARENT_ID" is used in sync-internals to build the node tree.
   dict->SetString("PARENT_ID", legacy_parent_id);
-  ADD_TO_DICT_WITH_TRANSFORM(dict, ctime, GetTimeDebugString);
-  ADD_TO_DICT_WITH_TRANSFORM(dict, mtime, GetTimeDebugString);
+  dict->SetString("CTIME", GetTimeDebugString(creation_time));
+  dict->SetString("MTIME", GetTimeDebugString(modification_time));
   return dict;
 }
 
-#undef ADD_TO_DICT
-#undef ADD_TO_DICT_WITH_TRANSFORM
-
 size_t EntityData::EstimateMemoryUsage() const {
   using base::trace_event::EstimateMemoryUsage;
   size_t memory_usage = 0;
diff --git a/components/sync/protocol/autofill_specifics.proto b/components/sync/protocol/autofill_specifics.proto
index 8322326..23c7dc8 100644
--- a/components/sync/protocol/autofill_specifics.proto
+++ b/components/sync/protocol/autofill_specifics.proto
@@ -232,8 +232,10 @@
 
   enum VirtualCardEnrollmentState {
     UNSPECIFIED = 0;
-    UNENROLLED = 1;
+    UNENROLLED = 1 [deprecated = true];
     ENROLLED = 2;
+    UNENROLLED_AND_NOT_ELIGIBLE = 3;
+    UNENROLLED_AND_ELIGIBLE = 4;
   }
 
   // Server-generated unique ID string. This is opaque to the client.
diff --git a/components/sync/protocol/proto_enum_conversions.cc b/components/sync/protocol/proto_enum_conversions.cc
index a702f3f8..14cef83 100644
--- a/components/sync/protocol/proto_enum_conversions.cc
+++ b/components/sync/protocol/proto_enum_conversions.cc
@@ -476,11 +476,14 @@
     sync_pb::WalletMaskedCreditCard::VirtualCardEnrollmentState
         virtual_card_enrollment_state) {
   ASSERT_ENUM_BOUNDS(sync_pb::WalletMaskedCreditCard,
-                     VirtualCardEnrollmentState, UNSPECIFIED, ENROLLED);
+                     VirtualCardEnrollmentState, UNSPECIFIED,
+                     UNENROLLED_AND_ELIGIBLE);
   switch (virtual_card_enrollment_state) {
     ENUM_CASE(sync_pb::WalletMaskedCreditCard, UNSPECIFIED);
     ENUM_CASE(sync_pb::WalletMaskedCreditCard, UNENROLLED);
     ENUM_CASE(sync_pb::WalletMaskedCreditCard, ENROLLED);
+    ENUM_CASE(sync_pb::WalletMaskedCreditCard, UNENROLLED_AND_NOT_ELIGIBLE);
+    ENUM_CASE(sync_pb::WalletMaskedCreditCard, UNENROLLED_AND_ELIGIBLE);
   }
   NOTREACHED();
   return "";
diff --git a/components/sync_device_info/device_info_prefs.cc b/components/sync_device_info/device_info_prefs.cc
index b1b8f5a..81c4940 100644
--- a/components/sync_device_info/device_info_prefs.cc
+++ b/components/sync_device_info/device_info_prefs.cc
@@ -74,8 +74,8 @@
 }
 
 void DeviceInfoPrefs::AddLocalCacheGuid(const std::string& cache_guid) {
-  ListPrefUpdate update_cache_guids(pref_service_,
-                                    kDeviceInfoRecentGUIDsWithTimestamps);
+  ListPrefUpdateDeprecated update_cache_guids(
+      pref_service_, kDeviceInfoRecentGUIDsWithTimestamps);
 
   for (auto it = update_cache_guids->GetList().begin();
        it != update_cache_guids->GetList().end(); it++) {
@@ -102,8 +102,8 @@
 }
 
 void DeviceInfoPrefs::GarbageCollectExpiredCacheGuids() {
-  ListPrefUpdate update_cache_guids(pref_service_,
-                                    kDeviceInfoRecentGUIDsWithTimestamps);
+  ListPrefUpdateDeprecated update_cache_guids(
+      pref_service_, kDeviceInfoRecentGUIDsWithTimestamps);
   update_cache_guids->EraseListValueIf([this](const auto& dict) {
     // Avoid crashes if the preference contains corrupt entries that are not
     // dictionaries, and meanwhile clean up these corrupt entries.
diff --git a/components/sync_device_info/device_info_prefs_unittest.cc b/components/sync_device_info/device_info_prefs_unittest.cc
index 7221ed1..3a38775f 100644
--- a/components/sync_device_info/device_info_prefs_unittest.cc
+++ b/components/sync_device_info/device_info_prefs_unittest.cc
@@ -56,8 +56,8 @@
 
   // Manipulate the preference directly to add a corrupt entry to the list,
   // which is a string instead of a dictionary.
-  ListPrefUpdate cache_guids_update(&pref_service_,
-                                    kDeviceInfoRecentGUIDsWithTimestamps);
+  ListPrefUpdateDeprecated cache_guids_update(
+      &pref_service_, kDeviceInfoRecentGUIDsWithTimestamps);
   cache_guids_update->Insert(cache_guids_update->GetList().begin(),
                              base::Value("corrupt_string_entry"));
 
diff --git a/components/sync_preferences/pref_model_associator_unittest.cc b/components/sync_preferences/pref_model_associator_unittest.cc
index 3d6505b..dd79788 100644
--- a/components/sync_preferences/pref_model_associator_unittest.cc
+++ b/components/sync_preferences/pref_model_associator_unittest.cc
@@ -168,7 +168,7 @@
 TEST_F(ListPreferenceMergeTest, ServerNull) {
   auto null_value = std::make_unique<base::Value>();
   {
-    ListPrefUpdate update(pref_service_.get(), kListPrefName);
+    ListPrefUpdateDeprecated update(pref_service_.get(), kListPrefName);
     base::ListValue* local_list_value = update.Get();
     local_list_value->Append(local_url0_);
   }
@@ -184,7 +184,7 @@
 TEST_F(ListPreferenceMergeTest, ServerEmpty) {
   std::unique_ptr<base::Value> empty_value(new base::ListValue);
   {
-    ListPrefUpdate update(pref_service_.get(), kListPrefName);
+    ListPrefUpdateDeprecated update(pref_service_.get(), kListPrefName);
     base::ListValue* local_list_value = update.Get();
     local_list_value->Append(local_url0_);
   }
@@ -199,7 +199,7 @@
 
 TEST_F(ListPreferenceMergeTest, Merge) {
   {
-    ListPrefUpdate update(pref_service_.get(), kListPrefName);
+    ListPrefUpdateDeprecated update(pref_service_.get(), kListPrefName);
     base::ListValue* local_list_value = update.Get();
     local_list_value->Append(local_url0_);
     local_list_value->Append(local_url1_);
@@ -220,7 +220,7 @@
 
 TEST_F(ListPreferenceMergeTest, Duplicates) {
   {
-    ListPrefUpdate update(pref_service_.get(), kListPrefName);
+    ListPrefUpdateDeprecated update(pref_service_.get(), kListPrefName);
     base::ListValue* local_list_value = update.Get();
     local_list_value->Append(local_url0_);
     local_list_value->Append(server_url0_);
@@ -241,7 +241,7 @@
 
 TEST_F(ListPreferenceMergeTest, Equals) {
   {
-    ListPrefUpdate update(pref_service_.get(), kListPrefName);
+    ListPrefUpdateDeprecated update(pref_service_.get(), kListPrefName);
     base::ListValue* local_list_value = update.Get();
     local_list_value->Append(server_url0_);
     local_list_value->Append(server_url1_);
@@ -288,7 +288,8 @@
 TEST_F(DictionaryPreferenceMergeTest, ServerNull) {
   auto null_value = std::make_unique<base::Value>();
   {
-    DictionaryPrefUpdate update(pref_service_.get(), kDictionaryPrefName);
+    DictionaryPrefUpdateDeprecated update(pref_service_.get(),
+                                          kDictionaryPrefName);
     base::DictionaryValue* local_dict_value = update.Get();
     SetContentPattern(local_dict_value, expression3_, 1);
   }
@@ -305,7 +306,8 @@
 TEST_F(DictionaryPreferenceMergeTest, ServerEmpty) {
   std::unique_ptr<base::Value> empty_value(new base::DictionaryValue);
   {
-    DictionaryPrefUpdate update(pref_service_.get(), kDictionaryPrefName);
+    DictionaryPrefUpdateDeprecated update(pref_service_.get(),
+                                          kDictionaryPrefName);
     base::DictionaryValue* local_dict_value = update.Get();
     SetContentPattern(local_dict_value, expression3_, 1);
   }
@@ -321,7 +323,8 @@
 
 TEST_F(DictionaryPreferenceMergeTest, MergeNoConflicts) {
   {
-    DictionaryPrefUpdate update(pref_service_.get(), kDictionaryPrefName);
+    DictionaryPrefUpdateDeprecated update(pref_service_.get(),
+                                          kDictionaryPrefName);
     base::DictionaryValue* local_dict_value = update.Get();
     SetContentPattern(local_dict_value, expression3_, 1);
   }
@@ -341,7 +344,8 @@
 
 TEST_F(DictionaryPreferenceMergeTest, MergeConflicts) {
   {
-    DictionaryPrefUpdate update(pref_service_.get(), kDictionaryPrefName);
+    DictionaryPrefUpdateDeprecated update(pref_service_.get(),
+                                          kDictionaryPrefName);
     base::DictionaryValue* local_dict_value = update.Get();
     SetContentPattern(local_dict_value, expression0_, 2);
     SetContentPattern(local_dict_value, expression2_, 1);
@@ -378,7 +382,8 @@
 
 TEST_F(DictionaryPreferenceMergeTest, Equal) {
   {
-    DictionaryPrefUpdate update(pref_service_.get(), kDictionaryPrefName);
+    DictionaryPrefUpdateDeprecated update(pref_service_.get(),
+                                          kDictionaryPrefName);
     base::DictionaryValue* local_dict_value = update.Get();
     SetContentPattern(local_dict_value, expression0_, 1);
     SetContentPattern(local_dict_value, expression1_, 2);
@@ -394,7 +399,8 @@
 
 TEST_F(DictionaryPreferenceMergeTest, ConflictButServerWins) {
   {
-    DictionaryPrefUpdate update(pref_service_.get(), kDictionaryPrefName);
+    DictionaryPrefUpdateDeprecated update(pref_service_.get(),
+                                          kDictionaryPrefName);
     base::DictionaryValue* local_dict_value = update.Get();
     SetContentPattern(local_dict_value, expression0_, 2);
     SetContentPattern(local_dict_value, expression1_, 2);
@@ -421,7 +427,7 @@
 
   bool MergeListPreference(const char* pref) {
     {
-      ListPrefUpdate update(pref_service_.get(), pref);
+      ListPrefUpdateDeprecated update(pref_service_.get(), pref);
       base::ListValue* local_list_value = update.Get();
       local_list_value->Append(url1_);
     }
@@ -437,7 +443,7 @@
 
   bool MergeDictionaryPreference(const char* pref) {
     {
-      DictionaryPrefUpdate update(pref_service_.get(), pref);
+      DictionaryPrefUpdateDeprecated update(pref_service_.get(), pref);
       base::DictionaryValue* local_dict_value = update.Get();
       SetContentPattern(local_dict_value, expression1_, 1);
     }
diff --git a/components/sync_preferences/pref_service_syncable_unittest.cc b/components/sync_preferences/pref_service_syncable_unittest.cc
index 64fac4c..41f63bb 100644
--- a/components/sync_preferences/pref_service_syncable_unittest.cc
+++ b/components/sync_preferences/pref_service_syncable_unittest.cc
@@ -299,7 +299,7 @@
 TEST_F(PrefServiceSyncableTest, ModelAssociationEmptyCloud) {
   prefs_.SetString(kStringPrefName, kExampleUrl0);
   {
-    ListPrefUpdate update(GetPrefs(), kListPrefName);
+    ListPrefUpdateDeprecated update(GetPrefs(), kListPrefName);
     base::ListValue* url_list = update.Get();
     url_list->Append(kExampleUrl0);
     url_list->Append(kExampleUrl1);
@@ -318,7 +318,7 @@
 TEST_F(PrefServiceSyncableTest, ModelAssociationCloudHasData) {
   prefs_.SetString(kStringPrefName, kExampleUrl0);
   {
-    ListPrefUpdate update(GetPrefs(), kListPrefName);
+    ListPrefUpdateDeprecated update(GetPrefs(), kListPrefName);
     base::ListValue* url_list = update.Get();
     url_list->Append(kExampleUrl0);
   }
@@ -508,7 +508,7 @@
 
 TEST_F(PrefServiceSyncableMergeTest, ShouldMergeSelectedListValues) {
   {
-    ListPrefUpdate update(&prefs_, kListPrefName);
+    ListPrefUpdateDeprecated update(&prefs_, kListPrefName);
     base::ListValue* url_list = update.Get();
     url_list->Append(kExampleUrl0);
     url_list->Append(kExampleUrl1);
@@ -589,7 +589,7 @@
 
 TEST_F(PrefServiceSyncableMergeTest, ShouldMergeSelectedDictionaryValues) {
   {
-    DictionaryPrefUpdate update(&prefs_, kDictPrefName);
+    DictionaryPrefUpdateDeprecated update(&prefs_, kDictPrefName);
     base::DictionaryValue* dict_value = update.Get();
     dict_value->Set("my_key1", std::make_unique<base::Value>("my_value1"));
     dict_value->Set("my_key3", std::make_unique<base::Value>("my_value3"));
diff --git a/components/test/data/payments/blob_url.js b/components/test/data/payments/blob_url.js
index 026ee80..23584f2c 100644
--- a/components/test/data/payments/blob_url.js
+++ b/components/test/data/payments/blob_url.js
@@ -7,8 +7,10 @@
 /** Requests payment via a blob URL. */
 function buy() { // eslint-disable-line no-unused-vars
   var spoof = function() {
+    // base64-encoded HTML page that defines a function, triggerPaymentRequest,
+    // which creates a basic-card PaymentRequest and calls show() on it.
     var payload =
-        'PGh0bWw+PGhlYWQ+PG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0yLCBtYXhpbXVtLXNjYWxlPTIiPjwvaGVhZD48Ym9keT48ZGl2IGlkPSJyZXN1bHQiPjwvZGl2PjxzY3JpcHQ+dHJ5IHsgIG5ldyBQYXltZW50UmVxdWVzdChbe3N1cHBvcnRlZE1ldGhvZHM6ICJiYXNpYy1jYXJkIn1dLCAgICB7dG90YWw6IHtsYWJlbDogIlQiLCBhbW91bnQ6IHtjdXJyZW5jeTogIlVTRCIsIHZhbHVlOiAiMS4wMCJ9fX0pICAuc2hvdygpICAudGhlbihmdW5jdGlvbihpbnN0cnVtZW50UmVzcG9uc2UpIHsgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInJlc3VsdCIpLmlubmVySFRNTCA9ICJSZXNvbHZlZCI7ICB9KS5jYXRjaChmdW5jdGlvbihlKSB7ICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJyZXN1bHQiKS5pbm5lckhUTUwgPSAiUmVqZWN0ZWQ6ICIgKyBlOyAgfSk7fSBjYXRjaChlKSB7ICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgicmVzdWx0IikuaW5uZXJIVE1MID0gIkV4Y2VwdGlvbjogIiArIGU7fTwvc2NyaXB0PjwvYm9keT48L2h0bWw+'; // eslint-disable-line max-len
+        'PGh0bWw+PGhlYWQ+PG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0yLCBtYXhpbXVtLXNjYWxlPTIiPjwvaGVhZD48Ym9keT48ZGl2IGlkPSJyZXN1bHQiPjwvZGl2PjxzY3JpcHQ+ZnVuY3Rpb24gdHJpZ2dlclBheW1lbnRSZXF1ZXN0KCkgeyB0cnkgeyAgbmV3IFBheW1lbnRSZXF1ZXN0KFt7c3VwcG9ydGVkTWV0aG9kczogImJhc2ljLWNhcmQifV0sICAgIHt0b3RhbDoge2xhYmVsOiAiVCIsIGFtb3VudDoge2N1cnJlbmN5OiAiVVNEIiwgdmFsdWU6ICIxLjAwIn19fSkgIC5zaG93KCkgIC50aGVuKGZ1bmN0aW9uKGluc3RydW1lbnRSZXNwb25zZSkgeyAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgicmVzdWx0IikuaW5uZXJIVE1MID0gIlJlc29sdmVkIjsgIH0pLmNhdGNoKGZ1bmN0aW9uKGUpIHsgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInJlc3VsdCIpLmlubmVySFRNTCA9ICJSZWplY3RlZDogIiArIGU7ICB9KTt9IGNhdGNoKGUpIHsgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJyZXN1bHQiKS5pbm5lckhUTUwgPSAiRXhjZXB0aW9uOiAiICsgZTt9fTwvc2NyaXB0PjwvYm9keT48L2h0bWw+'; // eslint-disable-line max-len
     document.write(atob(payload));
   };
   window.location.href =
diff --git a/components/translate/core/browser/translate_infobar_delegate_unittest.cc b/components/translate/core/browser/translate_infobar_delegate_unittest.cc
index fe5f33e..cdfe2be7 100644
--- a/components/translate/core/browser/translate_infobar_delegate_unittest.cc
+++ b/components/translate/core/browser/translate_infobar_delegate_unittest.cc
@@ -197,8 +197,8 @@
                                             testing::accept_languages_prefs);
   ON_CALL(*(client_.get()), GetTranslateAcceptLanguages())
       .WillByDefault(Return(&accept_languages));
-  ListPrefUpdate update(pref_service_.get(),
-                        translate::prefs::kBlockedLanguages);
+  ListPrefUpdateDeprecated update(pref_service_.get(),
+                                  translate::prefs::kBlockedLanguages);
   update->Append(kSourceLanguage);
   pref_service_->SetString(language::prefs::kAcceptLanguages, kSourceLanguage);
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -214,7 +214,7 @@
 }
 
 TEST_F(TranslateInfoBarDelegateTest, ShouldAutoAlwaysTranslate) {
-  DictionaryPrefUpdate update_translate_accepted_count(
+  DictionaryPrefUpdateDeprecated update_translate_accepted_count(
       pref_service_.get(), TranslatePrefs::kPrefTranslateAcceptedCount);
   base::Value* update_translate_accepted_dict =
       update_translate_accepted_count.Get();
@@ -248,7 +248,7 @@
 }
 
 TEST_F(TranslateInfoBarDelegateTest, ShouldNotAutoAlwaysTranslateUnknown) {
-  DictionaryPrefUpdate update_translate_accepted_count(
+  DictionaryPrefUpdateDeprecated update_translate_accepted_count(
       pref_service_.get(), TranslatePrefs::kPrefTranslateAcceptedCount);
   base::Value* update_translate_accepted_dict =
       update_translate_accepted_count.Get();
@@ -304,7 +304,7 @@
   ON_CALL(*(client_.get()), GetTranslateAcceptLanguages())
       .WillByDefault(Return(&accept_languages));
 
-  DictionaryPrefUpdate update_translate_denied_count(
+  DictionaryPrefUpdateDeprecated update_translate_denied_count(
       pref_service_.get(), TranslatePrefs::kPrefTranslateDeniedCount);
   base::Value* update_translate_denied_dict =
       update_translate_denied_count.Get();
diff --git a/components/translate/core/browser/translate_manager_unittest.cc b/components/translate/core/browser/translate_manager_unittest.cc
index fea1ccb..b285f28 100644
--- a/components/translate/core/browser/translate_manager_unittest.cc
+++ b/components/translate/core/browser/translate_manager_unittest.cc
@@ -684,15 +684,15 @@
 
   base::HistogramTester histogram_tester;
   prefs_.SetBoolean(prefs::kOfferTranslateEnabled, true);
-  translate_manager_->GetLanguageState()->LanguageDetermined("en", true);
+  translate_manager_->GetLanguageState()->LanguageDetermined("zu", true);
   network_notifier_.SimulateOnline();
 
-  translate_manager_->InitiateTranslation("en");
+  translate_manager_->InitiateTranslation("zu");
   EXPECT_THAT(histogram_tester.GetAllSamples(kInitiationStatusName),
               ElementsAre(Bucket(metrics::INITIATION_STATUS_SHOW_INFOBAR, 1),
                           Bucket(metrics::INITIATION_STATUS_SHOW_ICON, 1)));
 
-  translate_manager_->TranslatePage("en", "hi", false);
+  translate_manager_->TranslatePage("zu", "hi", false);
 
   // Accept languages should now contain "hi" because the user chose to
   // translate to it once.
@@ -1137,7 +1137,7 @@
 TEST_F(TranslateManagerTest, PredefinedTargetLanguage) {
   PrepareTranslateManager();
   manager_->set_application_locale("en");
-  ASSERT_TRUE(TranslateDownloadManager::IsSupportedLanguage("en"));
+  ASSERT_TRUE(TranslateDownloadManager::IsSupportedLanguage("zu"));
 
   ON_CALL(mock_translate_client_, IsTranslatableURL(GURL::EmptyGURL()))
       .WillByDefault(Return(true));
@@ -1152,19 +1152,19 @@
       "ru",
       translate_manager_->GetLanguageState()->GetPredefinedTargetLanguage());
 
-  translate_manager_->GetLanguageState()->LanguageDetermined("en", true);
+  translate_manager_->GetLanguageState()->LanguageDetermined("zu", true);
 
   EXPECT_CALL(
       mock_translate_client_,
-      ShowTranslateUI(translate::TRANSLATE_STEP_BEFORE_TRANSLATE, "en", "ru",
+      ShowTranslateUI(translate::TRANSLATE_STEP_BEFORE_TRANSLATE, "zu", "ru",
                       TranslateErrors::NONE, /*triggered_from_menu=*/false))
       .WillOnce(Return(true));
 
   base::HistogramTester histogram_tester;
-  translate_manager_->InitiateTranslation("en");
+  translate_manager_->InitiateTranslation("zu");
   EXPECT_THAT(
       histogram_tester.GetAllSamples(kInitiationStatusName),
-      ElementsAre(Bucket(
+      ::testing::Contains(Bucket(
           metrics::INITIATION_STATUS_SHOW_UI_PREDEFINED_TARGET_LANGUAGE, 1)));
 }
 
diff --git a/components/translate/core/browser/translate_prefs.cc b/components/translate/core/browser/translate_prefs.cc
index 6fd454e..251f0d2 100644
--- a/components/translate/core/browser/translate_prefs.cc
+++ b/components/translate/core/browser/translate_prefs.cc
@@ -244,7 +244,8 @@
   if (!IsBlockedLanguage(input_language)) {
     std::string canonical_lang(input_language);
     language::ToTranslateLanguageSynonym(&canonical_lang);
-    ListPrefUpdate update(prefs_, translate::prefs::kBlockedLanguages);
+    ListPrefUpdateDeprecated update(prefs_,
+                                    translate::prefs::kBlockedLanguages);
     update->Append(std::move(canonical_lang));
   }
   // Remove the blocked language from the always translate list if present.
@@ -259,7 +260,7 @@
   }
   std::string canonical_lang(input_language);
   language::ToTranslateLanguageSynonym(&canonical_lang);
-  ListPrefUpdate update(prefs_, translate::prefs::kBlockedLanguages);
+  ListPrefUpdateDeprecated update(prefs_, translate::prefs::kBlockedLanguages);
   update->EraseListValue(base::Value(std::move(canonical_lang)));
 }
 
@@ -557,14 +558,14 @@
 void TranslatePrefs::AddSiteToNeverPromptList(base::StringPiece site) {
   DCHECK(!site.empty());
   AddValueToNeverPromptList(kPrefNeverPromptSitesDeprecated, site);
-  DictionaryPrefUpdate update(prefs_, kPrefNeverPromptSitesWithTime);
+  DictionaryPrefUpdateDeprecated update(prefs_, kPrefNeverPromptSitesWithTime);
   update.Get()->SetKey(site, base::TimeToValue(base::Time::Now()));
 }
 
 void TranslatePrefs::RemoveSiteFromNeverPromptList(base::StringPiece site) {
   DCHECK(!site.empty());
   RemoveValueFromNeverPromptList(kPrefNeverPromptSitesDeprecated, site);
-  DictionaryPrefUpdate update(prefs_, kPrefNeverPromptSitesWithTime);
+  DictionaryPrefUpdateDeprecated update(prefs_, kPrefNeverPromptSitesWithTime);
   update.Get()->RemoveKey(site);
 }
 
@@ -607,7 +608,8 @@
 void TranslatePrefs::AddLanguagePairToAlwaysTranslateList(
     base::StringPiece source_language,
     base::StringPiece target_language) {
-  DictionaryPrefUpdate update(prefs_, prefs::kPrefAlwaysTranslateList);
+  DictionaryPrefUpdateDeprecated update(prefs_,
+                                        prefs::kPrefAlwaysTranslateList);
   DCHECK(update.Get()) << "Always translated pref is unregistered";
 
   // Get translate version of language codes.
@@ -625,7 +627,8 @@
 void TranslatePrefs::RemoveLanguagePairFromAlwaysTranslateList(
     base::StringPiece source_language,
     base::StringPiece target_language) {
-  DictionaryPrefUpdate update(prefs_, prefs::kPrefAlwaysTranslateList);
+  DictionaryPrefUpdateDeprecated update(prefs_,
+                                        prefs::kPrefAlwaysTranslateList);
   DCHECK(update.Get()) << "Always translate pref is unregistered";
 
   // Get translate version of language codes.
@@ -681,7 +684,7 @@
 
 void TranslatePrefs::IncrementTranslationDeniedCount(
     base::StringPiece language) {
-  DictionaryPrefUpdate update(prefs_, kPrefTranslateDeniedCount);
+  DictionaryPrefUpdateDeprecated update(prefs_, kPrefTranslateDeniedCount);
   base::Value* dict = update.Get();
 
   int count = dict->FindIntKey(language).value_or(0);
@@ -690,7 +693,7 @@
 }
 
 void TranslatePrefs::ResetTranslationDeniedCount(base::StringPiece language) {
-  DictionaryPrefUpdate update(prefs_, kPrefTranslateDeniedCount);
+  DictionaryPrefUpdateDeprecated update(prefs_, kPrefTranslateDeniedCount);
   update.Get()->SetIntKey(language, 0);
 }
 
@@ -702,7 +705,7 @@
 
 void TranslatePrefs::IncrementTranslationIgnoredCount(
     base::StringPiece language) {
-  DictionaryPrefUpdate update(prefs_, kPrefTranslateIgnoredCount);
+  DictionaryPrefUpdateDeprecated update(prefs_, kPrefTranslateIgnoredCount);
   base::Value* dict = update.Get();
 
   int count = dict->FindIntKey(language).value_or(0);
@@ -711,7 +714,7 @@
 }
 
 void TranslatePrefs::ResetTranslationIgnoredCount(base::StringPiece language) {
-  DictionaryPrefUpdate update(prefs_, kPrefTranslateIgnoredCount);
+  DictionaryPrefUpdateDeprecated update(prefs_, kPrefTranslateIgnoredCount);
   update.Get()->SetIntKey(language, 0);
 }
 
@@ -723,7 +726,7 @@
 
 void TranslatePrefs::IncrementTranslationAcceptedCount(
     base::StringPiece language) {
-  DictionaryPrefUpdate update(prefs_, kPrefTranslateAcceptedCount);
+  DictionaryPrefUpdateDeprecated update(prefs_, kPrefTranslateAcceptedCount);
   base::Value* dict = update.Get();
 
   int count = dict->FindIntKey(language).value_or(0);
@@ -732,7 +735,7 @@
 }
 
 void TranslatePrefs::ResetTranslationAcceptedCount(base::StringPiece language) {
-  DictionaryPrefUpdate update(prefs_, kPrefTranslateAcceptedCount);
+  DictionaryPrefUpdateDeprecated update(prefs_, kPrefTranslateAcceptedCount);
   update.Get()->SetIntKey(language, 0);
 }
 
@@ -746,7 +749,7 @@
 
 void TranslatePrefs::IncrementTranslationAutoAlwaysCount(
     base::StringPiece language) {
-  DictionaryPrefUpdate update(prefs_, kPrefTranslateAutoAlwaysCount);
+  DictionaryPrefUpdateDeprecated update(prefs_, kPrefTranslateAutoAlwaysCount);
   base::Value* dict = update.Get();
 
   int count = dict->FindIntKey(language).value_or(0);
@@ -756,7 +759,7 @@
 
 void TranslatePrefs::ResetTranslationAutoAlwaysCount(
     base::StringPiece language) {
-  DictionaryPrefUpdate update(prefs_, kPrefTranslateAutoAlwaysCount);
+  DictionaryPrefUpdateDeprecated update(prefs_, kPrefTranslateAutoAlwaysCount);
   update.Get()->SetIntKey(language, 0);
 }
 
@@ -768,7 +771,7 @@
 
 void TranslatePrefs::IncrementTranslationAutoNeverCount(
     base::StringPiece language) {
-  DictionaryPrefUpdate update(prefs_, kPrefTranslateAutoNeverCount);
+  DictionaryPrefUpdateDeprecated update(prefs_, kPrefTranslateAutoNeverCount);
   base::Value* dict = update.Get();
 
   int count = dict->FindIntKey(language).value_or(0);
@@ -778,7 +781,7 @@
 
 void TranslatePrefs::ResetTranslationAutoNeverCount(
     base::StringPiece language) {
-  DictionaryPrefUpdate update(prefs_, kPrefTranslateAutoNeverCount);
+  DictionaryPrefUpdateDeprecated update(prefs_, kPrefTranslateAutoNeverCount);
   update.Get()->SetIntKey(language, 0);
 }
 #endif  // defined(OS_ANDROID) || defined(OS_IOS)
@@ -955,11 +958,11 @@
   // Migration copies any sites on the deprecated never prompt pref to
   // the new version and clears all references to the old one. This will
   // make subsequent calls to migrate no-ops.
-  DictionaryPrefUpdate never_prompt_list_update(prefs_,
-                                                kPrefNeverPromptSitesWithTime);
+  DictionaryPrefUpdateDeprecated never_prompt_list_update(
+      prefs_, kPrefNeverPromptSitesWithTime);
   base::Value* never_prompt_list = never_prompt_list_update.Get();
   if (never_prompt_list) {
-    ListPrefUpdate deprecated_prompt_list_update(
+    ListPrefUpdateDeprecated deprecated_prompt_list_update(
         prefs_, kPrefNeverPromptSitesDeprecated);
     base::Value* deprecated_list = deprecated_prompt_list_update.Get();
     for (auto& site : deprecated_list->GetList()) {
@@ -1003,7 +1006,7 @@
 
 void TranslatePrefs::AddValueToNeverPromptList(const char* pref_id,
                                                base::StringPiece value) {
-  ListPrefUpdate update(prefs_, pref_id);
+  ListPrefUpdateDeprecated update(prefs_, pref_id);
   base::Value* never_prompt_list = update.Get();
   if (!never_prompt_list) {
     NOTREACHED() << "Unregistered never-translate pref";
@@ -1018,7 +1021,7 @@
 
 void TranslatePrefs::RemoveValueFromNeverPromptList(const char* pref_id,
                                                     base::StringPiece value) {
-  ListPrefUpdate update(prefs_, pref_id);
+  ListPrefUpdateDeprecated update(prefs_, pref_id);
   base::Value* never_prompt_list = update.Get();
   if (!never_prompt_list) {
     NOTREACHED() << "Unregistered never-translate pref";
diff --git a/components/translate/core/browser/translate_prefs_unittest.cc b/components/translate/core/browser/translate_prefs_unittest.cc
index 38e1f7b..b845330 100644
--- a/components/translate/core/browser/translate_prefs_unittest.cc
+++ b/components/translate/core/browser/translate_prefs_unittest.cc
@@ -944,7 +944,7 @@
                 .size(),
             2u);
   // Also put one of those sites on the new pref but migrated incorrectly.
-  DictionaryPrefUpdate never_prompt_list_update(
+  DictionaryPrefUpdateDeprecated never_prompt_list_update(
       &prefs_, TranslatePrefs::kPrefNeverPromptSitesWithTime);
   base::Value* never_prompt_list = never_prompt_list_update.Get();
   never_prompt_list->SetKey("migratedWrong.com", base::Value(0));
@@ -1138,10 +1138,12 @@
   EXPECT_FALSE(translate_prefs_->CanTranslateLanguage(
       &translate_accept_languages, "en"));
 
-  // Blocked languages that are not in accept languages are not blocked.
-  translate_prefs_->BlockLanguage("de");
-  EXPECT_TRUE(translate_prefs_->CanTranslateLanguage(
-      &translate_accept_languages, "de"));
+  if (!TranslatePrefs::IsDetailedLanguageSettingsEnabled()) {
+    // Blocked languages that are not in accept languages are not blocked.
+    translate_prefs_->BlockLanguage("de");
+    EXPECT_TRUE(translate_prefs_->CanTranslateLanguage(
+        &translate_accept_languages, "de"));
+  }
 
 // When the detailed language settings are enabled blocked languages not in
 // accept languages can be translated.
diff --git a/components/update_client/persisted_data.cc b/components/update_client/persisted_data.cc
index e86fe4b..e5fede21 100644
--- a/components/update_client/persisted_data.cc
+++ b/components/update_client/persisted_data.cc
@@ -112,7 +112,8 @@
     base::OnceClosure callback,
     const std::set<std::string>& active_ids) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DictionaryPrefUpdate update(pref_service_, kPersistedDataPreference);
+  DictionaryPrefUpdateDeprecated update(pref_service_,
+                                        kPersistedDataPreference);
   for (const auto& id : ids) {
     base::Value* app_key = GetOrCreateAppKey(id, update.Get());
     app_key->SetIntKey("dlrc", datenum);
@@ -149,7 +150,8 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!pref_service_)
     return;
-  DictionaryPrefUpdate update(pref_service_, kPersistedDataPreference);
+  DictionaryPrefUpdateDeprecated update(pref_service_,
+                                        kPersistedDataPreference);
   GetOrCreateAppKey(id, update.Get())->SetStringKey(key, value);
 }
 
diff --git a/components/user_manager/known_user.cc b/components/user_manager/known_user.cc
index db1c6e6..6e177bc 100644
--- a/components/user_manager/known_user.cc
+++ b/components/user_manager/known_user.cc
@@ -252,7 +252,7 @@
   if (!account_id.is_valid())
     return;
 
-  ListPrefUpdate update(local_state_, kKnownUsers);
+  ListPrefUpdateDeprecated update(local_state_, kKnownUsers);
   for (base::Value& element_value : update->GetList()) {
     if (element_value.is_dict()) {
       base::DictionaryValue* element =
@@ -783,7 +783,7 @@
   if (!account_id.is_valid())
     return;
 
-  ListPrefUpdate update(local_state_, kKnownUsers);
+  ListPrefUpdateDeprecated update(local_state_, kKnownUsers);
   base::Value::ListView update_view = update->GetList();
   for (auto it = update_view.begin(); it != update_view.end(); ++it) {
     base::DictionaryValue* element = nullptr;
@@ -797,7 +797,7 @@
 }
 
 void KnownUser::CleanEphemeralUsers() {
-  ListPrefUpdate update(local_state_, kKnownUsers);
+  ListPrefUpdateDeprecated update(local_state_, kKnownUsers);
   update->EraseListValueIf([](const auto& value) {
     if (!value.is_dict())
       return false;
@@ -808,7 +808,7 @@
 }
 
 void KnownUser::CleanObsoletePrefs() {
-  ListPrefUpdate update(local_state_, kKnownUsers);
+  ListPrefUpdateDeprecated update(local_state_, kKnownUsers);
   for (base::Value& user_entry : update.Get()->GetList()) {
     if (!user_entry.is_dict())
       continue;
diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc
index a2559fe..d506a8e 100644
--- a/components/user_manager/user_manager_base.cc
+++ b/components/user_manager/user_manager_base.cc
@@ -404,8 +404,8 @@
     return;
 
   {
-    DictionaryPrefUpdate oauth_status_update(GetLocalState(),
-                                             kUserOAuthTokenStatus);
+    DictionaryPrefUpdateDeprecated oauth_status_update(GetLocalState(),
+                                                       kUserOAuthTokenStatus);
     oauth_status_update->SetKey(
         account_id.GetUserEmail(),
         base::Value(static_cast<int>(oauth_token_status)));
@@ -427,8 +427,8 @@
     return;
 
   {
-    DictionaryPrefUpdate force_online_update(GetLocalState(),
-                                             kUserForceOnlineSignin);
+    DictionaryPrefUpdateDeprecated force_online_update(GetLocalState(),
+                                                       kUserForceOnlineSignin);
     force_online_update->SetKey(account_id.GetUserEmail(),
                                 base::Value(force_online_signin));
   }
@@ -445,8 +445,8 @@
     // Do not update local state if data stored or cached outside the user's
     // cryptohome is to be treated as ephemeral.
     if (!IsUserNonCryptohomeDataEphemeral(account_id)) {
-      DictionaryPrefUpdate display_name_update(GetLocalState(),
-                                               kUserDisplayName);
+      DictionaryPrefUpdateDeprecated display_name_update(GetLocalState(),
+                                                         kUserDisplayName);
       display_name_update->SetKey(account_id.GetUserEmail(),
                                   base::Value(display_name));
     }
@@ -476,7 +476,8 @@
   if (IsUserNonCryptohomeDataEphemeral(account_id))
     return;
 
-  DictionaryPrefUpdate display_email_update(GetLocalState(), kUserDisplayEmail);
+  DictionaryPrefUpdateDeprecated display_email_update(GetLocalState(),
+                                                      kUserDisplayEmail);
   display_email_update->SetKey(account_id.GetUserEmail(),
                                base::Value(display_email));
 }
@@ -490,7 +491,7 @@
   if (IsUserNonCryptohomeDataEphemeral(user->GetAccountId()))
     return;
 
-  DictionaryPrefUpdate user_type_update(GetLocalState(), kUserType);
+  DictionaryPrefUpdateDeprecated user_type_update(GetLocalState(), kUserType);
   user_type_update->SetKey(user->GetAccountId().GetAccountIdKey(),
                            base::Value(static_cast<int>(user->GetType())));
   GetLocalState()->CommitPendingWrite();
@@ -507,7 +508,8 @@
     std::u16string given_name = account_data.given_name();
     user->set_given_name(given_name);
     if (!IsUserNonCryptohomeDataEphemeral(account_id)) {
-      DictionaryPrefUpdate given_name_update(GetLocalState(), kUserGivenName);
+      DictionaryPrefUpdateDeprecated given_name_update(GetLocalState(),
+                                                       kUserGivenName);
       given_name_update->SetKey(account_id.GetUserEmail(),
                                 base::Value(given_name));
     }
@@ -908,7 +910,8 @@
 
 void UserManagerBase::AddUserRecord(User* user) {
   // Add the user to the front of the user list.
-  ListPrefUpdate prefs_users_update(GetLocalState(), kRegularUsersPref);
+  ListPrefUpdateDeprecated prefs_users_update(GetLocalState(),
+                                              kRegularUsersPref);
   prefs_users_update->Insert(prefs_users_update->GetList().begin(),
                              base::Value(user->GetAccountId().GetUserEmail()));
   users_.insert(users_.begin(), user);
@@ -1000,19 +1003,23 @@
 
 void UserManagerBase::RemoveNonCryptohomeData(const AccountId& account_id) {
   PrefService* prefs = GetLocalState();
-  DictionaryPrefUpdate prefs_display_name_update(prefs, kUserDisplayName);
+  DictionaryPrefUpdateDeprecated prefs_display_name_update(prefs,
+                                                           kUserDisplayName);
   prefs_display_name_update->RemoveKey(account_id.GetUserEmail());
 
-  DictionaryPrefUpdate prefs_given_name_update(prefs, kUserGivenName);
+  DictionaryPrefUpdateDeprecated prefs_given_name_update(prefs, kUserGivenName);
   prefs_given_name_update->RemoveKey(account_id.GetUserEmail());
 
-  DictionaryPrefUpdate prefs_display_email_update(prefs, kUserDisplayEmail);
+  DictionaryPrefUpdateDeprecated prefs_display_email_update(prefs,
+                                                            kUserDisplayEmail);
   prefs_display_email_update->RemoveKey(account_id.GetUserEmail());
 
-  DictionaryPrefUpdate prefs_oauth_update(prefs, kUserOAuthTokenStatus);
+  DictionaryPrefUpdateDeprecated prefs_oauth_update(prefs,
+                                                    kUserOAuthTokenStatus);
   prefs_oauth_update->RemoveKey(account_id.GetUserEmail());
 
-  DictionaryPrefUpdate prefs_force_online_update(prefs, kUserForceOnlineSignin);
+  DictionaryPrefUpdateDeprecated prefs_force_online_update(
+      prefs, kUserForceOnlineSignin);
   prefs_force_online_update->RemoveKey(account_id.GetUserEmail());
 
   KnownUser(prefs).RemovePrefs(account_id);
@@ -1026,7 +1033,8 @@
 User* UserManagerBase::RemoveRegularOrSupervisedUserFromList(
     const AccountId& account_id,
     bool notify) {
-  ListPrefUpdate prefs_users_update(GetLocalState(), kRegularUsersPref);
+  ListPrefUpdateDeprecated prefs_users_update(GetLocalState(),
+                                              kRegularUsersPref);
   prefs_users_update->ClearList();
   User* user = nullptr;
   for (UserList::iterator it = users_.begin(); it != users_.end();) {
diff --git a/components/variations/service/variations_field_trial_creator.cc b/components/variations/service/variations_field_trial_creator.cc
index 041ae3e..e555831 100644
--- a/components/variations/service/variations_field_trial_creator.cc
+++ b/components/variations/service/variations_field_trial_creator.cc
@@ -482,6 +482,8 @@
     if (!is_safe_seed) {
       // Store the current time as the last fetch time for Chrome's first run.
       GetSeedStore()->RecordLastFetchTime(base::Time::Now());
+      // Record freshness of "0", since we expect a first run seed to be fresh.
+      RecordSeedFreshness(base::TimeDelta());
     }
     return false;
   }
diff --git a/components/variations/service/variations_field_trial_creator_unittest.cc b/components/variations/service/variations_field_trial_creator_unittest.cc
index 0b7288c2..b0b27f1 100644
--- a/components/variations/service/variations_field_trial_creator_unittest.cc
+++ b/components/variations/service/variations_field_trial_creator_unittest.cc
@@ -504,12 +504,12 @@
   EXPECT_EQ(base::FieldTrialList::FindFullName(kTestSeedStudyName),
             kTestSeedExperimentName);
 
-  // Verify metrics. The seed freshness metric should not be recorded on first
-  // run.
+  // Verify metrics. The seed freshness metric should be recorded with a value
+  // of 0 on first run.
   histogram_tester.ExpectUniqueSample("Variations.CreateTrials.SeedExpiry",
                                       VariationsSeedExpiry::kFetchTimeMissing,
                                       1);
-  histogram_tester.ExpectTotalCount("Variations.SeedFreshness", 0);
+  histogram_tester.ExpectUniqueSample("Variations.SeedFreshness", 0, 1);
   histogram_tester.ExpectUniqueSample("Variations.SeedUsage",
                                       SeedUsage::kRegularSeedUsed, 1);
 }
@@ -1019,9 +1019,15 @@
         ChannelTestParams{.test_name = "Beta",
                           .channel = version_info::Channel::BETA,
                           .should_experiment_be_active = true},
+#if defined(OS_IOS)
+        ChannelTestParams{.test_name = "Stable",
+                          .channel = version_info::Channel::STABLE,
+                          .should_experiment_be_active = true}),
+#else
         ChannelTestParams{.test_name = "Stable",
                           .channel = version_info::Channel::STABLE,
                           .should_experiment_be_active = false}),
+#endif  // defined(OS_IOS)
     [](const ::testing::TestParamInfo<ChannelTestParams>& params) {
       return params.param.test_name;
     });
diff --git a/components/viz/service/display/display_resource_provider.h b/components/viz/service/display/display_resource_provider.h
index f69e597b..3fe35e2c 100644
--- a/components/viz/service/display/display_resource_provider.h
+++ b/components/viz/service/display/display_resource_provider.h
@@ -14,7 +14,6 @@
 #include <vector>
 
 #include "base/containers/flat_map.h"
-#include "base/containers/small_map.h"
 #include "base/memory/raw_ptr.h"
 #include "base/threading/thread_checker.h"
 #include "base/trace_event/memory_dump_provider.h"
diff --git a/components/viz/service/frame_sinks/external_begin_frame_source_android.cc b/components/viz/service/frame_sinks/external_begin_frame_source_android.cc
index e189e0fd..3d2fff7 100644
--- a/components/viz/service/frame_sinks/external_begin_frame_source_android.cc
+++ b/components/viz/service/frame_sinks/external_begin_frame_source_android.cc
@@ -204,9 +204,17 @@
 
 ExternalBeginFrameSourceAndroid::ExternalBeginFrameSourceAndroid(
     uint32_t restart_id,
-    float refresh_rate)
+    float refresh_rate,
+    bool requires_align_with_java)
     : ExternalBeginFrameSource(this, restart_id) {
-  achoreographer_ = AChoreographerImpl::Create(this);
+  // Android WebView requires begin frame to be inside the "animate" stage of
+  // input-animate-draw stages of the java Choreographer, which requires using
+  // java Choreographer.
+  if (requires_align_with_java) {
+    achoreographer_ = nullptr;
+  } else {
+    achoreographer_ = AChoreographerImpl::Create(this);
+  }
   if (!achoreographer_) {
     j_object_ = Java_ExternalBeginFrameSourceAndroid_Constructor(
         base::android::AttachCurrentThread(), reinterpret_cast<jlong>(this),
diff --git a/components/viz/service/frame_sinks/external_begin_frame_source_android.h b/components/viz/service/frame_sinks/external_begin_frame_source_android.h
index b7ed105..3ceaafa 100644
--- a/components/viz/service/frame_sinks/external_begin_frame_source_android.h
+++ b/components/viz/service/frame_sinks/external_begin_frame_source_android.h
@@ -21,7 +21,9 @@
     : public ExternalBeginFrameSource,
       public ExternalBeginFrameSourceClient {
  public:
-  ExternalBeginFrameSourceAndroid(uint32_t restart_id, float refresh_rate);
+  ExternalBeginFrameSourceAndroid(uint32_t restart_id,
+                                  float refresh_rate,
+                                  bool requires_align_with_java);
 
   ExternalBeginFrameSourceAndroid(const ExternalBeginFrameSourceAndroid&) =
       delete;
diff --git a/components/viz/service/frame_sinks/external_begin_frame_source_android_unittest.cc b/components/viz/service/frame_sinks/external_begin_frame_source_android_unittest.cc
index 5daad10..9057c80 100644
--- a/components/viz/service/frame_sinks/external_begin_frame_source_android_unittest.cc
+++ b/components/viz/service/frame_sinks/external_begin_frame_source_android_unittest.cc
@@ -43,7 +43,8 @@
  private:
   void InitOnThread() {
     begin_frame_source_ = std::make_unique<ExternalBeginFrameSourceAndroid>(
-        BeginFrameSource::kNotRestartableId, 60.f);
+        BeginFrameSource::kNotRestartableId, 60.f,
+        /*requires_align_with_java=*/false);
   }
 
   void AddObserverOnThread(uint32_t frame_count) {
diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
index 39fb437..a9be8eaf 100644
--- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
@@ -91,8 +91,9 @@
 #if defined(OS_ANDROID)
     hw_support_for_multiple_refresh_rates = true;
     external_begin_frame_source =
-        std::make_unique<ExternalBeginFrameSourceAndroid>(restart_id,
-                                                          params->refresh_rate);
+        std::make_unique<ExternalBeginFrameSourceAndroid>(
+            restart_id, params->refresh_rate,
+            /*requires_align_with_java=*/false);
 #else
     if (params->disable_frame_rate_limit) {
       synthetic_begin_frame_source =
diff --git a/components/webcrypto/algorithms/aes_cbc_unittest.cc b/components/webcrypto/algorithms/aes_cbc_unittest.cc
index 980f661..a885f95 100644
--- a/components/webcrypto/algorithms/aes_cbc_unittest.cc
+++ b/components/webcrypto/algorithms/aes_cbc_unittest.cc
@@ -90,8 +90,8 @@
 // Tests importing of keys (in a variety of formats), errors during import,
 // encryption, and decryption, using known answers.
 TEST_F(WebCryptoAesCbcTest, KnownAnswerEncryptDecrypt) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("aes_cbc.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("aes_cbc.json", &tests));
 
   for (size_t test_index = 0; test_index < tests.GetList().size();
        ++test_index) {
diff --git a/components/webcrypto/algorithms/aes_ctr_unittest.cc b/components/webcrypto/algorithms/aes_ctr_unittest.cc
index 54e5eecf..1f1595c 100644
--- a/components/webcrypto/algorithms/aes_ctr_unittest.cc
+++ b/components/webcrypto/algorithms/aes_ctr_unittest.cc
@@ -30,8 +30,8 @@
 class WebCryptoAesCtrTest : public WebCryptoTestBase {};
 
 TEST_F(WebCryptoAesCtrTest, EncryptDecryptKnownAnswer) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("aes_ctr.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("aes_ctr.json", &tests));
 
   for (size_t test_index = 0; test_index < tests.GetList().size();
        ++test_index) {
diff --git a/components/webcrypto/algorithms/aes_gcm_unittest.cc b/components/webcrypto/algorithms/aes_gcm_unittest.cc
index b98408e6..5b0a7f08 100644
--- a/components/webcrypto/algorithms/aes_gcm_unittest.cc
+++ b/components/webcrypto/algorithms/aes_gcm_unittest.cc
@@ -133,8 +133,8 @@
 //   * Test decryption with empty input
 //   * Test decryption with tag length of 0.
 TEST_F(WebCryptoAesGcmTest, SampleSets) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("aes_gcm.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("aes_gcm.json", &tests));
 
   // Note that WebCrypto appends the authentication tag to the ciphertext.
   for (size_t test_index = 0; test_index < tests.GetList().size();
diff --git a/components/webcrypto/algorithms/aes_kw_unittest.cc b/components/webcrypto/algorithms/aes_kw_unittest.cc
index 4c547af..27c4858 100644
--- a/components/webcrypto/algorithms/aes_kw_unittest.cc
+++ b/components/webcrypto/algorithms/aes_kw_unittest.cc
@@ -162,8 +162,8 @@
 
 TEST_F(WebCryptoAesKwTest, UnwrapFailures) {
   // This test exercises the code path common to all unwrap operations.
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("aes_kw.json", &tests));
   const base::Value& test_value = tests.GetList()[0];
   ASSERT_TRUE(test_value.is_dict());
   const base::DictionaryValue* test =
@@ -188,8 +188,8 @@
 }
 
 TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyWrapUnwrapKnownAnswer) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("aes_kw.json", &tests));
 
   for (size_t test_index = 0; test_index < tests.GetList().size();
        ++test_index) {
@@ -250,8 +250,8 @@
 // Unwrap a HMAC key using AES-KW, and then try doing a sign/verify with the
 // unwrapped key
 TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyUnwrapSignVerifyHmac) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("aes_kw.json", &tests));
 
   const base::Value& test_value = tests.GetList()[0];
   ASSERT_TRUE(test_value.is_dict());
@@ -303,8 +303,8 @@
 }
 
 TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyWrapUnwrapErrors) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("aes_kw.json", &tests));
   // Use 256 bits of data with a 256-bit KEK
   const base::Value& test_value = tests.GetList()[3];
   ASSERT_TRUE(test_value.is_dict());
@@ -346,8 +346,8 @@
 }
 
 TEST_F(WebCryptoAesKwTest, AesKwRawSymkeyUnwrapCorruptData) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("aes_kw.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("aes_kw.json", &tests));
   // Use 256 bits of data with a 256-bit KEK
   const base::Value& test_value = tests.GetList()[3];
   ASSERT_TRUE(test_value.is_dict());
diff --git a/components/webcrypto/algorithms/ecdh_unittest.cc b/components/webcrypto/algorithms/ecdh_unittest.cc
index cd8ceaae..6a79f8c 100644
--- a/components/webcrypto/algorithms/ecdh_unittest.cc
+++ b/components/webcrypto/algorithms/ecdh_unittest.cc
@@ -75,8 +75,8 @@
 class WebCryptoEcdhTest : public WebCryptoTestBase {};
 
 TEST_F(WebCryptoEcdhTest, DeriveBitsKnownAnswer) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("ecdh.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("ecdh.json", &tests));
 
   for (size_t test_index = 0; test_index < tests.GetList().size();
        ++test_index) {
@@ -120,8 +120,8 @@
 // 528 bits.
 ::testing::AssertionResult LoadTestKeys(blink::WebCryptoKey* public_key,
                                         blink::WebCryptoKey* private_key) {
-  base::ListValue tests;
-  if (!ReadJsonTestFileToList("ecdh.json", &tests))
+  base::Value tests;
+  if (!ReadJsonTestFileAsList("ecdh.json", &tests))
     return ::testing::AssertionFailure() << "Failed loading ecdh.json";
 
   const base::DictionaryValue* test = nullptr;
@@ -308,8 +308,8 @@
 TEST_F(WebCryptoEcdhTest, ImportKeyEmptyUsage) {
   blink::WebCryptoKey key;
 
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("ecdh.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("ecdh.json", &tests));
 
   const base::Value& test_value = tests.GetList()[0];
   ASSERT_TRUE(test_value.is_dict());
diff --git a/components/webcrypto/algorithms/ecdsa_unittest.cc b/components/webcrypto/algorithms/ecdsa_unittest.cc
index b829f09..9eeafc8 100644
--- a/components/webcrypto/algorithms/ecdsa_unittest.cc
+++ b/components/webcrypto/algorithms/ecdsa_unittest.cc
@@ -96,8 +96,8 @@
   // Import a public and private keypair from "ec_private_keys.json". It doesn't
   // really matter which one is used since they are all valid. In this case
   // using the first one.
-  base::ListValue private_keys;
-  ASSERT_TRUE(ReadJsonTestFileToList("ec_private_keys.json", &private_keys));
+  base::Value private_keys;
+  ASSERT_TRUE(ReadJsonTestFileAsList("ec_private_keys.json", &private_keys));
   const base::Value& key_value = private_keys.GetList()[0];
   ASSERT_TRUE(key_value.is_dict());
   const base::DictionaryValue* key_dict =
@@ -153,8 +153,8 @@
 // Tests verify() for ECDSA using an assortment of keys, curves and hashes.
 // These tests also include expected failures for bad signatures and keys.
 TEST_F(WebCryptoEcdsaTest, VerifyKnownAnswer) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("ecdsa.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("ecdsa.json", &tests));
 
   for (size_t test_index = 0; test_index < tests.GetList().size();
        ++test_index) {
@@ -237,8 +237,8 @@
 
 // Tests importing bad public/private keys in a variety of formats.
 TEST_F(WebCryptoEcdsaTest, ImportBadKeys) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("bad_ec_keys.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("bad_ec_keys.json", &tests));
 
   for (size_t test_index = 0; test_index < tests.GetList().size();
        ++test_index) {
@@ -270,8 +270,8 @@
 // The test imports a key first using JWK, and then exporting it to JWK and
 // PKCS8. It does the same thing using PKCS8 as the original source of truth.
 TEST_F(WebCryptoEcdsaTest, ImportExportPrivateKey) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("ec_private_keys.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("ec_private_keys.json", &tests));
 
   for (size_t test_index = 0; test_index < tests.GetList().size();
        ++test_index) {
diff --git a/components/webcrypto/algorithms/hmac_unittest.cc b/components/webcrypto/algorithms/hmac_unittest.cc
index 83c33984..42e2605 100644
--- a/components/webcrypto/algorithms/hmac_unittest.cc
+++ b/components/webcrypto/algorithms/hmac_unittest.cc
@@ -50,8 +50,8 @@
 class WebCryptoHmacTest : public WebCryptoTestBase {};
 
 TEST_F(WebCryptoHmacTest, HMACSampleSets) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("hmac.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("hmac.json", &tests));
   for (size_t test_index = 0; test_index < tests.GetList().size();
        ++test_index) {
     SCOPED_TRACE(test_index);
diff --git a/components/webcrypto/algorithms/rsa_oaep_unittest.cc b/components/webcrypto/algorithms/rsa_oaep_unittest.cc
index 2de31a1b..f90bba3 100644
--- a/components/webcrypto/algorithms/rsa_oaep_unittest.cc
+++ b/components/webcrypto/algorithms/rsa_oaep_unittest.cc
@@ -155,8 +155,8 @@
 }
 
 TEST_F(WebCryptoRsaOaepTest, EncryptDecryptKnownAnswerTest) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("rsa_oaep.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("rsa_oaep.json", &tests));
 
   for (size_t test_index = 0; test_index < tests.GetList().size();
        ++test_index) {
diff --git a/components/webcrypto/algorithms/rsa_pss_unittest.cc b/components/webcrypto/algorithms/rsa_pss_unittest.cc
index e5a8daa..0160c776 100644
--- a/components/webcrypto/algorithms/rsa_pss_unittest.cc
+++ b/components/webcrypto/algorithms/rsa_pss_unittest.cc
@@ -166,14 +166,15 @@
 //   * Verify over corrupted message should fail
 //   * Verification with corrupted signature should fail
 TEST_F(WebCryptoRsaPssTest, VerifyKnownAnswer) {
-  base::DictionaryValue test_data;
-  ASSERT_TRUE(ReadJsonTestFileToDictionary("rsa_pss.json", &test_data));
+  base::Value test_data;
+  ASSERT_TRUE(ReadJsonTestFile("rsa_pss.json", &test_data));
+  ASSERT_TRUE(test_data.is_dict());
 
-  const base::DictionaryValue* keys_dict = nullptr;
-  ASSERT_TRUE(test_data.GetDictionary("keys", &keys_dict));
+  const base::Value* keys_dict = test_data.FindDictKey("keys");
+  ASSERT_TRUE(keys_dict);
 
-  const base::ListValue* tests = nullptr;
-  ASSERT_TRUE(test_data.GetList("tests", &tests));
+  const base::Value* tests = test_data.FindListKey("tests");
+  ASSERT_TRUE(tests);
 
   for (size_t test_index = 0; test_index < tests->GetList().size();
        ++test_index) {
diff --git a/components/webcrypto/algorithms/rsa_ssa_unittest.cc b/components/webcrypto/algorithms/rsa_ssa_unittest.cc
index 5ebf78a6f..29fcb1e 100644
--- a/components/webcrypto/algorithms/rsa_ssa_unittest.cc
+++ b/components/webcrypto/algorithms/rsa_ssa_unittest.cc
@@ -202,8 +202,8 @@
 // be imported correctly, however every key after that would actually import
 // the first key.
 TEST_F(WebCryptoRsaSsaTest, ImportMultipleRSAPrivateKeysJwk) {
-  base::ListValue key_list;
-  ASSERT_TRUE(ReadJsonTestFileToList("rsa_private_keys.json", &key_list));
+  base::Value key_list;
+  ASSERT_TRUE(ReadJsonTestFileAsList("rsa_private_keys.json", &key_list));
 
   // For this test to be meaningful the keys MUST be kept alive before importing
   // new keys.
@@ -264,8 +264,8 @@
 // that the second import retrieves the first key. See http://crbug.com/378315
 // for how that could happen.
 TEST_F(WebCryptoRsaSsaTest, ImportJwkExistingModulusAndInvalid) {
-  base::ListValue key_list;
-  ASSERT_TRUE(ReadJsonTestFileToList("rsa_private_keys.json", &key_list));
+  base::Value key_list;
+  ASSERT_TRUE(ReadJsonTestFileAsList("rsa_private_keys.json", &key_list));
 
   // Import a 1024-bit private key.
   const base::Value& key1_props_value = key_list.GetList()[1];
@@ -639,8 +639,8 @@
 }
 
 TEST_F(WebCryptoRsaSsaTest, SignVerifyKnownAnswer) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("pkcs1v15_sign.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("pkcs1v15_sign.json", &tests));
 
   // Import the key pair.
   blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm(
@@ -997,8 +997,8 @@
 
 // Imports invalid JWK/SPKI/PKCS8 data and verifies that it fails as expected.
 TEST_F(WebCryptoRsaSsaTest, ImportInvalidKeyData) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("bad_rsa_keys.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("bad_rsa_keys.json", &tests));
 
   for (size_t test_index = 0; test_index < tests.GetList().size();
        ++test_index) {
diff --git a/components/webcrypto/algorithms/sha_unittest.cc b/components/webcrypto/algorithms/sha_unittest.cc
index 613fd61..48dd015 100644
--- a/components/webcrypto/algorithms/sha_unittest.cc
+++ b/components/webcrypto/algorithms/sha_unittest.cc
@@ -22,8 +22,8 @@
 class WebCryptoShaTest : public WebCryptoTestBase {};
 
 TEST_F(WebCryptoShaTest, DigestSampleSets) {
-  base::ListValue tests;
-  ASSERT_TRUE(ReadJsonTestFileToList("sha.json", &tests));
+  base::Value tests;
+  ASSERT_TRUE(ReadJsonTestFileAsList("sha.json", &tests));
 
   for (size_t test_index = 0; test_index < tests.GetList().size();
        ++test_index) {
diff --git a/components/webcrypto/algorithms/test_helpers.cc b/components/webcrypto/algorithms/test_helpers.cc
index 6f6be59..87e2f54 100644
--- a/components/webcrypto/algorithms/test_helpers.cc
+++ b/components/webcrypto/algorithms/test_helpers.cc
@@ -172,50 +172,35 @@
   return ::testing::AssertionSuccess();
 }
 
-::testing::AssertionResult ReadJsonTestFileToList(const char* test_file_name,
-                                                  base::ListValue* list) {
+::testing::AssertionResult ReadJsonTestFileAsList(const char* test_file_name,
+                                                  base::Value* value) {
   // Read the JSON.
   base::Value json;
   ::testing::AssertionResult result = ReadJsonTestFile(test_file_name, &json);
   if (!result)
     return result;
 
-  // Cast to an ListValue.
-  base::ListValue* json_as_list = nullptr;
-  if (!json.GetAsList(&json_as_list))
+  if (!json.is_list())
     return ::testing::AssertionFailure() << "The JSON was not a list";
 
-  *list = std::move(*json_as_list);
+  *value = std::move(json);
   return ::testing::AssertionSuccess();
 }
 
-::testing::AssertionResult ReadJsonTestFileToDictionary(
-    const char* test_file_name,
-    base::DictionaryValue* dict) {
-  // Read the JSON.
-  base::Value json;
-  ::testing::AssertionResult result = ReadJsonTestFile(test_file_name, &json);
-  if (!result)
-    return result;
-
-  // Cast to an DictionaryValue.
-  base::DictionaryValue* json_as_dict = nullptr;
-  if (!json.GetAsDictionary(&json_as_dict))
-    return ::testing::AssertionFailure() << "The JSON was not a dictionary";
-
-  *dict = std::move(*json_as_dict);
-  return ::testing::AssertionSuccess();
-}
-
-std::vector<uint8_t> GetBytesFromHexString(const base::DictionaryValue* dict,
+std::vector<uint8_t> GetBytesFromHexString(const base::Value* dict,
                                            const std::string& property_name) {
-  std::string hex_string;
-  if (!dict->GetString(property_name, &hex_string)) {
+  if (!dict->is_dict()) {
+    ADD_FAILURE() << "Value is not a dictionary";
+    return std::vector<uint8_t>();
+  }
+
+  const std::string* hex_string = dict->FindStringPath(property_name);
+  if (!hex_string) {
     ADD_FAILURE() << "Couldn't get string property: " << property_name;
     return std::vector<uint8_t>();
   }
 
-  return HexStringToBytes(hex_string);
+  return HexStringToBytes(*hex_string);
 }
 
 blink::WebCryptoAlgorithm GetDigestAlgorithm(const base::DictionaryValue* dict,
diff --git a/components/webcrypto/algorithms/test_helpers.h b/components/webcrypto/algorithms/test_helpers.h
index 40296348..ea06d625 100644
--- a/components/webcrypto/algorithms/test_helpers.h
+++ b/components/webcrypto/algorithms/test_helpers.h
@@ -25,7 +25,6 @@
 
 namespace base {
 class DictionaryValue;
-class ListValue;
 class Value;
 }
 
@@ -86,20 +85,16 @@
 // The file must be JSON, however it can also include C++ style comments.
 ::testing::AssertionResult ReadJsonTestFile(const char* test_file_name,
                                             base::Value* value);
-// Same as ReadJsonTestFile(), but returns the value as a List.
-::testing::AssertionResult ReadJsonTestFileToList(const char* test_file_name,
-                                                  base::ListValue* list);
-// Same as ReadJsonTestFile(), but returns the value as a Dictionary.
-::testing::AssertionResult ReadJsonTestFileToDictionary(
-    const char* test_file_name,
-    base::DictionaryValue* dict);
+// Same as ReadJsonTestFile(), but asserts the value is a list.
+::testing::AssertionResult ReadJsonTestFileAsList(const char* test_file_name,
+                                                  base::Value* list);
 
-// Reads a string property from the dictionary with path |property_name|
+// Reads a string property from the dictionary |dict| with path |property_name|
 // (which can include periods for nested dictionaries). Interprets the
 // string as a hex encoded string and converts it to a bytes list.
 //
-// Returns empty vector on failure.
-std::vector<uint8_t> GetBytesFromHexString(const base::DictionaryValue* dict,
+// Returns empty vector on failure or if |dict| is not a dictionary.
+std::vector<uint8_t> GetBytesFromHexString(const base::Value* dict,
                                            const std::string& property_name);
 
 // Reads a string property with path "property_name" and converts it to a
diff --git a/content/app/content_main.cc b/content/app/content_main.cc
index 5ad6c02..33e0970 100644
--- a/content/app/content_main.cc
+++ b/content/app/content_main.cc
@@ -176,6 +176,7 @@
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
         false
 #else
+        !command_line.HasSwitch(switches::kDisableMojoBroker) &&
         !mojo::PlatformChannel::CommandLineHasPassedEndpoint(command_line)
 #endif
         ;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 80b2e76e..fcb0b1d32 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -396,6 +396,8 @@
     "attribution_reporting/attribution_page_metrics.h",
     "attribution_reporting/attribution_policy.cc",
     "attribution_reporting/attribution_policy.h",
+    "attribution_reporting/attribution_report.cc",
+    "attribution_reporting/attribution_report.h",
     "attribution_reporting/attribution_storage.cc",
     "attribution_reporting/attribution_storage.h",
     "attribution_reporting/attribution_storage_delegate_impl.cc",
@@ -406,8 +408,6 @@
     "attribution_reporting/attribution_storage_sql_migrations.h",
     "attribution_reporting/attribution_utils.cc",
     "attribution_reporting/attribution_utils.h",
-    "attribution_reporting/event_attribution_report.cc",
-    "attribution_reporting/event_attribution_report.h",
     "attribution_reporting/rate_limit_table.cc",
     "attribution_reporting/rate_limit_table.h",
     "attribution_reporting/send_result.h",
@@ -2225,8 +2225,6 @@
     sources += [
       "accessibility/accessibility_event_recorder_uia_win.cc",
       "accessibility/accessibility_event_recorder_uia_win.h",
-      "accessibility/accessibility_event_recorder_win.cc",
-      "accessibility/accessibility_event_recorder_win.h",
       "accessibility/accessibility_tree_formatter_uia_win.cc",
       "accessibility/accessibility_tree_formatter_uia_win.h",
       "accessibility/accessibility_tree_formatter_win.cc",
diff --git a/content/browser/accessibility/accessibility_action_browsertest.cc b/content/browser/accessibility/accessibility_action_browsertest.cc
index fbff14b..31ede63 100644
--- a/content/browser/accessibility/accessibility_action_browsertest.cc
+++ b/content/browser/accessibility/accessibility_action_browsertest.cc
@@ -474,7 +474,7 @@
       target->CreateTextPositionAt(0);
   BrowserAccessibility::AXPosition end_of_line_1 =
       start_position->CreateNextLineEndPosition(
-          ui::AXBoundaryBehavior::kCrossBoundary);
+          ui::AXBoundaryBehavior::CrossBoundary);
   EXPECT_EQ(5, end_of_line_1->text_offset());
 #endif
 }
@@ -508,7 +508,7 @@
       target->CreateTextPositionAt(0);
   BrowserAccessibility::AXPosition end_of_line_1 =
       start_position->CreateNextLineEndPosition(
-          ui::AXBoundaryBehavior::kCrossBoundary);
+          ui::AXBoundaryBehavior::CrossBoundary);
   EXPECT_EQ(5, end_of_line_1->text_offset());
 #endif
 }
diff --git a/content/browser/accessibility/accessibility_auralinux_browsertest.cc b/content/browser/accessibility/accessibility_auralinux_browsertest.cc
index 38ccd2b0..0108a42 100644
--- a/content/browser/accessibility/accessibility_auralinux_browsertest.cc
+++ b/content/browser/accessibility/accessibility_auralinux_browsertest.cc
@@ -460,7 +460,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(AccessibilityAuraLinuxBrowserTest,
-                       DISABLED_TestParagraphTextAtOffsetWithBoundarySentence) {
+                       TestParagraphTextAtOffsetWithBoundarySentence) {
   LoadInitialAccessibilityTreeFromHtml(std::string(
       R"HTML(<!DOCTYPE html>
           <html>
diff --git a/content/browser/accessibility/accessibility_event_recorder_win.cc b/content/browser/accessibility/accessibility_event_recorder_win.cc
deleted file mode 100644
index 256d5c42..0000000
--- a/content/browser/accessibility/accessibility_event_recorder_win.cc
+++ /dev/null
@@ -1,344 +0,0 @@
-// Copyright 2014 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 "content/browser/accessibility/accessibility_event_recorder_win.h"
-
-#include <stdint.h>
-#include <wrl/client.h>
-
-#include <string>
-
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/win/scoped_bstr.h"
-#include "base/win/scoped_variant.h"
-#include "content/browser/accessibility/accessibility_event_recorder_uia_win.h"
-#include "content/browser/accessibility/browser_accessibility_manager.h"
-#include "content/browser/accessibility/browser_accessibility_win.h"
-#include "third_party/iaccessible2/ia2_api_all.h"
-#include "ui/accessibility/platform/inspect/ax_inspect_utils_win.h"
-#include "ui/base/win/atl_module.h"
-#include "ui/gfx/win/hwnd_util.h"
-
-namespace content {
-
-using ui::AccessibilityEventToString;
-using ui::GetHWNDBySelector;
-using ui::IAccessible2StateToString;
-using ui::IAccessibleRoleToString;
-using ui::IAccessibleStateToString;
-
-namespace {
-
-std::string RoleVariantToString(const base::win::ScopedVariant& role) {
-  if (role.type() == VT_I4) {
-    return base::WideToUTF8(IAccessibleRoleToString(V_I4(role.ptr())));
-  } else if (role.type() == VT_BSTR) {
-    return base::WideToUTF8(
-        std::wstring(V_BSTR(role.ptr()), SysStringLen(V_BSTR(role.ptr()))));
-  }
-  return std::string();
-}
-
-HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) {
-  Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
-  HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider));
-  return SUCCEEDED(hr)
-             ? service_provider->QueryService(IID_IAccessible2, accessible2)
-             : hr;
-}
-
-HRESULT QueryIAccessibleText(IAccessible* accessible,
-                             IAccessibleText** accessible_text) {
-  Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
-  HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider));
-  return SUCCEEDED(hr) ? service_provider->QueryService(IID_IAccessibleText,
-                                                        accessible_text)
-                       : hr;
-}
-
-std::string BstrToPrettyUTF8(BSTR bstr) {
-  std::wstring wstr(bstr, SysStringLen(bstr));
-
-  // IAccessibleText returns the text you get by appending all static text
-  // children, with an "embedded object character" for each non-text child.
-  // Pretty-print the embedded object character as <obj> so that test output
-  // is human-readable.
-  std::wstring embedded_character = base::UTF16ToWide(
-      std::u16string(1, BrowserAccessibilityComWin::kEmbeddedCharacter));
-  base::ReplaceChars(wstr, embedded_character, L"<obj>", &wstr);
-
-  return base::WideToUTF8(wstr);
-}
-
-std::string AccessibilityEventToStringUTF8(int32_t event_id) {
-  return base::WideToUTF8(AccessibilityEventToString(event_id));
-}
-
-}  // namespace
-
-// static
-AccessibilityEventRecorderWin* AccessibilityEventRecorderWin::instance_ =
-    nullptr;
-
-// static
-CALLBACK void AccessibilityEventRecorderWin::WinEventHookThunk(
-    HWINEVENTHOOK handle,
-    DWORD event,
-    HWND hwnd,
-    LONG obj_id,
-    LONG child_id,
-    DWORD event_thread,
-    DWORD event_time) {
-  if (instance_) {
-    instance_->OnWinEventHook(handle, event, hwnd, obj_id, child_id,
-                              event_thread, event_time);
-  }
-}
-
-AccessibilityEventRecorderWin::AccessibilityEventRecorderWin(
-    BrowserAccessibilityManager* manager,
-    base::ProcessId pid,
-    const ui::AXTreeSelector& selector)
-    : manager_(manager) {
-  CHECK(!instance_) << "There can be only one instance of"
-                    << " AccessibilityEventRecorder at a time.";
-
-  // Get pid by a selector if the selectors specifies a valid process.
-  HWND hwnd_by_selector = GetHWNDBySelector(selector);
-  if (hwnd_by_selector != NULL) {
-    DWORD pid_by_selector = 0;
-    GetWindowThreadProcessId(hwnd_by_selector, &pid_by_selector);
-    if (pid_by_selector != 0) {
-      pid = static_cast<DWORD>(pid_by_selector);
-    }
-  }
-
-  // For now, just use out of context events when running as a utility to watch
-  // events (no BrowserAccessibilityManager), because otherwise Chrome events
-  // are not getting reported. Being in context is better so that for
-  // TEXT_REMOVED and TEXT_INSERTED events, we can query the text that was
-  // inserted or removed and include that in the log.
-  int context = manager ? WINEVENT_INCONTEXT : WINEVENT_OUTOFCONTEXT;
-  win_event_hook_handle_ =
-      SetWinEventHook(EVENT_MIN, EVENT_MAX, GetModuleHandle(NULL),
-                      &AccessibilityEventRecorderWin::WinEventHookThunk, pid,
-                      0,  // Hook all threads
-                      context);
-  CHECK(win_event_hook_handle_);
-  instance_ = this;
-}
-
-AccessibilityEventRecorderWin::~AccessibilityEventRecorderWin() {
-  UnhookWinEvent(win_event_hook_handle_);
-  instance_ = nullptr;
-}
-
-void AccessibilityEventRecorderWin::OnWinEventHook(HWINEVENTHOOK handle,
-                                                   DWORD event,
-                                                   HWND hwnd,
-                                                   LONG obj_id,
-                                                   LONG child_id,
-                                                   DWORD event_thread,
-                                                   DWORD event_time) {
-  Microsoft::WRL::ComPtr<IAccessible> browser_accessible;
-  HRESULT hr = ::AccessibleObjectFromWindow(hwnd, obj_id,
-                                            IID_PPV_ARGS(&browser_accessible));
-  if (FAILED(hr)) {
-    // Note: our event hook will pick up some superfluous events we
-    // don't care about, so it's safe to just ignore these failures.
-    // Same below for other HRESULT checks.
-    VLOG(1) << "Ignoring result " << hr << " from AccessibleObjectFromWindow";
-    return;
-  }
-
-  base::win::ScopedVariant childid_variant(child_id);
-  Microsoft::WRL::ComPtr<IDispatch> dispatch;
-  hr = browser_accessible->get_accChild(childid_variant, &dispatch);
-  if (hr != S_OK || !dispatch) {
-    VLOG(1) << "Ignoring result " << hr << " and result " << dispatch.Get()
-            << " from get_accChild";
-    return;
-  }
-
-  Microsoft::WRL::ComPtr<IAccessible> iaccessible;
-  hr = dispatch.As(&iaccessible);
-  if (FAILED(hr)) {
-    VLOG(1) << "Ignoring result " << hr << " from QueryInterface";
-    return;
-  }
-
-  std::string event_str = AccessibilityEventToStringUTF8(event);
-  if (event_str.empty()) {
-    VLOG(1) << "Ignoring event " << event;
-    return;
-  }
-
-  base::win::ScopedVariant childid_self(CHILDID_SELF);
-  base::win::ScopedVariant role;
-  iaccessible->get_accRole(childid_self, role.Receive());
-  base::win::ScopedVariant state;
-  iaccessible->get_accState(childid_self, state.Receive());
-  int ia_state = V_I4(state.ptr());
-  std::string hwnd_class_name = base::WideToUTF8(gfx::GetClassName(hwnd));
-
-  // Caret is special:
-  // Log all caret events  that occur, with their window class, so that we can
-  // test to make sure they are only occurring on the desired window class.
-  if (ROLE_SYSTEM_CARET == V_I4(role.ptr())) {
-    std::wstring state_str = IAccessibleStateToString(ia_state);
-    std::string log = base::StringPrintf(
-        "%s role=ROLE_SYSTEM_CARET %ls window_class=%s", event_str.c_str(),
-        state_str.c_str(), hwnd_class_name.c_str());
-    OnEvent(log);
-    return;
-  }
-
-  if (only_web_events_) {
-    if (hwnd_class_name != "Chrome_RenderWidgetHostHWND")
-      return;
-
-    Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
-    hr = iaccessible->QueryInterface(IID_PPV_ARGS(&service_provider));
-    if (FAILED(hr))
-      return;
-
-    Microsoft::WRL::ComPtr<IAccessible> content_document;
-    hr = service_provider->QueryService(GUID_IAccessibleContentDocument,
-                                        IID_PPV_ARGS(&content_document));
-    if (FAILED(hr))
-      return;
-  }
-
-  base::win::ScopedBstr name_bstr;
-  iaccessible->get_accName(childid_self, name_bstr.Receive());
-  base::win::ScopedBstr value_bstr;
-  iaccessible->get_accValue(childid_self, value_bstr.Receive());
-
-  // Avoid flakiness. Events fired on a WINDOW are out of the control
-  // of a test.
-  if (role.type() == VT_I4 && ROLE_SYSTEM_WINDOW == V_I4(role.ptr())) {
-    VLOG(1) << "Ignoring event " << event << " on ROLE_SYSTEM_WINDOW";
-    return;
-  }
-
-  // Avoid flakiness. The "offscreen" state depends on whether the browser
-  // window is frontmost or not, and "hottracked" depends on whether the
-  // mouse cursor happens to be over the element.
-  ia_state &= (~STATE_SYSTEM_OFFSCREEN & ~STATE_SYSTEM_HOTTRACKED);
-
-  // The "readonly" state is set on almost every node and doesn't typically
-  // change, so filter it out to keep the output less verbose.
-  ia_state &= ~STATE_SYSTEM_READONLY;
-
-  AccessibleStates ia2_state = 0;
-  Microsoft::WRL::ComPtr<IAccessible2> iaccessible2;
-  hr = QueryIAccessible2(iaccessible.Get(), &iaccessible2);
-  bool has_ia2 = SUCCEEDED(hr) && iaccessible2;
-
-  std::wstring html_tag;
-  std::wstring obj_class;
-  std::wstring html_id;
-
-  if (has_ia2) {
-    // IA2::get_states can kill the recorder.
-    iaccessible2->get_states(&ia2_state);
-    if (!instance_)
-      return;
-
-    base::win::ScopedBstr attributes_bstr;
-    if (S_OK == iaccessible2->get_attributes(attributes_bstr.Receive())) {
-      std::vector<std::wstring> ia2_attributes = base::SplitString(
-          std::wstring(attributes_bstr.Get(), attributes_bstr.Length()),
-          std::wstring(1, ';'), base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
-      for (std::wstring& attr : ia2_attributes) {
-        if (base::StartsWith(attr, L"class:"))
-          obj_class = attr.substr(6);  // HTML or view class
-        if (base::StartsWith(attr, L"id:")) {
-          html_id = std::wstring(L"#");
-          html_id += attr.substr(3);
-        }
-        if (base::StartsWith(attr, L"tag:")) {
-          html_tag = attr.substr(4);
-        }
-      }
-    }
-  }
-
-  std::string log = base::StringPrintf("%s on", event_str.c_str());
-  if (!html_tag.empty()) {
-    // HTML node with tag
-    log += base::StringPrintf(" <%s%s%s%s>", base::WideToUTF8(html_tag).c_str(),
-                              base::WideToUTF8(html_id).c_str(),
-                              obj_class.empty() ? "" : ".",
-                              base::WideToUTF8(obj_class).c_str());
-  } else if (!obj_class.empty()) {
-    // Non-HTML node with class
-    log += base::StringPrintf(" class=%s", base::WideToUTF8(obj_class).c_str());
-  }
-
-  log += base::StringPrintf(" role=%s", RoleVariantToString(role).c_str());
-  if (name_bstr.Length() > 0)
-    log += base::StringPrintf(" name=\"%s\"",
-                              BstrToPrettyUTF8(name_bstr.Get()).c_str());
-  if (value_bstr.Length() > 0) {
-    bool is_document =
-        role.type() == VT_I4 && ROLE_SYSTEM_DOCUMENT == V_I4(role.ptr());
-    // Don't show actual document value, which is a URL, in order to avoid
-    // machine-based differences in tests.
-    log += is_document
-               ? " value~=[doc-url]"
-               : base::StringPrintf(" value=\"%s\"",
-                                    BstrToPrettyUTF8(value_bstr.Get()).c_str());
-  }
-  log += " ";
-  log += base::WideToUTF8(IAccessibleStateToString(ia_state));
-  log += " ";
-  log += base::WideToUTF8(IAccessible2StateToString(ia2_state));
-
-  // Group position, e.g. L3, 5 of 7
-  LONG group_level, similar_items_in_group, position_in_group;
-  if (has_ia2 &&
-      iaccessible2->get_groupPosition(&group_level, &similar_items_in_group,
-                                      &position_in_group) == S_OK) {
-    if (group_level)
-      log += base::StringPrintf(" level=%ld", group_level);
-    if (position_in_group)
-      log += base::StringPrintf(" PosInSet=%ld", position_in_group);
-    if (similar_items_in_group)
-      log += base::StringPrintf(" SetSize=%ld", similar_items_in_group);
-  }
-
-  // For TEXT_REMOVED and TEXT_INSERTED events, query the text that was
-  // inserted or removed and include that in the log.
-  Microsoft::WRL::ComPtr<IAccessibleText> accessible_text;
-  hr = QueryIAccessibleText(iaccessible.Get(), &accessible_text);
-  if (SUCCEEDED(hr)) {
-    if (event == IA2_EVENT_TEXT_REMOVED) {
-      IA2TextSegment old_text;
-      if (SUCCEEDED(accessible_text->get_oldText(&old_text))) {
-        log += base::StringPrintf(" old_text={'%s' start=%ld end=%ld}",
-                                  BstrToPrettyUTF8(old_text.text).c_str(),
-                                  old_text.start, old_text.end);
-      }
-    }
-    if (event == IA2_EVENT_TEXT_INSERTED) {
-      IA2TextSegment new_text;
-      if (SUCCEEDED(accessible_text->get_newText(&new_text))) {
-        log += base::StringPrintf(" new_text={'%s' start=%ld end=%ld}",
-                                  BstrToPrettyUTF8(new_text.text).c_str(),
-                                  new_text.start, new_text.end);
-      }
-    }
-  }
-
-  log =
-      base::UTF16ToUTF8(base::CollapseWhitespace(base::UTF8ToUTF16(log), true));
-  OnEvent(log);
-}
-
-}  // namespace content
diff --git a/content/browser/accessibility/accessibility_event_recorder_win.h b/content/browser/accessibility/accessibility_event_recorder_win.h
deleted file mode 100644
index 82aca2c..0000000
--- a/content/browser/accessibility/accessibility_event_recorder_win.h
+++ /dev/null
@@ -1,62 +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 CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_WIN_H_
-#define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_WIN_H_
-
-#include <oleacc.h>
-
-#include "base/memory/raw_ptr.h"
-#include "base/process/process_handle.h"
-#include "content/common/content_export.h"
-#include "ui/accessibility/platform/inspect/ax_event_recorder.h"
-#include "ui/accessibility/platform/inspect/ax_inspect.h"
-
-namespace content {
-
-class BrowserAccessibilityManager;
-
-class CONTENT_EXPORT AccessibilityEventRecorderWin
-    : public ui::AXEventRecorder {
- public:
-  AccessibilityEventRecorderWin(BrowserAccessibilityManager* manager,
-                                base::ProcessId pid,
-                                const ui::AXTreeSelector& selector);
-
-  AccessibilityEventRecorderWin(const AccessibilityEventRecorderWin&) = delete;
-  AccessibilityEventRecorderWin& operator=(
-      const AccessibilityEventRecorderWin&) = delete;
-
-  ~AccessibilityEventRecorderWin() override;
-
-  // Callback registered by SetWinEventHook. Just calls OnWinEventHook.
-  static CALLBACK void WinEventHookThunk(HWINEVENTHOOK handle,
-                                         DWORD event,
-                                         HWND hwnd,
-                                         LONG obj_id,
-                                         LONG child_id,
-                                         DWORD event_thread,
-                                         DWORD event_time);
-
- private:
-  // Called by the thunk registered by SetWinEventHook. Retrieves accessibility
-  // info about the node the event was fired on and appends a string to
-  // the event log.
-  void OnWinEventHook(HWINEVENTHOOK handle,
-                      DWORD event,
-                      HWND hwnd,
-                      LONG obj_id,
-                      LONG child_id,
-                      DWORD event_thread,
-                      DWORD event_time);
-
-  HWINEVENTHOOK win_event_hook_handle_;
-  // TODO: should be either removed or converted to a weakptr.
-  const raw_ptr<BrowserAccessibilityManager> manager_;
-  static AccessibilityEventRecorderWin* instance_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_WIN_H_
diff --git a/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc b/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc
index 2f6a9e0..2de8d87 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_fuchsia.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include "content/browser/accessibility/accessibility_tree_formatter_fuchsia.h"
+#include "base/notreached.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "content/browser/accessibility/browser_accessibility_fuchsia.h"
@@ -10,73 +12,157 @@
 namespace content {
 namespace {
 
+using FuchsiaAction = fuchsia::accessibility::semantics::Action;
+using FuchsiaCheckedState = fuchsia::accessibility::semantics::CheckedState;
+using FuchsiaRole = fuchsia::accessibility::semantics::Role;
+using FuchsiaToggledState = fuchsia::accessibility::semantics::ToggledState;
+
 constexpr const char* const kBoolAttributes[] = {
-    "hidden",
-    "focusable",
-    "has_input_focus",
-    "is_keyboard_key",
+    "hidden", "focusable", "has_input_focus", "is_keyboard_key", "selected",
 };
 
 constexpr const char* const kStringAttributes[] = {
-    "label",
-    "location",
-    "transform",
+    "label",           "actions",       "secondary_label",
+    "value",           "checked_state", "toggled_state",
+    "viewport_offset", "location",      "transform",
 };
 
-std::string FuchsiaRoleToString(
-    const fuchsia::accessibility::semantics::Role role) {
+constexpr const char* const kIntAttributes[] = {
+    "number_of_rows",   "number_of_columns", "row_index",
+    "cell_row_index",   "cell_column_index", "cell_row_span",
+    "cell_column_span",
+};
+
+constexpr const char* const kDoubleAttributes[] = {
+    "min_value",
+    "max_value",
+    "step_delta",
+};
+
+std::string FuchsiaRoleToString(const FuchsiaRole role) {
   switch (role) {
-    case fuchsia::accessibility::semantics::Role::BUTTON:
+    case FuchsiaRole::BUTTON:
       return "BUTTON";
-    case fuchsia::accessibility::semantics::Role::CELL:
+    case FuchsiaRole::CELL:
       return "CELL";
-    case fuchsia::accessibility::semantics::Role::CHECK_BOX:
+    case FuchsiaRole::CHECK_BOX:
       return "CHECK_BOX";
-    case fuchsia::accessibility::semantics::Role::COLUMN_HEADER:
+    case FuchsiaRole::COLUMN_HEADER:
       return "COLUMN_HEADER";
-    case fuchsia::accessibility::semantics::Role::GRID:
+    case FuchsiaRole::GRID:
       return "GRID";
-    case fuchsia::accessibility::semantics::Role::HEADER:
+    case FuchsiaRole::HEADER:
       return "HEADER";
-    case fuchsia::accessibility::semantics::Role::IMAGE:
+    case FuchsiaRole::IMAGE:
       return "IMAGE";
-    case fuchsia::accessibility::semantics::Role::LINK:
+    case FuchsiaRole::LINK:
       return "LINK";
-    case fuchsia::accessibility::semantics::Role::LIST:
+    case FuchsiaRole::LIST:
       return "LIST";
-    case fuchsia::accessibility::semantics::Role::LIST_ELEMENT_MARKER:
+    case FuchsiaRole::LIST_ELEMENT_MARKER:
       return "LIST_ELEMENT_MARKER";
-    case fuchsia::accessibility::semantics::Role::PARAGRAPH:
+    case FuchsiaRole::PARAGRAPH:
       return "PARAGRAPH";
-    case fuchsia::accessibility::semantics::Role::RADIO_BUTTON:
+    case FuchsiaRole::RADIO_BUTTON:
       return "RADIO_BUTTON";
-    case fuchsia::accessibility::semantics::Role::ROW_GROUP:
+    case FuchsiaRole::ROW_GROUP:
       return "ROW_GROUP";
-    case fuchsia::accessibility::semantics::Role::ROW_HEADER:
+    case FuchsiaRole::ROW_HEADER:
       return "ROW_HEADER";
-    case fuchsia::accessibility::semantics::Role::SEARCH_BOX:
+    case FuchsiaRole::SEARCH_BOX:
       return "SEARCH_BOX";
-    case fuchsia::accessibility::semantics::Role::SLIDER:
+    case FuchsiaRole::SLIDER:
       return "SLIDER";
-    case fuchsia::accessibility::semantics::Role::STATIC_TEXT:
+    case FuchsiaRole::STATIC_TEXT:
       return "STATIC_TEXT";
-    case fuchsia::accessibility::semantics::Role::TABLE:
+    case FuchsiaRole::TABLE:
       return "TABLE";
-    case fuchsia::accessibility::semantics::Role::TABLE_ROW:
+    case FuchsiaRole::TABLE_ROW:
       return "TABLE_ROW";
-    case fuchsia::accessibility::semantics::Role::TEXT_FIELD:
+    case FuchsiaRole::TEXT_FIELD:
       return "TEXT_FIELD";
-    case fuchsia::accessibility::semantics::Role::TEXT_FIELD_WITH_COMBO_BOX:
+    case FuchsiaRole::TEXT_FIELD_WITH_COMBO_BOX:
       return "TEXT_FIELD_WITH_COMBO_BOX";
-    case fuchsia::accessibility::semantics::Role::TOGGLE_SWITCH:
+    case FuchsiaRole::TOGGLE_SWITCH:
       return "TOGGLE_SWITCH";
-    case fuchsia::accessibility::semantics::Role::UNKNOWN:
+    case FuchsiaRole::UNKNOWN:
       return "UNKNOWN";
     default:
+      NOTREACHED();
       return std::string();
   }
 }
 
+std::string FuchsiaActionToString(FuchsiaAction action) {
+  switch (action) {
+    case FuchsiaAction::DEFAULT:
+      return "DEFAULT";
+    case FuchsiaAction::DECREMENT:
+      return "DECREMENT";
+    case FuchsiaAction::INCREMENT:
+      return "INCREMENT";
+    case FuchsiaAction::SECONDARY:
+      return "SECONDARY";
+    case FuchsiaAction::SET_FOCUS:
+      return "SET_FOCUS";
+    case FuchsiaAction::SET_VALUE:
+      return "SET_VALUE";
+    case FuchsiaAction::SHOW_ON_SCREEN:
+      return "SHOW_ON_SCREEN";
+    default:
+      NOTREACHED();
+      return std::string();
+  }
+}
+
+std::string FuchsiaActionsToString(const std::vector<FuchsiaAction>& actions) {
+  std::vector<std::string> fuchsia_actions;
+  for (const auto& action : actions) {
+    fuchsia_actions.push_back(FuchsiaActionToString(action));
+  }
+
+  if (fuchsia_actions.empty())
+    return std::string();
+
+  return "{" + base::JoinString(fuchsia_actions, ", ") + "}";
+}
+
+std::string CheckedStateToString(const FuchsiaCheckedState checked_state) {
+  switch (checked_state) {
+    case FuchsiaCheckedState::NONE:
+      return "NONE";
+    case FuchsiaCheckedState::CHECKED:
+      return "CHECKED";
+    case FuchsiaCheckedState::UNCHECKED:
+      return "UNCHECKED";
+    case FuchsiaCheckedState::MIXED:
+      return "MIXED";
+    default:
+      NOTREACHED();
+      return std::string();
+  }
+}
+
+std::string ToggledStateToString(const FuchsiaToggledState toggled_state) {
+  switch (toggled_state) {
+    case FuchsiaToggledState::ON:
+      return "ON";
+    case FuchsiaToggledState::OFF:
+      return "OFF";
+    case FuchsiaToggledState::INDETERMINATE:
+      return "INDETERMINATE";
+    default:
+      NOTREACHED();
+      return std::string();
+  }
+}
+
+std::string ViewportOffsetToString(
+    const fuchsia::ui::gfx::vec2& viewport_offset) {
+  return base::StringPrintf("(%.1f, %.1f)", viewport_offset.x,
+                            viewport_offset.y);
+}
+
 std::string Vec3ToString(const fuchsia::ui::gfx::vec3& vec) {
   return base::StringPrintf("(%.1f, %.1f, %.1f)", vec.x, vec.y, vec.z);
 }
@@ -104,6 +190,15 @@
 AccessibilityTreeFormatterFuchsia::~AccessibilityTreeFormatterFuchsia() =
     default;
 
+void AccessibilityTreeFormatterFuchsia::AddDefaultFilters(
+    std::vector<AXPropertyFilter>* property_filters) {
+  // Exclude spatial semantics by default to avoid flakiness.
+  AddPropertyFilter(property_filters, "location", AXPropertyFilter::DENY);
+  AddPropertyFilter(property_filters, "transform", AXPropertyFilter::DENY);
+  AddPropertyFilter(property_filters, "viewport_offset",
+                    AXPropertyFilter::DENY);
+}
+
 base::Value AccessibilityTreeFormatterFuchsia::BuildTree(
     ui::AXPlatformNodeDelegate* root) const {
   CHECK(root);
@@ -128,13 +223,21 @@
 
   base::ListValue children;
 
-  for (size_t i = 0; i < node.PlatformChildCount(); ++i) {
-    BrowserAccessibility* child_node = node.PlatformGetChild(i);
+  fuchsia::accessibility::semantics::Node fuchsia_node =
+      static_cast<const BrowserAccessibilityFuchsia&>(node).ToFuchsiaNodeData();
+
+  for (uint32_t child_id : fuchsia_node.child_ids()) {
+    ui::AXPlatformNodeFuchsia* child_node =
+        static_cast<ui::AXPlatformNodeFuchsia*>(
+            ui::AXPlatformNodeBase::GetFromUniqueId(child_id));
     DCHECK(child_node);
 
+    BrowserAccessibilityFuchsia* child_browser_accessibility =
+        static_cast<BrowserAccessibilityFuchsia*>(child_node->GetDelegate());
+
     std::unique_ptr<base::DictionaryValue> child_dict(
         new base::DictionaryValue);
-    RecursiveBuildTree(*child_node, child_dict.get());
+    RecursiveBuildTree(*child_browser_accessibility, child_dict.get());
     children.Append(std::move(child_dict));
   }
   dict->SetKey(kChildrenDictAttr, std::move(children));
@@ -164,6 +267,8 @@
   // Add fuchsia node attributes.
   dict->SetString("role", FuchsiaRoleToString(fuchsia_node.role()));
 
+  dict->SetString("actions", FuchsiaActionsToString(fuchsia_node.actions()));
+
   if (fuchsia_node.has_attributes()) {
     const fuchsia::accessibility::semantics::Attributes& attributes =
         fuchsia_node.attributes();
@@ -171,6 +276,63 @@
     if (attributes.has_label() && !attributes.label().empty())
       dict->SetString("label", attributes.label());
 
+    if (attributes.has_secondary_label() &&
+        !attributes.secondary_label().empty()) {
+      dict->SetString("secondary_label", attributes.secondary_label());
+    }
+
+    if (attributes.has_range()) {
+      const auto& range_attributes = attributes.range();
+
+      if (range_attributes.has_min_value())
+        dict->SetDouble("min_value", range_attributes.min_value());
+
+      if (range_attributes.has_max_value())
+        dict->SetDouble("max_value", range_attributes.max_value());
+
+      if (range_attributes.has_step_delta())
+        dict->SetDouble("step_delta", range_attributes.step_delta());
+    }
+
+    if (attributes.has_table_attributes()) {
+      const auto& table_attributes = attributes.table_attributes();
+
+      if (table_attributes.has_number_of_rows())
+        dict->SetInteger("number_of_rows", table_attributes.number_of_rows());
+
+      if (table_attributes.has_number_of_columns()) {
+        dict->SetInteger("number_of_columns",
+                         table_attributes.number_of_columns());
+      }
+    }
+
+    if (attributes.has_table_row_attributes()) {
+      const auto& table_row_attributes = attributes.table_row_attributes();
+
+      if (table_row_attributes.has_row_index())
+        dict->SetInteger("row_index", table_row_attributes.row_index());
+    }
+
+    if (attributes.has_table_cell_attributes()) {
+      const auto& table_cell_attributes = attributes.table_cell_attributes();
+
+      if (table_cell_attributes.has_row_index())
+        dict->SetInteger("cell_row_index", table_cell_attributes.row_index());
+
+      if (table_cell_attributes.has_column_index()) {
+        dict->SetInteger("cell_column_index",
+                         table_cell_attributes.column_index());
+      }
+
+      if (table_cell_attributes.has_row_span())
+        dict->SetInteger("cell_row_span", table_cell_attributes.row_span());
+
+      if (table_cell_attributes.has_column_span()) {
+        dict->SetInteger("cell_column_span",
+                         table_cell_attributes.column_span());
+      }
+    }
+
     if (attributes.has_is_keyboard_key())
       dict->SetBoolean("is_keyboard_key", attributes.is_keyboard_key());
   }
@@ -179,9 +341,30 @@
     const fuchsia::accessibility::semantics::States& states =
         fuchsia_node.states();
 
+    if (states.has_selected())
+      dict->SetBoolean("selected", states.selected());
+
+    if (states.has_checked_state()) {
+      dict->SetString("checked_state",
+                      CheckedStateToString(states.checked_state()));
+    }
+
     if (states.has_hidden())
       dict->SetBoolean("hidden", states.hidden());
 
+    if (states.has_value() && !states.value().empty())
+      dict->SetString("value", states.value());
+
+    if (states.has_viewport_offset()) {
+      dict->SetString("viewport_offset",
+                      ViewportOffsetToString(states.viewport_offset()));
+    }
+
+    if (states.has_toggled_state()) {
+      dict->SetString("toggled_state",
+                      ToggledStateToString(states.toggled_state()));
+    }
+
     if (states.has_focusable())
       dict->SetBoolean("focusable", states.focusable());
 
@@ -196,8 +379,6 @@
     dict->SetString("transform",
                     Mat4ToString(fuchsia_node.node_to_container_transform()));
   }
-
-  // TODO(fuchsia:88125): Add more fields.
 }
 
 std::string AccessibilityTreeFormatterFuchsia::ProcessTreeForOutput(
@@ -230,13 +411,29 @@
     if (!node.GetString(string_attribute, &value) || value.empty())
       continue;
 
-    bool include_by_default = strcmp(string_attribute, "transform") &&
-                              strcmp(string_attribute, "location");
     WriteAttribute(
-        include_by_default,
+        /*include_by_default=*/true,
         base::StringPrintf("%s='%s'", string_attribute, value.c_str()), &line);
   }
 
+  for (unsigned i = 0; i < base::size(kIntAttributes); i++) {
+    const char* attribute_name = kIntAttributes[i];
+    int value = node.FindIntKey(attribute_name).value_or(0);
+    if (value == 0)
+      continue;
+    WriteAttribute(true, base::StringPrintf("%s=%d", attribute_name, value),
+                   &line);
+  }
+
+  for (unsigned i = 0; i < base::size(kDoubleAttributes); i++) {
+    const char* attribute_name = kDoubleAttributes[i];
+    int value = node.FindIntKey(attribute_name).value_or(0);
+    if (value == 0)
+      continue;
+    WriteAttribute(true, base::StringPrintf("%s=%d", attribute_name, value),
+                   &line);
+  }
+
   return line;
 }
 
diff --git a/content/browser/accessibility/accessibility_tree_formatter_fuchsia.h b/content/browser/accessibility/accessibility_tree_formatter_fuchsia.h
index 1859d904..d6a0762 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_fuchsia.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_fuchsia.h
@@ -27,6 +27,8 @@
   base::Value BuildTree(ui::AXPlatformNodeDelegate* root) const override;
   base::Value BuildTreeForSelector(const AXTreeSelector&) const override;
   base::Value BuildNode(ui::AXPlatformNodeDelegate* node) const override;
+  void AddDefaultFilters(
+      std::vector<AXPropertyFilter>* property_filters) override;
 
  private:
   void RecursiveBuildTree(const BrowserAccessibility& node,
diff --git a/content/browser/accessibility/accessibility_win_browsertest.cc b/content/browser/accessibility/accessibility_win_browsertest.cc
index 62c3bbae..f51674f 100644
--- a/content/browser/accessibility/accessibility_win_browsertest.cc
+++ b/content/browser/accessibility/accessibility_win_browsertest.cc
@@ -1784,7 +1784,7 @@
   Microsoft::WRL::ComPtr<IAccessibleText> input_text;
   SetUpScrollableInputField(&input_text);
 
-  LONG contents_string_length = static_cast<LONG>(InputContentsString().size());
+  int contents_string_length = static_cast<int>(InputContentsString().size());
   constexpr LONG visible_characters_start = 21;
   LONG n_characters;
   ASSERT_HRESULT_SUCCEEDED(input_text->get_nCharacters(&n_characters));
@@ -1979,7 +1979,7 @@
   Microsoft::WRL::ComPtr<IAccessibleText> input_text;
   SetUpScrollableInputTypeSearchField(&input_text);
 
-  LONG contents_string_length = static_cast<LONG>(InputContentsString().size());
+  int contents_string_length = static_cast<int>(InputContentsString().size());
   constexpr LONG visible_characters_start = 21;
   LONG n_characters;
   ASSERT_HRESULT_SUCCEEDED(input_text->get_nCharacters(&n_characters));
@@ -2628,7 +2628,7 @@
   AccessibilityNotificationWaiter waiter(
       shell()->web_contents(), ui::kAXModeComplete,
       ax::mojom::Event::kTextSelectionChanged);
-  LONG contents_string_length = static_cast<LONG>(InputContentsString().size());
+  int contents_string_length = static_cast<int>(InputContentsString().size());
   start_offset = 0;
   end_offset = contents_string_length;
   EXPECT_HRESULT_FAILED(input_text->setSelection(1, start_offset, end_offset));
@@ -2664,7 +2664,7 @@
   LONG n_ranges = 1;
   IA2Range* ranges =
       reinterpret_cast<IA2Range*>(CoTaskMemAlloc(sizeof(IA2Range)));
-  LONG contents_string_length = static_cast<LONG>(InputContentsString().size());
+  int contents_string_length = static_cast<int>(InputContentsString().size());
   ranges[0].anchor = ax_input.Get();
   ranges[0].anchorOffset = -1;
   ranges[0].active = ax_input.Get();
@@ -2869,7 +2869,7 @@
   // There is no selection, just a caret.
   EXPECT_EQ(E_INVALIDARG, hr);
 
-  LONG contents_string_length = static_cast<LONG>(InputContentsString().size());
+  int contents_string_length = static_cast<int>(InputContentsString().size());
   AccessibilityNotificationWaiter waiter(
       shell()->web_contents(), ui::kAXModeComplete,
       ax::mojom::Event::kTextSelectionChanged);
@@ -2906,7 +2906,7 @@
   Microsoft::WRL::ComPtr<IAccessible2_4> ax_textarea;
   ASSERT_HRESULT_SUCCEEDED(textarea_text.As(&ax_textarea));
 
-  LONG contents_string_length = static_cast<LONG>(InputContentsString().size());
+  int contents_string_length = static_cast<int>(InputContentsString().size());
   LONG n_ranges = 1;
   IA2Range* ranges =
       reinterpret_cast<IA2Range*>(CoTaskMemAlloc(sizeof(IA2Range)));
@@ -3481,7 +3481,7 @@
   EXPECT_EQ(nullptr, text.Get());
   hr = input_text->get_textAtOffset(invalid_offset, IA2_TEXT_BOUNDARY_SENTENCE,
                                     &start_offset, &end_offset, text.Receive());
-  EXPECT_EQ(E_INVALIDARG, hr);
+  EXPECT_EQ(S_FALSE, hr);
   EXPECT_EQ(0, start_offset);
   EXPECT_EQ(0, end_offset);
   EXPECT_EQ(nullptr, text.Get());
@@ -3518,7 +3518,7 @@
   hr = input_text->get_textAtOffset(IA2_TEXT_OFFSET_LENGTH,
                                     IA2_TEXT_BOUNDARY_SENTENCE, &start_offset,
                                     &end_offset, text.Receive());
-  EXPECT_EQ(E_INVALIDARG, hr);
+  EXPECT_EQ(S_FALSE, hr);
   EXPECT_EQ(0, start_offset);
   EXPECT_EQ(0, end_offset);
   EXPECT_EQ(nullptr, text.Get());
@@ -3588,7 +3588,7 @@
   hr = textarea_text->get_textAtOffset(
       invalid_offset, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset, &end_offset,
       text.Receive());
-  EXPECT_EQ(E_INVALIDARG, hr);
+  EXPECT_EQ(S_FALSE, hr);
   EXPECT_EQ(0, start_offset);
   EXPECT_EQ(0, end_offset);
   EXPECT_EQ(nullptr, text.Get());
@@ -3627,7 +3627,7 @@
   hr = textarea_text->get_textAtOffset(
       IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset,
       &end_offset, text.Receive());
-  EXPECT_EQ(E_INVALIDARG, hr);
+  EXPECT_EQ(S_FALSE, hr);
   EXPECT_EQ(0, start_offset);
   EXPECT_EQ(0, end_offset);
   EXPECT_EQ(nullptr, text.Get());
@@ -3653,7 +3653,7 @@
   Microsoft::WRL::ComPtr<IAccessibleText> input_text;
   SetUpInputField(&input_text);
 
-  LONG contents_string_length = static_cast<LONG>(InputContentsString().size());
+  int contents_string_length = static_cast<int>(InputContentsString().size());
   for (LONG offset = 0; offset < contents_string_length; ++offset) {
     std::wstring expected_text(1, InputContentsString()[offset]);
     LONG expected_start_offset = offset;
@@ -3681,7 +3681,7 @@
   Microsoft::WRL::ComPtr<IAccessibleText> textarea_text;
   SetUpTextareaField(&textarea_text);
 
-  LONG contents_string_length = static_cast<LONG>(InputContentsString().size());
+  int contents_string_length = InputContentsString().size();
   for (LONG offset = 0; offset < contents_string_length; ++offset) {
     std::wstring expected_text(1, TextAreaContentsString()[offset]);
     LONG expected_start_offset = offset;
@@ -3869,7 +3869,7 @@
   CheckTextAtOffset(input_text, 39, IA2_TEXT_BOUNDARY_WORD, 38, 40, L", ");
 
   // Trailing final punctuation should not be part of the last word.
-  LONG contents_string_length = static_cast<LONG>(InputContentsString().size());
+  int contents_string_length = static_cast<int>(InputContentsString().size());
   CheckTextAtOffset(input_text, 40, IA2_TEXT_BOUNDARY_WORD, 40, 44, L"like");
   CheckTextAtOffset(input_text, 41, IA2_TEXT_BOUNDARY_WORD, 40, 44, L"like");
   CheckTextAtOffset(input_text, 44, IA2_TEXT_BOUNDARY_WORD, 44,
@@ -3934,7 +3934,7 @@
   CheckTextAtOffset(textarea_text, 39, IA2_TEXT_BOUNDARY_WORD, 38, 40, L", ");
 
   // Trailing final punctuation should not be part of the last word.
-  LONG contents_string_length = static_cast<LONG>(InputContentsString().size());
+  int contents_string_length = static_cast<int>(InputContentsString().size());
   CheckTextAtOffset(textarea_text, 40, IA2_TEXT_BOUNDARY_WORD, 40, 44, L"like");
   CheckTextAtOffset(textarea_text, 41, IA2_TEXT_BOUNDARY_WORD, 40, 44, L"like");
   CheckTextAtOffset(textarea_text, 44, IA2_TEXT_BOUNDARY_WORD, 44,
@@ -3977,78 +3977,33 @@
 }
 
 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
-                       DISABLED_TestTextAtOffsetWithBoundarySentence) {
+                       TestTextAtOffsetWithBoundarySentence) {
   Microsoft::WRL::ComPtr<IAccessibleText> input_text;
   SetUpInputField(&input_text);
 
-  const LONG contents_string_length =
-      static_cast<LONG>(InputContentsString().size());
-  const std::wstring expected_text = base::SysUTF8ToWide(InputContentsString());
-  for (LONG offset = 0; offset < contents_string_length; ++offset) {
-    CheckTextAtOffset(input_text, offset, IA2_TEXT_BOUNDARY_SENTENCE, 0,
-                      contents_string_length, expected_text);
-  }
-
-  // Test special offsets.
-  CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET,
-                    IA2_TEXT_BOUNDARY_SENTENCE, 0, contents_string_length,
-                    expected_text);
-  {
-    LONG start_offset = 0;
-    LONG end_offset = 0;
-    base::win::ScopedBstr text;
-    HRESULT hr = input_text->get_textAtOffset(
-        IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset,
-        &end_offset, text.Receive());
-    EXPECT_EQ(E_INVALIDARG, hr);
-    EXPECT_EQ(0, start_offset);
-    EXPECT_EQ(0, end_offset);
-    EXPECT_EQ(nullptr, text.Get());
-  }
+  // Sentence navigation is not currently implemented.
+  LONG start_offset = 0;
+  LONG end_offset = 0;
+  base::win::ScopedBstr text;
+  HRESULT hr =
+      input_text->get_textAtOffset(5, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset,
+                                   &end_offset, text.Receive());
+  EXPECT_EQ(S_FALSE, hr);
 }
 
 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
-                       DISABLED_TestMultiLineTextAtOffsetWithBoundarySentence) {
+                       TestMultiLineTextAtOffsetWithBoundarySentence) {
   Microsoft::WRL::ComPtr<IAccessibleText> textarea_text;
   SetUpTextareaField(&textarea_text);
 
-  const LONG contents_string_length =
-      static_cast<LONG>(TextAreaContentsString().size());
-  const std::vector<LONG> sentence_starts{0, 23, 24, 31, 32};
-  const std::vector<LONG> sentence_ends{23, 24, 31, 32, contents_string_length};
-  size_t sentence_index = 0;
-  for (LONG offset = 0; offset < contents_string_length &&
-                        sentence_index < sentence_starts.size();
-       ++offset) {
-    if (offset == sentence_starts[sentence_index + 1])
-      ++sentence_index;
-    LONG expected_start_offset = sentence_starts[sentence_index];
-    LONG expected_end_offset = sentence_ends[sentence_index];
-    const std::wstring expected_text =
-        base::SysUTF8ToWide(TextAreaContentsString().substr(
-            sentence_starts[sentence_index],
-            (sentence_ends[sentence_index] - sentence_starts[sentence_index])));
-    CheckTextAtOffset(textarea_text, offset, IA2_TEXT_BOUNDARY_SENTENCE,
-                      expected_start_offset, expected_end_offset,
-                      expected_text);
-  }
-
-  // Test special offsets.
-  CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET,
-                    IA2_TEXT_BOUNDARY_SENTENCE, 32, contents_string_length,
-                    base::SysUTF8ToWide(TextAreaContentsString().substr(32)));
-  {
-    LONG start_offset = 0;
-    LONG end_offset = 0;
-    base::win::ScopedBstr text;
-    HRESULT hr = textarea_text->get_textAtOffset(
-        IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset,
-        &end_offset, text.Receive());
-    EXPECT_EQ(E_INVALIDARG, hr);
-    EXPECT_EQ(0, start_offset);
-    EXPECT_EQ(0, end_offset);
-    EXPECT_EQ(nullptr, text.Get());
-  }
+  // Sentence navigation is not currently implemented.
+  LONG start_offset = 0;
+  LONG end_offset = 0;
+  base::win::ScopedBstr text;
+  HRESULT hr = textarea_text->get_textAtOffset(25, IA2_TEXT_BOUNDARY_SENTENCE,
+                                               &start_offset, &end_offset,
+                                               text.Receive());
+  EXPECT_EQ(S_FALSE, hr);
 }
 
 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
@@ -4057,7 +4012,7 @@
   SetUpInputField(&input_text);
 
   // Single line text fields should return the whole text.
-  LONG contents_string_length = static_cast<LONG>(InputContentsString().size());
+  int contents_string_length = static_cast<int>(InputContentsString().size());
   CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_LINE, 0,
                     contents_string_length,
                     base::SysUTF8ToWide(InputContentsString()));
@@ -4084,7 +4039,7 @@
                     L"WebKit \n");
 
   // Last line does not have a trailing newline.
-  LONG contents_string_length = static_cast<LONG>(InputContentsString().size());
+  int contents_string_length = static_cast<int>(InputContentsString().size());
   CheckTextAtOffset(textarea_text, 32, IA2_TEXT_BOUNDARY_LINE, 32,
                     contents_string_length, L"\"KHTML, like\".");
 
@@ -4122,8 +4077,8 @@
   // Blink represents the blank line with a newline character, so in total there
   // should be two more newlines. The second newline is not part of the HTML
   // value attribute however.
-  LONG contents_string_length =
-      static_cast<LONG>(InputContentsString().size()) + 1;
+  int contents_string_length =
+      static_cast<int>(InputContentsString().size()) + 1;
   CheckTextAtOffset(textarea_text, 32, IA2_TEXT_BOUNDARY_LINE, 32,
                     contents_string_length, L"\"KHTML, like\".\n");
   CheckTextAtOffset(textarea_text, 46, IA2_TEXT_BOUNDARY_LINE, 32,
diff --git a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
index 24b95ba..84f486ee 100644
--- a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
+++ b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
@@ -2759,7 +2759,7 @@
       /*expected_count*/ -1);
   ASSERT_HRESULT_SUCCEEDED(
       text_range_provider->ExpandToEnclosingUnit(TextUnit_Word));
-  EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"\xA0");
+  EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"\xA0\n");
 
   // Case 2: test on range that includes the whitespace and the following word.
   GetTextRangeProviderFromTextNode(*node, &text_range_provider);
@@ -2772,7 +2772,7 @@
                                    /*expected_count*/ 1);
   ASSERT_HRESULT_SUCCEEDED(
       text_range_provider->ExpandToEnclosingUnit(TextUnit_Word));
-  EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"\xA0");
+  EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"\xA0\n");
 
   // Case 3: test on degenerate range after whitespace.
   node = FindNode(ax::mojom::Role::kStaticText, "3.14");
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index 993a959..4f9cc56c 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -105,6 +105,26 @@
   return nullptr;
 }
 
+int GetBoundaryTextOffsetInsideBaseAnchor(
+    ax::mojom::MoveDirection direction,
+    const BrowserAccessibility::AXPosition& base,
+    const BrowserAccessibility::AXPosition& position) {
+  if (base->GetAnchor() == position->GetAnchor())
+    return position->text_offset();
+
+  // If the position is outside the anchor of the base position, then return
+  // the first or last position in the same direction.
+  switch (direction) {
+    case ax::mojom::MoveDirection::kNone:
+      NOTREACHED();
+      return position->text_offset();
+    case ax::mojom::MoveDirection::kBackward:
+      return base->CreatePositionAtStartOfAnchor()->text_offset();
+    case ax::mojom::MoveDirection::kForward:
+      return base->CreatePositionAtEndOfAnchor()->text_offset();
+  }
+}
+
 }  // namespace
 
 bool BrowserAccessibility::IsValid() const {
@@ -1137,6 +1157,29 @@
   return result;
 }
 
+absl::optional<int> BrowserAccessibility::FindTextBoundary(
+    ax::mojom::TextBoundary boundary,
+    int offset,
+    ax::mojom::MoveDirection direction,
+    ax::mojom::TextAffinity affinity) const {
+  const AXPosition position = CreateTextPositionAt(offset, affinity);
+
+  // On Windows and Linux ATK, searching for a text boundary should always stop
+  // at the boundary of the current object.
+  auto boundary_behavior = ui::AXBoundaryBehavior::StopAtAnchorBoundary;
+  // On Windows and Linux ATK, it is standard text navigation behavior to stop
+  // if we are searching in the backwards direction and the current position is
+  // already at the required text boundary.
+  DCHECK_NE(direction, ax::mojom::MoveDirection::kNone);
+  if (direction == ax::mojom::MoveDirection::kBackward)
+    boundary_behavior = ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary;
+
+  return GetBoundaryTextOffsetInsideBaseAnchor(
+      direction, position,
+      position->CreatePositionAtTextBoundary(boundary, direction,
+                                             boundary_behavior));
+}
+
 const std::vector<gfx::NativeViewAccessible>
 BrowserAccessibility::GetUIADirectChildrenInRange(
     ui::AXPlatformNodeDelegate* start,
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index dda961da..1a20f44 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -494,6 +494,12 @@
   int GetIndexInParent() override;
   gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;
 
+  absl::optional<int> FindTextBoundary(
+      ax::mojom::TextBoundary boundary,
+      int offset,
+      ax::mojom::MoveDirection direction,
+      ax::mojom::TextAffinity affinity) const override;
+
   const std::vector<gfx::NativeViewAccessible> GetUIADirectChildrenInRange(
       ui::AXPlatformNodeDelegate* start,
       ui::AXPlatformNodeDelegate* end) override;
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index 2b36a75..9d61258 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -1060,6 +1060,10 @@
 }
 
 std::u16string BrowserAccessibilityAndroid::GetRoleDescription() const {
+  // If an element has an aria-roledescription set, use that value by default.
+  if (HasStringAttribute(ax::mojom::StringAttribute::kRoleDescription))
+    return GetString16Attribute(ax::mojom::StringAttribute::kRoleDescription);
+
   content::ContentClient* content_client = content::GetContentClient();
 
   // As a special case, if we have a heading level return a string like
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index 72ea529..71230cb 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -2401,7 +2401,7 @@
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreateNextCharacterPosition(
-        ui::AXBoundaryBehavior::kCrossBoundary));
+        ui::AXBoundaryBehavior::CrossBoundary));
   }
 
   if ([attribute
@@ -2411,7 +2411,7 @@
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreatePreviousCharacterPosition(
-        ui::AXBoundaryBehavior::kCrossBoundary));
+        ui::AXBoundaryBehavior::CrossBoundary));
   }
 
   if ([attribute
@@ -2422,9 +2422,9 @@
       return nil;
 
     AXPosition startWordPosition = endPosition->CreatePreviousWordStartPosition(
-        ui::AXBoundaryBehavior::kStopAtAnchorBoundary);
+        ui::AXBoundaryBehavior::StopAtAnchorBoundary);
     AXPosition endWordPosition = endPosition->CreatePreviousWordEndPosition(
-        ui::AXBoundaryBehavior::kStopAtAnchorBoundary);
+        ui::AXBoundaryBehavior::StopAtAnchorBoundary);
     AXPosition startPosition = *startWordPosition <= *endWordPosition
                                    ? std::move(endWordPosition)
                                    : std::move(startWordPosition);
@@ -2440,9 +2440,9 @@
       return nil;
 
     AXPosition endWordPosition = startPosition->CreateNextWordEndPosition(
-        ui::AXBoundaryBehavior::kStopAtAnchorBoundary);
+        ui::AXBoundaryBehavior::StopAtAnchorBoundary);
     AXPosition startWordPosition = startPosition->CreateNextWordStartPosition(
-        ui::AXBoundaryBehavior::kStopAtAnchorBoundary);
+        ui::AXBoundaryBehavior::StopAtAnchorBoundary);
     AXPosition endPosition = *startWordPosition <= *endWordPosition
                                  ? std::move(startWordPosition)
                                  : std::move(endWordPosition);
@@ -2457,7 +2457,7 @@
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreateNextWordEndPosition(
-        ui::AXBoundaryBehavior::kCrossBoundary));
+        ui::AXBoundaryBehavior::CrossBoundary));
   }
 
   if ([attribute
@@ -2467,7 +2467,7 @@
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreatePreviousWordStartPosition(
-        ui::AXBoundaryBehavior::kCrossBoundary));
+        ui::AXBoundaryBehavior::CrossBoundary));
   }
 
   if ([attribute isEqualToString:
@@ -2503,9 +2503,9 @@
     // Make sure that the line start position is really at the start of the
     // current line.
     lineStartPosition = lineStartPosition->CreatePreviousLineStartPosition(
-        ui::AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+        ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
     AXPosition lineEndPosition = lineStartPosition->CreateNextLineEndPosition(
-        ui::AXBoundaryBehavior::kStopAtAnchorBoundary);
+        ui::AXBoundaryBehavior::StopAtAnchorBoundary);
     AXRange range(std::move(lineStartPosition), std::move(lineEndPosition));
     return CreateTextMarkerRange(std::move(range));
   }
@@ -2518,9 +2518,9 @@
       return nil;
 
     AXPosition startLinePosition = endPosition->CreatePreviousLineStartPosition(
-        ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+        ui::AXBoundaryBehavior::StopAtLastAnchorBoundary);
     AXPosition endLinePosition = endPosition->CreatePreviousLineEndPosition(
-        ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+        ui::AXBoundaryBehavior::StopAtLastAnchorBoundary);
     AXPosition startPosition = *startLinePosition <= *endLinePosition
                                    ? std::move(endLinePosition)
                                    : std::move(startLinePosition);
@@ -2536,9 +2536,9 @@
       return nil;
 
     AXPosition startLinePosition = startPosition->CreateNextLineStartPosition(
-        ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+        ui::AXBoundaryBehavior::StopAtLastAnchorBoundary);
     AXPosition endLinePosition = startPosition->CreateNextLineEndPosition(
-        ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+        ui::AXBoundaryBehavior::StopAtLastAnchorBoundary);
     AXPosition endPosition = *startLinePosition <= *endLinePosition
                                  ? std::move(startLinePosition)
                                  : std::move(endLinePosition);
@@ -2553,7 +2553,7 @@
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreateNextLineEndPosition(
-        ui::AXBoundaryBehavior::kCrossBoundary));
+        ui::AXBoundaryBehavior::CrossBoundary));
   }
 
   if ([attribute
@@ -2563,20 +2563,7 @@
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreatePreviousLineStartPosition(
-        ui::AXBoundaryBehavior::kCrossBoundary));
-  }
-
-  if ([attribute
-          isEqualToString:
-              NSAccessibilitySentenceTextMarkerRangeForTextMarkerParameterizedAttribute]) {
-    AXPosition position = AXTextMarkerToAXPosition(parameter);
-    if (position->IsNullPosition())
-      return nil;
-
-    AXRange range = position->ExpandToEnclosingTextBoundary(
-        ax::mojom::TextBoundary::kSentenceStartOrEnd,
-        ui::AXRangeExpandBehavior::kLeftFirst);
-    return CreateTextMarkerRange(std::move(range));
+        ui::AXBoundaryBehavior::CrossBoundary));
   }
 
   if ([attribute
@@ -2586,9 +2573,11 @@
     if (position->IsNullPosition())
       return nil;
 
-    AXRange range = position->ExpandToEnclosingTextBoundary(
-        ax::mojom::TextBoundary::kParagraphStartOrEnd,
-        ui::AXRangeExpandBehavior::kLeftFirst);
+    AXPosition startPosition = position->CreatePreviousParagraphStartPosition(
+        ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+    AXPosition endPosition = position->CreateNextParagraphEndPosition(
+        ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
+    AXRange range(std::move(startPosition), std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
 
@@ -2599,7 +2588,7 @@
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreateNextParagraphEndPosition(
-        ui::AXBoundaryBehavior::kCrossBoundary));
+        ui::AXBoundaryBehavior::CrossBoundary));
   }
 
   if ([attribute
@@ -2609,7 +2598,7 @@
     if (position->IsNullPosition())
       return nil;
     return CreateTextMarker(position->CreatePreviousParagraphStartPosition(
-        ui::AXBoundaryBehavior::kCrossBoundary));
+        ui::AXBoundaryBehavior::CrossBoundary));
   }
 
   if ([attribute
@@ -2620,9 +2609,9 @@
       return nil;
 
     AXPosition startPosition = position->CreatePreviousFormatStartPosition(
-        ui::AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+        ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
     AXPosition endPosition = position->CreateNextFormatEndPosition(
-        ui::AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+        ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
     AXRange range(std::move(startPosition), std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
@@ -2704,9 +2693,9 @@
     //
     // Note that hard line breaks are on a line of their own.
     AXPosition startPosition = position->CreatePreviousLineStartPosition(
-        ui::AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+        ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary);
     AXPosition endPosition = startPosition->CreateNextLineStartPosition(
-        ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+        ui::AXBoundaryBehavior::StopAtLastAnchorBoundary);
     AXRange range(std::move(startPosition), std::move(endPosition));
     return CreateTextMarkerRange(std::move(range));
   }
diff --git a/content/browser/accessibility/browser_accessibility_com_win.h b/content/browser/accessibility/browser_accessibility_com_win.h
index f484f5db..f6635ac3 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.h
+++ b/content/browser/accessibility/browser_accessibility_com_win.h
@@ -27,18 +27,12 @@
 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
 #include "ui/accessibility/platform/ax_platform_node_win.h"
 
-// These nonstandard GUIDs are taken directly from the Mozilla sources
-// (accessible/src/msaa/nsAccessNodeWrap.cpp); some documentation is here:
-// http://developer.mozilla.org/en/Accessibility/AT-APIs/ImplementationFeatures/MSAA
+// This nonstandard GUID is taken directly from the Mozilla sources
+// (https://searchfox.org/mozilla-central/source/accessible/windows/msaa/ServiceProvider.cpp#110).
 const GUID GUID_ISimpleDOM = {0x0c539790,
                               0x12e4,
                               0x11cf,
                               {0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8}};
-const GUID GUID_IAccessibleContentDocument = {
-    0xa5d8e1f3,
-    0x3571,
-    0x4d8f,
-    {0x95, 0x21, 0x07, 0xed, 0x28, 0xfb, 0x07, 0x2e}};
 
 namespace content {
 class BrowserAccessibilityWin;
diff --git a/content/browser/accessibility/browser_accessibility_fuchsia.cc b/content/browser/accessibility/browser_accessibility_fuchsia.cc
index 1f4151b..ac90112 100644
--- a/content/browser/accessibility/browser_accessibility_fuchsia.cc
+++ b/content/browser/accessibility/browser_accessibility_fuchsia.cc
@@ -13,9 +13,6 @@
 
 namespace content {
 
-// Error allowed for each edge when converting from gfx::RectF to gfx::Rect.
-constexpr float kRectConversionError = 0.5;
-
 using AXRole = ax::mojom::Role;
 using FuchsiaRole = fuchsia::accessibility::semantics::Role;
 
@@ -404,26 +401,7 @@
     return true;
   }
 
-  ui::AXActionData full_action_data = action_data;
-
-  if (action_data.action == ax::mojom::Action::kScrollToMakeVisible) {
-    // The scroll-to-make-visible action expects coordinates in the local
-    // coordinate space of |node|. So, we need to translate node's bounds to the
-    // origin.
-    gfx::Rect local_bounds = gfx::ToEnclosedRectIgnoringError(
-        GetData().relative_bounds.bounds, kRectConversionError);
-    local_bounds = gfx::Rect(local_bounds.size());
-
-    full_action_data.target_rect = local_bounds;
-    full_action_data.horizontal_scroll_alignment =
-        ax::mojom::ScrollAlignment::kScrollAlignmentCenter;
-    full_action_data.vertical_scroll_alignment =
-        ax::mojom::ScrollAlignment::kScrollAlignmentCenter;
-    full_action_data.scroll_behavior =
-        ax::mojom::ScrollBehavior::kScrollIfVisible;
-  }
-
-  return BrowserAccessibility::AccessibilityPerformAction(full_action_data);
+  return BrowserAccessibility::AccessibilityPerformAction(action_data);
 }
 
 }  // namespace content
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 24ae6b6..21abd0b 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -446,8 +446,8 @@
 #endif  // DCHECK_IS_ON()
 
   // Update the cached device scale factor.
-  if (delegate_ && !use_custom_device_scale_factor_for_testing_)
-    device_scale_factor_ = delegate_->AccessibilityGetDeviceScaleFactor();
+  if (!use_custom_device_scale_factor_for_testing_)
+    UpdateDeviceScaleFactor();
 
   // Optionally merge multiple tree updates into fewer updates.
   const std::vector<ui::AXTreeUpdate>* tree_updates = &details.updates;
@@ -1513,8 +1513,7 @@
 
 ui::AXNode* BrowserAccessibilityManager::GetNodeFromTree(
     const ui::AXNodeID node_id) const {
-  BrowserAccessibility* wrapper = GetFromID(node_id);
-  return wrapper ? wrapper->node() : nullptr;
+  return ax_tree()->GetFromId(node_id);
 }
 
 ui::AXPlatformNode* BrowserAccessibilityManager::GetPlatformNodeFromTree(
@@ -1822,4 +1821,9 @@
   return device_scale_factor_;
 }
 
+void BrowserAccessibilityManager::UpdateDeviceScaleFactor() {
+  if (delegate_)
+    device_scale_factor_ = delegate_->AccessibilityGetDeviceScaleFactor();
+}
+
 }  // namespace content
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h
index cc86de7..d5dea0383 100644
--- a/content/browser/accessibility/browser_accessibility_manager.h
+++ b/content/browser/accessibility/browser_accessibility_manager.h
@@ -455,14 +455,16 @@
       const BrowserAccessibility& end_object,
       int end_offset);
 
+  // TODO(abrusher): Make this method non-virtual, or preferably remove
+  // altogether. This method is temporarily virtual, because fuchsia has a
+  // different path to retrieve the device scale factor. This is a temporary
+  // measure while the flatland migration is in progress (fxbug.dev/90502).
+  virtual void UpdateDeviceScaleFactor();
+
   // Accessors.
   ui::AXTreeID ax_tree_id() const { return ax_tree_id_; }
 
-  // TODO(abrusher): Make this method non-virtual.
-  // This method is temporarily virtual, because fuchsia has a different path to
-  // retrieve the device scale factor. This is a temporary measure while the
-  // flatland migration is in progress (fxbug.dev/90502).
-  virtual float device_scale_factor() const;
+  float device_scale_factor() const;
   ui::AXTree* ax_tree() const { return tree_.get(); }
 
   // AXTreeObserver implementation.
diff --git a/content/browser/accessibility/browser_accessibility_manager_fuchsia.cc b/content/browser/accessibility/browser_accessibility_manager_fuchsia.cc
index 6804cbc..83c47a3f 100644
--- a/content/browser/accessibility/browser_accessibility_manager_fuchsia.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_fuchsia.cc
@@ -65,15 +65,6 @@
   }
 }
 
-float BrowserAccessibilityManagerFuchsia::device_scale_factor() const {
-  ui::AccessibilityBridgeFuchsia* accessibility_bridge =
-      GetAccessibilityBridge();
-  if (!accessibility_bridge)
-    return 1.f;
-
-  return accessibility_bridge->GetDeviceScaleFactor();
-}
-
 // static
 ui::AXTreeUpdate BrowserAccessibilityManagerFuchsia::GetEmptyDocument() {
   ui::AXNodeData empty_document;
@@ -118,4 +109,13 @@
                                                          hit_result_id);
 }
 
+void BrowserAccessibilityManagerFuchsia::UpdateDeviceScaleFactor() {
+  ui::AccessibilityBridgeFuchsia* accessibility_bridge =
+      GetAccessibilityBridge();
+  if (accessibility_bridge)
+    device_scale_factor_ = accessibility_bridge->GetDeviceScaleFactor();
+  else
+    BrowserAccessibilityManager::UpdateDeviceScaleFactor();
+}
+
 }  // namespace content
diff --git a/content/browser/accessibility/browser_accessibility_manager_fuchsia.h b/content/browser/accessibility/browser_accessibility_manager_fuchsia.h
index 541d3135..7fd3885 100644
--- a/content/browser/accessibility/browser_accessibility_manager_fuchsia.h
+++ b/content/browser/accessibility/browser_accessibility_manager_fuchsia.h
@@ -33,7 +33,7 @@
                       BrowserAccessibility* node,
                       int action_request_id) override;
   void FireFocusEvent(BrowserAccessibility* node) override;
-  float device_scale_factor() const override;
+  void UpdateDeviceScaleFactor() override;
 
   // Sends hit test result to fuchsia.
   void OnHitTestResult(int action_request_id, BrowserAccessibility* node);
diff --git a/content/browser/accessibility/browser_accessibility_manager_fuchsia_unittest.cc b/content/browser/accessibility/browser_accessibility_manager_fuchsia_unittest.cc
index 1ffb885..411b23a 100644
--- a/content/browser/accessibility/browser_accessibility_manager_fuchsia_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_fuchsia_unittest.cc
@@ -404,32 +404,39 @@
   const float kScaleFactor = 0.8f;
   mock_accessibility_bridge_->SetDeviceScaleFactor(kScaleFactor);
 
-  ui::AXTreeUpdate initial_state;
-  ui::AXTreeID tree_id = ui::AXTreeID::CreateNewAXTreeID();
-  initial_state.tree_data.tree_id = tree_id;
-  initial_state.has_tree_data = true;
-  initial_state.tree_data.loaded = true;
-  initial_state.root_id = 1;
-  initial_state.nodes.resize(1);
-  initial_state.nodes[0].id = 1;
-
-  auto* registry = ui::AccessibilityBridgeFuchsiaRegistry::GetInstance();
-  registry->RegisterAccessibilityBridge(tree_id,
-                                        mock_accessibility_bridge_.get());
   std::unique_ptr<BrowserAccessibilityManager> manager(
       BrowserAccessibilityManager::Create(
-          initial_state, mock_browser_accessibility_delegate_.get()));
+          mock_browser_accessibility_delegate_.get()));
   ASSERT_TRUE(manager);
 
+  auto* registry = ui::AccessibilityBridgeFuchsiaRegistry::GetInstance();
+  registry->RegisterAccessibilityBridge(manager->ax_tree_id(),
+                                        mock_accessibility_bridge_.get());
+
+  // Update the root node.
+  {
+    BrowserAccessibility* root_node = manager->GetRoot();
+    ASSERT_TRUE(root_node);
+    AXEventNotificationDetails event;
+    ui::AXTreeUpdate updated_state;
+    updated_state.tree_data.tree_id = manager->ax_tree_id();
+    updated_state.has_tree_data = true;
+    updated_state.tree_data.loaded = true;
+    updated_state.nodes.resize(1);
+    updated_state.nodes[0] = root_node->GetData();
+    event.updates.push_back(std::move(updated_state));
+    EXPECT_TRUE(manager->OnAccessibilityEvents(event));
+  }
+
   {
     const auto& node_updates = mock_accessibility_bridge_->node_updates();
-    ASSERT_EQ(node_updates.size(), 1u);
+    ASSERT_FALSE(node_updates.empty());
 
-    BrowserAccessibilityFuchsia* node_1 =
-        ToBrowserAccessibilityFuchsia(manager->GetFromID(1));
-    ASSERT_TRUE(node_1);
+    BrowserAccessibilityFuchsia* root_node =
+        ToBrowserAccessibilityFuchsia(manager->GetRoot());
+    ASSERT_TRUE(root_node);
 
-    EXPECT_EQ(node_updates[0].node_id(), node_1->GetFuchsiaNodeID());
+    EXPECT_EQ(node_updates.back().node_id(), root_node->GetFuchsiaNodeID());
 
     ASSERT_TRUE(node_updates[0].has_node_to_container_transform());
     const auto& transform =
@@ -580,8 +587,6 @@
   initial_state.nodes[0].id = 1;
   initial_state.nodes[0].child_ids.push_back(2);
   initial_state.nodes[1].id = 2;
-  initial_state.nodes[1].relative_bounds.bounds =
-      gfx::RectF(1.f, 2.f, 3.f, 4.f);
 
   auto* registry = ui::AccessibilityBridgeFuchsiaRegistry::GetInstance();
   registry->RegisterAccessibilityBridge(tree_id,
@@ -612,10 +617,6 @@
     ASSERT_TRUE(last_action_data);
     EXPECT_EQ(last_action_data->action,
               ax::mojom::Action::kScrollToMakeVisible);
-    EXPECT_EQ(last_action_data->target_rect.x(), 0.f);
-    EXPECT_EQ(last_action_data->target_rect.y(), 0.f);
-    EXPECT_EQ(last_action_data->target_rect.width(), 3.f);
-    EXPECT_EQ(last_action_data->target_rect.height(), 4.f);
   }
 }
 
diff --git a/content/browser/accessibility/browser_accessibility_unittest.cc b/content/browser/accessibility/browser_accessibility_unittest.cc
index b3001fb..04f8580 100644
--- a/content/browser/accessibility/browser_accessibility_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_unittest.cc
@@ -829,7 +829,7 @@
 
   BrowserAccessibility::AXPosition next_word_start =
       position->CreateNextWordStartPosition(
-          ui::AXBoundaryBehavior::kCrossBoundary);
+          ui::AXBoundaryBehavior::CrossBoundary);
   if (position->MaxTextOffset() == 0) {
     EXPECT_TRUE(next_word_start->IsNullPosition());
   } else {
@@ -841,7 +841,7 @@
 
   BrowserAccessibility::AXPosition next_word_end =
       position->CreateNextWordEndPosition(
-          ui::AXBoundaryBehavior::kCrossBoundary);
+          ui::AXBoundaryBehavior::CrossBoundary);
   if (position->MaxTextOffset() == 0) {
     EXPECT_TRUE(next_word_end->IsNullPosition());
   } else {
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
index fa52bf04..eb2d295 100644
--- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -493,8 +493,8 @@
   RunEventTest(FILE_PATH_LITERAL("caret-move.html"));
 }
 
-// Flaky on Windows, disabled on Linux: https://crbug.com/1186887
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
+// Flaky on Windows: https://crbug.com/1186887
+#if defined(OS_WIN)
 #define MAYBE_AccessibilityEventsCaretMoveHiddenInput \
   DISABLED_AccessibilityEventsCaretMoveHiddenInput
 #else
diff --git a/content/browser/accessibility/web_contents_accessibility_android.cc b/content/browser/accessibility/web_contents_accessibility_android.cc
index e0adbb3..f08cdcb 100644
--- a/content/browser/accessibility/web_contents_accessibility_android.cc
+++ b/content/browser/accessibility/web_contents_accessibility_android.cc
@@ -313,22 +313,35 @@
 void WebContentsAccessibilityAndroid::SetAXMode(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
-    jboolean screen_reader_mode) {
-  if (!features::IsComputeAXModeEnabled())
-    return;
-
+    jboolean screen_reader_mode,
+    jboolean is_accessibility_enabled) {
   BrowserAccessibilityStateImpl* accessibility_state =
       BrowserAccessibilityStateImpl::GetInstance();
 
-  if (screen_reader_mode) {
-    accessibility_state->AddAccessibilityModeFlags(ui::kAXModeComplete);
-  } else {
-    // Remove the mode flags present in kAXModeComplete but not in
-    // kAXModeBasic, thereby reverting the mode to kAXModeBasic while
-    // not touching any other flags.
-    ui::AXMode remove_mode_flags(ui::kAXModeComplete.mode() &
-                                 ~ui::kAXModeBasic.mode());
-    accessibility_state->RemoveAccessibilityModeFlags(remove_mode_flags);
+  if (!features::IsComputeAXModeEnabled() &&
+      !features::IsAutoDisableAccessibilityEnabled()) {
+    return;
+  }
+
+  if (features::IsAutoDisableAccessibilityEnabled()) {
+    if (!is_accessibility_enabled) {
+      accessibility_state->DisableAccessibility();
+    } else {
+      accessibility_state->AddAccessibilityModeFlags(ui::kAXModeComplete);
+    }
+  }
+
+  if (features::IsComputeAXModeEnabled()) {
+    if (screen_reader_mode) {
+      accessibility_state->AddAccessibilityModeFlags(ui::kAXModeComplete);
+    } else {
+      // Remove the mode flags present in kAXModeComplete but not in
+      // kAXModeBasic, thereby reverting the mode to kAXModeBasic while
+      // not touching any other flags.
+      ui::AXMode remove_mode_flags(ui::kAXModeComplete.mode() &
+                                   ~ui::kAXModeBasic.mode());
+      accessibility_state->RemoveAccessibilityModeFlags(remove_mode_flags);
+    }
   }
 }
 
diff --git a/content/browser/accessibility/web_contents_accessibility_android.h b/content/browser/accessibility/web_contents_accessibility_android.h
index 8969474..03b5687 100644
--- a/content/browser/accessibility/web_contents_accessibility_android.h
+++ b/content/browser/accessibility/web_contents_accessibility_android.h
@@ -92,7 +92,8 @@
               jboolean screen_reader_mode);
   void SetAXMode(JNIEnv* env,
                  const base::android::JavaParamRef<jobject>& obj,
-                 jboolean screen_reader_mode);
+                 jboolean screen_reader_mode,
+                 jboolean is_accessibility_enabled);
 
   base::android::ScopedJavaLocalRef<jstring> GetSupportedHtmlElementTypes(
       JNIEnv* env,
diff --git a/content/browser/android/content_feature_list.cc b/content/browser/android/content_feature_list.cc
index 3131e80..049c5b0b 100644
--- a/content/browser/android/content_feature_list.cc
+++ b/content/browser/android/content_feature_list.cc
@@ -7,6 +7,7 @@
 #include "base/notreached.h"
 #include "content/public/android/content_jni_headers/ContentFeatureListImpl_jni.h"
 #include "content/public/common/content_features.h"
+#include "ui/accessibility/accessibility_features.h"
 
 using base::android::ConvertJavaStringToUTF8;
 using base::android::JavaParamRef;
@@ -20,6 +21,7 @@
 // this array may either refer to features defined in the header of this file or
 // in other locations in the code base (e.g. content_features.h).
 const base::Feature* const kFeaturesExposedToJava[] = {
+    &features::kAutoDisableAccessibility,
     &features::kAccessibilityPageZoom,
     &features::kBackgroundMediaRendererHasModerateBinding,
     &features::kBindingManagementWaiveCpu,
diff --git a/content/browser/attribution_reporting/BUILD.gn b/content/browser/attribution_reporting/BUILD.gn
index ea116da..9184145 100644
--- a/content/browser/attribution_reporting/BUILD.gn
+++ b/content/browser/attribution_reporting/BUILD.gn
@@ -14,7 +14,7 @@
       types = [
         {
           mojom = "content.mojom.AttributionReportID"
-          cpp = "::content::EventAttributionReport::Id"
+          cpp = "::content::AttributionReport::Id"
         },
         {
           mojom = "content.mojom.SourceType"
@@ -23,7 +23,7 @@
       ]
       traits_headers = [
         "attribution_internals_mojom_traits.h",
-        "event_attribution_report.h",
+        "attribution_report.h",
       ]
       traits_sources = [ "attribution_internals_mojom_traits.cc" ]
     },
diff --git a/content/browser/attribution_reporting/attribution_host_utils.cc b/content/browser/attribution_reporting/attribution_host_utils.cc
index 0405f1d6..1526cbe 100644
--- a/content/browser/attribution_reporting/attribution_host_utils.cc
+++ b/content/browser/attribution_reporting/attribution_host_utils.cc
@@ -67,7 +67,7 @@
                                         source_type),
       source_type, impression.priority,
       policy.GetAttributionLogicForImpression(source_type),
-      /*impression_id=*/absl::nullopt);
+      /*source_id=*/absl::nullopt);
 
   attribution_manager.HandleSource(std::move(storable_impression));
   return VerifyResult{.allowed = true, .stored = true};
diff --git a/content/browser/attribution_reporting/attribution_internals_browsertest.cc b/content/browser/attribution_reporting/attribution_internals_browsertest.cc
index 0367b26..9c576bb 100644
--- a/content/browser/attribution_reporting/attribution_internals_browsertest.cc
+++ b/content/browser/attribution_reporting/attribution_internals_browsertest.cc
@@ -14,9 +14,9 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "content/browser/attribution_reporting/attribution_manager.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_storage.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
 #include "content/browser/attribution_reporting/send_result.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
@@ -68,7 +68,7 @@
         .WillByDefault(InvokeCallback<std::vector<StorableSource>>({}));
 
     ON_CALL(manager_, GetPendingReportsForWebUI)
-        .WillByDefault(InvokeCallback<std::vector<EventAttributionReport>>({}));
+        .WillByDefault(InvokeCallback<std::vector<AttributionReport>>({}));
   }
 
   void ClickRefreshButton() {
@@ -357,7 +357,7 @@
                             SendResult(SendResult::Status::kFailure,
                                        /*http_response_code=*/0));
   ON_CALL(manager_, GetPendingReportsForWebUI)
-      .WillByDefault(InvokeCallback<std::vector<EventAttributionReport>>(
+      .WillByDefault(InvokeCallback<std::vector<AttributionReport>>(
           {ReportBuilder(SourceBuilder(now)
                              .SetSourceType(StorableSource::SourceType::kEvent)
                              .SetAttributionLogic(
@@ -495,12 +495,12 @@
 
   OverrideWebUIAttributionManager();
 
-  EventAttributionReport report = ReportBuilder(SourceBuilder(now).Build())
-                                      .SetReportTime(now)
-                                      .SetPriority(7)
-                                      .Build();
+  AttributionReport report = ReportBuilder(SourceBuilder(now).Build())
+                                 .SetReportTime(now)
+                                 .SetPriority(7)
+                                 .Build();
   EXPECT_CALL(manager_, GetPendingReportsForWebUI)
-      .WillOnce(InvokeCallback<std::vector<EventAttributionReport>>({report}));
+      .WillOnce(InvokeCallback<std::vector<AttributionReport>>({report}));
 
   report.set_report_time(report.report_time() + base::Hours(1));
   manager_.NotifyReportSent(report, SendResult(SendResult::Status::kSent,
@@ -603,16 +603,16 @@
   EXPECT_TRUE(NavigateToURL(shell(), GURL(kAttributionInternalsUrl)));
 
   EXPECT_CALL(manager_, GetPendingReportsForWebUI)
-      .WillOnce(InvokeCallback<std::vector<EventAttributionReport>>(
+      .WillOnce(InvokeCallback<std::vector<AttributionReport>>(
           {ReportBuilder(SourceBuilder().Build())
                .SetPriority(7)
-               .SetReportId(EventAttributionReport::Id(5))
+               .SetReportId(AttributionReport::Id(5))
                .Build()}))
-      .WillOnce(InvokeCallback<std::vector<EventAttributionReport>>({}));
+      .WillOnce(InvokeCallback<std::vector<AttributionReport>>({}));
 
-  EXPECT_CALL(manager_, SendReportsForWebUI(
-                            ElementsAre(EventAttributionReport::Id(5)), _))
-      .WillOnce([](const std::vector<EventAttributionReport::Id>& ids,
+  EXPECT_CALL(manager_,
+              SendReportsForWebUI(ElementsAre(AttributionReport::Id(5)), _))
+      .WillOnce([](const std::vector<AttributionReport::Id>& ids,
                    base::OnceClosure done) { std::move(done).Run(); });
 
   OverrideWebUIAttributionManager();
diff --git a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
index 5adf9c0..b0ac94bb 100644
--- a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
+++ b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
@@ -13,8 +13,8 @@
 #include "base/notreached.h"
 #include "base/time/time.h"
 #include "content/browser/attribution_reporting/attribution_manager_impl.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_storage.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
 #include "content/browser/attribution_reporting/send_result.h"
 #include "content/browser/attribution_reporting/storable_source.h"
 #include "content/browser/storage_partition_impl.h"
@@ -78,7 +78,7 @@
 }
 
 mojom::WebUIAttributionReportPtr WebUIAttributionReport(
-    const EventAttributionReport& report,
+    const AttributionReport& report,
     int http_response_code,
     mojom::WebUIAttributionReport::Status status) {
   return mojom::WebUIAttributionReport::New(
@@ -94,10 +94,10 @@
 
 void ForwardReportsToWebUI(
     mojom::AttributionInternalsHandler::GetReportsCallback web_ui_callback,
-    std::vector<EventAttributionReport> pending_reports) {
+    std::vector<AttributionReport> pending_reports) {
   std::vector<mojom::WebUIAttributionReportPtr> web_ui_reports;
   web_ui_reports.reserve(pending_reports.size());
-  for (const EventAttributionReport& report : pending_reports) {
+  for (const AttributionReport& report : pending_reports) {
     web_ui_reports.push_back(WebUIAttributionReport(
         report, /*http_response_code=*/0,
         mojom::WebUIAttributionReport::Status::kPending));
@@ -156,7 +156,7 @@
 }
 
 void AttributionInternalsHandlerImpl::SendReports(
-    const std::vector<EventAttributionReport::Id>& ids,
+    const std::vector<AttributionReport::Id>& ids,
     mojom::AttributionInternalsHandler::SendReportsCallback callback) {
   if (AttributionManager* manager =
           manager_provider_->GetManager(web_ui_->GetWebContents())) {
@@ -214,7 +214,7 @@
 }
 
 void AttributionInternalsHandlerImpl::OnReportSent(
-    const EventAttributionReport& report,
+    const AttributionReport& report,
     const SendResult& info) {
   mojom::WebUIAttributionReport::Status status;
   switch (info.status) {
diff --git a/content/browser/attribution_reporting/attribution_internals_handler_impl.h b/content/browser/attribution_reporting/attribution_internals_handler_impl.h
index 221e621..ede336f 100644
--- a/content/browser/attribution_reporting/attribution_internals_handler_impl.h
+++ b/content/browser/attribution_reporting/attribution_internals_handler_impl.h
@@ -9,8 +9,8 @@
 #include "base/scoped_observation.h"
 #include "content/browser/attribution_reporting/attribution_internals.mojom.h"
 #include "content/browser/attribution_reporting/attribution_manager.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_storage.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
@@ -50,7 +50,7 @@
       override;
   void GetReports(
       mojom::AttributionInternalsHandler::GetReportsCallback callback) override;
-  void SendReports(const std::vector<EventAttributionReport::Id>& ids,
+  void SendReports(const std::vector<AttributionReport::Id>& ids,
                    mojom::AttributionInternalsHandler::SendReportsCallback
                        callback) override;
   void ClearStorage(mojom::AttributionInternalsHandler::ClearStorageCallback
@@ -69,7 +69,7 @@
   void OnReportsChanged() override;
   void OnSourceDeactivated(
       const AttributionStorage::DeactivatedSource& deactivated_source) override;
-  void OnReportSent(const EventAttributionReport& report,
+  void OnReportSent(const AttributionReport& report,
                     const SendResult& info) override;
   void OnReportDropped(
       const AttributionStorage::CreateReportResult& result) override;
diff --git a/content/browser/attribution_reporting/attribution_internals_mojom_traits.cc b/content/browser/attribution_reporting/attribution_internals_mojom_traits.cc
index cbc8265..0686575 100644
--- a/content/browser/attribution_reporting/attribution_internals_mojom_traits.cc
+++ b/content/browser/attribution_reporting/attribution_internals_mojom_traits.cc
@@ -8,10 +8,10 @@
 
 // static
 bool StructTraits<content::mojom::AttributionReportIDDataView,
-                  content::EventAttributionReport::Id>::
+                  content::AttributionReport::Id>::
     Read(content::mojom::AttributionReportIDDataView data,
-         content::EventAttributionReport::Id* out) {
-  *out = content::EventAttributionReport::Id(data.value());
+         content::AttributionReport::Id* out) {
+  *out = content::AttributionReport::Id(data.value());
   return true;
 }
 
diff --git a/content/browser/attribution_reporting/attribution_internals_mojom_traits.h b/content/browser/attribution_reporting/attribution_internals_mojom_traits.h
index 2d3187a8..8444c20 100644
--- a/content/browser/attribution_reporting/attribution_internals_mojom_traits.h
+++ b/content/browser/attribution_reporting/attribution_internals_mojom_traits.h
@@ -8,7 +8,7 @@
 #include <stdint.h>
 
 #include "content/browser/attribution_reporting/attribution_internals.mojom.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/storable_source.h"
 #include "mojo/public/cpp/bindings/enum_traits.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
@@ -46,14 +46,12 @@
 
 template <>
 class StructTraits<content::mojom::AttributionReportIDDataView,
-                   content::EventAttributionReport::Id> {
+                   content::AttributionReport::Id> {
  public:
-  static int64_t value(const content::EventAttributionReport::Id& id) {
-    return *id;
-  }
+  static int64_t value(const content::AttributionReport::Id& id) { return *id; }
 
   static bool Read(content::mojom::AttributionReportIDDataView data,
-                   content::EventAttributionReport::Id* out);
+                   content::AttributionReport::Id* out);
 };
 
 }  // namespace mojo
diff --git a/content/browser/attribution_reporting/attribution_manager.h b/content/browser/attribution_reporting/attribution_manager.h
index 5eb787768..ed4d57f3 100644
--- a/content/browser/attribution_reporting/attribution_manager.h
+++ b/content/browser/attribution_reporting/attribution_manager.h
@@ -10,8 +10,8 @@
 #include "base/callback_forward.h"
 #include "base/compiler_specific.h"
 #include "base/observer_list_types.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_storage.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
 
 namespace base {
 class Time;
@@ -59,7 +59,7 @@
     virtual void OnSourceDeactivated(
         const AttributionStorage::DeactivatedSource& source) {}
 
-    virtual void OnReportSent(const EventAttributionReport& report,
+    virtual void OnReportSent(const AttributionReport& report,
                               const SendResult& info) {}
 
     virtual void OnReportDropped(
@@ -88,13 +88,12 @@
   // Get all pending reports that are currently stored in this partition. Used
   // for populating WebUI.
   virtual void GetPendingReportsForWebUI(
-      base::OnceCallback<void(std::vector<EventAttributionReport>)>
-          callback) = 0;
+      base::OnceCallback<void(std::vector<AttributionReport>)> callback) = 0;
 
   // Sends the given reports immediately, and runs |done| once they have all
   // been sent.
   virtual void SendReportsForWebUI(
-      const std::vector<EventAttributionReport::Id>& ids,
+      const std::vector<AttributionReport::Id>& ids,
       base::OnceClosure done) = 0;
 
   // Returns the AttributionPolicy that is used to control API policies such
diff --git a/content/browser/attribution_reporting/attribution_manager_impl.cc b/content/browser/attribution_reporting/attribution_manager_impl.cc
index 14c773d..8fa06589 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_manager_impl.cc
@@ -18,10 +18,10 @@
 #include "base/time/time.h"
 #include "content/browser/attribution_reporting/attribution_network_sender_impl.h"
 #include "content/browser/attribution_reporting/attribution_policy.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_storage_delegate_impl.h"
 #include "content/browser/attribution_reporting/attribution_storage_sql.h"
 #include "content/browser/attribution_reporting/attribution_utils.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
 #include "content/browser/attribution_reporting/send_result.h"
 #include "content/browser/attribution_reporting/storable_source.h"
 #include "content/browser/attribution_reporting/storable_trigger.h"
@@ -102,8 +102,7 @@
 }
 
 // Called when |report| is to be sent over network, for logging metrics.
-void LogMetricsOnReportSend(const EventAttributionReport& report,
-                            base::Time now) {
+void LogMetricsOnReportSend(const AttributionReport& report, base::Time now) {
   // Use a large time range to capture users that might not open the browser for
   // a long time while a conversion report is pending. Revisit this range if it
   // is non-ideal for real world data.
@@ -120,6 +119,10 @@
                             time_from_conversion_to_report_send.InHours());
 }
 
+bool IsOffline() {
+  return content::GetNetworkConnectionTracker()->IsOffline();
+}
+
 }  // namespace
 
 AttributionManager* AttributionManagerProviderImpl::GetManager(
@@ -140,7 +143,6 @@
     scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy)
     : AttributionManagerImpl(
           storage_partition,
-          content::GetNetworkConnectionTracker(),
           user_data_directory,
           std::make_unique<AttributionPolicy>(
               base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -149,13 +151,11 @@
 
 AttributionManagerImpl::AttributionManagerImpl(
     StoragePartitionImpl* storage_partition,
-    network::NetworkConnectionTracker* network_connection_tracker,
     const base::FilePath& user_data_directory,
     std::unique_ptr<AttributionPolicy> policy,
     scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
     std::unique_ptr<NetworkSender> network_sender)
     : storage_partition_(storage_partition),
-      network_connection_tracker_(network_connection_tracker),
       attribution_storage_(base::SequenceBound<AttributionStorageSql>(
           g_storage_task_runner.Get(),
           user_data_directory,
@@ -170,17 +170,16 @@
                                 storage_partition)),
       weak_factory_(this) {
   DCHECK(storage_partition_);
-  DCHECK(network_connection_tracker_);
   DCHECK(attribution_policy_);
   DCHECK(network_sender_);
 
-  network_connection_tracker_->AddNetworkConnectionObserver(this);
+  content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
 
   OnConnectionChanged(network::mojom::ConnectionType::CONNECTION_UNKNOWN);
 }
 
 AttributionManagerImpl::~AttributionManagerImpl() {
-  network_connection_tracker_->RemoveNetworkConnectionObserver(this);
+  content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(this);
 
   // Browser contexts are not required to have a special storage policy.
   if (!special_storage_policy_ ||
@@ -283,13 +282,13 @@
 }
 
 void AttributionManagerImpl::GetPendingReportsForWebUI(
-    base::OnceCallback<void(std::vector<EventAttributionReport>)> callback) {
+    base::OnceCallback<void(std::vector<AttributionReport>)> callback) {
   GetAndHandleReports(std::move(callback),
                       /*max_report_time=*/base::Time::Max(), /*limit=*/1000);
 }
 
 void AttributionManagerImpl::SendReportsForWebUI(
-    const std::vector<EventAttributionReport::Id>& ids,
+    const std::vector<AttributionReport::Id>& ids,
     base::OnceClosure done) {
   attribution_storage_.AsyncCall(&AttributionStorage::GetReports)
       .WithArgs(ids)
@@ -324,7 +323,7 @@
 
 void AttributionManagerImpl::OnConnectionChanged(
     network::mojom::ConnectionType connection_type) {
-  if (network_connection_tracker_->IsOffline()) {
+  if (IsOffline()) {
     get_reports_to_send_timer_.Stop();
   } else if (absl::optional<AttributionPolicy::OfflineReportDelayConfig> delay =
                  attribution_policy_->GetOfflineReportDelayConfig()) {
@@ -354,7 +353,7 @@
 
 void AttributionManagerImpl::UpdateGetReportsToSendTimer(
     absl::optional<base::Time> time) {
-  if (!time.has_value() || network_connection_tracker_->IsOffline())
+  if (!time.has_value() || IsOffline())
     return;
 
   if (!get_reports_to_send_timer_.IsRunning() ||
@@ -365,7 +364,7 @@
 }
 
 void AttributionManagerImpl::StartGetReportsToSendTimer() {
-  if (network_connection_tracker_->IsOffline())
+  if (IsOffline())
     return;
 
   attribution_storage_.AsyncCall(&AttributionStorage::GetNextReportTime)
@@ -375,7 +374,7 @@
 }
 
 void AttributionManagerImpl::GetReportsToSend() {
-  DCHECK(!network_connection_tracker_->IsOffline());
+  DCHECK(!IsOffline());
 
   // We only get the next report time strictly after now, because if we are
   // sending a report now but haven't finished doing so and it is still present
@@ -392,8 +391,8 @@
 }
 
 void AttributionManagerImpl::OnGetReportsToSend(
-    std::vector<EventAttributionReport> reports) {
-  if (reports.empty() || network_connection_tracker_->IsOffline())
+    std::vector<AttributionReport> reports) {
+  if (reports.empty() || IsOffline())
     return;
 
   // Shuffle new reports to provide plausible deniability on the ordering of
@@ -409,14 +408,14 @@
 
 void AttributionManagerImpl::OnGetReportsToSendFromWebUI(
     base::OnceClosure done,
-    std::vector<EventAttributionReport> reports) {
-  if (reports.empty() || network_connection_tracker_->IsOffline()) {
+    std::vector<AttributionReport> reports) {
+  if (reports.empty() || IsOffline()) {
     std::move(done).Run();
     return;
   }
 
   base::Time now = base::Time::Now();
-  for (EventAttributionReport& report : reports) {
+  for (AttributionReport& report : reports) {
     report.set_report_time(now);
   }
 
@@ -424,12 +423,11 @@
   SendReports(std::move(reports), /*log_metrics=*/false, std::move(barrier));
 }
 
-void AttributionManagerImpl::SendReports(
-    std::vector<EventAttributionReport> reports,
-    bool log_metrics,
-    base::RepeatingClosure done) {
+void AttributionManagerImpl::SendReports(std::vector<AttributionReport> reports,
+                                         bool log_metrics,
+                                         base::RepeatingClosure done) {
   const base::Time now = base::Time::Now();
-  for (EventAttributionReport& report : reports) {
+  for (AttributionReport& report : reports) {
     DCHECK(report.report_id().has_value());
     DCHECK_LE(report.report_time(), now);
 
@@ -470,13 +468,13 @@
 }
 
 void AttributionManagerImpl::MarkReportCompleted(
-    EventAttributionReport::Id report_id) {
+    AttributionReport::Id report_id) {
   size_t num_removed = reports_being_sent_.erase(report_id);
   DCHECK_EQ(num_removed, 1u);
 }
 
 void AttributionManagerImpl::OnReportSent(base::OnceClosure done,
-                                          EventAttributionReport report,
+                                          AttributionReport report,
                                           SendResult info) {
   DCHECK(report.report_id().has_value());
 
@@ -507,7 +505,7 @@
         .Then(base::BindOnce(
             [](base::OnceClosure done,
                base::WeakPtr<AttributionManagerImpl> manager,
-               EventAttributionReport::Id report_id, base::Time new_report_time,
+               AttributionReport::Id report_id, base::Time new_report_time,
                bool success) {
               std::move(done).Run();
 
@@ -526,7 +524,7 @@
         .Then(base::BindOnce(
             [](base::OnceClosure done,
                base::WeakPtr<AttributionManagerImpl> manager,
-               EventAttributionReport::Id report_id, bool success) {
+               AttributionReport::Id report_id, bool success) {
               std::move(done).Run();
               RecordDeleteEvent(success ? DeleteEvent::kSucceeded
                                         : DeleteEvent::kFailed);
@@ -539,7 +537,7 @@
             std::move(done), weak_factory_.GetWeakPtr(), *report.report_id()));
 
     base::UmaHistogramEnumeration(
-        "Conversion.ReportSendOutcome",
+        "Conversions.ReportSendOutcome",
         ConvertToConversionReportSendOutcome(info.status));
   }
 
diff --git a/content/browser/attribution_reporting/attribution_manager_impl.h b/content/browser/attribution_reporting/attribution_manager_impl.h
index a98245d..3b515c4 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl.h
+++ b/content/browser/attribution_reporting/attribution_manager_impl.h
@@ -19,8 +19,8 @@
 #include "base/threading/sequence_bound.h"
 #include "base/timer/wall_clock_timer.h"
 #include "content/browser/attribution_reporting/attribution_manager.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_storage.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
 #include "content/common/content_export.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
 #include "storage/browser/quota/special_storage_policy.h"
@@ -110,9 +110,9 @@
   void GetActiveSourcesForWebUI(
       base::OnceCallback<void(std::vector<StorableSource>)> callback) override;
   void GetPendingReportsForWebUI(
-      base::OnceCallback<void(std::vector<EventAttributionReport>)> callback)
+      base::OnceCallback<void(std::vector<AttributionReport>)> callback)
       override;
-  void SendReportsForWebUI(const std::vector<EventAttributionReport::Id>& ids,
+  void SendReportsForWebUI(const std::vector<AttributionReport::Id>& ids,
                            base::OnceClosure done) override;
   const AttributionPolicy& GetAttributionPolicy() const override;
   void ClearData(base::Time delete_begin,
@@ -125,7 +125,6 @@
 
   AttributionManagerImpl(
       StoragePartitionImpl* storage_partition,
-      network::NetworkConnectionTracker* network_connection_tracker,
       const base::FilePath& user_data_directory,
       std::unique_ptr<AttributionPolicy> policy,
       scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
@@ -139,7 +138,7 @@
   // |max_report_time|, and calls |handler_function| on them; use a negative
   // number for no limit.
   using ReportsHandlerFunc =
-      base::OnceCallback<void(std::vector<EventAttributionReport>)>;
+      base::OnceCallback<void(std::vector<AttributionReport>)>;
   void GetAndHandleReports(ReportsHandlerFunc handler_function,
                            base::Time max_report_time,
                            int limit);
@@ -147,18 +146,18 @@
   void UpdateGetReportsToSendTimer(absl::optional<base::Time> time);
   void StartGetReportsToSendTimer();
   void GetReportsToSend();
-  void OnGetReportsToSend(std::vector<EventAttributionReport> reports);
+  void OnGetReportsToSend(std::vector<AttributionReport> reports);
 
   void OnGetReportsToSendFromWebUI(base::OnceClosure done,
-                                   std::vector<EventAttributionReport> reports);
+                                   std::vector<AttributionReport> reports);
 
-  void SendReports(std::vector<EventAttributionReport> reports,
+  void SendReports(std::vector<AttributionReport> reports,
                    bool log_metrics,
                    base::RepeatingClosure done);
   void OnReportSent(base::OnceClosure done,
-                    EventAttributionReport report,
+                    AttributionReport report,
                     SendResult info);
-  void MarkReportCompleted(EventAttributionReport::Id report_id);
+  void MarkReportCompleted(AttributionReport::Id report_id);
 
   void OnReportStored(AttributionStorage::CreateReportResult result);
 
@@ -171,14 +170,12 @@
   void HandleTriggerInternal(StorableTrigger trigger);
 
   // Friend to expose the AttributionStorage for certain tests.
-  friend std::vector<EventAttributionReport> GetAttributionsToReportForTesting(
+  friend std::vector<AttributionReport> GetAttributionsToReportForTesting(
       AttributionManagerImpl* manager,
       base::Time max_report_time);
 
   raw_ptr<StoragePartitionImpl> storage_partition_;
 
-  raw_ptr<network::NetworkConnectionTracker> network_connection_tracker_;
-
   base::SequenceBound<AttributionStorage> attribution_storage_;
 
   // Policy used for controlling API configurations such as reporting and
@@ -195,7 +192,7 @@
   // Set of all conversion IDs that are currently being sent, deleted, or
   // updated. The number of concurrent conversion reports being sent at any time
   // is expected to be small, so a `flat_set` is used.
-  base::flat_set<EventAttributionReport::Id> reports_being_sent_;
+  base::flat_set<AttributionReport::Id> reports_being_sent_;
 
   base::ObserverList<Observer> observers_;
 
diff --git a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
index 8b0e4b73..3dd04a1 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
@@ -24,14 +24,15 @@
 #include "base/test/mock_callback.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_storage.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
 #include "content/browser/attribution_reporting/send_result.h"
 #include "content/browser/attribution_reporting/storable_source.h"
 #include "content/browser/attribution_reporting/storable_trigger.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/network_service_instance.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_utils.h"
@@ -93,7 +94,7 @@
 
   MOCK_METHOD(void,
               OnReportSent,
-              (const EventAttributionReport& report, const SendResult& info),
+              (const AttributionReport& report, const SendResult& info),
               (override));
 
   MOCK_METHOD(void,
@@ -163,6 +164,10 @@
             base::MakeRefCounted<storage::MockSpecialStoragePolicy>()),
         network_sender_(new MockNetworkSender()) {
     EXPECT_TRUE(dir_.CreateUniqueTempDir());
+
+    content::SetNetworkConnectionTrackerForTesting(
+        network::TestNetworkConnectionTracker::GetInstance());
+
     CreateManager();
   }
 
@@ -170,8 +175,7 @@
     attribution_manager_ = absl::WrapUnique(new AttributionManagerImpl(
         static_cast<StoragePartitionImpl*>(
             browser_context_->GetDefaultStoragePartition()),
-        network::TestNetworkConnectionTracker::GetInstance(), dir_.GetPath(),
-        std::make_unique<ConstantOfflineReportDelayPolicy>(),
+        dir_.GetPath(), std::make_unique<ConstantOfflineReportDelayPolicy>(),
         mock_storage_policy_, absl::WrapUnique(network_sender_.get())));
   }
 
@@ -196,11 +200,11 @@
     return result;
   }
 
-  std::vector<EventAttributionReport> StoredReports() {
-    std::vector<EventAttributionReport> result;
+  std::vector<AttributionReport> StoredReports() {
+    std::vector<AttributionReport> result;
     base::RunLoop loop;
-    attribution_manager_->GetPendingReportsForWebUI(base::BindLambdaForTesting(
-        [&](std::vector<EventAttributionReport> reports) {
+    attribution_manager_->GetPendingReportsForWebUI(
+        base::BindLambdaForTesting([&](std::vector<AttributionReport> reports) {
           result = std::move(reports);
           loop.Quit();
         }));
@@ -259,7 +263,7 @@
   auto conversion = DefaultTrigger();
   attribution_manager_->HandleTrigger(conversion);
 
-  EventAttributionReport expected_report =
+  AttributionReport expected_report =
       ReportBuilder(impression)
           .SetTriggerData(conversion.trigger_data())
           .SetTriggerTime(base::Time::Now())
@@ -268,7 +272,7 @@
 
   // The external report ID is randomly generated by the storage delegate,
   // so zero it out here to avoid flakiness.
-  std::vector<EventAttributionReport> reports = StoredReports();
+  std::vector<AttributionReport> reports = StoredReports();
   for (auto& report : reports) {
     report.SetExternalReportIdForTesting(DefaultExternalReportID());
   }
@@ -420,7 +424,7 @@
       {SendResult::Status::kTransientFailure});
 
   // kFailed = 1.
-  histograms.ExpectUniqueSample("Conversion.ReportSendOutcome", 1, 1);
+  histograms.ExpectUniqueSample("Conversions.ReportSendOutcome", 1, 1);
 }
 
 TEST_F(AttributionManagerImplTest, RetryLogicOverridesGetReportTimer) {
@@ -486,7 +490,7 @@
   EXPECT_THAT(StoredReports(), IsEmpty());
 
   // kFailed = 1.
-  histograms.ExpectUniqueSample("Conversion.ReportSendOutcome", 1, 1);
+  histograms.ExpectUniqueSample("Conversions.ReportSendOutcome", 1, 1);
 }
 
 TEST_F(AttributionManagerImplTest, QueuedReportAlwaysFails_StopsSending) {
@@ -523,7 +527,7 @@
   EXPECT_THAT(StoredReports(), IsEmpty());
 
   // kFailed = 1.
-  histograms.ExpectUniqueSample("Conversion.ReportSendOutcome", 1, 1);
+  histograms.ExpectUniqueSample("Conversions.ReportSendOutcome", 1, 1);
 }
 
 TEST_F(AttributionManagerImplTest, ReportExpiredAtStartup_Sent) {
@@ -557,7 +561,7 @@
   EXPECT_THAT(network_sender_->calls(), IsEmpty());
 
   // kSent = 0.
-  histograms.ExpectUniqueSample("Conversion.ReportSendOutcome", 0, 1);
+  histograms.ExpectUniqueSample("Conversions.ReportSendOutcome", 0, 1);
 }
 
 TEST_F(AttributionManagerImplTest, QueuedReportSent_ObserversNotified) {
@@ -570,17 +574,17 @@
 
   EXPECT_CALL(
       observer,
-      OnReportSent(Property(&EventAttributionReport::source,
+      OnReportSent(Property(&AttributionReport::source,
                             Property(&StorableSource::source_event_id, 1u)),
                    _));
   EXPECT_CALL(
       observer,
-      OnReportSent(Property(&EventAttributionReport::source,
+      OnReportSent(Property(&AttributionReport::source,
                             Property(&StorableSource::source_event_id, 2u)),
                    _));
   EXPECT_CALL(
       observer,
-      OnReportSent(Property(&EventAttributionReport::source,
+      OnReportSent(Property(&AttributionReport::source,
                             Property(&StorableSource::source_event_id, 3u)),
                    _));
 
@@ -612,11 +616,11 @@
        SendResult::Status::kSent, SendResult::Status::kTransientFailure});
 
   // kSent = 0.
-  histograms.ExpectBucketCount("Conversion.ReportSendOutcome", 0, 2);
+  histograms.ExpectBucketCount("Conversions.ReportSendOutcome", 0, 2);
   // kFailed = 1.
-  histograms.ExpectBucketCount("Conversion.ReportSendOutcome", 1, 0);
+  histograms.ExpectBucketCount("Conversions.ReportSendOutcome", 1, 0);
   // kDropped = 2.
-  histograms.ExpectBucketCount("Conversion.ReportSendOutcome", 2, 1);
+  histograms.ExpectBucketCount("Conversions.ReportSendOutcome", 2, 1);
 }
 
 TEST_F(AttributionManagerImplTest, DroppedReport_ObserversNotified) {
@@ -635,11 +639,11 @@
 
     EXPECT_CALL(
         observer,
-        OnReportDropped(AllOf(
-            Property(&CreateReportResult::dropped_report,
-                     Optional(Property(&EventAttributionReport::priority, 1))),
-            Property(&CreateReportResult::status,
-                     CreateReportStatus::kSuccessDroppedLowerPriority))));
+        OnReportDropped(
+            AllOf(Property(&CreateReportResult::dropped_report,
+                           Optional(Property(&AttributionReport::priority, 1))),
+                  Property(&CreateReportResult::status,
+                           CreateReportStatus::kSuccessDroppedLowerPriority))));
 
     EXPECT_CALL(checkpoint, Call(2));
 
@@ -647,7 +651,7 @@
         observer,
         OnReportDropped(AllOf(
             Property(&CreateReportResult::dropped_report,
-                     Optional(Property(&EventAttributionReport::priority, -5))),
+                     Optional(Property(&AttributionReport::priority, -5))),
             Property(&CreateReportResult::status,
                      CreateReportStatus::kPriorityTooLow))));
 
@@ -655,18 +659,18 @@
 
     EXPECT_CALL(
         observer,
-        OnReportDropped(AllOf(
-            Property(&CreateReportResult::dropped_report,
-                     Optional(Property(&EventAttributionReport::priority, 2))),
-            Property(&CreateReportResult::status,
-                     CreateReportStatus::kSuccessDroppedLowerPriority))));
+        OnReportDropped(
+            AllOf(Property(&CreateReportResult::dropped_report,
+                           Optional(Property(&AttributionReport::priority, 2))),
+                  Property(&CreateReportResult::status,
+                           CreateReportStatus::kSuccessDroppedLowerPriority))));
     EXPECT_CALL(
         observer,
-        OnReportDropped(AllOf(
-            Property(&CreateReportResult::dropped_report,
-                     Optional(Property(&EventAttributionReport::priority, 3))),
-            Property(&CreateReportResult::status,
-                     CreateReportStatus::kSuccessDroppedLowerPriority))));
+        OnReportDropped(
+            AllOf(Property(&CreateReportResult::dropped_report,
+                           Optional(Property(&AttributionReport::priority, 3))),
+                  Property(&CreateReportResult::status,
+                           CreateReportStatus::kSuccessDroppedLowerPriority))));
   }
 
   attribution_manager_->HandleSource(
@@ -738,7 +742,7 @@
   attribution_manager_->HandleSource(
       SourceBuilder().SetExpiry(kImpressionExpiry).Build());
   attribution_manager_->HandleTrigger(DefaultTrigger());
-  std::vector<EventAttributionReport> reports = StoredReports();
+  std::vector<AttributionReport> reports = StoredReports();
   EXPECT_THAT(reports, SizeIs(1));
   EXPECT_THAT(network_sender_->calls(), IsEmpty());
 
@@ -756,7 +760,7 @@
       SourceBuilder().SetExpiry(kImpressionExpiry).Build());
   attribution_manager_->HandleTrigger(DefaultTrigger());
   attribution_manager_->HandleTrigger(DefaultTrigger());
-  std::vector<EventAttributionReport> reports = StoredReports();
+  std::vector<AttributionReport> reports = StoredReports();
   EXPECT_THAT(reports, SizeIs(2));
   EXPECT_THAT(network_sender_->calls(), IsEmpty());
 
@@ -795,7 +799,7 @@
   // started.
   EXPECT_THAT(
       StoredReports(),
-      ElementsAre(Property(&EventAttributionReport::report_time,
+      ElementsAre(Property(&AttributionReport::report_time,
                            start_time + kFirstReportingWindow +
                                base::Milliseconds(1) + kExpiredReportOffset)));
 
@@ -821,7 +825,7 @@
 
   // Ensure that this report does not receive additional delay.
   EXPECT_THAT(StoredReports(),
-              ElementsAre(Property(&EventAttributionReport::report_time,
+              ElementsAre(Property(&AttributionReport::report_time,
                                    start_time + kFirstReportingWindow)));
 
   EXPECT_THAT(network_sender_->calls(), IsEmpty());
@@ -1111,7 +1115,7 @@
   EXPECT_THAT(network_sender_->calls(), IsEmpty());
 
   // kDropped = 2.
-  histograms.ExpectBucketCount("Conversion.ReportSendOutcome", 2, 1);
+  histograms.ExpectBucketCount("Conversions.ReportSendOutcome", 2, 1);
 }
 
 TEST_F(AttributionManagerImplTest, Offline_NoReportSent) {
@@ -1169,7 +1173,7 @@
       SourceBuilder().SetExpiry(kImpressionExpiry).Build());
   attribution_manager_->HandleTrigger(DefaultTrigger());
 
-  attribution_manager_->SendReportsForWebUI({EventAttributionReport::Id(1)},
+  attribution_manager_->SendReportsForWebUI({AttributionReport::Id(1)},
                                             base::DoNothing());
   task_environment_.FastForwardBy(base::TimeDelta());
   EXPECT_THAT(network_sender_->calls(), SizeIs(1));
diff --git a/content/browser/attribution_reporting/attribution_network_sender_impl_unittest.cc b/content/browser/attribution_reporting/attribution_network_sender_impl_unittest.cc
index 66414299..de6a54d 100644
--- a/content/browser/attribution_reporting/attribution_network_sender_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_network_sender_impl_unittest.cc
@@ -45,7 +45,7 @@
 const char kReportUrl[] =
     "https://report.test/.well-known/attribution-reporting/report-attribution";
 
-EventAttributionReport DefaultReport() {
+AttributionReport DefaultReport() {
   return ReportBuilder(SourceBuilder(base::Time()).Build()).Build();
 }
 
@@ -147,7 +147,7 @@
                           .SetSourceEventId(100)
                           .SetSourceType(test_case.source_type)
                           .Build();
-    EventAttributionReport report =
+    AttributionReport report =
         ReportBuilder(impression).SetTriggerData(5).Build();
     network_sender_->SendReport(report.ReportURL(), report.ReportBody(),
                                 base::DoNothing());
@@ -168,7 +168,7 @@
           .SetReportingOrigin(url::Origin::Create(GURL("https://a.com")))
           .SetConversionOrigin(url::Origin::Create(GURL("https://sub.b.com")))
           .Build();
-  EventAttributionReport report = ReportBuilder(impression).Build();
+  AttributionReport report = ReportBuilder(impression).Build();
   network_sender_->SendReport(report.ReportURL(), report.ReportBody(),
                               base::DoNothing());
 
diff --git a/content/browser/attribution_reporting/attribution_report.cc b/content/browser/attribution_reporting/attribution_report.cc
new file mode 100644
index 0000000..3143fca3
--- /dev/null
+++ b/content/browser/attribution_reporting/attribution_report.cc
@@ -0,0 +1,106 @@
+// 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.
+
+#include "content/browser/attribution_reporting/attribution_report.h"
+
+#include <utility>
+
+#include "base/check.h"
+#include "base/json/json_writer.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "net/base/schemeful_site.h"
+#include "url/gurl.h"
+#include "url/url_canon.h"
+
+namespace content {
+
+AttributionReport::AttributionReport(StorableSource source,
+                                     uint64_t trigger_data,
+                                     base::Time trigger_time,
+                                     base::Time report_time,
+                                     int64_t priority,
+                                     base::GUID external_report_id,
+                                     absl::optional<Id> report_id)
+    : source_(std::move(source)),
+      trigger_data_(trigger_data),
+      trigger_time_(trigger_time),
+      report_time_(report_time),
+      priority_(priority),
+      external_report_id_(std::move(external_report_id)),
+      report_id_(report_id) {
+  DCHECK(external_report_id_.is_valid());
+}
+
+AttributionReport::AttributionReport(const AttributionReport& other) = default;
+
+AttributionReport& AttributionReport::operator=(
+    const AttributionReport& other) = default;
+
+AttributionReport::AttributionReport(AttributionReport&& other) = default;
+
+AttributionReport& AttributionReport::operator=(AttributionReport&& other) =
+    default;
+
+AttributionReport::~AttributionReport() = default;
+
+GURL AttributionReport::ReportURL() const {
+  url::Replacements<char> replacements;
+  static constexpr char kEndpointPath[] =
+      "/.well-known/attribution-reporting/report-attribution";
+  replacements.SetPath(kEndpointPath, url::Component(0, strlen(kEndpointPath)));
+  return source_.reporting_origin().GetURL().ReplaceComponents(replacements);
+}
+
+std::string AttributionReport::ReportBody(bool pretty_print) const {
+  base::Value dict(base::Value::Type::DICTIONARY);
+
+  dict.SetStringKey("attribution_destination",
+                    source_.ConversionDestination().Serialize());
+
+  // The API denotes these values as strings; a `uint64_t` cannot be put in
+  // a dict as an integer in order to be opaque to various API configurations.
+  dict.SetStringKey("source_event_id",
+                    base::NumberToString(source_.source_event_id()));
+
+  dict.SetStringKey("trigger_data", base::NumberToString(trigger_data_));
+
+  const char* source_type = nullptr;
+  switch (source_.source_type()) {
+    case StorableSource::SourceType::kNavigation:
+      source_type = "navigation";
+      break;
+    case StorableSource::SourceType::kEvent:
+      source_type = "event";
+      break;
+  }
+  dict.SetStringKey("source_type", source_type);
+
+  dict.SetStringKey("report_id", external_report_id_.AsLowercaseString());
+
+  // Write the dict to json;
+  std::string output_json;
+  bool success = base::JSONWriter::WriteWithOptions(
+      dict, pretty_print ? base::JSONWriter::OPTIONS_PRETTY_PRINT : 0,
+      &output_json);
+  DCHECK(success);
+  return output_json;
+}
+
+void AttributionReport::set_report_time(base::Time report_time) {
+  report_time_ = report_time;
+}
+
+void AttributionReport::set_failed_send_attempts(int failed_send_attempts) {
+  DCHECK_GE(failed_send_attempts, 0);
+  failed_send_attempts_ = failed_send_attempts;
+}
+
+void AttributionReport::SetExternalReportIdForTesting(
+    base::GUID external_report_id) {
+  DCHECK(external_report_id.is_valid());
+  external_report_id_ = std::move(external_report_id);
+}
+
+}  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_report.h b/content/browser/attribution_reporting/attribution_report.h
new file mode 100644
index 0000000..02adb55
--- /dev/null
+++ b/content/browser/attribution_reporting/attribution_report.h
@@ -0,0 +1,107 @@
+// 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 CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_REPORT_H_
+#define CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_REPORT_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/guid.h"
+#include "base/time/time.h"
+#include "base/types/strong_alias.h"
+#include "content/browser/attribution_reporting/storable_source.h"
+#include "content/common/content_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+class GURL;
+
+namespace content {
+
+// Class that contains all the data needed to serialize and send a conversion
+// report. This represents the report for a conversion event and its associated
+// source.
+class CONTENT_EXPORT AttributionReport {
+ public:
+  using Id = base::StrongAlias<AttributionReport, int64_t>;
+
+  // The conversion_id may not be set for a conversion report.
+  AttributionReport(StorableSource source,
+                    uint64_t trigger_data,
+                    base::Time trigger_time,
+                    base::Time report_time,
+                    int64_t priority,
+                    base::GUID external_report_id,
+                    absl::optional<Id> report_id);
+  AttributionReport(const AttributionReport& other);
+  AttributionReport& operator=(const AttributionReport& other);
+  AttributionReport(AttributionReport&& other);
+  AttributionReport& operator=(AttributionReport&& other);
+  ~AttributionReport();
+
+  // Returns the URL to which the report will be sent.
+  GURL ReportURL() const WARN_UNUSED_RESULT;
+
+  // Returns the JSON for the report body.
+  std::string ReportBody(bool pretty_print = false) const WARN_UNUSED_RESULT;
+
+  const StorableSource& source() const { return source_; }
+
+  uint64_t trigger_data() const { return trigger_data_; }
+
+  base::Time trigger_time() const { return trigger_time_; }
+
+  base::Time report_time() const { return report_time_; }
+
+  int64_t priority() const { return priority_; }
+
+  const base::GUID& external_report_id() const { return external_report_id_; }
+
+  absl::optional<Id> report_id() const { return report_id_; }
+
+  int failed_send_attempts() const { return failed_send_attempts_; }
+
+  void set_report_time(base::Time report_time);
+
+  void set_failed_send_attempts(int failed_send_attempts);
+
+  void SetExternalReportIdForTesting(base::GUID external_report_id);
+
+ private:
+  // Source associated with this conversion report.
+  StorableSource source_;
+
+  // Data provided at trigger time by the attribution destination. Depending on
+  // the source type, this contains the associated data in the trigger redirect.
+  uint64_t trigger_data_;
+
+  // The time the trigger occurred.
+  base::Time trigger_time_;
+
+  // The time this conversion report should be sent.
+  base::Time report_time_;
+
+  // Priority specified in conversion redirect.
+  int64_t priority_;
+
+  // External report ID for deduplicating reports received by the reporting
+  // origin.
+  base::GUID external_report_id_;
+
+  // Id assigned by storage to uniquely identify a completed conversion. If
+  // null, an ID has not been assigned yet.
+  absl::optional<Id> report_id_;
+
+  // Number of times the browser has tried and failed to send this report.
+  int failed_send_attempts_ = 0;
+
+  // When adding new members, the corresponding `operator==()` definition in
+  // `attribution_test_utils.h` should also be updated.
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_ATTRIBUTION_REPORTING_ATTRIBUTION_REPORT_H_
diff --git a/content/browser/attribution_reporting/attribution_storage.cc b/content/browser/attribution_reporting/attribution_storage.cc
index a3da27c..027e696d 100644
--- a/content/browser/attribution_reporting/attribution_storage.cc
+++ b/content/browser/attribution_reporting/attribution_storage.cc
@@ -15,7 +15,7 @@
 
 CreateReportResult::CreateReportResult(
     Status status,
-    absl::optional<EventAttributionReport> dropped_report,
+    absl::optional<AttributionReport> dropped_report,
     absl::optional<DeactivatedSource::Reason>
         dropped_report_source_deactivation_reason,
     absl::optional<base::Time> report_time)
@@ -50,8 +50,8 @@
   return status_;
 }
 
-const absl::optional<EventAttributionReport>&
-CreateReportResult::dropped_report() const {
+const absl::optional<AttributionReport>& CreateReportResult::dropped_report()
+    const {
   return dropped_report_;
 }
 
diff --git a/content/browser/attribution_reporting/attribution_storage.h b/content/browser/attribution_reporting/attribution_storage.h
index 2fdf6ee..8ea8e45 100644
--- a/content/browser/attribution_reporting/attribution_storage.h
+++ b/content/browser/attribution_reporting/attribution_storage.h
@@ -10,7 +10,7 @@
 
 #include "base/callback_forward.h"
 #include "base/compiler_specific.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/storable_source.h"
 #include "content/common/content_export.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -169,7 +169,7 @@
 
     explicit CreateReportResult(
         Status status,
-        absl::optional<EventAttributionReport> dropped_report = absl::nullopt,
+        absl::optional<AttributionReport> dropped_report = absl::nullopt,
         absl::optional<DeactivatedSource::Reason>
             dropped_report_source_deactivation_reason = absl::nullopt,
         absl::optional<base::Time> report_time = absl::nullopt);
@@ -183,7 +183,7 @@
 
     Status status() const;
 
-    const absl::optional<EventAttributionReport>& dropped_report() const;
+    const absl::optional<AttributionReport>& dropped_report() const;
 
     absl::optional<base::Time> report_time() const;
 
@@ -194,7 +194,7 @@
 
     // Null unless `status` is `kSuccessDroppedLowerPriority`,
     // `kRateLimited`, `kPriorityTooLow`, or `kDroppedForNoise`.
-    absl::optional<EventAttributionReport> dropped_report_;
+    absl::optional<AttributionReport> dropped_report_;
 
     // Null unless `dropped_report_`'s source was deactivated.
     absl::optional<DeactivatedSource::Reason>
@@ -214,7 +214,7 @@
   // |max_report_time|. This call is logically const, and does not modify the
   // underlying storage. |limit| limits the number of reports to return; use
   // a negative number for no limit.
-  virtual std::vector<EventAttributionReport> GetAttributionsToReport(
+  virtual std::vector<AttributionReport> GetAttributionsToReport(
       base::Time max_report_time,
       int limit = -1) WARN_UNUSED_RESULT = 0;
 
@@ -224,9 +224,8 @@
 
   // Returns the reports with the given IDs. This call is logically const, and
   // does not modify the underlying storage.
-  virtual std::vector<EventAttributionReport> GetReports(
-      const std::vector<EventAttributionReport::Id>& ids)
-      WARN_UNUSED_RESULT = 0;
+  virtual std::vector<AttributionReport> GetReports(
+      const std::vector<AttributionReport::Id>& ids) WARN_UNUSED_RESULT = 0;
 
   // Returns all active sources in storage. Active sources are all
   // sources that can still convert. Sources that: are past expiry,
@@ -239,12 +238,12 @@
 
   // Deletes the report with the given |report_id|. Returns
   // false if an error occurred.
-  virtual bool DeleteReport(EventAttributionReport::Id report_id) = 0;
+  virtual bool DeleteReport(AttributionReport::Id report_id) = 0;
 
   // Updates the number of failures associated with the given report, and sets
   // its report time to the given value. Should be called after a transient
   // failure to send the report so that it is retried later.
-  virtual bool UpdateReportForSendFailure(EventAttributionReport::Id report_id,
+  virtual bool UpdateReportForSendFailure(AttributionReport::Id report_id,
                                           base::Time new_report_time) = 0;
 
   // Adjusts the report time of all reports that should have been sent while the
diff --git a/content/browser/attribution_reporting/attribution_storage_delegate_impl_unittest.cc b/content/browser/attribution_reporting/attribution_storage_delegate_impl_unittest.cc
index d631bd7..0306c73 100644
--- a/content/browser/attribution_reporting/attribution_storage_delegate_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_delegate_impl_unittest.cc
@@ -6,8 +6,8 @@
 
 #include "base/guid.h"
 #include "base/time/time.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
 #include "content/browser/attribution_reporting/storable_source.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -17,11 +17,11 @@
 
 constexpr base::TimeDelta kDefaultExpiry = base::Days(30);
 
-EventAttributionReport GetReport(base::Time impression_time,
-                                 base::Time trigger_time,
-                                 base::TimeDelta expiry = kDefaultExpiry,
-                                 StorableSource::SourceType source_type =
-                                     StorableSource::SourceType::kNavigation) {
+AttributionReport GetReport(base::Time impression_time,
+                            base::Time trigger_time,
+                            base::TimeDelta expiry = kDefaultExpiry,
+                            StorableSource::SourceType source_type =
+                                StorableSource::SourceType::kNavigation) {
   return ReportBuilder(SourceBuilder(impression_time)
                            .SetExpiry(expiry)
                            .SetSourceType(source_type)
@@ -34,7 +34,7 @@
 
 TEST(AttributionStorageDelegateImplTest, ImmediateConversion_FirstWindowUsed) {
   base::Time impression_time = base::Time::Now();
-  const EventAttributionReport report =
+  const AttributionReport report =
       GetReport(impression_time, /*trigger_time=*/impression_time);
   EXPECT_EQ(impression_time + base::Days(2),
             AttributionStorageDelegateImpl().GetReportTime(
@@ -45,8 +45,7 @@
      ConversionImmediatelyBeforeWindow_NextWindowUsed) {
   base::Time impression_time = base::Time::Now();
   base::Time trigger_time = impression_time + base::Days(2) - base::Minutes(1);
-  const EventAttributionReport report =
-      GetReport(impression_time, trigger_time);
+  const AttributionReport report = GetReport(impression_time, trigger_time);
   EXPECT_EQ(impression_time + base::Days(7),
             AttributionStorageDelegateImpl().GetReportTime(
                 report.source(), report.trigger_time()));
@@ -59,8 +58,7 @@
   // The deadline for a window is 1 hour before the window. Use a time just
   // before the deadline.
   base::Time trigger_time = impression_time + base::Days(2) - base::Minutes(61);
-  const EventAttributionReport report =
-      GetReport(impression_time, trigger_time);
+  const AttributionReport report = GetReport(impression_time, trigger_time);
   EXPECT_EQ(impression_time + base::Days(2),
             AttributionStorageDelegateImpl().GetReportTime(
                 report.source(), report.trigger_time()));
@@ -72,8 +70,8 @@
   base::Time trigger_time = impression_time + base::Hours(1);
 
   // Set the impression to expire before the two day window.
-  const EventAttributionReport report = GetReport(impression_time, trigger_time,
-                                                  /*expiry=*/base::Hours(2));
+  const AttributionReport report = GetReport(impression_time, trigger_time,
+                                             /*expiry=*/base::Hours(2));
   EXPECT_EQ(impression_time + base::Days(2),
             AttributionStorageDelegateImpl().GetReportTime(
                 report.source(), report.trigger_time()));
@@ -85,8 +83,8 @@
   base::Time trigger_time = impression_time + base::Days(3);
 
   // Set the impression to expire before the two day window.
-  const EventAttributionReport report = GetReport(impression_time, trigger_time,
-                                                  /*expiry=*/base::Days(4));
+  const AttributionReport report = GetReport(impression_time, trigger_time,
+                                             /*expiry=*/base::Days(4));
 
   // The expiry window is reported one hour after expiry time.
   EXPECT_EQ(impression_time + base::Days(4) + base::Hours(1),
@@ -100,8 +98,8 @@
   base::Time trigger_time = impression_time + base::Days(7);
 
   // Set the impression to expire before the two day window.
-  const EventAttributionReport report = GetReport(impression_time, trigger_time,
-                                                  /*expiry=*/base::Days(9));
+  const AttributionReport report = GetReport(impression_time, trigger_time,
+                                             /*expiry=*/base::Days(9));
 
   // The expiry window is reported one hour after expiry time.
   EXPECT_EQ(impression_time + base::Days(9) + base::Hours(1),
@@ -113,7 +111,7 @@
      SourceTypeEvent_ExpiryLessThanTwoDays_TwoDaysUsed) {
   base::Time impression_time = base::Time::Now();
   base::Time trigger_time = impression_time + base::Days(3);
-  const EventAttributionReport report =
+  const AttributionReport report =
       GetReport(impression_time, trigger_time,
                 /*expiry=*/base::Days(1), StorableSource::SourceType::kEvent);
   EXPECT_EQ(impression_time + base::Days(2) + base::Hours(1),
@@ -125,7 +123,7 @@
      SourceTypeEvent_ExpiryGreaterThanTwoDays_ExpiryUsed) {
   base::Time impression_time = base::Time::Now();
   base::Time trigger_time = impression_time + base::Days(3);
-  const EventAttributionReport report =
+  const AttributionReport report =
       GetReport(impression_time, trigger_time,
                 /*expiry=*/base::Days(4), StorableSource::SourceType::kEvent);
   EXPECT_EQ(impression_time + base::Days(4) + base::Hours(1),
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.cc b/content/browser/attribution_reporting/attribution_storage_sql.cc
index 5db1ed37..1a22031 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql.cc
@@ -21,8 +21,8 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/time/time.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_storage_sql_migrations.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
 #include "content/browser/attribution_reporting/sql_utils.h"
 #include "content/browser/attribution_reporting/storable_source.h"
 #include "content/browser/attribution_reporting/storable_trigger.h"
@@ -351,7 +351,7 @@
 
   for (auto& deactivated_source : deactivated_sources) {
     absl::optional<std::vector<int64_t>> dedup_keys =
-        ReadDedupKeys(*deactivated_source.source.impression_id());
+        ReadDedupKeys(*deactivated_source.source.source_id());
     if (!dedup_keys.has_value())
       return absl::nullopt;
     deactivated_source.source.SetDedupKeys(std::move(*dedup_keys));
@@ -462,13 +462,13 @@
     const base::Time report_time =
         delegate_->GetReportTime(source, trigger_time);
 
-    EventAttributionReport report(source, event_source_trigger_data,
-                                  /*trigger_time=*/trigger_time,
-                                  /*report_time=*/report_time,
-                                  /*priority=*/0,
-                                  /*external_report_id=*/
-                                  delegate_->NewReportID(),
-                                  /*report_id=*/absl::nullopt);
+    AttributionReport report(source, event_source_trigger_data,
+                             /*trigger_time=*/trigger_time,
+                             /*report_time=*/report_time,
+                             /*priority=*/0,
+                             /*external_report_id=*/
+                             delegate_->NewReportID(),
+                             /*report_id=*/absl::nullopt);
 
     if (!StoreReport(report, source_id))
       return {};
@@ -490,11 +490,11 @@
 // one should be stored.
 AttributionStorageSql::MaybeReplaceLowerPriorityReportResult
 AttributionStorageSql::MaybeReplaceLowerPriorityReport(
-    const EventAttributionReport& report,
+    const AttributionReport& report,
     int num_conversions,
     int64_t conversion_priority,
-    absl::optional<EventAttributionReport>& replaced_report) {
-  DCHECK(report.source().impression_id().has_value());
+    absl::optional<AttributionReport>& replaced_report) {
+  DCHECK(report.source().source_id().has_value());
   DCHECK_GE(num_conversions, 0);
 
   // If there's already capacity for the new report, there's nothing to do.
@@ -516,7 +516,7 @@
       "LIMIT 1";
   sql::Statement min_priority_statement(
       db_->GetCachedStatement(SQL_FROM_HERE, kMinPrioritySql));
-  min_priority_statement.BindInt64(0, *report.source().impression_id().value());
+  min_priority_statement.BindInt64(0, *report.source().source_id().value());
   min_priority_statement.BindTime(1, report.report_time());
 
   const bool has_matching_report = min_priority_statement.Step();
@@ -530,7 +530,7 @@
         "UPDATE impressions SET active = 0 WHERE impression_id = ?";
     sql::Statement deactivate_statement(
         db_->GetCachedStatement(SQL_FROM_HERE, kDeactivateSql));
-    deactivate_statement.BindInt64(0, *report.source().impression_id().value());
+    deactivate_statement.BindInt64(0, *report.source().source_id().value());
     return deactivate_statement.Run()
                ? MaybeReplaceLowerPriorityReportResult::
                      kDropNewReportSourceDeactivated
@@ -538,7 +538,7 @@
   }
 
   int64_t min_priority = min_priority_statement.ColumnInt64(0);
-  EventAttributionReport::Id conversion_id_with_min_priority(
+  AttributionReport::Id conversion_id_with_min_priority(
       min_priority_statement.ColumnInt64(1));
 
   // If the new report's priority is less than all existing ones, or if its
@@ -550,7 +550,7 @@
     return MaybeReplaceLowerPriorityReportResult::kDropNewReport;
   }
 
-  absl::optional<EventAttributionReport> replaced =
+  absl::optional<AttributionReport> replaced =
       GetReport(conversion_id_with_min_priority);
   if (!replaced.has_value()) {
     return MaybeReplaceLowerPriorityReportResult::kError;
@@ -658,12 +658,11 @@
   const base::Time report_time =
       delegate_->GetReportTime(source_to_attribute->source,
                                /*trigger_time=*/current_time);
-  EventAttributionReport report(std::move(source_to_attribute->source),
-                                trigger_data,
-                                /*trigger_time=*/current_time,
-                                /*report_time=*/report_time, trigger.priority(),
-                                /*external_report_id=*/delegate_->NewReportID(),
-                                /*report_id=*/absl::nullopt);
+  AttributionReport report(std::move(source_to_attribute->source), trigger_data,
+                           /*trigger_time=*/current_time,
+                           /*report_time=*/report_time, trigger.priority(),
+                           /*external_report_id=*/delegate_->NewReportID(),
+                           /*report_id=*/absl::nullopt);
 
   switch (
       rate_limit_table_.AttributionAllowed(db_.get(), report, current_time)) {
@@ -681,7 +680,7 @@
     return CreateReportResult(CreateReportStatus::kInternalError);
   }
 
-  absl::optional<EventAttributionReport> replaced_report;
+  absl::optional<AttributionReport> replaced_report;
   const auto maybe_replace_lower_priority_report_result =
       MaybeReplaceLowerPriorityReport(report,
                                       source_to_attribute->num_conversions,
@@ -716,8 +715,8 @@
                              StorableSource::AttributionLogic::kTruthfully;
 
   if (create_report) {
-    DCHECK(report.source().impression_id().has_value());
-    if (!StoreReport(report, *report.source().impression_id())) {
+    DCHECK(report.source().source_id().has_value());
+    if (!StoreReport(report, *report.source().source_id())) {
       return CreateReportResult(CreateReportStatus::kInternalError);
     }
   }
@@ -730,8 +729,8 @@
         "INSERT INTO dedup_keys(impression_id,dedup_key)VALUES(?,?)";
     sql::Statement insert_dedup_key_statement(
         db_->GetCachedStatement(SQL_FROM_HERE, kInsertDedupKeySql));
-    insert_dedup_key_statement.BindInt64(
-        0, *report.source().impression_id().value());
+    insert_dedup_key_statement.BindInt64(0,
+                                         *report.source().source_id().value());
     insert_dedup_key_statement.BindInt64(1, *trigger.dedup_key());
     if (!insert_dedup_key_statement.Run()) {
       return CreateReportResult(CreateReportStatus::kInternalError);
@@ -749,8 +748,8 @@
         SQL_FROM_HERE, kUpdateImpressionForConversionSql));
 
     // Update the attributed source.
-    impression_update_statement.BindInt64(
-        0, *report.source().impression_id().value());
+    impression_update_statement.BindInt64(0,
+                                          *report.source().source_id().value());
     if (!impression_update_statement.Run()) {
       return CreateReportResult(CreateReportStatus::kInternalError);
     }
@@ -790,7 +789,7 @@
       /*dropped_report_source_deactivation_reason=*/absl::nullopt, report_time);
 }
 
-bool AttributionStorageSql::StoreReport(const EventAttributionReport& report,
+bool AttributionStorageSql::StoreReport(const AttributionReport& report,
                                         StorableSource::Id source_id) {
   static constexpr char kStoreReportSql[] =
       "INSERT INTO conversions"
@@ -812,12 +811,12 @@
 
 // Helper to deserialize report rows. See `GetReport()` for the expected
 // ordering of columns used for the input to this function.
-absl::optional<EventAttributionReport> ReadReportFromStatement(
+absl::optional<AttributionReport> ReadReportFromStatement(
     sql::Statement& statement) {
   uint64_t trigger_data = DeserializeUint64(statement.ColumnInt64(0));
   base::Time trigger_time = statement.ColumnTime(1);
   base::Time report_time = statement.ColumnTime(2);
-  EventAttributionReport::Id conversion_id(statement.ColumnInt64(3));
+  AttributionReport::Id conversion_id(statement.ColumnInt64(3));
   int64_t conversion_priority = statement.ColumnInt64(4);
   int failed_send_attempts = statement.ColumnInt(5);
   base::GUID external_report_id =
@@ -848,7 +847,7 @@
     return absl::nullopt;
   }
 
-  // Create the source and EventAttributionReport objects from the retrieved
+  // Create the source and AttributionReport objects from the retrieved
   // columns.
   StorableSource source(source_event_id, std::move(impression_origin),
                         std::move(conversion_origin),
@@ -856,18 +855,18 @@
                         expiry_time, *source_type, attribution_source_priority,
                         *attribution_logic, source_id);
 
-  EventAttributionReport report(std::move(source), trigger_data, trigger_time,
-                                report_time, conversion_priority,
-                                std::move(external_report_id), conversion_id);
+  AttributionReport report(std::move(source), trigger_data, trigger_time,
+                           report_time, conversion_priority,
+                           std::move(external_report_id), conversion_id);
   report.set_failed_send_attempts(failed_send_attempts);
   return report;
 }
 
 }  // namespace
 
-std::vector<EventAttributionReport>
-AttributionStorageSql::GetAttributionsToReport(base::Time max_report_time,
-                                               int limit) {
+std::vector<AttributionReport> AttributionStorageSql::GetAttributionsToReport(
+    base::Time max_report_time,
+    int limit) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!LazyInit(DbCreationPolicy::kIgnoreIfAbsent))
     return {};
@@ -890,9 +889,9 @@
   statement.BindTime(0, max_report_time);
   statement.BindInt(1, limit);
 
-  std::vector<EventAttributionReport> reports;
+  std::vector<AttributionReport> reports;
   while (statement.Step()) {
-    absl::optional<EventAttributionReport> report =
+    absl::optional<AttributionReport> report =
         ReadReportFromStatement(statement);
     if (report.has_value())
       reports.push_back(std::move(*report));
@@ -924,23 +923,23 @@
   return absl::nullopt;
 }
 
-std::vector<EventAttributionReport> AttributionStorageSql::GetReports(
-    const std::vector<EventAttributionReport::Id>& ids) {
+std::vector<AttributionReport> AttributionStorageSql::GetReports(
+    const std::vector<AttributionReport::Id>& ids) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!LazyInit(DbCreationPolicy::kIgnoreIfAbsent))
     return {};
 
-  std::vector<EventAttributionReport> reports;
-  for (EventAttributionReport::Id id : ids) {
-    absl::optional<EventAttributionReport> report = GetReport(id);
+  std::vector<AttributionReport> reports;
+  for (AttributionReport::Id id : ids) {
+    absl::optional<AttributionReport> report = GetReport(id);
     if (report.has_value())
       reports.push_back(std::move(*report));
   }
   return reports;
 }
 
-absl::optional<EventAttributionReport> AttributionStorageSql::GetReport(
-    EventAttributionReport::Id conversion_id) {
+absl::optional<AttributionReport> AttributionStorageSql::GetReport(
+    AttributionReport::Id conversion_id) {
   static constexpr char kGetReportSql[] =
       "SELECT C.conversion_data,C.conversion_time,C.report_time,"
       "C.conversion_id,C.priority,C.failed_send_attempts,C.external_report_id,"
@@ -1018,7 +1017,7 @@
   return delete_sources_from_paged_select(select_inactive_statement);
 }
 
-bool AttributionStorageSql::DeleteReport(EventAttributionReport::Id report_id) {
+bool AttributionStorageSql::DeleteReport(AttributionReport::Id report_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!LazyInit(DbCreationPolicy::kIgnoreIfAbsent))
     return true;
@@ -1026,7 +1025,7 @@
 }
 
 bool AttributionStorageSql::DeleteReportInternal(
-    EventAttributionReport::Id report_id) {
+    AttributionReport::Id report_id) {
   static constexpr char kDeleteReportSql[] =
       "DELETE FROM conversions WHERE conversion_id = ?";
   sql::Statement statement(
@@ -1036,7 +1035,7 @@
 }
 
 bool AttributionStorageSql::UpdateReportForSendFailure(
-    EventAttributionReport::Id conversion_id,
+    AttributionReport::Id conversion_id,
     base::Time new_report_time) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!LazyInit(DbCreationPolicy::kIgnoreIfAbsent))
@@ -1124,7 +1123,7 @@
   statement.BindTime(1, delete_end);
 
   std::vector<StorableSource::Id> source_ids_to_delete;
-  std::vector<EventAttributionReport::Id> conversion_ids_to_delete;
+  std::vector<AttributionReport::Id> conversion_ids_to_delete;
   while (statement.Step()) {
     if (filter.Run(DeserializeOrigin(statement.ColumnString(0))) ||
         filter.Run(DeserializeOrigin(statement.ColumnString(1))) ||
@@ -1157,7 +1156,7 @@
   if (!DeleteSources(source_ids_to_delete))
     return;
 
-  for (EventAttributionReport::Id conversion_id : conversion_ids_to_delete) {
+  for (AttributionReport::Id conversion_id : conversion_ids_to_delete) {
     if (!DeleteReportInternal(conversion_id))
       return;
   }
@@ -1404,7 +1403,7 @@
 
   for (auto& source : sources) {
     absl::optional<std::vector<int64_t>> dedup_keys =
-        ReadDedupKeys(*source.impression_id());
+        ReadDedupKeys(*source.source_id());
     if (!dedup_keys.has_value())
       return {};
     source.SetDedupKeys(std::move(*dedup_keys));
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.h b/content/browser/attribution_reporting/attribution_storage_sql.h
index 89b3356..3b351cc 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.h
+++ b/content/browser/attribution_reporting/attribution_storage_sql.h
@@ -83,15 +83,15 @@
       int deactivated_source_return_limit = -1) override;
   CreateReportResult MaybeCreateAndStoreReport(
       const StorableTrigger& trigger) override;
-  std::vector<EventAttributionReport> GetAttributionsToReport(
+  std::vector<AttributionReport> GetAttributionsToReport(
       base::Time max_report_time,
       int limit = -1) override;
   absl::optional<base::Time> GetNextReportTime(base::Time time) override;
-  std::vector<EventAttributionReport> GetReports(
-      const std::vector<EventAttributionReport::Id>& ids) override;
+  std::vector<AttributionReport> GetReports(
+      const std::vector<AttributionReport::Id>& ids) override;
   std::vector<StorableSource> GetActiveSources(int limit = -1) override;
-  bool DeleteReport(EventAttributionReport::Id report_id) override;
-  bool UpdateReportForSendFailure(EventAttributionReport::Id report_id,
+  bool DeleteReport(AttributionReport::Id report_id) override;
+  bool UpdateReportForSendFailure(AttributionReport::Id report_id,
                                   base::Time new_report_time) override;
   absl::optional<base::Time> AdjustOfflineReportTimes(
       base::TimeDelta min_delay,
@@ -126,7 +126,7 @@
   // Deletes the report with `report_id` without checking the the DB
   // initialization status or the number of deleted rows. Returns false on
   // failure.
-  bool DeleteReportInternal(EventAttributionReport::Id report_id)
+  bool DeleteReportInternal(AttributionReport::Id report_id)
       VALID_CONTEXT_REQUIRED(sequence_checker_) WARN_UNUSED_RESULT;
 
   bool HasCapacityForStoringSource(const std::string& serialized_origin)
@@ -161,14 +161,13 @@
     kReplaceOldReport,
   };
   MaybeReplaceLowerPriorityReportResult MaybeReplaceLowerPriorityReport(
-      const EventAttributionReport& report,
+      const AttributionReport& report,
       int num_conversions,
       int64_t conversion_priority,
-      absl::optional<EventAttributionReport>& replaced_report)
+      absl::optional<AttributionReport>& replaced_report)
       VALID_CONTEXT_REQUIRED(sequence_checker_) WARN_UNUSED_RESULT;
 
-  absl::optional<EventAttributionReport> GetReport(
-      EventAttributionReport::Id report_id)
+  absl::optional<AttributionReport> GetReport(AttributionReport::Id report_id)
       VALID_CONTEXT_REQUIRED(sequence_checker_) WARN_UNUSED_RESULT;
 
   absl::optional<std::vector<int64_t>> ReadDedupKeys(
@@ -182,8 +181,8 @@
       VALID_CONTEXT_REQUIRED(sequence_checker_) WARN_UNUSED_RESULT;
 
   // Stores |report| in the database, but uses |source_id| rather than
-  // |EventAttributionReport::impression::impression_id()|, which may be null.
-  bool StoreReport(const EventAttributionReport& report,
+  // |AttributionReport::source::source_id()|, which may be null.
+  bool StoreReport(const AttributionReport& report,
                    StorableSource::Id source_id)
       VALID_CONTEXT_REQUIRED(sequence_checker_) WARN_UNUSED_RESULT;
 
diff --git a/content/browser/attribution_reporting/attribution_storage_sql_migrations.cc b/content/browser/attribution_reporting/attribution_storage_sql_migrations.cc
index 99d96d4..83f57f84 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql_migrations.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql_migrations.cc
@@ -8,8 +8,8 @@
 
 #include "base/guid.h"
 #include "base/metrics/histogram_functions.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_storage.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
 #include "content/browser/attribution_reporting/sql_utils.h"
 #include "content/browser/attribution_reporting/storable_source.h"
 #include "net/base/schemeful_site.h"
@@ -29,18 +29,18 @@
 }
 
 WARN_UNUSED_RESULT
-EventAttributionReport::Id NextConversionId(EventAttributionReport::Id id) {
-  return EventAttributionReport::Id(*id + 1);
+AttributionReport::Id NextConversionId(AttributionReport::Id id) {
+  return AttributionReport::Id(*id + 1);
 }
 
 struct ImpressionIdAndConversionOrigin {
-  StorableSource::Id impression_id;
+  StorableSource::Id source_id;
   url::Origin conversion_origin;
 };
 
 std::vector<ImpressionIdAndConversionOrigin>
 GetImpressionIdAndConversionOrigins(sql::Database* db,
-                                    StorableSource::Id start_impression_id) {
+                                    StorableSource::Id start_source_id) {
   static constexpr char kGetImpressionsSql[] =
       "SELECT impression_id,conversion_origin "
       "FROM impressions "
@@ -50,18 +50,18 @@
 
   sql::Statement statement(
       db->GetCachedStatement(SQL_FROM_HERE, kGetImpressionsSql));
-  statement.BindInt64(0, *start_impression_id);
+  statement.BindInt64(0, *start_source_id);
 
   const int kNumImpressions = 100;
   statement.BindInt(1, kNumImpressions);
 
   std::vector<ImpressionIdAndConversionOrigin> impressions;
   while (statement.Step()) {
-    StorableSource::Id impression_id(statement.ColumnInt64(0));
+    StorableSource::Id source_id(statement.ColumnInt64(0));
     url::Origin conversion_origin =
         DeserializeOrigin(statement.ColumnString(1));
 
-    impressions.push_back({impression_id, std::move(conversion_origin)});
+    impressions.push_back({source_id, std::move(conversion_origin)});
   }
   if (!statement.Succeeded())
     return {};
@@ -69,13 +69,13 @@
 }
 
 struct ImpressionIdAndImpressionOrigin {
-  StorableSource::Id impression_id;
+  StorableSource::Id source_id;
   url::Origin impression_origin;
 };
 
 std::vector<ImpressionIdAndImpressionOrigin>
 GetImpressionIdAndImpressionOrigins(sql::Database* db,
-                                    StorableSource::Id start_impression_id) {
+                                    StorableSource::Id start_source_id) {
   static constexpr char kGetImpressionsSql[] =
       "SELECT impression_id,impression_origin "
       "FROM impressions "
@@ -85,27 +85,27 @@
 
   sql::Statement statement(
       db->GetCachedStatement(SQL_FROM_HERE, kGetImpressionsSql));
-  statement.BindInt64(0, *start_impression_id);
+  statement.BindInt64(0, *start_source_id);
 
   const int kNumImpressions = 100;
   statement.BindInt(1, kNumImpressions);
 
   std::vector<ImpressionIdAndImpressionOrigin> impressions;
   while (statement.Step()) {
-    StorableSource::Id impression_id(statement.ColumnInt64(0));
+    StorableSource::Id source_id(statement.ColumnInt64(0));
     url::Origin impression_origin =
         DeserializeOrigin(statement.ColumnString(1));
 
-    impressions.push_back({impression_id, std::move(impression_origin)});
+    impressions.push_back({source_id, std::move(impression_origin)});
   }
   if (!statement.Succeeded())
     return {};
   return impressions;
 }
 
-std::vector<EventAttributionReport::Id> GetConversionIds(
+std::vector<AttributionReport::Id> GetConversionIds(
     sql::Database* db,
-    EventAttributionReport::Id start_conversion_id) {
+    AttributionReport::Id start_conversion_id) {
   static constexpr char kGetConversionsSql[] =
       "SELECT conversion_id FROM conversions "
       "WHERE conversion_id >= ? "
@@ -119,7 +119,7 @@
   const int kNumConversions = 100;
   statement.BindInt(1, kNumConversions);
 
-  std::vector<EventAttributionReport::Id> conversion_ids;
+  std::vector<AttributionReport::Id> conversion_ids;
   while (statement.Step()) {
     conversion_ids.emplace_back(statement.ColumnInt64(0));
   }
@@ -204,13 +204,13 @@
       // dynamically.
       update_destination_statement.BindString(
           0, net::SchemefulSite(impression.conversion_origin).Serialize());
-      update_destination_statement.BindInt64(1, *impression.impression_id);
+      update_destination_statement.BindInt64(1, *impression.source_id);
       update_destination_statement.Run();
     }
 
     // Fetch the next batch of rows from the database.
     impressions = GetImpressionIdAndConversionOrigins(
-        db, NextImpressionId(impressions.back().impression_id));
+        db, NextImpressionId(impressions.back().source_id));
   }
 
   // Create the pre-existing impression table indices on the new table.
@@ -532,14 +532,14 @@
       // The impression site is derived from the impression origin dynamically.
       update_impression_site_statement.BindString(
           0, net::SchemefulSite(impression.impression_origin).Serialize());
-      update_impression_site_statement.BindInt64(1, *impression.impression_id);
+      update_impression_site_statement.BindInt64(1, *impression.source_id);
       if (!update_impression_site_statement.Run())
         return false;
     }
 
     // Fetch the next batch of rows from the database.
     impressions = GetImpressionIdAndImpressionOrigins(
-        db, NextImpressionId(impressions.back().impression_id));
+        db, NextImpressionId(impressions.back().source_id));
   }
 
   // Create the pre-existing impression table indices on the new table.
@@ -573,13 +573,13 @@
 }
 
 struct ImpressionIdAndImpressionData {
-  StorableSource::Id impression_id;
+  StorableSource::Id source_id;
   std::string impression_data;
 };
 
 std::vector<ImpressionIdAndImpressionData> GetImpressionIdAndImpressionData(
     sql::Database* db,
-    StorableSource::Id start_impression_id) {
+    StorableSource::Id start_source_id) {
   static constexpr char kGetImpressionsSql[] =
       "SELECT impression_id,impression_data "
       "FROM impressions "
@@ -589,17 +589,17 @@
 
   sql::Statement statement(
       db->GetCachedStatement(SQL_FROM_HERE, kGetImpressionsSql));
-  statement.BindInt64(0, *start_impression_id);
+  statement.BindInt64(0, *start_source_id);
 
   const int kNumImpressions = 100;
   statement.BindInt(1, kNumImpressions);
 
   std::vector<ImpressionIdAndImpressionData> impressions;
   while (statement.Step()) {
-    StorableSource::Id impression_id(statement.ColumnInt64(0));
+    StorableSource::Id source_id(statement.ColumnInt64(0));
     std::string impression_data = statement.ColumnString(1);
 
-    impressions.push_back({impression_id, std::move(impression_data)});
+    impressions.push_back({source_id, std::move(impression_data)});
   }
   if (!statement.Succeeded())
     return {};
@@ -674,13 +674,13 @@
       update_impression_data_statement.Reset(/*clear_bound_vars=*/true);
       update_impression_data_statement.BindInt64(
           0, SerializeUint64(impression_data));
-      update_impression_data_statement.BindInt64(1, *impression.impression_id);
+      update_impression_data_statement.BindInt64(1, *impression.source_id);
       update_impression_data_statement.Run();
     }
 
     // Fetch the next batch of rows from the database.
     impressions = GetImpressionIdAndImpressionData(
-        db, NextImpressionId(impressions.back().impression_id));
+        db, NextImpressionId(impressions.back().source_id));
   }
 
   static constexpr char kDropOldImpressionTableSql[] = "DROP TABLE impressions";
@@ -1164,8 +1164,8 @@
   //
   // We update a subset of rows at a time to avoid pulling the entire
   // conversions table into memory.
-  std::vector<EventAttributionReport::Id> conversion_ids =
-      GetConversionIds(db, EventAttributionReport::Id(0));
+  std::vector<AttributionReport::Id> conversion_ids =
+      GetConversionIds(db, AttributionReport::Id(0));
 
   static constexpr char kUpdateExternalReportIdSql[] =
       "UPDATE new_conversions SET external_report_id = ? "
@@ -1175,7 +1175,7 @@
 
   while (!conversion_ids.empty()) {
     // Perform the column updates for each row we pulled into memory.
-    for (EventAttributionReport::Id conversion_id : conversion_ids) {
+    for (AttributionReport::Id conversion_id : conversion_ids) {
       update_statement.Reset(/*clear_bound_vars=*/true);
 
       base::GUID external_report_id = delegate->NewReportID();
diff --git a/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc b/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc
index 09117841..c8d0787 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc
@@ -15,8 +15,8 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
 #include "content/browser/attribution_reporting/storable_source.h"
 #include "content/browser/attribution_reporting/storable_trigger.h"
 #include "sql/database.h"
@@ -401,7 +401,7 @@
   EXPECT_THAT(storage()->GetActiveSources(), IsEmpty());
 
   task_environment_.FastForwardBy(base::Days(1));
-  EXPECT_TRUE(storage()->DeleteReport(EventAttributionReport::Id(1)));
+  EXPECT_TRUE(storage()->DeleteReport(AttributionReport::Id(1)));
   storage()->ClearData(
       base::Time::Min(), base::Time::Max(),
       base::BindRepeating(std::equal_to<url::Origin>(), impression_origin));
@@ -461,7 +461,7 @@
   EXPECT_THAT(storage()->GetActiveSources(), IsEmpty());
 
   task_environment_.FastForwardBy(base::Days(1));
-  EXPECT_TRUE(storage()->DeleteReport(EventAttributionReport::Id(1)));
+  EXPECT_TRUE(storage()->DeleteReport(AttributionReport::Id(1)));
   storage()->ClearData(
       base::Time::Min(), base::Time::Max(),
       base::BindRepeating(std::equal_to<url::Origin>(), conversion_origin));
@@ -542,7 +542,7 @@
 
   EXPECT_THAT(
       storage()->GetAttributionsToReport(base::Time::Now()),
-      ElementsAre(Property(&EventAttributionReport::trigger_data, kMaxUint64)));
+      ElementsAre(Property(&AttributionReport::trigger_data, kMaxUint64)));
 }
 
 TEST_F(AttributionStorageSqlTest, ImpressionNotExpired_NotDeleted) {
@@ -638,7 +638,7 @@
   // Advance past the default report time.
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime));
 
-  std::vector<EventAttributionReport> reports =
+  std::vector<AttributionReport> reports =
       storage()->GetAttributionsToReport(base::Time::Now());
   EXPECT_THAT(reports, SizeIs(1));
   EXPECT_TRUE(storage()->DeleteReport(*reports[0].report_id()));
diff --git a/content/browser/attribution_reporting/attribution_storage_unittest.cc b/content/browser/attribution_reporting/attribution_storage_unittest.cc
index f1a2d237..500335ad 100644
--- a/content/browser/attribution_reporting/attribution_storage_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_unittest.cc
@@ -21,9 +21,9 @@
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_storage_sql.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
 #include "content/browser/attribution_reporting/storable_source.h"
 #include "content/browser/attribution_reporting/storable_trigger.h"
 #include "content/public/common/url_constants.h"
@@ -79,8 +79,8 @@
 
   // Given a |conversion|, returns the expected conversion report properties at
   // the current timestamp.
-  EventAttributionReport GetExpectedReport(const StorableSource& impression,
-                                           const StorableTrigger& conversion) {
+  AttributionReport GetExpectedReport(const StorableSource& impression,
+                                      const StorableTrigger& conversion) {
     return ReportBuilder(impression)
         .SetTriggerData(conversion.trigger_data())
         .SetTriggerTime(base::Time::Now())
@@ -95,7 +95,7 @@
     return storage_->MaybeCreateAndStoreReport(conversion).status();
   }
 
-  void DeleteReports(const std::vector<EventAttributionReport>& reports) {
+  void DeleteReports(const std::vector<AttributionReport>& reports) {
     for (const auto& report : reports) {
       EXPECT_TRUE(storage_->DeleteReport(*report.report_id()));
     }
@@ -133,7 +133,7 @@
             storage->MaybeCreateAndStoreReport(DefaultTrigger()).status());
   EXPECT_THAT(storage->GetAttributionsToReport(base::Time::Now()), IsEmpty());
   EXPECT_THAT(storage->GetActiveSources(), IsEmpty());
-  EXPECT_TRUE(storage->DeleteReport(EventAttributionReport::Id(0)));
+  EXPECT_TRUE(storage->DeleteReport(AttributionReport::Id(0)));
   EXPECT_NO_FATAL_FAILURE(storage->ClearData(
       base::Time::Min(), base::Time::Max(), base::NullCallback()));
   EXPECT_EQ(
@@ -209,9 +209,8 @@
 
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime));
 
-  EXPECT_THAT(
-      storage()->GetAttributionsToReport(base::Time::Now()),
-      ElementsAre(Property(&EventAttributionReport::trigger_data, 456u)));
+  EXPECT_THAT(storage()->GetAttributionsToReport(base::Time::Now()),
+              ElementsAre(Property(&AttributionReport::trigger_data, 456u)));
 }
 
 TEST_F(AttributionStorageTest, ImpressionExpired_NoConversionsStored) {
@@ -264,8 +263,7 @@
   EXPECT_EQ(CreateReportStatus::kSuccess,
             MaybeCreateAndStoreReport(conversion));
 
-  EventAttributionReport expected_report =
-      GetExpectedReport(impression, conversion);
+  AttributionReport expected_report = GetExpectedReport(impression, conversion);
 
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime));
 
@@ -310,7 +308,7 @@
 
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime));
 
-  std::vector<EventAttributionReport> reports =
+  std::vector<AttributionReport> reports =
       storage()->GetAttributionsToReport(base::Time::Now());
   EXPECT_THAT(reports, SizeIs(1));
   DeleteReports(reports);
@@ -379,7 +377,7 @@
   auto conversion = DefaultTrigger();
   EXPECT_EQ(CreateReportStatus::kSuccess,
             MaybeCreateAndStoreReport(conversion));
-  EventAttributionReport expected_report =
+  AttributionReport expected_report =
       GetExpectedReport(new_impression, conversion);
 
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime));
@@ -414,7 +412,7 @@
   EXPECT_EQ(CreateReportStatus::kSuccess,
             MaybeCreateAndStoreReport(conversion));
 
-  EventAttributionReport expected_report =
+  AttributionReport expected_report =
       GetExpectedReport(first_impression, conversion);
 
   // Verify it was the first impression that converted.
@@ -440,7 +438,7 @@
   auto third_impression = SourceBuilder().SetSourceEventId(10).Build();
   storage()->StoreSource(third_impression);
 
-  EventAttributionReport third_expected_conversion =
+  AttributionReport third_expected_conversion =
       GetExpectedReport(third_impression, conversion);
   EXPECT_EQ(CreateReportStatus::kSuccess,
             MaybeCreateAndStoreReport(conversion));
@@ -482,9 +480,9 @@
             MaybeCreateAndStoreReport(DefaultTrigger()));
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime));
 
-  std::vector<EventAttributionReport> first_call_reports =
+  std::vector<AttributionReport> first_call_reports =
       storage()->GetAttributionsToReport(base::Time::Now());
-  std::vector<EventAttributionReport> second_call_reports =
+  std::vector<AttributionReport> second_call_reports =
       storage()->GetAttributionsToReport(base::Time::Now());
 
   // Expect that |GetAttributionsToReport()| did not delete any conversions.
@@ -676,7 +674,7 @@
 
   task_environment_.FastForwardBy(base::Days(1));
 
-  const EventAttributionReport expected_report =
+  const AttributionReport expected_report =
       GetExpectedReport(impression, conversion);
 
   EXPECT_EQ(CreateReportStatus::kSuccess,
@@ -776,7 +774,7 @@
                              CreateReportStatus::kRateLimited),
                     Property(&CreateReportResult::dropped_report, IsTrue())));
 
-  const EventAttributionReport expected_report =
+  const AttributionReport expected_report =
       GetExpectedReport(impression, conversion);
 
   EXPECT_THAT(storage()->GetAttributionsToReport(base::Time::Max()),
@@ -855,9 +853,9 @@
 
   EXPECT_THAT(storage()->GetAttributionsToReport(base::Time::Now()),
               ElementsAre(AllOf(
-                  Property(&EventAttributionReport::source,
+                  Property(&AttributionReport::source,
                            Property(&StorableSource::source_event_id, 5u)),
-                  Property(&EventAttributionReport::trigger_data, 7u))));
+                  Property(&AttributionReport::trigger_data, 7u))));
 }
 
 TEST_F(AttributionStorageTest, NeverAttributeImpression_RateLimitsNotChanged) {
@@ -885,7 +883,7 @@
   EXPECT_EQ(CreateReportStatus::kRateLimited,
             MaybeCreateAndStoreReport(conversion));
 
-  const EventAttributionReport expected_report =
+  const AttributionReport expected_report =
       GetExpectedReport(impression, conversion);
 
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime));
@@ -1088,7 +1086,7 @@
 
   EXPECT_THAT(
       storage()->GetAttributionsToReport(base::Time::Now()),
-      ElementsAre(Property(&EventAttributionReport::source,
+      ElementsAre(Property(&AttributionReport::source,
                            Property(&StorableSource::source_event_id, 5u))));
 }
 
@@ -1113,7 +1111,7 @@
 
   EXPECT_THAT(
       storage()->GetAttributionsToReport(base::Time::Now()),
-      ElementsAre(Property(&EventAttributionReport::source,
+      ElementsAre(Property(&AttributionReport::source,
                            Property(&StorableSource::source_event_id, 5u))));
 }
 
@@ -1147,7 +1145,7 @@
           .Build();
   storage()->StoreSource(impression);
 
-  const EventAttributionReport expected_report =
+  const AttributionReport expected_report =
       ReportBuilder(impression)
           .SetTriggerData(7)
           .SetTriggerTime(base::Time::Now())
@@ -1189,11 +1187,11 @@
   EXPECT_THAT(
       storage()->MaybeCreateAndStoreReport(
           TriggerBuilder().SetPriority(2).SetTriggerData(21).Build()),
-      AllOf(Property(&CreateReportResult::status,
-                     CreateReportStatus::kSuccessDroppedLowerPriority),
-            Property(&CreateReportResult::dropped_report,
-                     Optional(Property(&EventAttributionReport::trigger_data,
-                                       20u)))));
+      AllOf(
+          Property(&CreateReportResult::status,
+                   CreateReportStatus::kSuccessDroppedLowerPriority),
+          Property(&CreateReportResult::dropped_report,
+                   Optional(Property(&AttributionReport::trigger_data, 20u)))));
 
   storage()->StoreSource(
       SourceBuilder().SetSourceEventId(7).SetPriority(2).Build());
@@ -1206,23 +1204,23 @@
   EXPECT_THAT(
       storage()->MaybeCreateAndStoreReport(
           TriggerBuilder().SetPriority(0).SetTriggerData(23).Build()),
-      AllOf(Property(&CreateReportResult::status,
-                     CreateReportStatus::kPriorityTooLow),
-            Property(&CreateReportResult::dropped_report,
-                     Optional(Property(&EventAttributionReport::trigger_data,
-                                       23u)))));
+      AllOf(
+          Property(&CreateReportResult::status,
+                   CreateReportStatus::kPriorityTooLow),
+          Property(&CreateReportResult::dropped_report,
+                   Optional(Property(&AttributionReport::trigger_data, 23u)))));
 
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime));
 
   EXPECT_THAT(
       storage()->GetAttributionsToReport(base::Time::Now()),
       ElementsAre(
-          AllOf(Property(&EventAttributionReport::source,
+          AllOf(Property(&AttributionReport::source,
                          Property(&StorableSource::source_event_id, 5u)),
-                Property(&EventAttributionReport::trigger_data, 21u)),
-          AllOf(Property(&EventAttributionReport::source,
+                Property(&AttributionReport::trigger_data, 21u)),
+          AllOf(Property(&AttributionReport::source,
                          Property(&StorableSource::source_event_id, 7u)),
-                Property(&EventAttributionReport::trigger_data, 22u))));
+                Property(&AttributionReport::trigger_data, 22u))));
 }
 
 TEST_F(AttributionStorageTest, TriggerPriority_Simple) {
@@ -1245,7 +1243,7 @@
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime));
 
   EXPECT_THAT(storage()->GetAttributionsToReport(base::Time::Now()),
-              ElementsAre(Property(&EventAttributionReport::trigger_data, 9u)));
+              ElementsAre(Property(&AttributionReport::trigger_data, 9u)));
 }
 
 TEST_F(AttributionStorageTest, TriggerPriority_SamePriorityDeletesMostRecent) {
@@ -1277,8 +1275,8 @@
                 TriggerBuilder().SetPriority(2).SetTriggerData(5).Build()));
 
   EXPECT_THAT(storage()->GetAttributionsToReport(base::Time::Max()),
-              ElementsAre(Property(&EventAttributionReport::trigger_data, 3u),
-                          Property(&EventAttributionReport::trigger_data, 5u)));
+              ElementsAre(Property(&AttributionReport::trigger_data, 3u),
+                          Property(&AttributionReport::trigger_data, 5u)));
 }
 
 TEST_F(AttributionStorageTest, TriggerPriority_DeactivatesImpression) {
@@ -1378,11 +1376,10 @@
                     .Build()));
 
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime));
-  EXPECT_THAT(
-      storage()->GetAttributionsToReport(base::Time::Now()),
-      ElementsAre(Property(&EventAttributionReport::trigger_data, 71u),
-                  Property(&EventAttributionReport::trigger_data, 72u),
-                  Property(&EventAttributionReport::trigger_data, 73u)));
+  EXPECT_THAT(storage()->GetAttributionsToReport(base::Time::Now()),
+              ElementsAre(Property(&AttributionReport::trigger_data, 71u),
+                          Property(&AttributionReport::trigger_data, 72u),
+                          Property(&AttributionReport::trigger_data, 73u)));
 
   EXPECT_THAT(
       storage()->GetActiveSources(),
@@ -1411,10 +1408,10 @@
 
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime));
 
-  std::vector<EventAttributionReport> actual_reports =
+  std::vector<AttributionReport> actual_reports =
       storage()->GetAttributionsToReport(base::Time::Now());
   EXPECT_THAT(actual_reports,
-              ElementsAre(Property(&EventAttributionReport::trigger_data, 3u)));
+              ElementsAre(Property(&AttributionReport::trigger_data, 3u)));
 
   // Simulate the report being sent and deleted from storage.
   DeleteReports(actual_reports);
@@ -1445,15 +1442,15 @@
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime));
 
   EXPECT_THAT(storage()->GetAttributionsToReport(base::Time::Now()),
-              ElementsAre(Property(&EventAttributionReport::priority, 13)));
+              ElementsAre(Property(&AttributionReport::priority, 13)));
 }
 
 TEST_F(AttributionStorageTest, NoIDReuse_Impression) {
   storage()->StoreSource(SourceBuilder().Build());
   auto sources = storage()->GetActiveSources();
   EXPECT_THAT(sources,
-              ElementsAre(Property(&StorableSource::impression_id, IsTrue())));
-  const StorableSource::Id id1 = *sources.front().impression_id();
+              ElementsAre(Property(&StorableSource::source_id, IsTrue())));
+  const StorableSource::Id id1 = *sources.front().source_id();
 
   storage()->ClearData(base::Time::Min(), base::Time::Max(),
                        base::NullCallback());
@@ -1462,8 +1459,8 @@
   storage()->StoreSource(SourceBuilder().Build());
   sources = storage()->GetActiveSources();
   EXPECT_THAT(sources,
-              ElementsAre(Property(&StorableSource::impression_id, IsTrue())));
-  const StorableSource::Id id2 = *sources.front().impression_id();
+              ElementsAre(Property(&StorableSource::source_id, IsTrue())));
+  const StorableSource::Id id2 = *sources.front().source_id();
 
   EXPECT_NE(id1, id2);
 }
@@ -1473,9 +1470,9 @@
   EXPECT_EQ(CreateReportStatus::kSuccess,
             MaybeCreateAndStoreReport(DefaultTrigger()));
   auto reports = storage()->GetAttributionsToReport(base::Time::Max());
-  EXPECT_THAT(reports, ElementsAre(Property(&EventAttributionReport::report_id,
-                                            IsTrue())));
-  const EventAttributionReport::Id id1 = *reports.front().report_id();
+  EXPECT_THAT(reports,
+              ElementsAre(Property(&AttributionReport::report_id, IsTrue())));
+  const AttributionReport::Id id1 = *reports.front().report_id();
 
   storage()->ClearData(base::Time::Min(), base::Time::Max(),
                        base::NullCallback());
@@ -1485,9 +1482,9 @@
   EXPECT_EQ(CreateReportStatus::kSuccess,
             MaybeCreateAndStoreReport(DefaultTrigger()));
   reports = storage()->GetAttributionsToReport(base::Time::Max());
-  EXPECT_THAT(reports, ElementsAre(Property(&EventAttributionReport::report_id,
-                                            IsTrue())));
-  const EventAttributionReport::Id id2 = *reports.front().report_id();
+  EXPECT_THAT(reports,
+              ElementsAre(Property(&AttributionReport::report_id, IsTrue())));
+  const AttributionReport::Id id2 = *reports.front().report_id();
 
   EXPECT_NE(id1, id2);
 }
@@ -1499,11 +1496,11 @@
 
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime));
 
-  std::vector<EventAttributionReport> actual_reports =
+  std::vector<AttributionReport> actual_reports =
       storage()->GetAttributionsToReport(base::Time::Now());
   EXPECT_THAT(
       actual_reports,
-      ElementsAre(Property(&EventAttributionReport::failed_send_attempts, 0)));
+      ElementsAre(Property(&AttributionReport::failed_send_attempts, 0)));
 
   const base::TimeDelta delay = base::Days(2);
   const base::Time new_report_time = actual_reports[0].report_time() + delay;
@@ -1512,11 +1509,10 @@
 
   task_environment_.FastForwardBy(delay);
 
-  EXPECT_THAT(
-      storage()->GetAttributionsToReport(base::Time::Now()),
-      ElementsAre(AllOf(
-          Property(&EventAttributionReport::failed_send_attempts, 1),
-          Property(&EventAttributionReport::report_time, new_report_time))));
+  EXPECT_THAT(storage()->GetAttributionsToReport(base::Time::Now()),
+              ElementsAre(AllOf(
+                  Property(&AttributionReport::failed_send_attempts, 1),
+                  Property(&AttributionReport::report_time, new_report_time))));
 }
 
 TEST_F(AttributionStorageTest, StoreSource_ReturnsDeactivatedSources) {
@@ -1595,9 +1591,8 @@
       AllOf(
           Property(&CreateReportResult::status,
                    CreateReportStatus::kPriorityTooLow),
-          Property(
-              &CreateReportResult::dropped_report,
-              Optional(Property(&EventAttributionReport::source, source1))),
+          Property(&CreateReportResult::dropped_report,
+                   Optional(Property(&AttributionReport::source, source1))),
           Property(&CreateReportResult::GetDeactivatedSource,
                    DeactivatedSource(
                        source1,
@@ -1611,7 +1606,7 @@
 
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime));
 
-  std::vector<EventAttributionReport> actual_reports =
+  std::vector<AttributionReport> actual_reports =
       storage()->GetAttributionsToReport(base::Time::Now());
   EXPECT_EQ(1u, actual_reports.size());
   EXPECT_EQ(DefaultExternalReportID(), actual_reports[0].external_report_id());
@@ -1630,7 +1625,7 @@
       base::Time::Now() + base::Milliseconds(kReportTime);
 
   EXPECT_THAT(storage()->GetAttributionsToReport(base::Time::Max()),
-              ElementsAre(Property(&EventAttributionReport::report_time,
+              ElementsAre(Property(&AttributionReport::report_time,
                                    original_report_time)));
 
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime));
@@ -1642,7 +1637,7 @@
   // The report time should not be changed as it is equal to now, not strictly
   // less than it.
   EXPECT_THAT(storage()->GetAttributionsToReport(base::Time::Max()),
-              ElementsAre(Property(&EventAttributionReport::report_time,
+              ElementsAre(Property(&AttributionReport::report_time,
                                    original_report_time)));
 
   task_environment_.FastForwardBy(base::Milliseconds(1));
@@ -1654,9 +1649,9 @@
             new_report_time);
 
   // The report time should be changed as it is strictly less than now.
-  EXPECT_THAT(storage()->GetAttributionsToReport(base::Time::Max()),
-              ElementsAre(Property(&EventAttributionReport::report_time,
-                                   new_report_time)));
+  EXPECT_THAT(
+      storage()->GetAttributionsToReport(base::Time::Max()),
+      ElementsAre(Property(&AttributionReport::report_time, new_report_time)));
 }
 
 TEST_F(AttributionStorageTest, AdjustOfflineReportTimes_Range) {
@@ -1668,7 +1663,7 @@
       base::Time::Now() + base::Milliseconds(kReportTime);
 
   EXPECT_THAT(storage()->GetAttributionsToReport(base::Time::Max()),
-              ElementsAre(Property(&EventAttributionReport::report_time,
+              ElementsAre(Property(&AttributionReport::report_time,
                                    original_report_time)));
 
   task_environment_.FastForwardBy(base::Milliseconds(kReportTime + 1));
@@ -1678,7 +1673,7 @@
 
   EXPECT_THAT(
       storage()->GetAttributionsToReport(base::Time::Max()),
-      ElementsAre(Property(&EventAttributionReport::report_time,
+      ElementsAre(Property(&AttributionReport::report_time,
                            AllOf(Ge(base::Time::Now() + base::Hours(1)),
                                  Le(base::Time::Now() + base::Hours(3))))));
 }
diff --git a/content/browser/attribution_reporting/attribution_test_utils.cc b/content/browser/attribution_reporting/attribution_test_utils.cc
index 33ff921..4b8e1c1a 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.cc
+++ b/content/browser/attribution_reporting/attribution_test_utils.cc
@@ -145,9 +145,8 @@
     observer.OnSourceDeactivated(source);
 }
 
-void MockAttributionManager::NotifyReportSent(
-    const EventAttributionReport& report,
-    const SendResult& info) {
+void MockAttributionManager::NotifyReportSent(const AttributionReport& report,
+                                              const SendResult& info) {
   for (Observer& observer : observers_)
     observer.OnReportSent(report, info);
 }
@@ -211,9 +210,9 @@
   return *this;
 }
 
-SourceBuilder& SourceBuilder::SetImpressionId(
-    absl::optional<StorableSource::Id> impression_id) {
-  impression_id_ = impression_id;
+SourceBuilder& SourceBuilder::SetSourceId(
+    absl::optional<StorableSource::Id> source_id) {
+  source_id_ = source_id;
   return *this;
 }
 
@@ -227,7 +226,7 @@
       source_event_id_, impression_origin_, conversion_origin_,
       reporting_origin_, impression_time_,
       /*expiry_time=*/impression_time_ + expiry_, source_type_, priority_,
-      attribution_logic_, impression_id_);
+      attribution_logic_, source_id_);
   impression.SetDedupKeys(dedup_keys_);
   return impression;
 }
@@ -315,15 +314,14 @@
 }
 
 ReportBuilder& ReportBuilder::SetReportId(
-    absl::optional<EventAttributionReport::Id> id) {
+    absl::optional<AttributionReport::Id> id) {
   report_id_ = id;
   return *this;
 }
 
-EventAttributionReport ReportBuilder::Build() const {
-  return EventAttributionReport(source_, trigger_data_, trigger_time_,
-                                report_time_, priority_, external_report_id_,
-                                report_id_);
+AttributionReport ReportBuilder::Build() const {
+  return AttributionReport(source_, trigger_data_, trigger_time_, report_time_,
+                           priority_, external_report_id_, report_id_);
 }
 
 // Custom comparator for `StorableSource` that does not take impression IDs
@@ -343,9 +341,8 @@
 // Custom comparator for comparing two vectors of conversion reports. Does not
 // compare impression and conversion IDs as they are set by the underlying
 // sqlite db and should not be tested.
-bool operator==(const EventAttributionReport& a,
-                const EventAttributionReport& b) {
-  const auto tie = [](const EventAttributionReport& conversion) {
+bool operator==(const AttributionReport& a, const AttributionReport& b) {
+  const auto tie = [](const AttributionReport& conversion) {
     return std::make_tuple(conversion.source(), conversion.trigger_data(),
                            conversion.trigger_time(), conversion.report_time(),
                            conversion.priority(),
@@ -483,9 +480,9 @@
       << ",source_type=" << impression.source_type()
       << ",priority=" << impression.priority()
       << ",attribution_logic=" << impression.attribution_logic()
-      << ",impression_id="
-      << (impression.impression_id()
-              ? base::NumberToString(**impression.impression_id())
+      << ",source_id="
+      << (impression.source_id()
+              ? base::NumberToString(**impression.source_id())
               : "null")
       << ",dedup_keys=[";
 
@@ -498,8 +495,7 @@
   return out << "]}";
 }
 
-std::ostream& operator<<(std::ostream& out,
-                         const EventAttributionReport& report) {
+std::ostream& operator<<(std::ostream& out, const AttributionReport& report) {
   return out << "{source=" << report.source()
              << ",trigger_data=" << report.trigger_data()
              << ",trigger_time=" << report.trigger_time()
@@ -542,16 +538,16 @@
              << ",reason=" << deactivated_source.reason << "}";
 }
 
-std::vector<EventAttributionReport> GetAttributionsToReportForTesting(
+std::vector<AttributionReport> GetAttributionsToReportForTesting(
     AttributionManagerImpl* manager,
     base::Time max_report_time) {
   base::RunLoop run_loop;
-  std::vector<EventAttributionReport> attribution_reports;
+  std::vector<AttributionReport> attribution_reports;
   manager->attribution_storage_
       .AsyncCall(&AttributionStorage::GetAttributionsToReport)
       .WithArgs(max_report_time, /*limit=*/-1)
       .Then(base::BindOnce(base::BindLambdaForTesting(
-          [&](std::vector<EventAttributionReport> reports) {
+          [&](std::vector<AttributionReport> reports) {
             attribution_reports = std::move(reports);
             run_loop.Quit();
           })));
diff --git a/content/browser/attribution_reporting/attribution_test_utils.h b/content/browser/attribution_reporting/attribution_test_utils.h
index 65caab3..2c114ac 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.h
+++ b/content/browser/attribution_reporting/attribution_test_utils.h
@@ -21,8 +21,8 @@
 #include "content/browser/attribution_reporting/attribution_manager.h"
 #include "content/browser/attribution_reporting/attribution_manager_impl.h"
 #include "content/browser/attribution_reporting/attribution_policy.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_storage.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
 #include "content/browser/attribution_reporting/rate_limit_table.h"
 #include "content/browser/attribution_reporting/send_result.h"
 #include "content/browser/attribution_reporting/storable_source.h"
@@ -175,12 +175,12 @@
   MOCK_METHOD(
       void,
       GetPendingReportsForWebUI,
-      (base::OnceCallback<void(std::vector<EventAttributionReport>)> callback),
+      (base::OnceCallback<void(std::vector<AttributionReport>)> callback),
       (override));
 
   MOCK_METHOD(void,
               SendReportsForWebUI,
-              (const std::vector<EventAttributionReport::Id>& ids,
+              (const std::vector<AttributionReport::Id>& ids,
                base::OnceClosure done),
               (override));
 
@@ -200,7 +200,7 @@
   void NotifyReportsChanged();
   void NotifySourceDeactivated(
       const AttributionStorage::DeactivatedSource& source);
-  void NotifyReportSent(const EventAttributionReport& report,
+  void NotifyReportSent(const AttributionReport& report,
                         const SendResult& info);
   void NotifyReportDropped(
       const AttributionStorage::CreateReportResult& result);
@@ -236,8 +236,8 @@
   SourceBuilder& SetAttributionLogic(
       StorableSource::AttributionLogic attribution_logic) WARN_UNUSED_RESULT;
 
-  SourceBuilder& SetImpressionId(
-      absl::optional<StorableSource::Id> impression_id) WARN_UNUSED_RESULT;
+  SourceBuilder& SetSourceId(absl::optional<StorableSource::Id> source_id)
+      WARN_UNUSED_RESULT;
 
   SourceBuilder& SetDedupKeys(std::vector<int64_t> dedup_keys)
       WARN_UNUSED_RESULT;
@@ -256,7 +256,7 @@
   int64_t priority_ = 0;
   StorableSource::AttributionLogic attribution_logic_ =
       StorableSource::AttributionLogic::kTruthfully;
-  absl::optional<StorableSource::Id> impression_id_;
+  absl::optional<StorableSource::Id> source_id_;
   std::vector<int64_t> dedup_keys_;
 };
 
@@ -299,7 +299,7 @@
   absl::optional<int64_t> dedup_key_ = absl::nullopt;
 };
 
-// Helper class to construct an `EventAttributionReport` for tests using default
+// Helper class to construct an `AttributionReport` for tests using default
 // data.
 class ReportBuilder {
  public:
@@ -317,10 +317,10 @@
   ReportBuilder& SetExternalReportId(base::GUID external_report_id)
       WARN_UNUSED_RESULT;
 
-  ReportBuilder& SetReportId(absl::optional<EventAttributionReport::Id> id)
+  ReportBuilder& SetReportId(absl::optional<AttributionReport::Id> id)
       WARN_UNUSED_RESULT;
 
-  EventAttributionReport Build() const WARN_UNUSED_RESULT;
+  AttributionReport Build() const WARN_UNUSED_RESULT;
 
  private:
   StorableSource source_;
@@ -329,13 +329,12 @@
   base::Time report_time_;
   int64_t priority_ = 0;
   base::GUID external_report_id_;
-  absl::optional<EventAttributionReport::Id> report_id_;
+  absl::optional<AttributionReport::Id> report_id_;
 };
 
 bool operator==(const StorableSource& a, const StorableSource& b);
 
-bool operator==(const EventAttributionReport& a,
-                const EventAttributionReport& b);
+bool operator==(const AttributionReport& a, const AttributionReport& b);
 
 bool operator==(const SendResult& a, const SendResult& b);
 
@@ -358,8 +357,7 @@
 
 std::ostream& operator<<(std::ostream& out, const StorableSource& impression);
 
-std::ostream& operator<<(std::ostream& out,
-                         const EventAttributionReport& report);
+std::ostream& operator<<(std::ostream& out, const AttributionReport& report);
 
 std::ostream& operator<<(std::ostream& out, SendResult::Status status);
 
@@ -372,7 +370,7 @@
     std::ostream& out,
     const AttributionStorage::DeactivatedSource& deactivated_source);
 
-std::vector<EventAttributionReport> GetAttributionsToReportForTesting(
+std::vector<AttributionReport> GetAttributionsToReportForTesting(
     AttributionManagerImpl* manager,
     base::Time max_report_time) WARN_UNUSED_RESULT;
 
diff --git a/content/browser/attribution_reporting/attributions_browsertest.cc b/content/browser/attribution_reporting/attributions_browsertest.cc
index c635acf..60e1909 100644
--- a/content/browser/attribution_reporting/attributions_browsertest.cc
+++ b/content/browser/attribution_reporting/attributions_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/test/values_test_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "content/browser/attribution_reporting/attribution_manager_impl.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
 #include "content/public/common/content_switches.h"
@@ -40,6 +41,11 @@
 #include "services/network/public/cpp/network_connection_tracker.h"
 #endif
 
+#if defined(OS_FUCHSIA)
+#include "content/public/browser/network_service_instance.h"
+#include "services/network/test/test_network_connection_tracker.h"
+#endif
+
 namespace content {
 
 namespace {
@@ -154,7 +160,19 @@
 
 class AttributionsBrowserTest : public ContentBrowserTest {
  public:
-  AttributionsBrowserTest() { AttributionManagerImpl::RunInMemoryForTesting(); }
+  AttributionsBrowserTest() {
+    AttributionManagerImpl::RunInMemoryForTesting();
+
+#if defined(OS_FUCHSIA)
+    // Fuchsia's network connection tracker always seems to indicate offline in
+    // these tests, so override the tracker with a test one, which defaults to
+    // online. See crbug.com/1285057 for details.
+    network_connection_tracker_ =
+        network::TestNetworkConnectionTracker::CreateInstance();
+    content::SetNetworkConnectionTrackerForTesting(
+        network::TestNetworkConnectionTracker::GetInstance());
+#endif
+  }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitch(switches::kConversionsDebugMode);
@@ -192,6 +210,11 @@
 
  private:
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
+
+#if defined(OS_FUCHSIA)
+  std::unique_ptr<network::TestNetworkConnectionTracker>
+      network_connection_tracker_;
+#endif
 };
 
 // Verifies that storage initialization does not hang when initialized in a
diff --git a/content/browser/attribution_reporting/event_attribution_report.cc b/content/browser/attribution_reporting/event_attribution_report.cc
deleted file mode 100644
index 255192a..0000000
--- a/content/browser/attribution_reporting/event_attribution_report.cc
+++ /dev/null
@@ -1,109 +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.
-
-#include "content/browser/attribution_reporting/event_attribution_report.h"
-
-#include <utility>
-
-#include "base/check.h"
-#include "base/json/json_writer.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/values.h"
-#include "net/base/schemeful_site.h"
-#include "url/gurl.h"
-#include "url/url_canon.h"
-
-namespace content {
-
-EventAttributionReport::EventAttributionReport(StorableSource source,
-                                               uint64_t trigger_data,
-                                               base::Time trigger_time,
-                                               base::Time report_time,
-                                               int64_t priority,
-                                               base::GUID external_report_id,
-                                               absl::optional<Id> report_id)
-    : source_(std::move(source)),
-      trigger_data_(trigger_data),
-      trigger_time_(trigger_time),
-      report_time_(report_time),
-      priority_(priority),
-      external_report_id_(std::move(external_report_id)),
-      report_id_(report_id) {
-  DCHECK(external_report_id_.is_valid());
-}
-
-EventAttributionReport::EventAttributionReport(
-    const EventAttributionReport& other) = default;
-
-EventAttributionReport& EventAttributionReport::operator=(
-    const EventAttributionReport& other) = default;
-
-EventAttributionReport::EventAttributionReport(EventAttributionReport&& other) =
-    default;
-
-EventAttributionReport& EventAttributionReport::operator=(
-    EventAttributionReport&& other) = default;
-
-EventAttributionReport::~EventAttributionReport() = default;
-
-GURL EventAttributionReport::ReportURL() const {
-  url::Replacements<char> replacements;
-  static constexpr char kEndpointPath[] =
-      "/.well-known/attribution-reporting/report-attribution";
-  replacements.SetPath(kEndpointPath, url::Component(0, strlen(kEndpointPath)));
-  return source_.reporting_origin().GetURL().ReplaceComponents(replacements);
-}
-
-std::string EventAttributionReport::ReportBody(bool pretty_print) const {
-  base::Value dict(base::Value::Type::DICTIONARY);
-
-  dict.SetStringKey("attribution_destination",
-                    source_.ConversionDestination().Serialize());
-
-  // The API denotes these values as strings; a `uint64_t` cannot be put in
-  // a dict as an integer in order to be opaque to various API configurations.
-  dict.SetStringKey("source_event_id",
-                    base::NumberToString(source_.source_event_id()));
-
-  dict.SetStringKey("trigger_data", base::NumberToString(trigger_data_));
-
-  const char* source_type = nullptr;
-  switch (source_.source_type()) {
-    case StorableSource::SourceType::kNavigation:
-      source_type = "navigation";
-      break;
-    case StorableSource::SourceType::kEvent:
-      source_type = "event";
-      break;
-  }
-  dict.SetStringKey("source_type", source_type);
-
-  dict.SetStringKey("report_id", external_report_id_.AsLowercaseString());
-
-  // Write the dict to json;
-  std::string output_json;
-  bool success = base::JSONWriter::WriteWithOptions(
-      dict, pretty_print ? base::JSONWriter::OPTIONS_PRETTY_PRINT : 0,
-      &output_json);
-  DCHECK(success);
-  return output_json;
-}
-
-void EventAttributionReport::set_report_time(base::Time report_time) {
-  report_time_ = report_time;
-}
-
-void EventAttributionReport::set_failed_send_attempts(
-    int failed_send_attempts) {
-  DCHECK_GE(failed_send_attempts, 0);
-  failed_send_attempts_ = failed_send_attempts;
-}
-
-void EventAttributionReport::SetExternalReportIdForTesting(
-    base::GUID external_report_id) {
-  DCHECK(external_report_id.is_valid());
-  external_report_id_ = std::move(external_report_id);
-}
-
-}  // namespace content
diff --git a/content/browser/attribution_reporting/event_attribution_report.h b/content/browser/attribution_reporting/event_attribution_report.h
deleted file mode 100644
index 40a7465..0000000
--- a/content/browser/attribution_reporting/event_attribution_report.h
+++ /dev/null
@@ -1,107 +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 CONTENT_BROWSER_ATTRIBUTION_REPORTING_EVENT_ATTRIBUTION_REPORT_H_
-#define CONTENT_BROWSER_ATTRIBUTION_REPORTING_EVENT_ATTRIBUTION_REPORT_H_
-
-#include <stdint.h>
-
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "base/guid.h"
-#include "base/time/time.h"
-#include "base/types/strong_alias.h"
-#include "content/browser/attribution_reporting/storable_source.h"
-#include "content/common/content_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-class GURL;
-
-namespace content {
-
-// Class that contains all the data needed to serialize and send a conversion
-// report. This represents the report for a conversion event and its associated
-// source.
-class CONTENT_EXPORT EventAttributionReport {
- public:
-  using Id = base::StrongAlias<EventAttributionReport, int64_t>;
-
-  // The conversion_id may not be set for a conversion report.
-  EventAttributionReport(StorableSource source,
-                         uint64_t trigger_data,
-                         base::Time trigger_time,
-                         base::Time report_time,
-                         int64_t priority,
-                         base::GUID external_report_id,
-                         absl::optional<Id> report_id);
-  EventAttributionReport(const EventAttributionReport& other);
-  EventAttributionReport& operator=(const EventAttributionReport& other);
-  EventAttributionReport(EventAttributionReport&& other);
-  EventAttributionReport& operator=(EventAttributionReport&& other);
-  ~EventAttributionReport();
-
-  // Returns the URL to which the report will be sent.
-  GURL ReportURL() const WARN_UNUSED_RESULT;
-
-  // Returns the JSON for the report body.
-  std::string ReportBody(bool pretty_print = false) const WARN_UNUSED_RESULT;
-
-  const StorableSource& source() const { return source_; }
-
-  uint64_t trigger_data() const { return trigger_data_; }
-
-  base::Time trigger_time() const { return trigger_time_; }
-
-  base::Time report_time() const { return report_time_; }
-
-  int64_t priority() const { return priority_; }
-
-  const base::GUID& external_report_id() const { return external_report_id_; }
-
-  absl::optional<Id> report_id() const { return report_id_; }
-
-  int failed_send_attempts() const { return failed_send_attempts_; }
-
-  void set_report_time(base::Time report_time);
-
-  void set_failed_send_attempts(int failed_send_attempts);
-
-  void SetExternalReportIdForTesting(base::GUID external_report_id);
-
- private:
-  // Source associated with this conversion report.
-  StorableSource source_;
-
-  // Data provided at trigger time by the attribution destination. Depending on
-  // the source type, this contains the associated data in the trigger redirect.
-  uint64_t trigger_data_;
-
-  // The time the trigger occurred.
-  base::Time trigger_time_;
-
-  // The time this conversion report should be sent.
-  base::Time report_time_;
-
-  // Priority specified in conversion redirect.
-  int64_t priority_;
-
-  // External report ID for deduplicating reports received by the reporting
-  // origin.
-  base::GUID external_report_id_;
-
-  // Id assigned by storage to uniquely identify a completed conversion. If
-  // null, an ID has not been assigned yet.
-  absl::optional<Id> report_id_;
-
-  // Number of times the browser has tried and failed to send this report.
-  int failed_send_attempts_ = 0;
-
-  // When adding new members, the corresponding `operator==()` definition in
-  // `attribution_test_utils.h` should also be updated.
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_ATTRIBUTION_REPORTING_EVENT_ATTRIBUTION_REPORT_H_
diff --git a/content/browser/attribution_reporting/rate_limit_table.cc b/content/browser/attribution_reporting/rate_limit_table.cc
index 54efcee3..d1cf5f6a 100644
--- a/content/browser/attribution_reporting/rate_limit_table.cc
+++ b/content/browser/attribution_reporting/rate_limit_table.cc
@@ -6,7 +6,7 @@
 
 #include "base/check.h"
 #include "base/time/time.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/sql_utils.h"
 #include "net/base/schemeful_site.h"
 #include "sql/database.h"
@@ -114,13 +114,13 @@
 }
 
 bool RateLimitTable::AddRateLimit(sql::Database* db,
-                                  const EventAttributionReport& report) {
+                                  const AttributionReport& report) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(report.source().impression_id().has_value());
+  DCHECK(report.source().source_id().has_value());
 
   return AddRow(db,
                 AttributionTypeFromSourceType(report.source().source_type()),
-                *report.source().impression_id(),
+                *report.source().source_id(),
                 report.source().ImpressionSite().Serialize(),
                 SerializeOrigin(report.source().impression_origin()),
                 report.source().ConversionDestination().Serialize(),
@@ -177,7 +177,7 @@
 
 AttributionAllowedStatus RateLimitTable::AttributionAllowed(
     sql::Database* db,
-    const EventAttributionReport& report,
+    const AttributionReport& report,
     base::Time now) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -382,7 +382,7 @@
     const StorableSource& source,
     const std::vector<AggregateHistogramContribution>& contributions) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(source.impression_id().has_value());
+  DCHECK(source.source_id().has_value());
 
   base::Time now = base::Time::Now();
 
@@ -419,7 +419,7 @@
       SerializeOrigin(source.conversion_origin());
 
   for (const auto& contribution : contributions) {
-    if (!AddRow(db, AttributionType::kAggregate, *source.impression_id(),
+    if (!AddRow(db, AttributionType::kAggregate, *source.source_id(),
                 serialized_impression_site, serialized_impression_origin,
                 serialized_conversion_destination, serialized_conversion_origin,
                 now, contribution.bucket, contribution.value)) {
diff --git a/content/browser/attribution_reporting/rate_limit_table.h b/content/browser/attribution_reporting/rate_limit_table.h
index ac315cb..5d8476d 100644
--- a/content/browser/attribution_reporting/rate_limit_table.h
+++ b/content/browser/attribution_reporting/rate_limit_table.h
@@ -28,7 +28,7 @@
 
 namespace content {
 
-class EventAttributionReport;
+class AttributionReport;
 
 struct AggregateHistogramContribution {
   std::string bucket;
@@ -60,14 +60,14 @@
   // Adds a rate limit to the table for an event-level report.
   // Returns false on failure.
   bool AddRateLimit(sql::Database* db,
-                    const EventAttributionReport& report) WARN_UNUSED_RESULT;
+                    const AttributionReport& report) WARN_UNUSED_RESULT;
 
   // Checks if the given attribution is allowed according to the data in the
   // table and policy as specified by the delegate.
-  AttributionAllowedStatus AttributionAllowed(
-      sql::Database* db,
-      const EventAttributionReport& report,
-      base::Time now) WARN_UNUSED_RESULT;
+  AttributionAllowedStatus AttributionAllowed(sql::Database* db,
+                                              const AttributionReport& report,
+                                              base::Time now)
+      WARN_UNUSED_RESULT;
 
   // Attempts to add a set of histogram contributions to the rate limit. Returns
   // `kAllowed` if the contributions were added, `kNotAllowed` if the reports
diff --git a/content/browser/attribution_reporting/rate_limit_table_unittest.cc b/content/browser/attribution_reporting/rate_limit_table_unittest.cc
index 2e20746..9ec76d1 100644
--- a/content/browser/attribution_reporting/rate_limit_table_unittest.cc
+++ b/content/browser/attribution_reporting/rate_limit_table_unittest.cc
@@ -11,8 +11,8 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
+#include "content/browser/attribution_reporting/attribution_report.h"
 #include "content/browser/attribution_reporting/attribution_test_utils.h"
-#include "content/browser/attribution_reporting/event_attribution_report.h"
 #include "content/browser/attribution_reporting/storable_source.h"
 #include "sql/database.h"
 #include "sql/statement.h"
@@ -38,16 +38,16 @@
     table_ = std::make_unique<RateLimitTable>(delegate_.get());
   }
 
-  EventAttributionReport NewConversionReport(
+  AttributionReport NewConversionReport(
       url::Origin impression_origin,
       url::Origin conversion_origin,
-      StorableSource::Id impression_id = StorableSource::Id(0),
+      StorableSource::Id source_id = StorableSource::Id(0),
       StorableSource::SourceType source_type =
           StorableSource::SourceType::kNavigation) {
     return ReportBuilder(SourceBuilder()
                              .SetImpressionOrigin(std::move(impression_origin))
                              .SetConversionOrigin(std::move(conversion_origin))
-                             .SetImpressionId(impression_id)
+                             .SetSourceId(source_id)
                              .SetSourceType(source_type)
                              .Build())
         .SetTriggerTime(base::Time::Now())
@@ -469,7 +469,7 @@
                 SourceBuilder()
                     .SetImpressionOrigin(example_a)
                     .SetConversionOrigin(example_b)
-                    .SetImpressionId(StorableSource::Id(1))
+                    .SetSourceId(StorableSource::Id(1))
                     .Build(),
                 {{.bucket = "a", .value = 2}}));
   EXPECT_EQ(1u, GetRateLimitRows(&db));
@@ -589,7 +589,7 @@
       SourceBuilder()
           .SetImpressionOrigin(url::Origin::Create(GURL("https://a.example/")))
           .SetConversionOrigin(url::Origin::Create(GURL("https://b.example/")))
-          .SetImpressionId(StorableSource::Id(1))
+          .SetSourceId(StorableSource::Id(1))
           .Build();
 
   EXPECT_EQ(AttributionAllowedStatus::kAllowed,
diff --git a/content/browser/attribution_reporting/storable_source.cc b/content/browser/attribution_reporting/storable_source.cc
index 72429280..1e6a031 100644
--- a/content/browser/attribution_reporting/storable_source.cc
+++ b/content/browser/attribution_reporting/storable_source.cc
@@ -18,7 +18,7 @@
                                SourceType source_type,
                                int64_t priority,
                                AttributionLogic attribution_logic,
-                               absl::optional<Id> impression_id)
+                               absl::optional<Id> source_id)
     : source_event_id_(source_event_id),
       impression_origin_(std::move(impression_origin)),
       conversion_origin_(std::move(conversion_origin)),
@@ -28,7 +28,7 @@
       source_type_(source_type),
       priority_(priority),
       attribution_logic_(attribution_logic),
-      impression_id_(impression_id) {
+      source_id_(source_id) {
   // 30 days is the max allowed expiry for an impression.
   DCHECK_GE(base::Days(30), expiry_time - impression_time);
   // The impression must expire strictly after it occurred.
diff --git a/content/browser/attribution_reporting/storable_source.h b/content/browser/attribution_reporting/storable_source.h
index 96c1996..ac715a34 100644
--- a/content/browser/attribution_reporting/storable_source.h
+++ b/content/browser/attribution_reporting/storable_source.h
@@ -60,7 +60,7 @@
                  SourceType source_type,
                  int64_t priority,
                  AttributionLogic attribution_logic,
-                 absl::optional<Id> impression_id);
+                 absl::optional<Id> source_id);
   StorableSource(const StorableSource& other);
   StorableSource& operator=(const StorableSource& other);
   StorableSource(StorableSource&& other);
@@ -89,9 +89,7 @@
 
   base::Time expiry_time() const WARN_UNUSED_RESULT { return expiry_time_; }
 
-  absl::optional<Id> impression_id() const WARN_UNUSED_RESULT {
-    return impression_id_;
-  }
+  absl::optional<Id> source_id() const WARN_UNUSED_RESULT { return source_id_; }
 
   SourceType source_type() const WARN_UNUSED_RESULT { return source_type_; }
 
@@ -133,7 +131,7 @@
   AttributionLogic attribution_logic_;
 
   // If null, an ID has not been assigned yet.
-  absl::optional<Id> impression_id_;
+  absl::optional<Id> source_id_;
 
   // Dedup keys associated with the impression. Only set in values returned from
   // `AttributionStorage::GetActiveImpressions()`.
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 60f107c..f0b455e 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -1629,7 +1629,7 @@
                               "memory_threshold_for_back_forward_cache_in_mb",
                               memory_threshold);
     EnableFeatureAndSetParams(blink::features::kLoadingTasksUnfreezable,
-                              "max_buffered_bytes", "1000");
+                              "max_buffered_bytes_per_process", "1000");
 
     BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
   }
diff --git a/content/browser/back_forward_cache_features_browsertest.cc b/content/browser/back_forward_cache_features_browsertest.cc
index 67120e24..950f2c5 100644
--- a/content/browser/back_forward_cache_features_browsertest.cc
+++ b/content/browser/back_forward_cache_features_browsertest.cc
@@ -3602,8 +3602,14 @@
       {}, {}, {}, FROM_HERE);
 }
 
+// TODO(https://crbug.com/1286474): This test is flaking on some Android bots.
+#if defined(OS_ANDROID)
+#define MAYBE_PresentationConnectionClosed DISABLED_PresentationConnectionClosed
+#else
+#define MAYBE_PresentationConnectionClosed PresentationConnectionClosed
+#endif  // defined(OS_ANDROID)
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
-                       PresentationConnectionClosed) {
+                       MAYBE_PresentationConnectionClosed) {
   ASSERT_TRUE(CreateHttpsServer()->Start());
   GURL url_a(https_server()->GetURL(
       "a.com", "/back_forward_cache/presentation_controller.html"));
diff --git a/content/browser/back_forward_cache_internal_browsertest.cc b/content/browser/back_forward_cache_internal_browsertest.cc
index b0cb199e..7c4ed1d 100644
--- a/content/browser/back_forward_cache_internal_browsertest.cc
+++ b/content/browser/back_forward_cache_internal_browsertest.cc
@@ -1959,7 +1959,7 @@
     BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
     command_line->AppendSwitch(switches::kDisableBackForwardCache);
     EnableFeatureAndSetParams(blink::features::kLoadingTasksUnfreezable,
-                              "max_buffered_bytes", "1000");
+                              "max_buffered_bytes_per_process", "1000");
   }
 };
 
diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc
index e328077..480972ab 100644
--- a/content/browser/background_sync/background_sync_manager.cc
+++ b/content/browser/background_sync/background_sync_manager.cc
@@ -795,7 +795,7 @@
   }
 
   HasMainFrameWindowClient(
-      url::Origin::Create(sw_registration->scope().DeprecatedGetOriginAsURL()),
+      sw_registration->key(),
       base::BindOnce(&BackgroundSyncManager::RegisterDidCheckIfMainFrame,
                      weak_ptr_factory_.GetWeakPtr(), sw_registration_id,
                      std::move(options), std::move(callback)));
@@ -856,9 +856,7 @@
   }
 
   SyncAndNotificationPermissions permission = GetBackgroundSyncPermission(
-      service_worker_context_,
-      url::Origin::Create(sw_registration->scope().DeprecatedGetOriginAsURL()),
-      sync_type);
+      service_worker_context_, sw_registration->key().origin(), sync_type);
   RegisterDidAskForPermission(sw_registration_id, std::move(options),
                               std::move(callback), permission);
 }
@@ -892,8 +890,7 @@
       LookupActiveRegistration(blink::mojom::BackgroundSyncRegistrationInfo(
           sw_registration_id, options.tag, GetBackgroundSyncType(options)));
 
-  url::Origin origin =
-      url::Origin::Create(sw_registration->scope().DeprecatedGetOriginAsURL());
+  const url::Origin& origin = sw_registration->key().origin();
 
   if (GetBackgroundSyncType(options) ==
       blink::mojom::BackgroundSyncType::ONE_SHOT) {
@@ -1001,10 +998,8 @@
           GetDelayAsString(registration.delay_until() - clock_->Now())}});
   }
 
-  AddOrUpdateActiveRegistration(
-      sw_registration_id,
-      url::Origin::Create(sw_registration->scope().DeprecatedGetOriginAsURL()),
-      registration);
+  AddOrUpdateActiveRegistration(sw_registration_id,
+                                sw_registration->key().origin(), registration);
 
   StoreRegistrations(
       sw_registration_id,
@@ -1415,10 +1410,10 @@
   }
 }
 
-void BackgroundSyncManager::HasMainFrameWindowClient(const url::Origin& origin,
-                                                     BoolCallback callback) {
-  service_worker_context_->HasMainFrameWindowClient(blink::StorageKey(origin),
-                                                    std::move(callback));
+void BackgroundSyncManager::HasMainFrameWindowClient(
+    const blink::StorageKey& key,
+    BoolCallback callback) {
+  service_worker_context_->HasMainFrameWindowClient(key, std::move(callback));
 }
 
 void BackgroundSyncManager::GetRegistrationsImpl(
@@ -2011,8 +2006,7 @@
       registration->num_attempts() == registration->max_attempts() - 1;
 
   HasMainFrameWindowClient(
-      url::Origin::Create(
-          service_worker_registration->scope().DeprecatedGetOriginAsURL()),
+      service_worker_registration->key(),
       base::BindOnce(&BackgroundSyncMetrics::RecordEventStarted, sync_type));
 
   if (sync_type == BackgroundSyncType::ONE_SHOT) {
@@ -2069,18 +2063,16 @@
 
   // The event ran to completion, we should count it, no matter what happens
   // from here.
-  url::Origin origin = url::Origin::Create(
-      service_worker_registration->scope().DeprecatedGetOriginAsURL());
+  const blink::StorageKey& key = service_worker_registration->key();
   HasMainFrameWindowClient(
-      origin,
-      base::BindOnce(&BackgroundSyncMetrics::RecordEventResult,
-                     registration_info->sync_type,
-                     status_code == blink::ServiceWorkerStatusCode::kOk));
+      key, base::BindOnce(&BackgroundSyncMetrics::RecordEventResult,
+                          registration_info->sync_type,
+                          status_code == blink::ServiceWorkerStatusCode::kOk));
 
   op_scheduler_.ScheduleOperation(base::BindOnce(
       &BackgroundSyncManager::EventCompleteImpl, weak_ptr_factory_.GetWeakPtr(),
-      std::move(registration_info), std::move(keepalive), status_code, origin,
-      op_scheduler_.WrapCallbackToRunNext(std::move(callback))));
+      std::move(registration_info), std::move(keepalive), status_code,
+      key.origin(), op_scheduler_.WrapCallbackToRunNext(std::move(callback))));
 }
 
 void BackgroundSyncManager::EventCompleteImpl(
diff --git a/content/browser/background_sync/background_sync_manager.h b/content/browser/background_sync/background_sync_manager.h
index 43c73802..8cc85a0 100644
--- a/content/browser/background_sync/background_sync_manager.h
+++ b/content/browser/background_sync/background_sync_manager.h
@@ -227,7 +227,7 @@
       const std::string& tag,
       scoped_refptr<ServiceWorkerVersion> active_version,
       ServiceWorkerVersion::StatusCallback callback);
-  virtual void HasMainFrameWindowClient(const url::Origin& origin,
+  virtual void HasMainFrameWindowClient(const blink::StorageKey& key,
                                         BoolCallback callback);
 
  private:
diff --git a/content/browser/bluetooth/bluetooth_blocklist.cc b/content/browser/bluetooth/bluetooth_blocklist.cc
index 7ba1666..8ec7c15 100644
--- a/content/browser/bluetooth/bluetooth_blocklist.cc
+++ b/content/browser/bluetooth/bluetooth_blocklist.cc
@@ -130,18 +130,6 @@
 void BluetoothBlocklist::PopulateWithDefaultValues() {
   blocklisted_uuids_.clear();
 
-  // Testing from Web Tests Note:
-  //
-  // Random UUIDs for object & exclude permutations that do not exist in the
-  // standard blocklist are included to facilitate integration testing from
-  // Web Tests.  Unit tests can dynamically modify the blocklist, but don't
-  // offer the full integration test to the Web Bluetooth Javascript bindings.
-  //
-  // This is done for simplicity as opposed to exposing a testing API that can
-  // add to the blocklist over time, which would be over engineered.
-  //
-  // Remove testing UUIDs if the specified blocklist is updated to include UUIDs
-  // that match the specific permutations.
   DCHECK(BluetoothUUID("00001800-0000-1000-8000-00805f9b34fb") ==
          BluetoothUUID("1800"));
 
@@ -161,12 +149,26 @@
   Add(BluetoothUUID("2a02"), Value::EXCLUDE_WRITES);
   Add(BluetoothUUID("2a03"), Value::EXCLUDE);
   Add(BluetoothUUID("2a25"), Value::EXCLUDE);
-  // Characteristics for Web Tests:
-  Add(BluetoothUUID("bad1c9a2-9a5b-4015-8b60-1579bbbf2135"),
-      Value::EXCLUDE_READS);
   // Descriptors:
   Add(BluetoothUUID("2902"), Value::EXCLUDE_WRITES);
   Add(BluetoothUUID("2903"), Value::EXCLUDE_WRITES);
+
+  // Testing from Web Tests Note:
+  //
+  // Random UUIDs for object & exclude permutations that do not exist in the
+  // standard blocklist are included to facilitate integration testing from
+  // Web Tests.  Unit tests can dynamically modify the blocklist, but don't
+  // offer the full integration test to the Web Bluetooth Javascript bindings.
+  //
+  // This is done for simplicity as opposed to exposing a testing API that can
+  // add to the blocklist over time, which would be over engineered.
+  //
+  // Remove testing UUIDs if the specified blocklist is updated to include UUIDs
+  // that match the specific permutations.
+  //
+  // Characteristics for Web Tests:
+  Add(BluetoothUUID("bad1c9a2-9a5b-4015-8b60-1579bbbf2135"),
+      Value::EXCLUDE_READS);
   // Descriptors for Web Tests:
   Add(BluetoothUUID("bad2ddcf-60db-45cd-bef9-fd72b153cf7c"), Value::EXCLUDE);
   Add(BluetoothUUID("bad3ec61-3cc3-4954-9702-7977df514114"),
diff --git a/content/browser/cache_storage/cache_storage_dispatcher_host.cc b/content/browser/cache_storage/cache_storage_dispatcher_host.cc
index 5aa0291..4ff536d 100644
--- a/content/browser/cache_storage/cache_storage_dispatcher_host.cc
+++ b/content/browser/cache_storage/cache_storage_dispatcher_host.cc
@@ -632,7 +632,6 @@
  public:
   CacheStorageImpl(
       CacheStorageDispatcherHost* host,
-      const blink::StorageKey& storage_key,
       const absl::optional<storage::BucketLocator>& bucket,
       bool incognito,
       const CrossOriginEmbedderPolicy& cross_origin_embedder_policy,
@@ -640,7 +639,6 @@
           coep_reporter,
       storage::mojom::CacheStorageOwner owner)
       : host_(host),
-        storage_key_(storage_key),
         bucket_(bucket),
         cross_origin_embedder_policy_(cross_origin_embedder_policy),
         coep_reporter_(std::move(coep_reporter)),
@@ -859,7 +857,7 @@
           // against the requesting document's origin and
           // Cross-Origin-Embedder-Policy (COEP).
           if (ResponseBlockedByCrossOriginResourcePolicy(
-                  response.get(), self->storage_key_.origin(),
+                  response.get(), self->bucket_->storage_key.origin(),
                   self->cross_origin_embedder_policy_, self->coep_reporter_)) {
             std::move(callback).Run(blink::mojom::MatchResult::NewStatus(
                 CacheStorageError::kErrorCrossOriginResourcePolicy));
@@ -919,7 +917,6 @@
                            TRACE_ID_GLOBAL(trace_id),
                            TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
                            "cache_name", utf8_cache_name);
-    content::CacheStorage* cache_storage = GetOrCreateCacheStorage();
     auto cb = base::BindOnce(
         [](base::WeakPtr<CacheStorageImpl> self, base::TimeTicks start_time,
            int64_t trace_id, blink::mojom::CacheStorage::OpenCallback callback,
@@ -952,7 +949,7 @@
                 coep_reporter.InitWithNewPipeAndPassReceiver());
           }
           auto cache_impl = std::make_unique<CacheImpl>(
-              self->host_, std::move(cache_handle), self->storage_key_,
+              self->host_, std::move(cache_handle), self->bucket_->storage_key,
               self->cross_origin_embedder_policy_, std::move(coep_reporter),
               self->owner_);
           self->host_->AddCacheReceiver(
@@ -973,6 +970,7 @@
       return;
     }
 
+    content::CacheStorage* cache_storage = GetOrCreateCacheStorage();
     if (!cache_storage) {
       std::move(cb).Run(CacheStorageCacheHandle(),
                         MakeErrorStorage(ErrorStorageType::kStorageHandleNull));
@@ -990,15 +988,18 @@
   content::CacheStorage* GetOrCreateCacheStorage() {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     DCHECK(host_);
+    DCHECK(bucket_.has_value())
+        << "A bucket must exist to get or create a CacheStorage";
+
     if (!cache_storage_handle_.value())
-      cache_storage_handle_ = host_->OpenCacheStorage(storage_key_, owner_);
+      cache_storage_handle_ =
+          host_->OpenCacheStorage(bucket_->storage_key, owner_);
     return cache_storage_handle_.value();
   }
 
   // Owns this.
   const raw_ptr<CacheStorageDispatcherHost> host_;
 
-  const blink::StorageKey storage_key_;
   // absl::nullopt when bucket retrieval has failed.
   const absl::optional<storage::BucketLocator> bucket_;
   const CrossOriginEmbedderPolicy cross_origin_embedder_policy_;
@@ -1030,9 +1031,12 @@
     storage::mojom::CacheStorageOwner owner,
     mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (bucket.has_value())
+    DCHECK_EQ(bucket->storage_key, storage_key);
+
   bool incognito = context_ ? context_->is_incognito() : false;
   auto impl = std::make_unique<CacheStorageImpl>(
-      this, storage_key, bucket, incognito, cross_origin_embedder_policy,
+      this, bucket, incognito, cross_origin_embedder_policy,
       std::move(coep_reporter), owner);
   receivers_.Add(std::move(impl), std::move(receiver));
 }
diff --git a/content/browser/child_process_launcher_helper_win.cc b/content/browser/child_process_launcher_helper_win.cc
index 92604148..ceeb9bc 100644
--- a/content/browser/child_process_launcher_helper_win.cc
+++ b/content/browser/child_process_launcher_helper_win.cc
@@ -48,7 +48,9 @@
     FileMappedForLaunch& files_to_register,
     base::LaunchOptions* options) {
   DCHECK(CurrentlyOnProcessLauncherTaskRunner());
-  if (!delegate_->ShouldLaunchElevated()) {
+  if (delegate_->ShouldLaunchElevated()) {
+    options->elevated = true;
+  } else {
     mojo_channel_->PrepareToPassRemoteEndpoint(&options->handles_to_inherit,
                                                command_line());
   }
@@ -64,10 +66,12 @@
   DCHECK(CurrentlyOnProcessLauncherTaskRunner());
   *is_synchronous_launch = true;
   if (delegate_->ShouldLaunchElevated()) {
+    DCHECK(options.elevated);
     // When establishing a Mojo connection, the pipe path has already been added
     // to the command line.
     base::LaunchOptions win_options;
     win_options.start_hidden = true;
+    win_options.elevated = true;
     ChildProcessLauncherHelper::Process process;
     process.process = base::LaunchElevatedProcess(*command_line(), win_options);
     *launch_result = process.process.IsValid() ? LAUNCH_RESULT_SUCCESS
diff --git a/content/browser/code_cache/generated_code_cache_context.cc b/content/browser/code_cache/generated_code_cache_context.cc
index 8e174d5..077b44cc 100644
--- a/content/browser/code_cache/generated_code_cache_context.cc
+++ b/content/browser/code_cache/generated_code_cache_context.cc
@@ -44,8 +44,12 @@
   DETACH_FROM_SEQUENCE(sequence_checker_);
   if (base::FeatureList::IsEnabled(
           features::kNavigationThreadingOptimizations)) {
-    task_runner_ = base::ThreadPool::CreateSingleThreadTaskRunner(
-        {base::TaskPriority::USER_VISIBLE});
+    if (base::FeatureList::IsEnabled(features::kThreadingOptimizationsOnIO)) {
+      task_runner_ = GetIOThreadTaskRunner({});
+    } else {
+      task_runner_ = base::ThreadPool::CreateSingleThreadTaskRunner(
+          {base::TaskPriority::USER_BLOCKING});
+    }
   } else {
     task_runner_ = GetUIThreadTaskRunner({});
   }
diff --git a/content/browser/cookie_store/cookie_change_subscription.cc b/content/browser/cookie_store/cookie_change_subscription.cc
index 61dfa9ab..ae941cd 100644
--- a/content/browser/cookie_store/cookie_change_subscription.cc
+++ b/content/browser/cookie_store/cookie_change_subscription.cc
@@ -7,6 +7,8 @@
 #include <utility>
 
 #include "content/browser/cookie_store/cookie_change_subscriptions.pb.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/common/content_client.h"
 #include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_util.h"
 #include "net/cookies/same_party_context.h"
@@ -183,7 +185,9 @@
           net::CookieAccessParams{
               access_semantics,
               network::IsUrlPotentiallyTrustworthy(url_),
-              net::cookie_util::GetSamePartyStatus(cookie, net_options),
+              net::cookie_util::GetSamePartyStatus(
+                  cookie, net_options,
+                  GetContentClient()->browser()->IsFirstPartySetsEnabled()),
           })
       .status.IsInclude();
 }
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 319697b9..4c15051c 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -593,8 +593,9 @@
 // TODO(crbug.com/1147911) Android Lollipop has a problem with capturing
 // screenshot.
 // TODO(crbug.com/1156767) Flaky on linux-lacros-tester-rel
+// TODO(crbug.com/1286261): Failing on MacOS.
 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS_ASH) || \
-    BUILDFLAG(IS_CHROMEOS_LACROS)
+    BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_MAC)
 #define MAYBE_CaptureScreenshotBeyondViewport_InnerScrollbarsAreShown \
   DISABLED_CaptureScreenshotBeyondViewport_InnerScrollbarsAreShown
 #else
diff --git a/content/browser/fenced_frame/fenced_frame_browsertest.cc b/content/browser/fenced_frame/fenced_frame_browsertest.cc
index 0bc9c70..f38afd1 100644
--- a/content/browser/fenced_frame/fenced_frame_browsertest.cc
+++ b/content/browser/fenced_frame/fenced_frame_browsertest.cc
@@ -264,6 +264,40 @@
   EXPECT_FALSE(fenced_frame_root_node->IsLoading());
 }
 
+// Test that when the documents inside the fenced frame tree are loading,
+// WebContentsObserver::DocumentAvailableInMainFrame is not invoked for fenced
+// frames as it is only invoked for primary main frames.
+IN_PROC_BROWSER_TEST_F(FencedFrameBrowserTest, DocumentAvailableInMainFrame) {
+  // Initialize a MockWebContentsObserver to ensure that
+  // DocumentAvailableInMainFrame is only invoked for primary main
+  // RenderFrameHosts.
+  testing::NiceMock<MockWebContentsObserver> web_contents_observer(
+      web_contents());
+  testing::InSequence s;
+
+  // Navigate to an initial primary page. This should result in invoking
+  // DocumentAvailableInMainFrame once.
+  EXPECT_CALL(web_contents_observer,
+              DocumentAvailableInMainFrame(primary_main_frame_host()))
+      .Times(1);
+  EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
+                                         "fencedframe.test", "/title1.html")));
+  RenderFrameHostImplWrapper primary_rfh(primary_main_frame_host());
+
+  // Once the fenced frame completes loading, it shouldn't result in
+  // invoking DocumentAvailableInMainFrame.
+  EXPECT_CALL(web_contents_observer, DocumentAvailableInMainFrame(testing::_))
+      .Times(0);
+  const GURL fenced_frame_url = embedded_test_server()->GetURL(
+      "fencedframe.test", "/fenced_frames/title1.html");
+  RenderFrameHostImplWrapper inner_fenced_frame_rfh(
+      fenced_frame_test_helper().CreateFencedFrame(primary_rfh.get(),
+                                                   fenced_frame_url));
+  FrameTreeNode* fenced_frame_root_node =
+      inner_fenced_frame_rfh->frame_tree_node();
+  EXPECT_FALSE(fenced_frame_root_node->IsLoading());
+}
+
 namespace {
 
 enum class FrameTypeWithOrigin {
diff --git a/content/browser/interest_group/ad_auction_service_impl.cc b/content/browser/interest_group/ad_auction_service_impl.cc
index fd46537f..99b5dda 100644
--- a/content/browser/interest_group/ad_auction_service_impl.cc
+++ b/content/browser/interest_group/ad_auction_service_impl.cc
@@ -336,6 +336,47 @@
   auctions_.insert(std::move(auction));
 }
 
+namespace {
+class FencedFrameURLMappingObserver
+    : public FencedFrameURLMapping::MappingResultObserver {
+ public:
+  FencedFrameURLMappingObserver() = default;
+  ~FencedFrameURLMappingObserver() override = default;
+
+  void OnFencedFrameURLMappingComplete(
+      absl::optional<GURL> mapped_url,
+      absl::optional<FencedFrameURLMapping::PendingAdComponentsMap>
+          pending_ad_components_map) override {
+    mapped_url_ = mapped_url;
+    called_ = true;
+  }
+
+  bool called_;
+  absl::optional<GURL> mapped_url_;
+};
+
+}  // namespace
+
+void AdAuctionServiceImpl::DeprecatedGetURLFromURN(
+    const GURL& urn_url,
+    DeprecatedGetURLFromURNCallback callback) {
+  if (!FencedFrameURLMapping::IsValidUrnUuidURL(urn_url)) {
+    std::move(callback).Run(absl::nullopt);
+    return;
+  }
+  FencedFrameURLMappingObserver obs;
+  content::FencedFrameURLMapping& mapping =
+      static_cast<RenderFrameHostImpl*>(render_frame_host())
+          ->GetPage()
+          .fenced_frame_urls_map();
+  // FLEDGE URN URLs should already be mapped, so the observer will be called
+  // synchronously.
+  mapping.ConvertFencedFrameURNToURL(urn_url, &obs);
+  if (!obs.called_)
+    mapping.RemoveObserverForURN(urn_url, &obs);
+  std::move(callback).Run(std::move(obs.mapped_url_));
+}
+
 void AdAuctionServiceImpl::CreateAdRequest(
     blink::mojom::AdRequestConfigPtr config,
     CreateAdRequestCallback callback) {
diff --git a/content/browser/interest_group/ad_auction_service_impl.h b/content/browser/interest_group/ad_auction_service_impl.h
index 772c57c..72c7d5b 100644
--- a/content/browser/interest_group/ad_auction_service_impl.h
+++ b/content/browser/interest_group/ad_auction_service_impl.h
@@ -47,6 +47,9 @@
   void UpdateAdInterestGroups() override;
   void RunAdAuction(blink::mojom::AuctionAdConfigPtr config,
                     RunAdAuctionCallback callback) override;
+  void DeprecatedGetURLFromURN(
+      const GURL& urn_url,
+      DeprecatedGetURLFromURNCallback callback) override;
   void CreateAdRequest(blink::mojom::AdRequestConfigPtr config,
                        CreateAdRequestCallback callback) override;
   void FinalizeAd(const std::string& ads_guid,
diff --git a/content/browser/interest_group/ad_auction_service_impl_unittest.cc b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
index 2e5ae14..4812d1a 100644
--- a/content/browser/interest_group/ad_auction_service_impl_unittest.cc
+++ b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
@@ -403,8 +403,8 @@
 
   int GetJoinCount(const url::Origin& owner, const std::string& name) {
     for (const auto& interest_group : GetInterestGroupsForOwner(owner)) {
-      if (interest_group.bidding_group->group.name == name) {
-        return interest_group.bidding_group->signals->join_count;
+      if (interest_group.interest_group.name == name) {
+        return interest_group.bidding_browser_signals->join_count;
       }
     }
     return 0;
@@ -782,7 +782,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   EXPECT_EQ(group.name, kInterestGroupName);
   ASSERT_TRUE(group.bidding_url.has_value());
   EXPECT_EQ(group.bidding_url->spec(),
@@ -833,7 +833,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   EXPECT_EQ(group.name, kInterestGroupName);
   ASSERT_TRUE(group.bidding_url.has_value());
   EXPECT_EQ(
@@ -885,7 +885,7 @@
   const auto groups_before_update = GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups_before_update.size(), 1u);
   const base::Time kExpirationTime =
-      groups_before_update[0].bidding_group->group.expiry;
+      groups_before_update[0].interest_group.expiry;
 
   UpdateInterestGroupNoFlush();
   task_environment()->RunUntilIdle();
@@ -894,7 +894,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   EXPECT_EQ(group.name, kInterestGroupName);
   EXPECT_EQ(group.expiry, kExpirationTime);
   ASSERT_TRUE(group.ads.has_value());
@@ -937,7 +937,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   EXPECT_EQ(group.name, kInterestGroupName);
   ASSERT_TRUE(group.bidding_url.has_value());
   EXPECT_EQ(
@@ -995,7 +995,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -1038,7 +1038,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -1110,12 +1110,12 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 2u);
-  const auto& first_group = groups[0].bidding_group->group.name == kGroupName1
-                                ? groups[0].bidding_group->group
-                                : groups[1].bidding_group->group;
-  const auto& second_group = groups[0].bidding_group->group.name == kGroupName2
-                                 ? groups[0].bidding_group->group
-                                 : groups[1].bidding_group->group;
+  const auto& first_group = groups[0].interest_group.name == kGroupName1
+                                ? groups[0].interest_group
+                                : groups[1].interest_group;
+  const auto& second_group = groups[0].interest_group.name == kGroupName2
+                                 ? groups[0].interest_group
+                                 : groups[1].interest_group;
 
   EXPECT_EQ(first_group.name, kGroupName1);
   ASSERT_TRUE(first_group.ads.has_value());
@@ -1186,7 +1186,7 @@
   std::vector<StorageInterestGroup> origin_b_groups =
       GetInterestGroupsForOwner(kOriginB);
   ASSERT_EQ(origin_b_groups.size(), 1u);
-  const auto& origin_b_group = origin_b_groups[0].bidding_group->group;
+  const auto& origin_b_group = origin_b_groups[0].interest_group;
   EXPECT_EQ(origin_b_group.name, kInterestGroupName);
   ASSERT_TRUE(origin_b_group.ads.has_value());
   ASSERT_EQ(origin_b_group.ads->size(), 1u);
@@ -1198,7 +1198,7 @@
   std::vector<StorageInterestGroup> origin_a_groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(origin_a_groups.size(), 1u);
-  const auto& origin_a_group = origin_a_groups[0].bidding_group->group;
+  const auto& origin_a_group = origin_a_groups[0].interest_group;
   ASSERT_TRUE(origin_a_group.ads.has_value());
   ASSERT_EQ(origin_a_group.ads->size(), 1u);
   EXPECT_EQ(origin_a_group.ads.value()[0].render_url.spec(),
@@ -1288,7 +1288,7 @@
   std::vector<StorageInterestGroup> origin_c_groups =
       GetInterestGroupsForOwner(kOriginC);
   ASSERT_EQ(origin_c_groups.size(), 1u);
-  const auto& origin_c_group = origin_c_groups[0].bidding_group->group;
+  const auto& origin_c_group = origin_c_groups[0].interest_group;
   EXPECT_EQ(origin_c_group.name, kInterestGroupName);
   ASSERT_TRUE(origin_c_group.ads.has_value());
   ASSERT_EQ(origin_c_group.ads->size(), 1u);
@@ -1300,7 +1300,7 @@
   std::vector<StorageInterestGroup> origin_a_groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(origin_a_groups.size(), 1u);
-  const auto& origin_a_group = origin_a_groups[0].bidding_group->group;
+  const auto& origin_a_group = origin_a_groups[0].interest_group;
   ASSERT_TRUE(origin_a_group.ads.has_value());
   ASSERT_EQ(origin_a_group.ads->size(), 1u);
   EXPECT_EQ(origin_a_group.ads.value()[0].render_url.spec(),
@@ -1321,7 +1321,7 @@
   std::vector<StorageInterestGroup> origin_b_groups =
       GetInterestGroupsForOwner(kOriginB);
   ASSERT_EQ(origin_b_groups.size(), 1u);
-  const auto& origin_b_group = origin_b_groups[0].bidding_group->group;
+  const auto& origin_b_group = origin_b_groups[0].interest_group;
   ASSERT_TRUE(origin_b_group.ads.has_value());
   ASSERT_EQ(origin_b_group.ads->size(), 1u);
   EXPECT_EQ(origin_b_group.ads.value()[0].render_url.spec(),
@@ -1363,7 +1363,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -1398,7 +1398,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -1450,7 +1450,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -1484,7 +1484,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -1519,7 +1519,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -1715,7 +1715,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -1757,7 +1757,7 @@
   std::vector<StorageInterestGroup> prev_groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(prev_groups.size(), 1u);
-  const auto& prev_signals = prev_groups[0].bidding_group->signals;
+  const auto& prev_signals = prev_groups[0].bidding_browser_signals;
   EXPECT_EQ(prev_signals->join_count, 1);
   EXPECT_EQ(prev_signals->bid_count, 2);
   EXPECT_EQ(prev_signals->prev_wins.size(), 1u);
@@ -1769,8 +1769,8 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
-  const auto& signals = groups[0].bidding_group->signals;
+  const auto& group = groups[0].interest_group;
+  const auto& signals = groups[0].bidding_browser_signals;
 
   EXPECT_EQ(signals->join_count, 1);
   EXPECT_EQ(signals->bid_count, 2);
@@ -1819,7 +1819,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -1841,7 +1841,7 @@
   std::vector<StorageInterestGroup> groups2 =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups2.size(), 1u);
-  const auto& group2 = groups2[0].bidding_group->group;
+  const auto& group2 = groups2[0].interest_group;
   ASSERT_TRUE(group2.ads.has_value());
   ASSERT_EQ(group2.ads->size(), 1u);
   EXPECT_EQ(group2.ads.value()[0].render_url.spec(),
@@ -1860,7 +1860,7 @@
   std::vector<StorageInterestGroup> groups3 =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups3.size(), 1u);
-  const auto& group3 = groups3[0].bidding_group->group;
+  const auto& group3 = groups3[0].interest_group;
   ASSERT_TRUE(group3.ads.has_value());
   ASSERT_EQ(group3.ads->size(), 1u);
   EXPECT_EQ(group3.ads.value()[0].render_url.spec(),
@@ -1878,7 +1878,7 @@
   std::vector<StorageInterestGroup> groups4 =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups4.size(), 1u);
-  const auto& group4 = groups4[0].bidding_group->group;
+  const auto& group4 = groups4[0].interest_group;
   ASSERT_TRUE(group4.ads.has_value());
   ASSERT_EQ(group4.ads->size(), 1u);
   EXPECT_EQ(group4.ads.value()[0].render_url.spec(),
@@ -1918,7 +1918,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -1941,7 +1941,7 @@
   std::vector<StorageInterestGroup> groups2 =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups2.size(), 1u);
-  const auto& group2 = groups2[0].bidding_group->group;
+  const auto& group2 = groups2[0].interest_group;
   ASSERT_TRUE(group2.ads.has_value());
   ASSERT_EQ(group2.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -1962,7 +1962,7 @@
   std::vector<StorageInterestGroup> groups3 =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups3.size(), 1u);
-  const auto& group3 = groups3[0].bidding_group->group;
+  const auto& group3 = groups3[0].interest_group;
   ASSERT_TRUE(group3.ads.has_value());
   ASSERT_EQ(group3.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -1981,7 +1981,7 @@
   std::vector<StorageInterestGroup> groups4 =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups4.size(), 1u);
-  const auto& group4 = groups4[0].bidding_group->group;
+  const auto& group4 = groups4[0].interest_group;
   ASSERT_TRUE(group4.ads.has_value());
   ASSERT_EQ(group4.ads->size(), 1u);
   EXPECT_EQ(group4.ads.value()[0].render_url.spec(),
@@ -2019,7 +2019,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -2042,7 +2042,7 @@
   std::vector<StorageInterestGroup> groups2 =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups2.size(), 1u);
-  const auto& group2 = groups2[0].bidding_group->group;
+  const auto& group2 = groups2[0].interest_group;
   ASSERT_TRUE(group2.ads.has_value());
   ASSERT_EQ(group2.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -2062,7 +2062,7 @@
   std::vector<StorageInterestGroup> groups3 =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups3.size(), 1u);
-  const auto& group3 = groups3[0].bidding_group->group;
+  const auto& group3 = groups3[0].interest_group;
   ASSERT_TRUE(group3.ads.has_value());
   ASSERT_EQ(group3.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -2081,7 +2081,7 @@
   std::vector<StorageInterestGroup> groups4 =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups4.size(), 1u);
-  const auto& group4 = groups4[0].bidding_group->group;
+  const auto& group4 = groups4[0].interest_group;
   ASSERT_TRUE(group4.ads.has_value());
   ASSERT_EQ(group4.ads->size(), 1u);
   EXPECT_EQ(group4.ads.value()[0].render_url.spec(),
@@ -2120,7 +2120,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -2143,7 +2143,7 @@
   std::vector<StorageInterestGroup> groups2 =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups2.size(), 1u);
-  const auto& group2 = groups2[0].bidding_group->group;
+  const auto& group2 = groups2[0].interest_group;
   ASSERT_TRUE(group2.ads.has_value());
   ASSERT_EQ(group2.ads->size(), 1u);
   EXPECT_EQ(group2.ads.value()[0].render_url.spec(),
@@ -2187,7 +2187,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   ASSERT_TRUE(group.ads.has_value());
   ASSERT_EQ(group.ads->size(), 1u);
   EXPECT_EQ(group.ads.value()[0].render_url.spec(),
@@ -2646,7 +2646,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   EXPECT_EQ(group.name, kInterestGroupName);
   ASSERT_TRUE(group.bidding_url.has_value());
   EXPECT_EQ(group.bidding_url->spec(),
@@ -2683,7 +2683,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginA);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   EXPECT_EQ(group.name, kInterestGroupName);
   ASSERT_TRUE(group.bidding_url.has_value());
   EXPECT_EQ(group.bidding_url->spec(),
@@ -2732,7 +2732,7 @@
   std::vector<StorageInterestGroup> groups =
       GetInterestGroupsForOwner(kOriginC);
   ASSERT_EQ(groups.size(), 1u);
-  const auto& group = groups[0].bidding_group->group;
+  const auto& group = groups[0].interest_group;
   EXPECT_EQ(group.name, kInterestGroupName);
   ASSERT_TRUE(group.bidding_url.has_value());
   EXPECT_EQ(group.bidding_url->spec(),
diff --git a/content/browser/interest_group/auction_process_manager_unittest.cc b/content/browser/interest_group/auction_process_manager_unittest.cc
index cccd0f1..cb26ed3 100644
--- a/content/browser/interest_group/auction_process_manager_unittest.cc
+++ b/content/browser/interest_group/auction_process_manager_unittest.cc
@@ -48,11 +48,14 @@
 
   void LoadBidderWorklet(
       mojo::PendingReceiver<auction_worklet::mojom::BidderWorklet>
-          bidder_worklet,
-      bool should_pause_on_start,
-      mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory,
-      auction_worklet::mojom::BiddingInterestGroupPtr bidding_interest_group)
-      override {
+          bidder_worklet_receiver,
+      bool pause_for_debugger_on_start,
+      mojo::PendingRemote<network::mojom::URLLoaderFactory>
+          pending_url_loader_factory,
+      const GURL& script_source_url,
+      const absl::optional<GURL>& bidding_wasm_helper_url,
+      const absl::optional<GURL>& trusted_bidding_signals_url,
+      const url::Origin& top_window_origin) override {
     NOTREACHED();
   }
 
diff --git a/content/browser/interest_group/auction_runner.cc b/content/browser/interest_group/auction_runner.cc
index 388e758..90fb387 100644
--- a/content/browser/interest_group/auction_runner.cc
+++ b/content/browser/interest_group/auction_runner.cc
@@ -192,9 +192,9 @@
     for (auto bidder = std::make_move_iterator(interest_groups.begin());
          bidder != std::make_move_iterator(interest_groups.end()); ++bidder) {
       // Ignore interest groups with no bidding script or no ads.
-      if (!bidder->bidding_group->group.bidding_url)
+      if (!bidder->interest_group.bidding_url)
         continue;
-      if (bidder->bidding_group->group.ads->empty())
+      if (bidder->interest_group.ads->empty())
         continue;
       bid_states_.emplace_back(BidState());
       bid_states_.back().bidder = std::move(*bidder);
@@ -288,7 +288,7 @@
     if (interest_group_manager_->auction_process_manager()
             .RequestWorkletService(
                 AuctionProcessManager::WorkletType::kBidder,
-                bid_state.bidder.bidding_group->group.owner,
+                bid_state.bidder.interest_group.owner,
                 bid_state.process_handle.get(),
                 base::BindOnce(&AuctionRunner::OnBidderWorkletProcessReceived,
                                base::Unretained(this), &bid_state))) {
@@ -303,22 +303,35 @@
   bid_state->state = BidState::State::kGeneratingBid;
   LoadBidderWorklet(*bid_state,
                     /*disconnect_handler=*/base::BindOnce(
-                        &AuctionRunner::OnGenerateBidCrashed,
+                        &AuctionRunner::OnBidderWorkletDisconnected,
                         base::Unretained(this), bid_state));
+  const blink::InterestGroup& interest_group = bid_state->bidder.interest_group;
   bid_state->bidder_worklet->GenerateBid(
+      auction_worklet::mojom::BidderWorkletNonSharedParams::New(
+          interest_group.name, interest_group.trusted_bidding_signals_keys,
+          interest_group.user_bidding_signals, interest_group.ads,
+          interest_group.ad_components),
       auction_config_->shareable_auction_ad_config->auction_signals,
-      PerBuyerSignals(bid_state), browser_signals_->top_frame_origin,
-      browser_signals_->seller, auction_start_time_,
+      PerBuyerSignals(bid_state), browser_signals_->seller,
+      bid_state->bidder.bidding_browser_signals.Clone(), auction_start_time_,
       base::BindOnce(&AuctionRunner::OnGenerateBidComplete,
                      base::Unretained(this), bid_state));
 }
 
-void AuctionRunner::OnGenerateBidCrashed(BidState* state) {
-  OnGenerateBidComplete(
-      state, auction_worklet::mojom::BidderWorkletBidPtr(),
-      std::vector<std::string>{
-          base::StrCat({state->bidder.bidding_group->group.bidding_url->spec(),
-                        " crashed while trying to run generateBid()."})});
+void AuctionRunner::OnBidderWorkletDisconnected(
+    BidState* state,
+    uint32_t custom_reason,
+    const std::string& description) {
+  std::vector<std::string> errors;
+  if (description.empty()) {
+    errors.emplace_back(
+        base::StrCat({state->bidder.interest_group.bidding_url->spec(),
+                      " crashed while trying to run generateBid()."}));
+  } else {
+    errors.push_back(description);
+  }
+  OnGenerateBidComplete(state, auction_worklet::mojom::BidderWorkletBidPtr(),
+                        errors);
 }
 
 void AuctionRunner::OnGenerateBidComplete(
@@ -338,8 +351,7 @@
 
   // Ignore invalid bids.
   if (bid) {
-    state->bid_ad =
-        ValidateBidAndGetAd(*bid, state->bidder.bidding_group->group);
+    state->bid_ad = ValidateBidAndGetAd(*bid, state->bidder.interest_group);
     if (!state->bid_ad)
       bid.reset();
   }
@@ -402,7 +414,7 @@
   seller_worklet_->ScoreAd(
       state->bid_result->ad, state->bid_result->bid,
       auction_config_->shareable_auction_ad_config.Clone(),
-      state->bidder.bidding_group->group.owner, state->bid_result->render_url,
+      state->bidder.interest_group.owner, state->bid_result->render_url,
       state->bid_result->ad_components ? *state->bid_result->ad_components
                                        : std::vector<GURL>(),
       state->bid_result->bid_duration.InMilliseconds(),
@@ -451,8 +463,8 @@
   const auto& per_buyer_signals =
       auction_config_->shareable_auction_ad_config->per_buyer_signals;
   if (per_buyer_signals.has_value()) {
-    auto it = per_buyer_signals.value().find(
-        state->bidder.bidding_group->group.owner);
+    auto it =
+        per_buyer_signals.value().find(state->bidder.interest_group.owner);
     if (it != per_buyer_signals.value().end())
       return it->second;
   }
@@ -476,8 +488,8 @@
     if (bid_state.bid_result) {
       some_bidder_bid = true;
       interest_group_manager_->RecordInterestGroupBid(
-          bid_state.bidder.bidding_group->group.owner,
-          bid_state.bidder.bidding_group->group.name);
+          bid_state.bidder.interest_group.owner,
+          bid_state.bidder.interest_group.name);
     }
   }
 
@@ -499,7 +511,7 @@
 
   seller_worklet_->ReportResult(
       auction_config_->shareable_auction_ad_config.Clone(),
-      top_bidder_->bidder.bidding_group->group.owner,
+      top_bidder_->bidder.interest_group.owner,
       top_bidder_->bid_result->render_url, top_bidder_->bid_result->bid,
       top_bidder_->seller_score,
       base::BindOnce(&AuctionRunner::OnReportSellerResultComplete,
@@ -534,7 +546,7 @@
       std::make_unique<AuctionProcessManager::ProcessHandle>();
   if (interest_group_manager_->auction_process_manager().RequestWorkletService(
           AuctionProcessManager::WorkletType::kBidder,
-          top_bidder_->bidder.bidding_group->group.owner,
+          top_bidder_->bidder.interest_group.owner,
           top_bidder_->process_handle.get(),
           base::BindOnce(&AuctionRunner::ReportBidWin, base::Unretained(this),
                          signals_for_winner))) {
@@ -560,18 +572,15 @@
 
   // Load the script for the top-scoring bidder worklet again, and invoke its
   // ReportWin() method.
-  LoadBidderWorklet(
-      *top_bidder_, /*disconnect_handler=*/base::BindOnce(
-          &AuctionRunner::FailAuction, base::Unretained(this),
-          AuctionResult::kWinningBidderWorkletCrashed,
-          base::StrCat(
-              {top_bidder_->bidder.bidding_group->group.bidding_url->spec(),
-               " crashed while trying to run reportWin()."})));
+  LoadBidderWorklet(*top_bidder_, /*disconnect_handler=*/base::BindOnce(
+                        &AuctionRunner::WinningBidderWorkletDisconnected,
+                        base::Unretained(this)));
   top_bidder_->bidder_worklet->ReportWin(
+      top_bidder_->bidder.interest_group.name,
       auction_config_->shareable_auction_ad_config->auction_signals,
-      PerBuyerSignals(top_bidder_), browser_signals_->top_frame_origin,
-      signals_for_winner_arg, top_bidder_->bid_result->render_url,
-      top_bidder_->bid_result->bid, auction_config_->seller,
+      PerBuyerSignals(top_bidder_), signals_for_winner_arg,
+      top_bidder_->bid_result->render_url, top_bidder_->bid_result->bid,
+      auction_config_->seller,
       base::BindOnce(&AuctionRunner::OnReportBidWinComplete,
                      base::Unretained(this)));
 }
@@ -590,6 +599,24 @@
   ReportSuccess();
 }
 
+void AuctionRunner::WinningBidderWorkletDisconnected(
+    uint32_t custom_reason,
+    const std::string& description) {
+  std::vector<std::string> errors;
+  if (description.empty()) {
+    // Empty `description` means the process crashed.
+    FailAuction(
+        AuctionResult::kWinningBidderWorkletCrashed,
+        {base::StrCat({top_bidder_->bidder.interest_group.bidding_url->spec(),
+                       " crashed while trying to run reportWin()."})});
+  } else {
+    // Non-empty `description` means there was a different error (e.g., script
+    // failed to load). This currently does not fail the auction.
+    errors_.push_back(description);
+    ReportSuccess();
+  }
+}
+
 void AuctionRunner::ReportSuccess() {
   DCHECK(callback_);
   DCHECK(top_bidder_->bid_result);
@@ -611,8 +638,8 @@
   }
 
   interest_group_manager_->RecordInterestGroupWin(
-      top_bidder_->bidder.bidding_group->group.owner,
-      top_bidder_->bidder.bidding_group->group.name, ad_metadata);
+      top_bidder_->bidder.interest_group.owner,
+      top_bidder_->bidder.interest_group.name, ad_metadata);
 
   std::move(callback_).Run(this, top_bidder_->bid_result->render_url,
                            top_bidder_->bid_result->ad_components,
@@ -658,14 +685,13 @@
   }
 }
 
-void AuctionRunner::LoadBidderWorklet(BidState& bid_state,
-                                      base::OnceClosure disconnect_handler) {
-  auction_worklet::mojom::BiddingInterestGroup* bidder =
-      bid_state.bidder.bidding_group.get();
+void AuctionRunner::LoadBidderWorklet(
+    BidState& bid_state,
+    mojo::ConnectionErrorWithReasonCallback disconnect_handler) {
+  const blink::InterestGroup& interest_group = bid_state.bidder.interest_group;
 
   mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory;
-  GURL bidding_url =
-      bid_state.bidder.bidding_group->group.bidding_url.value_or(GURL());
+  GURL bidding_url = interest_group.bidding_url.value_or(GURL());
   bid_state.url_loader_factory = std::make_unique<AuctionURLLoaderFactoryProxy>(
       url_loader_factory.InitWithNewPipeAndPassReceiver(),
       // BidderWorklets don't need frame URLLoaderFactories.
@@ -674,8 +700,8 @@
                           base::Unretained(delegate_)),
       browser_signals_->top_frame_origin, frame_origin_,
       /*is_for_seller=*/false, delegate_->GetClientSecurityState(), bidding_url,
-      bid_state.bidder.bidding_group->group.bidding_wasm_helper_url,
-      bidder->group.trusted_bidding_signals_url);
+      bid_state.bidder.interest_group.bidding_wasm_helper_url,
+      interest_group.trusted_bidding_signals_url);
 
   mojo::PendingReceiver<auction_worklet::mojom::BidderWorklet>
       worklet_receiver = bid_state.bidder_worklet.BindNewPipeAndPassReceiver();
@@ -686,8 +712,11 @@
   bid_state.process_handle->GetService()->LoadBidderWorklet(
       std::move(worklet_receiver),
       bid_state.bidder_worklet_debug->should_pause_on_start(),
-      std::move(url_loader_factory), bidder->Clone());
-  bid_state.bidder_worklet.set_disconnect_handler(
+      std::move(url_loader_factory), *interest_group.bidding_url,
+      interest_group.bidding_wasm_helper_url,
+      interest_group.trusted_bidding_signals_url,
+      browser_signals_->top_frame_origin);
+  bid_state.bidder_worklet.set_disconnect_with_reason_handler(
       std::move(disconnect_handler));
 }
 
diff --git a/content/browser/interest_group/auction_runner.h b/content/browser/interest_group/auction_runner.h
index c225d95..9022c670 100644
--- a/content/browser/interest_group/auction_runner.h
+++ b/content/browser/interest_group/auction_runner.h
@@ -21,6 +21,7 @@
 #include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
 #include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
 #include "content/services/auction_worklet/public/mojom/seller_worklet.mojom.h"
+#include "mojo/public/cpp/bindings/connection_error_callback.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/mojom/client_security_state.mojom-forward.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
@@ -266,7 +267,9 @@
   // bid.
   void OnBidderWorkletProcessReceived(BidState* bid_state);
 
-  void OnGenerateBidCrashed(BidState* state);
+  void OnBidderWorkletDisconnected(BidState* state,
+                                   uint32_t custom_reason,
+                                   const std::string& description);
   void OnGenerateBidComplete(BidState* state,
                              auction_worklet::mojom::BidderWorkletBidPtr bid,
                              const std::vector<std::string>& errors);
@@ -305,6 +308,11 @@
   void OnReportBidWinComplete(const absl::optional<GURL>& bidder_report_url,
                               const std::vector<std::string>& error_msgs);
 
+  // Called when the BidderWorklet that won an auction is disconnected during
+  // the ReportWin() call.
+  void WinningBidderWorkletDisconnected(uint32_t custom_reason,
+                                        const std::string& description);
+
   // Completes the auction, invoking `callback_` and preventing any future
   // calls into `this` by closing mojo pipes and disposing of weak pointers. The
   // owner must be able to safely delete `this` when the callback is invoked.
@@ -319,8 +327,9 @@
 
   // Loads a bidder worklet for `bid_state`, setting the provided disconnect
   // handler.
-  void LoadBidderWorklet(BidState& bid_state,
-                         base::OnceClosure disconnect_handler);
+  void LoadBidderWorklet(
+      BidState& bid_state,
+      mojo::ConnectionErrorWithReasonCallback disconnect_handler);
 
   const raw_ptr<Delegate> delegate_;
   const raw_ptr<InterestGroupManager> interest_group_manager_;
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index b8fcf4fc..936fa8c 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -314,12 +314,15 @@
 
   // auction_worklet::mojom::BidderWorklet implementation:
 
-  void GenerateBid(const absl::optional<std::string>& auction_signals_json,
-                   const absl::optional<std::string>& per_buyer_signals_json,
-                   const url::Origin& top_window_origin,
-                   const url::Origin& seller_origin,
-                   base::Time auction_start_time,
-                   GenerateBidCallback generate_bid_callback) override {
+  void GenerateBid(
+      auction_worklet::mojom::BidderWorkletNonSharedParamsPtr
+          bidder_worklet_non_shared_params,
+      const absl::optional<std::string>& auction_signals_json,
+      const absl::optional<std::string>& per_buyer_signals_json,
+      const url::Origin& seller_origin,
+      auction_worklet::mojom::BiddingBrowserSignalsPtr bidding_browser_signals,
+      base::Time auction_start_time,
+      GenerateBidCallback generate_bid_callback) override {
     // While the real BidderWorklet implementation supports multiple pending
     // callbacks, this class does not.
     DCHECK(!generate_bid_callback_);
@@ -328,9 +331,9 @@
       generate_bid_run_loop_->Quit();
   }
 
-  void ReportWin(const absl::optional<std::string>& auction_signals_json,
+  void ReportWin(const std::string& interest_group_name,
+                 const absl::optional<std::string>& auction_signals_json,
                  const absl::optional<std::string>& per_buyer_signals_json,
-                 const url::Origin& top_window_origin,
                  const std::string& seller_signals_json,
                  const GURL& browser_signal_render_url,
                  double browser_signal_bid,
@@ -631,21 +634,22 @@
   void LoadBidderWorklet(
       mojo::PendingReceiver<auction_worklet::mojom::BidderWorklet>
           bidder_worklet_receiver,
-      bool should_pause_on_start,
+      bool pause_for_debugger_on_start,
       mojo::PendingRemote<network::mojom::URLLoaderFactory>
           pending_url_loader_factory,
-      auction_worklet::mojom::BiddingInterestGroupPtr bidding_interest_group)
-      override {
+      const GURL& script_source_url,
+      const absl::optional<GURL>& bidding_wasm_helper_url,
+      const absl::optional<GURL>& trusted_bidding_signals_url,
+      const url::Origin& top_window_origin) override {
     // Make sure this request came over the right pipe.
+    url::Origin owner = url::Origin::Create(script_source_url);
     EXPECT_EQ(receiver_display_name_map_[receiver_set_.current_receiver()],
               ComputeDisplayName(AuctionProcessManager::WorkletType::kBidder,
-                                 bidding_interest_group->group.owner));
+                                 url::Origin::Create(script_source_url)));
 
-    InterestGroupId interest_group_id(bidding_interest_group->group.owner,
-                                      bidding_interest_group->group.name);
-    EXPECT_EQ(0u, bidder_worklets_.count(interest_group_id));
+    EXPECT_EQ(0u, bidder_worklets_.count(script_source_url));
     bidder_worklets_.emplace(std::make_pair(
-        interest_group_id, std::make_unique<MockBidderWorklet>(
+        script_source_url, std::make_unique<MockBidderWorklet>(
                                std::move(bidder_worklet_receiver),
                                std::move(pending_url_loader_factory))));
     // Whenever a worklet is created, one of the RunLoops should be waiting for
@@ -706,14 +710,11 @@
     EXPECT_EQ(1u, bidder_worklets_.size());
   }
 
-  // Returns the MockBidderWorklet created for the specified interest group
-  // origin and name, if there is one.
+  // Returns the MockBidderWorklet created for the specified script URL, if
+  // there is one.
   std::unique_ptr<MockBidderWorklet> TakeBidderWorklet(
-      const url::Origin& interest_group_owner_origin,
-      const std::string& interest_group_name) {
-    InterestGroupId interest_group_id(interest_group_owner_origin,
-                                      interest_group_name);
-    auto it = bidder_worklets_.find(interest_group_id);
+      const GURL& script_source_url) {
+    auto it = bidder_worklets_.find(script_source_url);
     if (it == bidder_worklets_.end())
       return nullptr;
     std::unique_ptr<MockBidderWorklet> out = std::move(it->second);
@@ -740,11 +741,8 @@
       wait_for_worklets_run_loop_->Quit();
   }
 
-  // An interest group is uniquely identified by its owner's origin and name.
-  using InterestGroupId = std::pair<url::Origin, std::string>;
-
-  std::map<InterestGroupId, std::unique_ptr<MockBidderWorklet>>
-      bidder_worklets_;
+  // Map of script URLs to bidder worklets.
+  std::map<GURL, std::unique_ptr<MockBidderWorklet>> bidder_worklets_;
 
   std::unique_ptr<MockSellerWorklet> seller_worklet_;
 
@@ -917,19 +915,17 @@
 
     // Add previous wins and bids to the interest group manager.
     for (auto& bidder : bidders) {
-      for (int i = 0; i < bidder.bidding_group->signals->join_count; i++) {
+      for (int i = 0; i < bidder.bidding_browser_signals->join_count; i++) {
         interest_group_manager_->JoinInterestGroup(
-            bidder.bidding_group->group,
-            bidder.bidding_group->group.owner.GetURL());
+            bidder.interest_group, bidder.interest_group.owner.GetURL());
       }
-      for (int i = 0; i < bidder.bidding_group->signals->bid_count; i++) {
+      for (int i = 0; i < bidder.bidding_browser_signals->bid_count; i++) {
         interest_group_manager_->RecordInterestGroupBid(
-            bidder.bidding_group->group.owner,
-            bidder.bidding_group->group.name);
+            bidder.interest_group.owner, bidder.interest_group.name);
       }
-      for (const auto& prev_win : bidder.bidding_group->signals->prev_wins) {
+      for (const auto& prev_win : bidder.bidding_browser_signals->prev_wins) {
         interest_group_manager_->RecordInterestGroupWin(
-            bidder.bidding_group->group.owner, bidder.bidding_group->group.name,
+            bidder.interest_group.owner, bidder.interest_group.name,
             prev_win->ad_json);
         // Add some time between interest group wins, so that they'll be added
         // to the database in the order they appear. Their times will *not*
@@ -988,11 +984,11 @@
   void OnBidder1GroupsRetrieved(
       std::vector<StorageInterestGroup> storage_interest_groups) {
     for (auto& bidder : storage_interest_groups) {
-      if (bidder.bidding_group->group.owner == kBidder1 &&
-          bidder.bidding_group->group.name == kBidder1Name) {
-        result_.bidder1_bid_count = bidder.bidding_group->signals->bid_count;
+      if (bidder.interest_group.owner == kBidder1 &&
+          bidder.interest_group.name == kBidder1Name) {
+        result_.bidder1_bid_count = bidder.bidding_browser_signals->bid_count;
         result_.bidder1_prev_wins =
-            std::move(bidder.bidding_group->signals->prev_wins);
+            std::move(bidder.bidding_browser_signals->prev_wins);
         SortPrevWins(result_.bidder1_prev_wins);
         break;
       }
@@ -1005,11 +1001,11 @@
   void OnBidder2GroupsRetrieved(
       std::vector<StorageInterestGroup> storage_interest_groups) {
     for (auto& bidder : storage_interest_groups) {
-      if (bidder.bidding_group->group.owner == kBidder2 &&
-          bidder.bidding_group->group.name == kBidder2Name) {
-        result_.bidder2_bid_count = bidder.bidding_group->signals->bid_count;
+      if (bidder.interest_group.owner == kBidder2 &&
+          bidder.interest_group.name == kBidder2Name) {
+        result_.bidder2_bid_count = bidder.bidding_browser_signals->bid_count;
         result_.bidder2_prev_wins =
-            std::move(bidder.bidding_group->signals->prev_wins);
+            std::move(bidder.bidding_browser_signals->prev_wins);
         SortPrevWins(result_.bidder2_prev_wins);
         break;
       }
@@ -1018,7 +1014,7 @@
     auction_run_loop_->Quit();
   }
 
-  auction_worklet::mojom::BiddingInterestGroupPtr MakeInterestGroup(
+  StorageInterestGroup MakeInterestGroup(
       url::Origin owner,
       std::string name,
       absl::optional<GURL> bidding_url,
@@ -1055,16 +1051,18 @@
     previous_wins.push_back(auction_worklet::mojom::PreviousWin::New(
         base::Time::Now(), R"({"winner": -2})"));
 
-    return auction_worklet::mojom::BiddingInterestGroup::New(
-        blink::InterestGroup(
-            base::Time::Max(), std::move(owner), std::move(name),
-            std::move(bidding_url),
-            /* bidding_wasm_helper_url = */ absl::nullopt,
-            /* update_url = */ GURL(), std::move(trusted_bidding_signals_url),
-            std::move(trusted_bidding_signals_keys), absl::nullopt,
-            std::move(ads), std::move(ad_components)),
+    StorageInterestGroup storage_group;
+    storage_group.interest_group = blink::InterestGroup(
+        base::Time::Max(), std::move(owner), std::move(name),
+        std::move(bidding_url),
+        /* bidding_wasm_helper_url = */ absl::nullopt,
+        /* update_url = */ GURL(), std::move(trusted_bidding_signals_url),
+        std::move(trusted_bidding_signals_keys), absl::nullopt, std::move(ads),
+        std::move(ad_components));
+    storage_group.bidding_browser_signals =
         auction_worklet::mojom::BiddingBrowserSignals::New(
-            3, 5, std::move(previous_wins)));
+            3, 5, std::move(previous_wins));
+    return storage_group;
   }
 
   void StartStandardAuction() {
@@ -2343,82 +2341,68 @@
 }
 
 TEST_F(AuctionRunnerTest, AllBiddersCrashBeforeBidding) {
-  for (bool seller_worklet_loads_first : {false, true}) {
-    observer_log_.clear();
-    SCOPED_TRACE(seller_worklet_loads_first);
+  StartStandardAuctionWithMockService();
+  auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
+  ASSERT_TRUE(seller_worklet);
+  auto bidder1_worklet =
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
+  ASSERT_TRUE(bidder1_worklet);
+  auto bidder2_worklet =
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder2Url);
+  ASSERT_TRUE(bidder2_worklet);
 
-    StartStandardAuctionWithMockService();
-    auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
-    ASSERT_TRUE(seller_worklet);
-    auto bidder1_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-        kBidder1, kBidder1Name);
-    ASSERT_TRUE(bidder1_worklet);
-    auto bidder2_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-        kBidder2, kBidder2Name);
-    ASSERT_TRUE(bidder2_worklet);
+  seller_worklet->CompleteLoading();
 
-    if (seller_worklet_loads_first) {
-      seller_worklet->CompleteLoading();
-      mock_auction_process_manager_->Flush();
-    }
-    EXPECT_FALSE(auction_complete_);
+  EXPECT_FALSE(auction_complete_);
 
-    EXPECT_THAT(observer_log_,
-                testing::UnorderedElementsAre(
-                    "Create https://adstuff.publisher1.com/auction.js",
-                    "Create https://adplatform.com/offers.js",
-                    "Create https://anotheradthing.com/bids.js"));
+  EXPECT_THAT(observer_log_,
+              testing::UnorderedElementsAre(
+                  "Create https://adstuff.publisher1.com/auction.js",
+                  "Create https://adplatform.com/offers.js",
+                  "Create https://anotheradthing.com/bids.js"));
 
-    EXPECT_THAT(LiveDebuggables(),
-                testing::UnorderedElementsAre(
-                    "https://adplatform.com/offers.js",
-                    "https://anotheradthing.com/bids.js",
-                    "https://adstuff.publisher1.com/auction.js"));
+  EXPECT_THAT(LiveDebuggables(),
+              testing::UnorderedElementsAre(
+                  "https://adplatform.com/offers.js",
+                  "https://anotheradthing.com/bids.js",
+                  "https://adstuff.publisher1.com/auction.js"));
 
-    bidder1_worklet.reset();
-    bidder2_worklet.reset();
+  bidder1_worklet.reset();
+  bidder2_worklet.reset();
 
-    task_environment_.RunUntilIdle();
+  task_environment_.RunUntilIdle();
 
-    EXPECT_THAT(observer_log_,
-                testing::UnorderedElementsAre(
-                    "Create https://adstuff.publisher1.com/auction.js",
-                    "Create https://adplatform.com/offers.js",
-                    "Create https://anotheradthing.com/bids.js",
-                    "Destroy https://adplatform.com/offers.js",
-                    "Destroy https://anotheradthing.com/bids.js",
-                    "Destroy https://adstuff.publisher1.com/auction.js"));
+  EXPECT_THAT(observer_log_,
+              testing::UnorderedElementsAre(
+                  "Create https://adstuff.publisher1.com/auction.js",
+                  "Create https://adplatform.com/offers.js",
+                  "Create https://anotheradthing.com/bids.js",
+                  "Destroy https://adplatform.com/offers.js",
+                  "Destroy https://anotheradthing.com/bids.js",
+                  "Destroy https://adstuff.publisher1.com/auction.js"));
 
-    EXPECT_THAT(LiveDebuggables(), testing::ElementsAre());
+  EXPECT_THAT(LiveDebuggables(), testing::ElementsAre());
 
-    // The auction isn't failed until the seller worklet has completed loading.
-    if (!seller_worklet_loads_first)
-      seller_worklet->CompleteLoading();
+  auction_run_loop_->Run();
 
-    auction_run_loop_->Run();
+  EXPECT_FALSE(result_.ad_url);
+  EXPECT_FALSE(result_.ad_component_urls);
+  EXPECT_FALSE(result_.seller_report_url);
+  EXPECT_FALSE(result_.bidder_report_url);
+  EXPECT_EQ(5, result_.bidder1_bid_count);
+  EXPECT_EQ(3u, result_.bidder1_prev_wins.size());
+  EXPECT_EQ(5, result_.bidder2_bid_count);
+  EXPECT_EQ(3u, result_.bidder2_prev_wins.size());
+  EXPECT_THAT(
+      result_.errors,
+      testing::UnorderedElementsAre(
+          base::StringPrintf("%s crashed while trying to run generateBid().",
+                             kBidder1Url.spec().c_str()),
+          base::StringPrintf("%s crashed while trying to run generateBid().",
+                             kBidder2Url.spec().c_str())));
 
-    EXPECT_FALSE(result_.ad_url);
-    EXPECT_FALSE(result_.ad_component_urls);
-    EXPECT_FALSE(result_.seller_report_url);
-    EXPECT_FALSE(result_.bidder_report_url);
-    EXPECT_EQ(5, result_.bidder1_bid_count);
-    EXPECT_EQ(3u, result_.bidder1_prev_wins.size());
-    EXPECT_EQ(5, result_.bidder2_bid_count);
-    EXPECT_EQ(3u, result_.bidder2_prev_wins.size());
-    EXPECT_THAT(
-        result_.errors,
-        testing::UnorderedElementsAre(
-            base::StringPrintf("%s crashed while trying to run generateBid().",
-                               kBidder1Url.spec().c_str()),
-            base::StringPrintf("%s crashed while trying to run generateBid().",
-                               kBidder2Url.spec().c_str())));
-
-    // Need to close the AuctionWorkletService pipes so callbacks can be
-    // destroyed without DCHECKing.
-    mock_auction_process_manager_->ClosePipes();
-    CheckHistograms(AuctionRunner::AuctionResult::kNoBids,
-                    2 /* expected_interest_groups */, 2 /* expected_owners */);
-  }
+  CheckHistograms(AuctionRunner::AuctionResult::kNoBids,
+                  2 /* expected_interest_groups */, 2 /* expected_owners */);
 }
 
 // Test the case a single bidder worklet crashes before bidding. The auction
@@ -2426,103 +2410,94 @@
 TEST_F(AuctionRunnerTest, BidderCrashBeforeBidding) {
   for (bool other_bidder_finishes_first : {false, true}) {
     SCOPED_TRACE(other_bidder_finishes_first);
-    for (bool seller_worklet_loads_first : {false, true}) {
-      SCOPED_TRACE(seller_worklet_loads_first);
-      observer_log_.clear();
-      StartStandardAuctionWithMockService();
-      auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
-      ASSERT_TRUE(seller_worklet);
-      auto bidder1_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-          kBidder1, kBidder1Name);
-      ASSERT_TRUE(bidder1_worklet);
-      auto bidder2_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-          kBidder2, kBidder2Name);
-      ASSERT_TRUE(bidder2_worklet);
 
-      ASSERT_FALSE(auction_complete_);
-      if (seller_worklet_loads_first)
-        seller_worklet->CompleteLoading();
-      if (other_bidder_finishes_first) {
-        bidder2_worklet->InvokeGenerateBidCallback(7 /* bid */,
-                                                   GURL("https://ad2.com/"));
-        // The bidder pipe should be closed after it bids.
-        EXPECT_TRUE(bidder2_worklet->PipeIsClosed());
-        bidder2_worklet.reset();
-      }
-      mock_auction_process_manager_->Flush();
+    observer_log_.clear();
+    StartStandardAuctionWithMockService();
+    auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
+    ASSERT_TRUE(seller_worklet);
+    auto bidder1_worklet =
+        mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
+    ASSERT_TRUE(bidder1_worklet);
+    auto bidder2_worklet =
+        mock_auction_process_manager_->TakeBidderWorklet(kBidder2Url);
+    ASSERT_TRUE(bidder2_worklet);
 
-      ASSERT_FALSE(auction_complete_);
+    seller_worklet->CompleteLoading();
 
-      // Close Bidder1's pipe.
-      bidder1_worklet.reset();
-      // Can't flush the closed pipe without reaching into AuctionRunner, so use
-      // RunUntilIdle() instead.
-      task_environment_.RunUntilIdle();
-
-      if (!seller_worklet_loads_first)
-        seller_worklet->CompleteLoading();
-      if (!other_bidder_finishes_first) {
-        bidder2_worklet->InvokeGenerateBidCallback(7 /* bid */,
-                                                   GURL("https://ad2.com/"));
-        // The bidder pipe should be closed after it bids.
-        EXPECT_TRUE(bidder2_worklet->PipeIsClosed());
-        bidder2_worklet.reset();
-      }
-      mock_auction_process_manager_->Flush();
-
-      EXPECT_THAT(observer_log_,
-                  testing::UnorderedElementsAre(
-                      "Create https://adstuff.publisher1.com/auction.js",
-                      "Create https://adplatform.com/offers.js",
-                      "Create https://anotheradthing.com/bids.js",
-                      "Destroy https://adplatform.com/offers.js",
-                      "Destroy https://anotheradthing.com/bids.js"));
-
-      EXPECT_THAT(
-          LiveDebuggables(),
-          testing::ElementsAre("https://adstuff.publisher1.com/auction.js"));
-
-      // The auction should be scored without waiting on the crashed kBidder1.
-      auto score_ad_params = seller_worklet->WaitForScoreAd();
-      EXPECT_EQ(kBidder2, score_ad_params.interest_group_owner);
-      EXPECT_EQ(7, score_ad_params.bid);
-      std::move(score_ad_params.callback).Run(/*score=*/11, /*errors=*/{});
-
-      // Finish the auction.
-      seller_worklet->WaitForReportResult();
-      seller_worklet->InvokeReportResultCallback();
-
-      // Worklet 2 should be reloaded and ReportWin() invoked.
-      mock_auction_process_manager_->WaitForWinningBidderReload();
-      bidder2_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-          kBidder2, kBidder2Name);
-      bidder2_worklet->WaitForReportWin();
-      bidder2_worklet->InvokeReportWinCallback();
-
-      // Bidder2 won, Bidder1 crashed.
-      auction_run_loop_->Run();
-      EXPECT_EQ(GURL("https://ad2.com/"), result_.ad_url);
-      EXPECT_FALSE(result_.ad_component_urls);
-      EXPECT_FALSE(result_.seller_report_url);
-      EXPECT_FALSE(result_.bidder_report_url);
-      EXPECT_EQ(5, result_.bidder1_bid_count);
-      EXPECT_EQ(3u, result_.bidder1_prev_wins.size());
-      EXPECT_EQ(6, result_.bidder2_bid_count);
-      ASSERT_EQ(4u, result_.bidder2_prev_wins.size());
-      EXPECT_EQ(R"({"render_url":"https://ad2.com/"})",
-                result_.bidder2_prev_wins[3]->ad_json);
-      EXPECT_THAT(result_.errors,
-                  testing::ElementsAre(base::StringPrintf(
-                      "%s crashed while trying to run generateBid().",
-                      kBidder1Url.spec().c_str())));
-      CheckHistograms(AuctionRunner::AuctionResult::kSuccess,
-                      2 /* expected_interest_groups */,
-                      2 /* expected_owners */);
-
-      // Need to close the AuctionWorkletService pipes so callbacks can be
-      // destroyed without DCHECKing.
-      mock_auction_process_manager_->ClosePipes();
+    ASSERT_FALSE(auction_complete_);
+    if (other_bidder_finishes_first) {
+      bidder2_worklet->InvokeGenerateBidCallback(7 /* bid */,
+                                                 GURL("https://ad2.com/"));
+      // The bidder pipe should be closed after it bids.
+      EXPECT_TRUE(bidder2_worklet->PipeIsClosed());
+      bidder2_worklet.reset();
     }
+    mock_auction_process_manager_->Flush();
+
+    ASSERT_FALSE(auction_complete_);
+
+    // Close Bidder1's pipe.
+    bidder1_worklet.reset();
+    // Can't flush the closed pipe without reaching into AuctionRunner, so use
+    // RunUntilIdle() instead.
+    task_environment_.RunUntilIdle();
+
+    if (!other_bidder_finishes_first) {
+      bidder2_worklet->InvokeGenerateBidCallback(7 /* bid */,
+                                                 GURL("https://ad2.com/"));
+      // The bidder pipe should be closed after it bids.
+      EXPECT_TRUE(bidder2_worklet->PipeIsClosed());
+      bidder2_worklet.reset();
+    }
+    mock_auction_process_manager_->Flush();
+
+    EXPECT_THAT(observer_log_,
+                testing::UnorderedElementsAre(
+                    "Create https://adstuff.publisher1.com/auction.js",
+                    "Create https://adplatform.com/offers.js",
+                    "Create https://anotheradthing.com/bids.js",
+                    "Destroy https://adplatform.com/offers.js",
+                    "Destroy https://anotheradthing.com/bids.js"));
+
+    EXPECT_THAT(
+        LiveDebuggables(),
+        testing::ElementsAre("https://adstuff.publisher1.com/auction.js"));
+
+    // The auction should be scored without waiting on the crashed kBidder1.
+    auto score_ad_params = seller_worklet->WaitForScoreAd();
+    EXPECT_EQ(kBidder2, score_ad_params.interest_group_owner);
+    EXPECT_EQ(7, score_ad_params.bid);
+    std::move(score_ad_params.callback).Run(/*score=*/11, /*errors=*/{});
+
+    // Finish the auction.
+    seller_worklet->WaitForReportResult();
+    seller_worklet->InvokeReportResultCallback();
+
+    // Worklet 2 should be reloaded and ReportWin() invoked.
+    mock_auction_process_manager_->WaitForWinningBidderReload();
+    bidder2_worklet =
+        mock_auction_process_manager_->TakeBidderWorklet(kBidder2Url);
+    bidder2_worklet->WaitForReportWin();
+    bidder2_worklet->InvokeReportWinCallback();
+
+    // Bidder2 won, Bidder1 crashed.
+    auction_run_loop_->Run();
+    EXPECT_EQ(GURL("https://ad2.com/"), result_.ad_url);
+    EXPECT_FALSE(result_.ad_component_urls);
+    EXPECT_FALSE(result_.seller_report_url);
+    EXPECT_FALSE(result_.bidder_report_url);
+    EXPECT_EQ(5, result_.bidder1_bid_count);
+    EXPECT_EQ(3u, result_.bidder1_prev_wins.size());
+    EXPECT_EQ(6, result_.bidder2_bid_count);
+    ASSERT_EQ(4u, result_.bidder2_prev_wins.size());
+    EXPECT_EQ(R"({"render_url":"https://ad2.com/"})",
+              result_.bidder2_prev_wins[3]->ad_json);
+    EXPECT_THAT(result_.errors,
+                testing::ElementsAre(base::StringPrintf(
+                    "%s crashed while trying to run generateBid().",
+                    kBidder1Url.spec().c_str())));
+    CheckHistograms(AuctionRunner::AuctionResult::kSuccess,
+                    2 /* expected_interest_groups */, 2 /* expected_owners */);
   }
 }
 
@@ -2534,10 +2509,10 @@
   auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
   ASSERT_TRUE(seller_worklet);
   auto bidder1_worklet =
-      mock_auction_process_manager_->TakeBidderWorklet(kBidder1, kBidder1Name);
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
   ASSERT_TRUE(bidder1_worklet);
   auto bidder2_worklet =
-      mock_auction_process_manager_->TakeBidderWorklet(kBidder2, kBidder2Name);
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder2Url);
   ASSERT_TRUE(bidder2_worklet);
 
   seller_worklet->CompleteLoading();
@@ -2569,7 +2544,7 @@
   seller_worklet->InvokeReportResultCallback();
   mock_auction_process_manager_->WaitForWinningBidderReload();
   bidder1_worklet =
-      mock_auction_process_manager_->TakeBidderWorklet(kBidder1, kBidder1Name);
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
   bidder1_worklet->WaitForReportWin();
   bidder1_worklet.reset();
   auction_run_loop_->Run();
@@ -2607,11 +2582,11 @@
 
     auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
     ASSERT_TRUE(seller_worklet);
-    auto bidder1_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-        kBidder1, kBidder1Name);
+    auto bidder1_worklet =
+        mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
     ASSERT_TRUE(bidder1_worklet);
-    auto bidder2_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-        kBidder2, kBidder2Name);
+    auto bidder2_worklet =
+        mock_auction_process_manager_->TakeBidderWorklet(kBidder2Url);
     ASSERT_TRUE(bidder2_worklet);
 
     // While loop to allow breaking when the crash stage is reached.
@@ -2726,8 +2701,8 @@
 
     auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
     ASSERT_TRUE(seller_worklet);
-    auto bidder_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-        kBidder1, kBidder1Name);
+    auto bidder_worklet =
+        mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
     ASSERT_TRUE(bidder_worklet);
 
     seller_worklet->CompleteLoading();
@@ -2746,8 +2721,8 @@
       seller_worklet->WaitForReportResult();
       seller_worklet->InvokeReportResultCallback();
       mock_auction_process_manager_->WaitForWinningBidderReload();
-      bidder_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-          kBidder1, kBidder1Name);
+      bidder_worklet =
+          mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
       bidder_worklet->WaitForReportWin();
       bidder_worklet->InvokeReportWinCallback();
       auction_run_loop_->Run();
@@ -2814,8 +2789,8 @@
 
     auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
     ASSERT_TRUE(seller_worklet);
-    auto bidder_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-        kBidder1, kBidder1Name);
+    auto bidder_worklet =
+        mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
     ASSERT_TRUE(bidder_worklet);
 
     seller_worklet->CompleteLoading();
@@ -2833,8 +2808,8 @@
       seller_worklet->WaitForReportResult();
       seller_worklet->InvokeReportResultCallback();
       mock_auction_process_manager_->WaitForWinningBidderReload();
-      bidder_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-          kBidder1, kBidder1Name);
+      bidder_worklet =
+          mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
       bidder_worklet->WaitForReportWin();
       bidder_worklet->InvokeReportWinCallback();
       auction_run_loop_->Run();
@@ -2989,11 +2964,11 @@
 
     auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
     ASSERT_TRUE(seller_worklet);
-    auto bidder1_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-        kBidder1, kBidder1Name);
+    auto bidder1_worklet =
+        mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
     ASSERT_TRUE(bidder1_worklet);
-    auto bidder2_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-        kBidder2, kBidder2Name);
+    auto bidder2_worklet =
+        mock_auction_process_manager_->TakeBidderWorklet(kBidder2Url);
     ASSERT_TRUE(bidder2_worklet);
 
     seller_worklet->CompleteLoading();
@@ -3033,10 +3008,10 @@
   auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
   ASSERT_TRUE(seller_worklet);
   auto bidder1_worklet =
-      mock_auction_process_manager_->TakeBidderWorklet(kBidder1, kBidder1Name);
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
   ASSERT_TRUE(bidder1_worklet);
   auto bidder2_worklet =
-      mock_auction_process_manager_->TakeBidderWorklet(kBidder2, kBidder2Name);
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder2Url);
   ASSERT_TRUE(bidder2_worklet);
 
   seller_worklet->CompleteLoading();
@@ -3081,10 +3056,10 @@
   auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
   ASSERT_TRUE(seller_worklet);
   auto bidder1_worklet =
-      mock_auction_process_manager_->TakeBidderWorklet(kBidder1, kBidder1Name);
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
   ASSERT_TRUE(bidder1_worklet);
   auto bidder2_worklet =
-      mock_auction_process_manager_->TakeBidderWorklet(kBidder2, kBidder2Name);
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder2Url);
   ASSERT_TRUE(bidder2_worklet);
 
   seller_worklet->CompleteLoading();
@@ -3103,7 +3078,7 @@
       GURL("https://valid.url.that.is.thrown.out.test/"));
   mock_auction_process_manager_->WaitForWinningBidderReload();
   bidder1_worklet =
-      mock_auction_process_manager_->TakeBidderWorklet(kBidder1, kBidder1Name);
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
   bidder1_worklet->WaitForReportWin();
   bidder1_worklet->InvokeReportWinCallback(GURL("http://not.https.test/"));
   auction_run_loop_->Run();
@@ -3131,10 +3106,10 @@
   auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
   ASSERT_TRUE(seller_worklet);
   auto bidder1_worklet =
-      mock_auction_process_manager_->TakeBidderWorklet(kBidder1, kBidder1Name);
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
   ASSERT_TRUE(bidder1_worklet);
   auto bidder2_worklet =
-      mock_auction_process_manager_->TakeBidderWorklet(kBidder2, kBidder2Name);
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder2Url);
   ASSERT_TRUE(bidder2_worklet);
 
   // It should be possible to request the seller URL from the seller's
@@ -3220,10 +3195,10 @@
   auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
   ASSERT_TRUE(seller_worklet);
   auto bidder1_worklet =
-      mock_auction_process_manager_->TakeBidderWorklet(kBidder1, kBidder1Name);
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
   ASSERT_TRUE(bidder1_worklet);
   auto bidder2_worklet =
-      mock_auction_process_manager_->TakeBidderWorklet(kBidder2, kBidder2Name);
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder2Url);
   ASSERT_TRUE(bidder2_worklet);
 
   seller_worklet->CompleteLoading();
@@ -3248,7 +3223,7 @@
   seller_worklet->InvokeReportResultCallback();
   mock_auction_process_manager_->WaitForWinningBidderReload();
   bidder2_worklet =
-      mock_auction_process_manager_->TakeBidderWorklet(kBidder2, kBidder2Name);
+      mock_auction_process_manager_->TakeBidderWorklet(kBidder2Url);
   bidder2_worklet->WaitForReportWin();
   bidder2_worklet->InvokeReportWinCallback();
   auction_run_loop_->Run();
@@ -3281,11 +3256,11 @@
 
     auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
     ASSERT_TRUE(seller_worklet);
-    auto bidder1_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-        kBidder1, kBidder1Name);
+    auto bidder1_worklet =
+        mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
     ASSERT_TRUE(bidder1_worklet);
-    auto bidder2_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-        kBidder2, kBidder2Name);
+    auto bidder2_worklet =
+        mock_auction_process_manager_->TakeBidderWorklet(kBidder2Url);
     ASSERT_TRUE(bidder2_worklet);
 
     seller_worklet->CompleteLoading();
@@ -3316,10 +3291,10 @@
     // InterestGroups - only the InterestGroup that was picked as the winner
     // will be non-null.
     mock_auction_process_manager_->WaitForWinningBidderReload();
-    bidder1_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-        kBidder1, kBidder1Name);
-    bidder2_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-        kBidder2, kBidder2Name);
+    bidder1_worklet =
+        mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
+    bidder2_worklet =
+        mock_auction_process_manager_->TakeBidderWorklet(kBidder2Url);
 
     if (bidder1_worklet) {
       seen_bidder1_win = true;
@@ -3395,11 +3370,11 @@
 
       auto seller_worklet = mock_auction_process_manager_->TakeSellerWorklet();
       ASSERT_TRUE(seller_worklet);
-      auto bidder1_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-          kBidder1, kBidder1Name);
+      auto bidder1_worklet =
+          mock_auction_process_manager_->TakeBidderWorklet(kBidder1Url);
       ASSERT_TRUE(bidder1_worklet);
-      auto bidder2_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-          kBidder2, kBidder2Name);
+      auto bidder2_worklet =
+          mock_auction_process_manager_->TakeBidderWorklet(kBidder2Url);
       ASSERT_TRUE(bidder2_worklet);
 
       seller_worklet->CompleteLoading();
@@ -3444,8 +3419,7 @@
 
       mock_auction_process_manager_->WaitForWinningBidderReload();
       auto winning_worklet = mock_auction_process_manager_->TakeBidderWorklet(
-          bidder1_wins ? kBidder1 : kBidder2,
-          bidder1_wins ? kBidder1Name : kBidder2Name);
+          bidder1_wins ? kBidder1Url : kBidder2Url);
       winning_worklet->WaitForReportWin();
       winning_worklet->InvokeReportWinCallback();
       auction_run_loop_->Run();
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index 6457be3..220d49e5 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -372,18 +372,18 @@
   std::vector<std::pair<url::Origin, std::string>> GetAllInterestGroups() {
     std::vector<std::pair<url::Origin, std::string>> interest_groups;
     for (const auto& owner : GetAllInterestGroupsOwners()) {
-      for (const auto& interest_group : GetInterestGroupsForOwner(owner)) {
-        interest_groups.emplace_back(interest_group.bidding_group->group.owner,
-                                     interest_group.bidding_group->group.name);
+      for (const auto& storage_group : GetInterestGroupsForOwner(owner)) {
+        interest_groups.emplace_back(storage_group.interest_group.owner,
+                                     storage_group.interest_group.name);
       }
     }
     return interest_groups;
   }
 
   int GetJoinCount(const url::Origin& owner, const std::string& name) {
-    for (const auto& interest_group : GetInterestGroupsForOwner(owner)) {
-      if (interest_group.bidding_group->group.name == name) {
-        return interest_group.bidding_group->signals->join_count;
+    for (const auto& storage_group : GetInterestGroupsForOwner(owner)) {
+      if (storage_group.interest_group.name == name) {
+        return storage_group.bidding_browser_signals->join_count;
       }
     }
     return 0;
@@ -707,10 +707,23 @@
         static_cast<RenderFrameHostImpl*>(adapter.render_frame_host())
             ->GetPage()
             .fenced_frame_urls_map();
-    absl::optional<FencedFrameURLMapping::PendingAdComponentsMap> ignored;
     fenced_frame_urls_map.ConvertFencedFrameURNToURL(urn_url, observer);
   }
 
+  absl::optional<GURL> ConvertFencedFrameURNToURLInJS(
+      const GURL& urn_url,
+      const absl::optional<ToRenderFrameHost> execution_target =
+          absl::nullopt) {
+    ToRenderFrameHost adapter(execution_target ? *execution_target : shell());
+    EvalJsResult result = EvalJs(adapter, JsReplace(R"(
+      navigator.deprecatedURNToURL($1)
+    )",
+                                                    urn_url));
+    if (!result.error.empty() || result.value.is_none())
+      return absl::nullopt;
+    return GURL(result.ExtractString());
+  }
+
   WebContentsImpl* web_contents() const {
     return static_cast<WebContentsImpl*>(shell()->web_contents());
   }
@@ -2516,17 +2529,17 @@
       GetInterestGroupsForOwner(origin);
   EXPECT_EQ(storage_interest_groups.size(), 1u);
   EXPECT_EQ(
-      storage_interest_groups.front().bidding_group->signals->prev_wins.size(),
+      storage_interest_groups.front().bidding_browser_signals->prev_wins.size(),
       0u);
-  EXPECT_EQ(storage_interest_groups.front().bidding_group->signals->bid_count,
+  EXPECT_EQ(storage_interest_groups.front().bidding_browser_signals->bid_count,
             0);
   std::vector<StorageInterestGroup> storage_interest_groups2 =
       GetInterestGroupsForOwner(origin2);
   EXPECT_EQ(storage_interest_groups2.size(), 1u);
-  EXPECT_EQ(
-      storage_interest_groups2.front().bidding_group->signals->prev_wins.size(),
-      0u);
-  EXPECT_EQ(storage_interest_groups2.front().bidding_group->signals->bid_count,
+  EXPECT_EQ(storage_interest_groups2.front()
+                .bidding_browser_signals->prev_wins.size(),
+            0u);
+  EXPECT_EQ(storage_interest_groups2.front().bidding_browser_signals->bid_count,
             0);
 
   std::string auction_config = JsReplace(
@@ -2545,21 +2558,21 @@
   storage_interest_groups = GetInterestGroupsForOwner(origin);
   storage_interest_groups2 = GetInterestGroupsForOwner(origin2);
   EXPECT_EQ(
-      storage_interest_groups.front().bidding_group->signals->prev_wins.size(),
+      storage_interest_groups.front().bidding_browser_signals->prev_wins.size(),
       1u);
-  EXPECT_EQ(
-      storage_interest_groups2.front().bidding_group->signals->prev_wins.size(),
-      0u);
+  EXPECT_EQ(storage_interest_groups2.front()
+                .bidding_browser_signals->prev_wins.size(),
+            0u);
   EXPECT_EQ(
       storage_interest_groups.front()
-          .bidding_group->signals->prev_wins.front()
+          .bidding_browser_signals->prev_wins.front()
           ->ad_json,
       JsReplace(
           R"({"render_url":$1,"metadata":{"ad":"metadata","here":[1,2]}})",
           ad1_url));
-  EXPECT_EQ(storage_interest_groups.front().bidding_group->signals->bid_count,
+  EXPECT_EQ(storage_interest_groups.front().bidding_browser_signals->bid_count,
             1);
-  EXPECT_EQ(storage_interest_groups2.front().bidding_group->signals->bid_count,
+  EXPECT_EQ(storage_interest_groups2.front().bidding_browser_signals->bid_count,
             1);
 
   // Run auction again. Interest group shoes of owner `test_url2` wins.
@@ -2568,19 +2581,19 @@
   storage_interest_groups = GetInterestGroupsForOwner(origin);
   storage_interest_groups2 = GetInterestGroupsForOwner(origin2);
   EXPECT_EQ(
-      storage_interest_groups.front().bidding_group->signals->prev_wins.size(),
-      1u);
-  EXPECT_EQ(
-      storage_interest_groups2.front().bidding_group->signals->prev_wins.size(),
+      storage_interest_groups.front().bidding_browser_signals->prev_wins.size(),
       1u);
   EXPECT_EQ(storage_interest_groups2.front()
-                .bidding_group->signals->prev_wins.front()
+                .bidding_browser_signals->prev_wins.size(),
+            1u);
+  EXPECT_EQ(storage_interest_groups2.front()
+                .bidding_browser_signals->prev_wins.front()
                 ->ad_json,
             JsReplace(R"({"render_url":$1})", ad2_url));
   // First interest group didn't bid this time.
-  EXPECT_EQ(storage_interest_groups.front().bidding_group->signals->bid_count,
+  EXPECT_EQ(storage_interest_groups.front().bidding_browser_signals->bid_count,
             1);
-  EXPECT_EQ(storage_interest_groups2.front().bidding_group->signals->bid_count,
+  EXPECT_EQ(storage_interest_groups2.front().bidding_browser_signals->bid_count,
             2);
 
   // Run auction third time, and only interest group "shoes" bids this time.
@@ -2597,19 +2610,19 @@
   storage_interest_groups = GetInterestGroupsForOwner(origin);
   storage_interest_groups2 = GetInterestGroupsForOwner(origin2);
   EXPECT_EQ(
-      storage_interest_groups.front().bidding_group->signals->prev_wins.size(),
+      storage_interest_groups.front().bidding_browser_signals->prev_wins.size(),
       1u);
-  EXPECT_EQ(
-      storage_interest_groups2.front().bidding_group->signals->prev_wins.size(),
-      2u);
   EXPECT_EQ(storage_interest_groups2.front()
-                .bidding_group->signals->prev_wins.back()
+                .bidding_browser_signals->prev_wins.size(),
+            2u);
+  EXPECT_EQ(storage_interest_groups2.front()
+                .bidding_browser_signals->prev_wins.back()
                 ->ad_json,
             JsReplace(R"({"render_url":$1})", ad2_url));
   // First interest group didn't bid this time.
-  EXPECT_EQ(storage_interest_groups.front().bidding_group->signals->bid_count,
+  EXPECT_EQ(storage_interest_groups.front().bidding_browser_signals->bid_count,
             1);
-  EXPECT_EQ(storage_interest_groups2.front().bidding_group->signals->bid_count,
+  EXPECT_EQ(storage_interest_groups2.front().bidding_browser_signals->bid_count,
             3);
 }
 
@@ -3452,7 +3465,7 @@
           [](const std::vector<StorageInterestGroup>& groups) {
             if (groups.size() != 1)
               return false;
-            const auto& group = groups[0].bidding_group->group;
+            const auto& group = groups[0].interest_group;
             return group.name == "cars" && group.bidding_url.has_value() &&
                    group.bidding_url->path() ==
                        "/interest_group/new_bidding_logic.js" &&
@@ -3519,7 +3532,7 @@
           [](const std::vector<StorageInterestGroup>& groups) {
             if (groups.size() != 1)
               return false;
-            const auto& group = groups[0].bidding_group->group;
+            const auto& group = groups[0].interest_group;
             return group.name == "cars" && group.bidding_url.has_value() &&
                    group.bidding_url->path() ==
                        "/interest_group/bidding_logic.js" &&
@@ -3629,8 +3642,10 @@
       TestFencedFrameURLMappingResultObserver observer;
       ConvertFencedFrameURNToURL(*maybe_url, &observer);
       EXPECT_TRUE(observer.mapped_url());
+      absl::optional<GURL> decoded_URL = observer.mapped_url();
+      EXPECT_EQ(decoded_URL, ConvertFencedFrameURNToURLInJS(*maybe_url));
       NavigateIframeAndCheckURL(web_contents(), *maybe_url,
-                                *observer.mapped_url());
+                                decoded_URL.value_or(GURL()));
       return *observer.mapped_url();
     }
     return absl::nullopt;
@@ -4256,8 +4271,7 @@
           [&](const std::vector<StorageInterestGroup>& storage_groups) {
             bool found_updated_group = false;
             for (const auto& storage_group : storage_groups) {
-              const blink::InterestGroup& group =
-                  storage_group.bidding_group->group;
+              const blink::InterestGroup& group = storage_group.interest_group;
               if (group.name == kPubliclyUpdateGroupName) {
                 EXPECT_EQ(initial_bidding_url, group.bidding_url);
               } else {
@@ -4687,6 +4701,12 @@
   }
 }
 
+// navigator.deprecatedURNToURL returns null for an invalid URN.
+IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, InvalidURN) {
+  GURL invalid_urn("urn:uuid:c36973b5-e5d9-de59-e4c4-364f137b3c7a");
+  EXPECT_EQ(absl::nullopt, ConvertFencedFrameURNToURLInJS(invalid_urn));
+}
+
 }  // namespace
 
 }  // namespace content
diff --git a/content/browser/interest_group/interest_group_manager.cc b/content/browser/interest_group/interest_group_manager.cc
index 800edf7..6bc0d44f 100644
--- a/content/browser/interest_group/interest_group_manager.cc
+++ b/content/browser/interest_group/interest_group_manager.cc
@@ -163,16 +163,15 @@
 void InterestGroupManager::DidUpdateInterestGroupsOfOwnerDbLoad(
     url::Origin owner,
     network::mojom::ClientSecurityStatePtr client_security_state,
-    std::vector<StorageInterestGroup> interest_groups) {
+    std::vector<StorageInterestGroup> storage_groups) {
   net::IsolationInfo per_update_isolation_info =
       net::IsolationInfo::CreateTransient();
 
-  for (auto& interest_group : interest_groups) {
-    if (!interest_group.bidding_group->group.update_url)
+  for (auto& storage_group : storage_groups) {
+    if (!storage_group.interest_group.update_url)
       continue;
     auto resource_request = std::make_unique<network::ResourceRequest>();
-    resource_request->url =
-        interest_group.bidding_group->group.update_url.value();
+    resource_request->url = storage_group.interest_group.update_url.value();
     resource_request->redirect_mode = network::mojom::RedirectMode::kError;
     resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
     resource_request->request_initiator = owner;
@@ -193,7 +192,7 @@
             base::BindOnce(
                 &InterestGroupManager::DidUpdateInterestGroupsOfOwnerNetFetch,
                 weak_factory_.GetWeakPtr(), simple_url_loader_it, owner,
-                interest_group.bidding_group->group.name),
+                storage_group.interest_group.name),
             kMaxUpdateSize);
   }
 }
diff --git a/content/browser/interest_group/interest_group_manager.h b/content/browser/interest_group/interest_group_manager.h
index e5e212d7..286e867 100644
--- a/content/browser/interest_group/interest_group_manager.h
+++ b/content/browser/interest_group/interest_group_manager.h
@@ -136,7 +136,7 @@
   void DidUpdateInterestGroupsOfOwnerDbLoad(
       url::Origin owner,
       network::mojom::ClientSecurityStatePtr client_security_state,
-      std::vector<StorageInterestGroup> interest_groups);
+      std::vector<StorageInterestGroup> storage_groups);
   void DidUpdateInterestGroupsOfOwnerNetFetch(
       UrlLoadersList::iterator simple_url_loader,
       url::Origin owner,
diff --git a/content/browser/interest_group/interest_group_storage.cc b/content/browser/interest_group/interest_group_storage.cc
index 6e26e9e1..e29beb10 100644
--- a/content/browser/interest_group/interest_group_storage.cc
+++ b/content/browser/interest_group/interest_group_storage.cc
@@ -38,7 +38,6 @@
 namespace {
 
 using auction_worklet::mojom::BiddingBrowserSignalsPtr;
-using auction_worklet::mojom::BiddingInterestGroupPtr;
 using auction_worklet::mojom::PreviousWinPtr;
 
 const base::FilePath::CharType kDatabasePath[] =
@@ -984,7 +983,7 @@
                      const url::Origin& owner,
                      const std::string& name,
                      base::Time win_time_after,
-                     BiddingInterestGroupPtr& output) {
+                     BiddingBrowserSignalsPtr& output) {
   // clang-format off
   sql::Statement prev_wins(
       db.GetCachedStatement(SQL_FROM_HERE,
@@ -1004,10 +1003,10 @@
   prev_wins.BindString(1, name);
   prev_wins.BindTime(2, win_time_after);
   while (prev_wins.Step()) {
-    PreviousWinPtr prev_win = auction_worklet::mojom::PreviousWin::New();
-    prev_win->time = prev_wins.ColumnTime(0);
-    prev_win->ad_json = prev_wins.ColumnString(1);
-    output->signals->prev_wins.push_back(std::move(prev_win));
+    PreviousWinPtr prev_win = auction_worklet::mojom::PreviousWin::New(
+        /*time=*/prev_wins.ColumnTime(0),
+        /*ad_json=*/prev_wins.ColumnString(1));
+    output->prev_wins.push_back(std::move(prev_win));
   }
   return prev_wins.Succeeded();
 }
@@ -1016,7 +1015,7 @@
                   const url::Origin& owner,
                   const std::string& name,
                   base::Time joined_after,
-                  BiddingInterestGroupPtr& output) {
+                  BiddingBrowserSignalsPtr& output) {
   // clang-format off
   sql::Statement join_count(
       db.GetCachedStatement(SQL_FROM_HERE,
@@ -1034,7 +1033,7 @@
   join_count.BindString(1, name);
   join_count.BindTime(2, joined_after);
   while (join_count.Step()) {
-    output->signals->join_count = join_count.ColumnInt64(0);
+    output->join_count = join_count.ColumnInt64(0);
   }
   return join_count.Succeeded();
 }
@@ -1043,7 +1042,7 @@
                  const url::Origin& owner,
                  const std::string& name,
                  base::Time now,
-                 BiddingInterestGroupPtr& output) {
+                 BiddingBrowserSignalsPtr& output) {
   // clang-format off
   sql::Statement bid_count(
       db.GetCachedStatement(SQL_FROM_HERE,
@@ -1061,7 +1060,7 @@
   bid_count.BindString(1, name);
   bid_count.BindTime(2, now - InterestGroupStorage::kHistoryLength);
   while (bid_count.Step()) {
-    output->signals->bid_count = bid_count.ColumnInt64(0);
+    output->bid_count = bid_count.ColumnInt64(0);
   }
   return bid_count.Succeeded();
 }
@@ -1104,25 +1103,22 @@
     std::string name,
     base::Time now) {
   StorageInterestGroup db_interest_group;
-  db_interest_group.bidding_group =
-      auction_worklet::mojom::BiddingInterestGroup::New();
-  if (!DoLoadInterestGroup(db, owner, name,
-                           db_interest_group.bidding_group->group))
+  if (!DoLoadInterestGroup(db, owner, name, db_interest_group.interest_group))
     return absl::nullopt;
 
-  if (!DoGetInterestGroupNameKAnonymity(
-          db, owner, db_interest_group.bidding_group->group.name,
-          db_interest_group.name_kanon)) {
+  if (!DoGetInterestGroupNameKAnonymity(db, owner,
+                                        db_interest_group.interest_group.name,
+                                        db_interest_group.name_kanon)) {
     return absl::nullopt;
   }
-  if (db_interest_group.bidding_group->group.update_url &&
+  if (db_interest_group.interest_group.update_url &&
       !DoGetInterestGroupUpdateURLKAnonymity(
-          db, db_interest_group.bidding_group->group.update_url.value(),
+          db, db_interest_group.interest_group.update_url.value(),
           db_interest_group.update_url_kanon)) {
     return absl::nullopt;
   }
-  if (db_interest_group.bidding_group->group.ads) {
-    for (auto& ad : db_interest_group.bidding_group->group.ads.value()) {
+  if (db_interest_group.interest_group.ads) {
+    for (auto& ad : db_interest_group.interest_group.ads.value()) {
       absl::optional<StorageInterestGroup::KAnonymityData> ad_kanon;
       if (!DoGetAdsKAnonymity(db, ad.render_url, ad_kanon)) {
         return absl::nullopt;
@@ -1132,9 +1128,8 @@
       db_interest_group.ads_kanon.push_back(std::move(ad_kanon).value());
     }
   }
-  if (db_interest_group.bidding_group->group.ad_components) {
-    for (auto& ad :
-         db_interest_group.bidding_group->group.ad_components.value()) {
+  if (db_interest_group.interest_group.ad_components) {
+    for (auto& ad : db_interest_group.interest_group.ad_components.value()) {
       absl::optional<StorageInterestGroup::KAnonymityData> ad_kanon;
       if (!DoGetAdsKAnonymity(db, ad.render_url, ad_kanon)) {
         return absl::nullopt;
@@ -1145,19 +1140,19 @@
     }
   }
 
-  db_interest_group.bidding_group->signals =
+  db_interest_group.bidding_browser_signals =
       auction_worklet::mojom::BiddingBrowserSignals::New();
   if (!GetJoinCount(db, owner, name, now - InterestGroupStorage::kHistoryLength,
-                    db_interest_group.bidding_group)) {
+                    db_interest_group.bidding_browser_signals)) {
     return absl::nullopt;
   }
   if (!GetBidCount(db, owner, name, now - InterestGroupStorage::kHistoryLength,
-                   db_interest_group.bidding_group)) {
+                   db_interest_group.bidding_browser_signals)) {
     return absl::nullopt;
   }
   if (!GetPreviousWins(db, owner, name,
                        now - InterestGroupStorage::kHistoryLength,
-                       db_interest_group.bidding_group)) {
+                       db_interest_group.bidding_browser_signals)) {
     return absl::nullopt;
   }
   return db_interest_group;
@@ -1379,10 +1374,11 @@
       first_idx = 0;
     for (size_t group_idx = first_idx;
          group_idx < maybe_interest_groups.value().size(); group_idx++) {
-      if (!DoRemoveInterestGroup(db, affected_origin,
-                                 maybe_interest_groups.value()[group_idx]
-                                     .bidding_group->group.name))
+      if (!DoRemoveInterestGroup(
+              db, affected_origin,
+              maybe_interest_groups.value()[group_idx].interest_group.name)) {
         return false;
+      }
     }
   }
   return true;
diff --git a/content/browser/interest_group/interest_group_storage_unittest.cc b/content/browser/interest_group/interest_group_storage_unittest.cc
index 07076d4..b2fa44b 100644
--- a/content/browser/interest_group/interest_group_storage_unittest.cc
+++ b/content/browser/interest_group/interest_group_storage_unittest.cc
@@ -193,10 +193,10 @@
     std::vector<StorageInterestGroup> interest_groups =
         storage->GetInterestGroupsForOwner(test_origin);
     EXPECT_EQ(1u, interest_groups.size());
-    EXPECT_EQ(test_origin, interest_groups[0].bidding_group->group.owner);
-    EXPECT_EQ("example", interest_groups[0].bidding_group->group.name);
-    EXPECT_EQ(1, interest_groups[0].bidding_group->signals->join_count);
-    EXPECT_EQ(0, interest_groups[0].bidding_group->signals->bid_count);
+    EXPECT_EQ(test_origin, interest_groups[0].interest_group.owner);
+    EXPECT_EQ("example", interest_groups[0].interest_group.name);
+    EXPECT_EQ(1, interest_groups[0].bidding_browser_signals->join_count);
+    EXPECT_EQ(0, interest_groups[0].bidding_browser_signals->bid_count);
   }
   histograms.ExpectUniqueSample("Storage.InterestGroup.PerSiteCount", 1u, 1);
 }
@@ -222,9 +222,9 @@
   std::vector<StorageInterestGroup> interest_groups =
       storage->GetInterestGroupsForOwner(test_origin);
   EXPECT_EQ(1u, interest_groups.size());
-  EXPECT_EQ("example", interest_groups[0].bidding_group->group.name);
-  EXPECT_EQ(2, interest_groups[0].bidding_group->signals->join_count);
-  EXPECT_EQ(0, interest_groups[0].bidding_group->signals->bid_count);
+  EXPECT_EQ("example", interest_groups[0].interest_group.name);
+  EXPECT_EQ(2, interest_groups[0].bidding_browser_signals->join_count);
+  EXPECT_EQ(0, interest_groups[0].bidding_browser_signals->bid_count);
 
   storage->JoinInterestGroup(NewInterestGroup(test_origin, "example2"),
                              test_origin.GetURL());
@@ -240,9 +240,9 @@
 
   interest_groups = storage->GetInterestGroupsForOwner(test_origin);
   EXPECT_EQ(1u, interest_groups.size());
-  EXPECT_EQ("example2", interest_groups[0].bidding_group->group.name);
-  EXPECT_EQ(1, interest_groups[0].bidding_group->signals->join_count);
-  EXPECT_EQ(0, interest_groups[0].bidding_group->signals->bid_count);
+  EXPECT_EQ("example2", interest_groups[0].interest_group.name);
+  EXPECT_EQ(1, interest_groups[0].bidding_browser_signals->join_count);
+  EXPECT_EQ(0, interest_groups[0].bidding_browser_signals->bid_count);
 
   origins = storage->GetAllInterestGroupOwners();
   EXPECT_EQ(1u, origins.size());
@@ -264,25 +264,25 @@
   std::vector<StorageInterestGroup> interest_groups =
       storage->GetInterestGroupsForOwner(test_origin);
   EXPECT_EQ(1u, interest_groups.size());
-  EXPECT_EQ("example", interest_groups[0].bidding_group->group.name);
-  EXPECT_EQ(1, interest_groups[0].bidding_group->signals->join_count);
-  EXPECT_EQ(0, interest_groups[0].bidding_group->signals->bid_count);
+  EXPECT_EQ("example", interest_groups[0].interest_group.name);
+  EXPECT_EQ(1, interest_groups[0].bidding_browser_signals->join_count);
+  EXPECT_EQ(0, interest_groups[0].bidding_browser_signals->bid_count);
 
   storage->RecordInterestGroupBid(test_origin, "example");
 
   interest_groups = storage->GetInterestGroupsForOwner(test_origin);
   EXPECT_EQ(1u, interest_groups.size());
-  EXPECT_EQ("example", interest_groups[0].bidding_group->group.name);
-  EXPECT_EQ(1, interest_groups[0].bidding_group->signals->join_count);
-  EXPECT_EQ(1, interest_groups[0].bidding_group->signals->bid_count);
+  EXPECT_EQ("example", interest_groups[0].interest_group.name);
+  EXPECT_EQ(1, interest_groups[0].bidding_browser_signals->join_count);
+  EXPECT_EQ(1, interest_groups[0].bidding_browser_signals->bid_count);
 
   storage->RecordInterestGroupBid(test_origin, "example");
 
   interest_groups = storage->GetInterestGroupsForOwner(test_origin);
   EXPECT_EQ(1u, interest_groups.size());
-  EXPECT_EQ("example", interest_groups[0].bidding_group->group.name);
-  EXPECT_EQ(1, interest_groups[0].bidding_group->signals->join_count);
-  EXPECT_EQ(2, interest_groups[0].bidding_group->signals->bid_count);
+  EXPECT_EQ("example", interest_groups[0].interest_group.name);
+  EXPECT_EQ(1, interest_groups[0].bidding_browser_signals->join_count);
+  EXPECT_EQ(2, interest_groups[0].bidding_browser_signals->bid_count);
 }
 
 TEST_F(InterestGroupStorageTest, RecordsWins) {
@@ -302,9 +302,9 @@
   std::vector<StorageInterestGroup> interest_groups =
       storage->GetInterestGroupsForOwner(test_origin);
   ASSERT_EQ(1u, interest_groups.size());
-  EXPECT_EQ("example", interest_groups[0].bidding_group->group.name);
-  EXPECT_EQ(1, interest_groups[0].bidding_group->signals->join_count);
-  EXPECT_EQ(0, interest_groups[0].bidding_group->signals->bid_count);
+  EXPECT_EQ("example", interest_groups[0].interest_group.name);
+  EXPECT_EQ(1, interest_groups[0].bidding_browser_signals->join_count);
+  EXPECT_EQ(0, interest_groups[0].bidding_browser_signals->bid_count);
 
   std::string ad1_json = "{url: '" + ad1_url.spec() + "'}";
   storage->RecordInterestGroupBid(test_origin, "example");
@@ -312,9 +312,9 @@
 
   interest_groups = storage->GetInterestGroupsForOwner(test_origin);
   ASSERT_EQ(1u, interest_groups.size());
-  EXPECT_EQ("example", interest_groups[0].bidding_group->group.name);
-  EXPECT_EQ(1, interest_groups[0].bidding_group->signals->join_count);
-  EXPECT_EQ(1, interest_groups[0].bidding_group->signals->bid_count);
+  EXPECT_EQ("example", interest_groups[0].interest_group.name);
+  EXPECT_EQ(1, interest_groups[0].bidding_browser_signals->join_count);
+  EXPECT_EQ(1, interest_groups[0].bidding_browser_signals->bid_count);
 
   // Add the second win *after* the first so we can check ordering.
   task_environment().FastForwardBy(base::Seconds(1));
@@ -324,15 +324,15 @@
 
   interest_groups = storage->GetInterestGroupsForOwner(test_origin);
   ASSERT_EQ(1u, interest_groups.size());
-  EXPECT_EQ("example", interest_groups[0].bidding_group->group.name);
-  EXPECT_EQ(1, interest_groups[0].bidding_group->signals->join_count);
-  EXPECT_EQ(2, interest_groups[0].bidding_group->signals->bid_count);
-  EXPECT_EQ(2u, interest_groups[0].bidding_group->signals->prev_wins.size());
+  EXPECT_EQ("example", interest_groups[0].interest_group.name);
+  EXPECT_EQ(1, interest_groups[0].bidding_browser_signals->join_count);
+  EXPECT_EQ(2, interest_groups[0].bidding_browser_signals->bid_count);
+  EXPECT_EQ(2u, interest_groups[0].bidding_browser_signals->prev_wins.size());
   // Ad wins should be listed in reverse chronological order.
   EXPECT_EQ(ad2_json,
-            interest_groups[0].bidding_group->signals->prev_wins[0]->ad_json);
+            interest_groups[0].bidding_browser_signals->prev_wins[0]->ad_json);
   EXPECT_EQ(ad1_json,
-            interest_groups[0].bidding_group->signals->prev_wins[1]->ad_json);
+            interest_groups[0].bidding_browser_signals->prev_wins[1]->ad_json);
 
   // Try delete
   storage->DeleteInterestGroupData(
@@ -374,13 +374,13 @@
   groups = storage->GetInterestGroupsForOwner(test_origin);
 
   ASSERT_EQ(2u, groups.size());
-  EXPECT_EQ(name, groups[1].bidding_group->group.name);
+  EXPECT_EQ(name, groups[1].interest_group.name);
   ASSERT_TRUE(groups[1].name_kanon);
   EXPECT_EQ(key, groups[1].name_kanon->key);
   EXPECT_EQ(0, groups[1].name_kanon->k);
   EXPECT_EQ(base::Time::Min(), groups[1].name_kanon->last_updated);
 
-  EXPECT_EQ(name2, groups[0].bidding_group->group.name);
+  EXPECT_EQ(name2, groups[0].interest_group.name);
   ASSERT_TRUE(groups[0].name_kanon);
   EXPECT_EQ(key2, groups[0].name_kanon->key);
   EXPECT_EQ(0, groups[0].name_kanon->k);
@@ -393,13 +393,13 @@
   groups = storage->GetInterestGroupsForOwner(test_origin);
 
   ASSERT_EQ(2u, groups.size());
-  EXPECT_EQ(name, groups[1].bidding_group->group.name);
+  EXPECT_EQ(name, groups[1].interest_group.name);
   ASSERT_TRUE(groups[1].name_kanon);
   EXPECT_EQ(key, groups[1].name_kanon->key);
   EXPECT_EQ(10, groups[1].name_kanon->k);
   EXPECT_EQ(update_time, groups[1].name_kanon->last_updated);
 
-  EXPECT_EQ(name2, groups[0].bidding_group->group.name);
+  EXPECT_EQ(name2, groups[0].interest_group.name);
   ASSERT_TRUE(groups[0].name_kanon);
   EXPECT_EQ(key2, groups[0].name_kanon->key);
   EXPECT_EQ(0, groups[0].name_kanon->k);
@@ -414,13 +414,13 @@
   groups = storage->GetInterestGroupsForOwner(test_origin);
 
   ASSERT_EQ(2u, groups.size());
-  EXPECT_EQ(name, groups[1].bidding_group->group.name);
+  EXPECT_EQ(name, groups[1].interest_group.name);
   ASSERT_TRUE(groups[1].name_kanon);
   EXPECT_EQ(key, groups[1].name_kanon->key);
   EXPECT_EQ(12, groups[1].name_kanon->k);
   EXPECT_EQ(update_time, groups[1].name_kanon->last_updated);
 
-  EXPECT_EQ(name2, groups[0].bidding_group->group.name);
+  EXPECT_EQ(name2, groups[0].interest_group.name);
   ASSERT_TRUE(groups[0].name_kanon);
   EXPECT_EQ(key2, groups[0].name_kanon->key);
   EXPECT_EQ(0, groups[0].name_kanon->k);
@@ -454,13 +454,13 @@
   groups = storage->GetInterestGroupsForOwner(test_origin);
 
   ASSERT_EQ(2u, groups.size());
-  EXPECT_EQ("name2", groups[0].bidding_group->group.name);
+  EXPECT_EQ("name2", groups[0].interest_group.name);
   ASSERT_TRUE(groups[0].update_url_kanon);
   EXPECT_EQ(update_url, groups[0].update_url_kanon->key);
   EXPECT_EQ(0, groups[0].update_url_kanon->k);
   EXPECT_EQ(base::Time::Min(), groups[0].update_url_kanon->last_updated);
 
-  EXPECT_EQ("name", groups[1].bidding_group->group.name);
+  EXPECT_EQ("name", groups[1].interest_group.name);
   ASSERT_TRUE(groups[1].update_url_kanon);
   EXPECT_EQ(update_url, groups[1].update_url_kanon->key);
   EXPECT_EQ(0, groups[1].update_url_kanon->k);
@@ -473,13 +473,13 @@
   groups = storage->GetInterestGroupsForOwner(test_origin);
 
   ASSERT_EQ(2u, groups.size());
-  EXPECT_EQ("name2", groups[0].bidding_group->group.name);
+  EXPECT_EQ("name2", groups[0].interest_group.name);
   ASSERT_TRUE(groups[0].update_url_kanon);
   EXPECT_EQ(update_url, groups[0].update_url_kanon->key);
   EXPECT_EQ(10, groups[0].update_url_kanon->k);
   EXPECT_EQ(update_time, groups[0].update_url_kanon->last_updated);
 
-  EXPECT_EQ("name", groups[1].bidding_group->group.name);
+  EXPECT_EQ("name", groups[1].interest_group.name);
   ASSERT_TRUE(groups[1].update_url_kanon);
   EXPECT_EQ(update_url, groups[1].update_url_kanon->key);
   EXPECT_EQ(10, groups[1].update_url_kanon->k);
@@ -494,13 +494,13 @@
   groups = storage->GetInterestGroupsForOwner(test_origin);
 
   ASSERT_EQ(2u, groups.size());
-  EXPECT_EQ("name2", groups[0].bidding_group->group.name);
+  EXPECT_EQ("name2", groups[0].interest_group.name);
   ASSERT_TRUE(groups[0].update_url_kanon);
   EXPECT_EQ(update_url, groups[0].update_url_kanon->key);
   EXPECT_EQ(12, groups[0].update_url_kanon->k);
   EXPECT_EQ(update_time, groups[0].update_url_kanon->last_updated);
 
-  EXPECT_EQ("name", groups[1].bidding_group->group.name);
+  EXPECT_EQ("name", groups[1].interest_group.name);
   ASSERT_TRUE(groups[1].update_url_kanon);
   EXPECT_EQ(update_url, groups[1].update_url_kanon->key);
   EXPECT_EQ(12, groups[1].update_url_kanon->k);
@@ -706,13 +706,13 @@
   std::vector<StorageInterestGroup> storage_interest_groups =
       storage->GetInterestGroupsForOwner(partial_origin);
   ASSERT_EQ(1u, storage_interest_groups.size());
-  EXPECT_TRUE(partial.IsEqualForTesting(
-      storage_interest_groups[0].bidding_group->group));
+  EXPECT_TRUE(
+      partial.IsEqualForTesting(storage_interest_groups[0].interest_group));
 
   storage_interest_groups = storage->GetInterestGroupsForOwner(full_origin);
   ASSERT_EQ(1u, storage_interest_groups.size());
   EXPECT_TRUE(
-      full.IsEqualForTesting(storage_interest_groups[0].bidding_group->group));
+      full.IsEqualForTesting(storage_interest_groups[0].interest_group));
 
   // Test update as well.
   InterestGroup updated = full;
@@ -730,8 +730,8 @@
 
   storage_interest_groups = storage->GetInterestGroupsForOwner(full_origin);
   ASSERT_EQ(1u, storage_interest_groups.size());
-  EXPECT_TRUE(updated.IsEqualForTesting(
-      storage_interest_groups[0].bidding_group->group));
+  EXPECT_TRUE(
+      updated.IsEqualForTesting(storage_interest_groups[0].interest_group));
 }
 
 TEST_F(InterestGroupStorageTest, DeleteOriginDeleteAll) {
@@ -838,7 +838,7 @@
 
   std::vector<std::string> remaining_groups;
   for (const auto& db_group : interest_groups) {
-    remaining_groups.push_back(db_group.bidding_group->group.name);
+    remaining_groups.push_back(db_group.interest_group.name);
   }
   std::vector<std::string> remaining_groups_expected(
       added_groups.begin() + kExcessOwners, added_groups.end());
@@ -977,9 +977,9 @@
 
   interest_groups = storage->GetInterestGroupsForOwner(keep_origin);
   EXPECT_EQ(1u, interest_groups.size());
-  EXPECT_EQ("keep", interest_groups[0].bidding_group->group.name);
-  EXPECT_EQ(1, interest_groups[0].bidding_group->signals->join_count);
-  EXPECT_EQ(0, interest_groups[0].bidding_group->signals->bid_count);
+  EXPECT_EQ("keep", interest_groups[0].interest_group.name);
+  EXPECT_EQ(1, interest_groups[0].bidding_browser_signals->join_count);
+  EXPECT_EQ(0, interest_groups[0].bidding_browser_signals->bid_count);
   next_maintenance_time = base::Time::Now() + InterestGroupStorage::kIdlePeriod;
 
   // All the groups should still be in the database since they shouldn't have
@@ -1002,9 +1002,9 @@
 
   interest_groups = storage->GetAllInterestGroupsUnfilteredForTesting();
   EXPECT_EQ(1u, interest_groups.size());
-  EXPECT_EQ("keep", interest_groups[0].bidding_group->group.name);
-  EXPECT_EQ(1, interest_groups[0].bidding_group->signals->join_count);
-  EXPECT_EQ(0, interest_groups[0].bidding_group->signals->bid_count);
+  EXPECT_EQ("keep", interest_groups[0].interest_group.name);
+  EXPECT_EQ(1, interest_groups[0].bidding_browser_signals->join_count);
+  EXPECT_EQ(0, interest_groups[0].bidding_browser_signals->bid_count);
 }
 
 }  // namespace content
diff --git a/content/browser/interest_group/storage_interest_group.cc b/content/browser/interest_group/storage_interest_group.cc
index 2ae715ba..d0b26ebb 100644
--- a/content/browser/interest_group/storage_interest_group.cc
+++ b/content/browser/interest_group/storage_interest_group.cc
@@ -5,14 +5,11 @@
 #include "content/browser/interest_group/storage_interest_group.h"
 
 #include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
+#include "third_party/blink/public/common/interest_group/interest_group.h"
 
 namespace content {
 
 StorageInterestGroup::StorageInterestGroup() = default;
-StorageInterestGroup::StorageInterestGroup(
-    auction_worklet::mojom::BiddingInterestGroupPtr group) {
-  this->bidding_group = std::move(group);
-}
 StorageInterestGroup::StorageInterestGroup(StorageInterestGroup&&) = default;
 StorageInterestGroup::~StorageInterestGroup() = default;
 
diff --git a/content/browser/interest_group/storage_interest_group.h b/content/browser/interest_group/storage_interest_group.h
index 4e7aaad..79182a3 100644
--- a/content/browser/interest_group/storage_interest_group.h
+++ b/content/browser/interest_group/storage_interest_group.h
@@ -12,6 +12,7 @@
 #include "base/time/time.h"
 #include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom-forward.h"
 #include "mojo/public/cpp/bindings/struct_ptr.h"
+#include "third_party/blink/public/common/interest_group/interest_group.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -22,8 +23,6 @@
 // process.
 struct CONTENT_EXPORT StorageInterestGroup {
   StorageInterestGroup();
-  explicit StorageInterestGroup(
-      auction_worklet::mojom::BiddingInterestGroupPtr group);
   StorageInterestGroup(StorageInterestGroup&&);
   StorageInterestGroup& operator=(StorageInterestGroup&&) = default;
   ~StorageInterestGroup();
@@ -48,7 +47,8 @@
     base::Time last_updated;
   };
 
-  auction_worklet::mojom::BiddingInterestGroupPtr bidding_group;
+  blink::InterestGroup interest_group;
+  auction_worklet::mojom::BiddingBrowserSignalsPtr bidding_browser_signals;
   absl::optional<KAnonymityData> name_kanon;
   absl::optional<KAnonymityData> update_url_kanon;
   std::vector<KAnonymityData> ads_kanon;
diff --git a/content/browser/locks/lock_manager_browsertest.cc b/content/browser/locks/lock_manager_browsertest.cc
index 0cd31d6..622efeb 100644
--- a/content/browser/locks/lock_manager_browsertest.cc
+++ b/content/browser/locks/lock_manager_browsertest.cc
@@ -228,7 +228,13 @@
 
 // Verify that content::FeatureObserver is notified that a frame stopped holding
 // locks when it is navigated away.
-IN_PROC_BROWSER_TEST_F(LockManagerBrowserTest, ObserverNavigate) {
+// TODO(crbug.com/1286329): Flakes on Linux.
+#if defined(OS_LINUX)
+#define MAYBE_ObserverNavigate DISABLED_ObserverNavigate
+#else
+#define MAYBE_ObserverNavigate ObserverNavigate
+#endif
+IN_PROC_BROWSER_TEST_F(LockManagerBrowserTest, MAYBE_ObserverNavigate) {
   if (!CheckShouldRunTestAndNavigate())
     return;
 
diff --git a/content/browser/net/cookie_store_factory.cc b/content/browser/net/cookie_store_factory.cc
index d41058b..44deebe 100644
--- a/content/browser/net/cookie_store_factory.cc
+++ b/content/browser/net/cookie_store_factory.cc
@@ -23,16 +23,19 @@
 CookieStoreConfig::CookieStoreConfig()
     : restore_old_session_cookies(false),
       persist_session_cookies(false),
+      first_party_sets_enabled(false),
       crypto_delegate(nullptr) {
   // Default to an in-memory cookie store.
 }
 
 CookieStoreConfig::CookieStoreConfig(const base::FilePath& path,
                                      bool restore_old_session_cookies,
-                                     bool persist_session_cookies)
+                                     bool persist_session_cookies,
+                                     bool first_party_sets_enabled)
     : path(path),
       restore_old_session_cookies(restore_old_session_cookies),
       persist_session_cookies(persist_session_cookies),
+      first_party_sets_enabled(first_party_sets_enabled),
       crypto_delegate(nullptr) {
   CHECK(!path.empty() ||
         (!restore_old_session_cookies && !persist_session_cookies));
@@ -47,8 +50,8 @@
 
   if (config.path.empty()) {
     // Empty path means in-memory store.
-    cookie_monster =
-        std::make_unique<net::CookieMonster>(nullptr /* store */, net_log);
+    cookie_monster = std::make_unique<net::CookieMonster>(
+        nullptr /* store */, net_log, config.first_party_sets_enabled);
   } else {
     scoped_refptr<base::SequencedTaskRunner> client_task_runner =
         config.client_task_runner;
@@ -70,8 +73,8 @@
             config.path, client_task_runner, background_task_runner,
             config.restore_old_session_cookies, config.crypto_delegate));
 
-    cookie_monster =
-        std::make_unique<net::CookieMonster>(std::move(sqlite_store), net_log);
+    cookie_monster = std::make_unique<net::CookieMonster>(
+        std::move(sqlite_store), net_log, config.first_party_sets_enabled);
     if (config.persist_session_cookies)
       cookie_monster->SetPersistSessionCookies(true);
   }
diff --git a/content/browser/network_service_browsertest.cc b/content/browser/network_service_browsertest.cc
index 148101a..70e3f453d 100644
--- a/content/browser/network_service_browsertest.cc
+++ b/content/browser/network_service_browsertest.cc
@@ -554,7 +554,7 @@
     : public NetworkServiceBrowserTest {
  public:
   NetworkServiceWithoutFirstPartySetBrowserTest() {
-    scoped_feature_list_.InitAndDisableFeature(net::features::kFirstPartySets);
+    scoped_feature_list_.InitAndDisableFeature(features::kFirstPartySets);
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/content/browser/portal/portal_browsertest.cc b/content/browser/portal/portal_browsertest.cc
index b2e41123..4dc8191 100644
--- a/content/browser/portal/portal_browsertest.cc
+++ b/content/browser/portal/portal_browsertest.cc
@@ -2072,7 +2072,6 @@
     AccessibilityNotificationWaiter waiter(web_contents_impl,
                                            ui::kAXModeComplete,
                                            ax::mojom::Event::kLayoutComplete);
-    waiter.WaitForNotification();
     EXPECT_EQ(blink::mojom::PortalActivateResult::kPredecessorWasAdopted,
               activated_observer.WaitForActivateResult());
     adoption_observer.WaitUntilPortalCreated();
diff --git a/content/browser/renderer_host/navigation_controller_impl.cc b/content/browser/renderer_host/navigation_controller_impl.cc
index 5f71a5c..6bff2df6 100644
--- a/content/browser/renderer_host/navigation_controller_impl.cc
+++ b/content/browser/renderer_host/navigation_controller_impl.cc
@@ -1312,6 +1312,15 @@
       NOTREACHED();
       break;
   }
+  // If any navigation has committed on the main frame or created a new
+  // NavigationEntry for a subframe, we can no longer be on an "initial"
+  // NavigationEntry (although the NavigationEntry object itself might be reused
+  // from an originally-initial NavigationEntry, but it would already lose its
+  // "initial" status at this point).
+  // TODO(https://crbug.com/524208): Also remove the "initial" status after
+  // AUTO_SUBFRAME navigations.
+  DCHECK(!GetLastCommittedEntry()->IsInitialEntry() ||
+         (details->type == NAVIGATION_TYPE_AUTO_SUBFRAME));
 
   // At this point, we know that the navigation has just completed, so
   // record the time.
@@ -2737,6 +2746,12 @@
     return;
   }
 
+  if (GetLastCommittedEntry() && GetLastCommittedEntry()->IsInitialEntry()) {
+    // We should remove the old entry's "initial" status, as it will no longer
+    // be the only NavigationEntry.
+    GetLastCommittedEntry()->RemoveInitialEntryStatusIfNecessary();
+  }
+
   // We shouldn't see replace == true when there's no committed entries.
   DCHECK(!replace);
 
diff --git a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
index 1651c14a..ebc0c6b 100644
--- a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
@@ -4605,7 +4605,8 @@
 
 // Tests that the initial NavigationEntry loses its "initial" status when
 // any navigation commits on the main frame, including navigations where the
-// initial NavigationEntry is reused.
+// initial NavigationEntry is reused. On subframe navigations, the "initial"
+// status will be removed iff a new NavigationEntry is created.
 IN_PROC_BROWSER_TEST_P(NavigationControllerBrowserTest,
                        InitialNavigationEntryLosesStatus) {
   GURL main_window_url(embedded_test_server()->GetURL("/title1.html"));
@@ -4621,6 +4622,7 @@
     // Stop the navigation so that it won't affect future navigations.
     new_contents->Stop();
     NavigationControllerImpl& controller = new_contents->GetController();
+    EXPECT_EQ(1, controller.GetEntryCount());
     NavigationEntryImpl* last_entry = controller.GetLastCommittedEntry();
     EXPECT_TRUE(last_entry->IsInitialEntry());
     EXPECT_EQ(GURL::EmptyGURL(), last_entry->GetURL());
@@ -4630,6 +4632,7 @@
 
     // We replaced the previous NavigationEntry and it's no longer on the
     // initial NavigationEntry.
+    EXPECT_EQ(1, controller.GetEntryCount());
     EXPECT_NE(last_entry, controller.GetLastCommittedEntry());
     last_entry = controller.GetLastCommittedEntry();
     EXPECT_FALSE(last_entry->IsInitialEntry());
@@ -4646,6 +4649,7 @@
     // Stop the navigation so that it won't affect future navigations.
     new_contents->Stop();
     NavigationControllerImpl& controller = new_contents->GetController();
+    EXPECT_EQ(1, controller.GetEntryCount());
     NavigationEntryImpl* last_entry = controller.GetLastCommittedEntry();
     EXPECT_TRUE(last_entry->IsInitialEntry());
     EXPECT_EQ(GURL::EmptyGURL(), last_entry->GetURL());
@@ -4658,6 +4662,7 @@
 
     // We reused the previous NavigationEntry, but it loses its "initial"
     // status.
+    EXPECT_EQ(1, controller.GetEntryCount());
     EXPECT_EQ(last_entry, controller.GetLastCommittedEntry());
     EXPECT_FALSE(last_entry->IsInitialEntry());
     EXPECT_EQ(GURL("about:blank"), last_entry->GetURL());
@@ -4673,6 +4678,7 @@
     // Stop the navigation so that it won't affect future navigations.
     new_contents->Stop();
     NavigationControllerImpl& controller = new_contents->GetController();
+    EXPECT_EQ(1, controller.GetEntryCount());
     NavigationEntryImpl* last_entry = controller.GetLastCommittedEntry();
     EXPECT_TRUE(last_entry->IsInitialEntry());
     EXPECT_EQ(GURL::EmptyGURL(), last_entry->GetURL());
@@ -4688,6 +4694,7 @@
     // The initial NavigationEntry keeps its "initial" status even when a
     // subframe navigated and added a FrameNavigationEntry inside the initial
     // NavigationEntry.
+    EXPECT_EQ(1, controller.GetEntryCount());
     EXPECT_EQ(last_entry, controller.GetLastCommittedEntry());
     EXPECT_TRUE(last_entry->IsInitialEntry());
     EXPECT_EQ(GURL::EmptyGURL(), last_entry->GetURL());
@@ -4697,15 +4704,34 @@
     FrameNavigateParamsCapturer capturer(child);
     GURL subframe_url_2(embedded_test_server()->GetURL("/title2.html"));
     NavigateFrameToURL(child, subframe_url_2);
+    EXPECT_EQ(subframe_url_2, child->current_url());
     capturer.Wait();
 
-    // The subframe creates a new NavigationEntry, which replaces the initial
-    // NavigationEntry.
+    // The subframe appended a new NavigationEntry, which is not marked as the
+    // initial NavigationEntry, and also causes the original initial
+    // NavigationEntry to lose its "initial" status too, as it is no longer the
+    // only NavigationEntry.
+    EXPECT_EQ(2, controller.GetEntryCount());
     EXPECT_NE(last_entry, controller.GetLastCommittedEntry());
+    EXPECT_FALSE(last_entry->IsInitialEntry());
+    EXPECT_EQ(GURL("about:blank"), last_entry->GetURL());
+
     last_entry = controller.GetLastCommittedEntry();
     EXPECT_FALSE(last_entry->IsInitialEntry());
-    EXPECT_EQ(GURL::EmptyGURL(), last_entry->GetURL());
-    EXPECT_EQ(subframe_url_2, child->current_url());
+    EXPECT_EQ(GURL("about:blank"), last_entry->GetURL());
+
+    // Do a back navigation.
+    TestNavigationObserver back_load_observer(new_contents);
+    controller.GoBack();
+    back_load_observer.Wait();
+
+    // The new window is back on the first NavigationEntry, which is no longer
+    // marked as the initial NavigationEntry.
+    EXPECT_EQ(2, controller.GetEntryCount());
+    EXPECT_EQ(subframe_url, child->current_url());
+    last_entry = controller.GetLastCommittedEntry();
+    EXPECT_FALSE(last_entry->IsInitialEntry());
+    EXPECT_EQ(GURL("about:blank"), last_entry->GetURL());
   }
 }
 
diff --git a/content/browser/renderer_host/navigation_entry_impl.cc b/content/browser/renderer_host/navigation_entry_impl.cc
index 32993b8..867ec70 100644
--- a/content/browser/renderer_host/navigation_entry_impl.cc
+++ b/content/browser/renderer_host/navigation_entry_impl.cc
@@ -724,6 +724,17 @@
   return can_load_local_resources_;
 }
 
+void NavigationEntryImpl::RemoveInitialEntryStatusIfNecessary() {
+  if (!is_initial_entry_)
+    return;
+  is_initial_entry_ = false;
+  if (GetURL().is_empty()) {
+    // The NavigationEntry is no longer the initial entry. Ensure that we now
+    // use "about:blank" as the URL, instead of an empty URL.
+    SetURL(GURL(url::kAboutBlankURL));
+  }
+}
+
 bool NavigationEntryImpl::IsInitialEntry() {
   return is_initial_entry_;
 }
@@ -734,7 +745,7 @@
                               ClonePolicy::kShareFrameEntries);
   // When we are not deep-copying, the NavigationEntry is going to be used for
   // a new committed navigation, so it loses its "initial" status.
-  entry->set_is_initial_entry(false);
+  entry->RemoveInitialEntryStatusIfNecessary();
   return entry;
 }
 
@@ -756,7 +767,7 @@
       root_frame_tree_node, nullptr, ClonePolicy::kShareFrameEntries);
   // When we are not deep-copying, the NavigationEntry is going to be used for
   // a new committed navigation, so it loses its "initial" status.
-  entry->set_is_initial_entry(false);
+  entry->RemoveInitialEntryStatusIfNecessary();
   return entry;
 }
 
diff --git a/content/browser/renderer_host/navigation_entry_impl.h b/content/browser/renderer_host/navigation_entry_impl.h
index 88c1740..8f58bfd 100644
--- a/content/browser/renderer_host/navigation_entry_impl.h
+++ b/content/browser/renderer_host/navigation_entry_impl.h
@@ -430,9 +430,10 @@
     back_forward_cache_metrics_ = metrics;
   }
 
-  void set_is_initial_entry(bool is_initial_entry) {
-    is_initial_entry_ = is_initial_entry;
-  }
+  // If this is an "initial NavigationEntry", removes the "initial" status and
+  // ensures that the URL saved in the entry is "about:blank" instead of an
+  // empty URL.
+  void RemoveInitialEntryStatusIfNecessary();
 
   void set_did_not_exist_without_initial_navigation_entry(
       bool did_not_exist_without_initial_navigation_entry) {
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 074b73ed..a66ed4e 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -640,13 +640,6 @@
                    process_id, frame_token.GetAs<blink::RemoteFrameToken>()));
 }
 
-// Takes the lower 31 bits of the metric-name-hash of a Mojo interface |name|.
-base::Histogram::Sample HashInterfaceNameToHistogramSample(
-    base::StringPiece name) {
-  return base::strict_cast<base::Histogram::Sample>(
-      static_cast<int32_t>(base::HashMetricName(name) & 0x7fffffffull));
-}
-
 // Set crash keys that will help understand the circumstances of a renderer
 // kill.  Note that the commit URL is already reported in a crash key, and
 // additional keys are logged in RenderProcessHostImpl::ShutdownForBadMessage.
@@ -1175,41 +1168,6 @@
 };
 #endif  // BUILDFLAG(ENABLE_PLUGINS)
 
-class RenderFrameHostImpl::DroppedInterfaceRequestLogger
-    : public blink::mojom::BrowserInterfaceBroker {
- public:
-  explicit DroppedInterfaceRequestLogger(
-      mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker> receiver) {
-    receiver_.Bind(std::move(receiver));
-  }
-
-  DroppedInterfaceRequestLogger(const DroppedInterfaceRequestLogger&) = delete;
-  DroppedInterfaceRequestLogger& operator=(
-      const DroppedInterfaceRequestLogger&) = delete;
-
-  ~DroppedInterfaceRequestLogger() override {
-    UMA_HISTOGRAM_EXACT_LINEAR("RenderFrameHostImpl.DroppedInterfaceRequests",
-                               num_dropped_requests_, 20);
-  }
-
- protected:
-  // blink::mojom::BrowserInterfaceBroker
-  void GetInterface(mojo::GenericPendingReceiver receiver) override {
-    ++num_dropped_requests_;
-    auto interface_name = receiver.interface_name().value_or("");
-    base::UmaHistogramSparse(
-        "RenderFrameHostImpl.DroppedInterfaceRequestName",
-        HashInterfaceNameToHistogramSample(interface_name));
-    DLOG(WARNING)
-        << "InterfaceRequest was dropped, the document is no longer active: "
-        << interface_name;
-  }
-
- private:
-  mojo::Receiver<blink::mojom::BrowserInterfaceBroker> receiver_{this};
-  int num_dropped_requests_ = 0;
-};
-
 struct PendingNavigation {
   blink::mojom::CommonNavigationParamsPtr common_params;
   blink::mojom::BeginNavigationParamsPtr begin_navigation_params;
@@ -5465,10 +5423,13 @@
   GetPage().set_is_document_available_in_main_document(true);
   GetPage().set_uses_temporary_zoom_level(uses_temporary_zoom_level);
 
-  // In case of prerendering, we dispatch DocumentAvailableInMainFrame on
-  // activation. This is done to avoid notifying observers about a load event
-  // triggered from an inactive RenderFrameHost.
-  if (lifecycle_state() == LifecycleStateImpl::kPrerendering)
+  // Don't dispatch DocumentAvailableInMainFrame for non-primary
+  // RenderFrameHosts. As most of the observers are interested only in taking
+  // into account and can interact with or send IPCs to only the current
+  // document in the primary main frame. Since the WebContents could be hosting
+  // more than one main frame (e.g., fenced frame, prerender pages or pending
+  // delete RFHs), return early for other cases.
+  if (!IsInPrimaryMainFrame())
     return;
 
   delegate_->DocumentAvailableInMainFrame(this);
@@ -10459,10 +10420,12 @@
     // If the navigation went through the browser before committing, it's
     // possible to calculate the referrer only using information known by the
     // browser.
-    // TODO(https://crbug.com/1131832): Calculate the referrer for same-document
-    // navigations and the synchronous about:blank commit in the browser too,
-    // and remove `referrer` from DidCommitProvisionalLoadParams.
+    // TODO(https://crbug.com/1131832): Get rid of params->referrer completely.
     params->referrer = GetReferrerForDidCommitParams(navigation_request.get());
+  } else {
+    // For renderer-initiated same-document navigations and the initial
+    // about:blank navigation, the referrer policy shouldn't change.
+    params->referrer->policy = policy_container_host_->referrer_policy();
   }
 
   if (!navigation_request) {
@@ -11112,10 +11075,7 @@
 
   if (interface_params) {
     if (broker_receiver_.is_bound()) {
-      auto broker_receiver_of_previous_document = broker_receiver_.Unbind();
-      dropped_interface_request_logger_ =
-          std::make_unique<DroppedInterfaceRequestLogger>(
-              std::move(broker_receiver_of_previous_document));
+      broker_receiver_.reset();
     }
     BindBrowserInterfaceBrokerReceiver(
         std::move(interface_params->browser_interface_broker_receiver));
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 553a02f4..19a38dd 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -2591,7 +2591,6 @@
   FRIEND_TEST_ALL_PREFIXES(DocumentUserDataTest, CheckInPendingDeletionState);
   FRIEND_TEST_ALL_PREFIXES(WebContentsImplBrowserTest, FrozenAndUnfrozenIPC);
 
-  class DroppedInterfaceRequestLogger;
   class SubresourceLoaderFactoriesConfig;
 
   enum class FencedFrameStatus {
@@ -3739,14 +3738,6 @@
   // prerendered.
   std::unique_ptr<MojoBinderPolicyApplier> mojo_binder_policy_applier_;
 
-  // Logs interface requests that arrive after the frame has already committed a
-  // non-same-document navigation, and has already unbound
-  // |broker_receiver_| from the interface connection that had been used to
-  // service RenderFrame::GetBrowserInterfaceBroker for the previously active
-  // document in the frame.
-  std::unique_ptr<DroppedInterfaceRequestLogger>
-      dropped_interface_request_logger_;
-
   // IPC-friendly token that represents this host.
   const blink::LocalFrameToken frame_token_;
 
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index 51b5031..5ed9969 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -22,7 +22,6 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.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/scoped_run_loop_timeout.h"
@@ -615,11 +614,6 @@
   }
 };
 
-mojo::ScopedMessagePipeHandle CreateDisconnectedMessagePipeHandle() {
-  mojo::MessagePipe pipe;
-  return std::move(pipe.handle0);
-}
-
 }  // namespace
 
 // Tests that a beforeunload dialog in an iframe doesn't stop the beforeunload
@@ -2549,103 +2543,6 @@
       innermost_iframe->current_frame_host()->ComputeSiteForCookies().IsNull());
 }
 
-// Verify that if the UMA histograms are correctly recording if interface
-// broker requests are getting dropped because they racily arrive from the
-// previously active document (after the next navigation already committed).
-IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
-                       DroppedInterfaceRequestCounter) {
-  const GURL kUrl1(embedded_test_server()->GetURL("/title1.html"));
-  const GURL kUrl2(embedded_test_server()->GetURL("/title2.html"));
-  const GURL kUrl3(embedded_test_server()->GetURL("/title3.html"));
-  const GURL kUrl4(embedded_test_server()->GetURL("/empty.html"));
-
-  // The 31-bit hash of the string "content.mojom.MojoWebTestHelper".
-  const int32_t kHashOfContentMojomMojoWebTestHelper = 0x77b7b3d6;
-
-  // Client ends of the fake interface broker receivers injected for the first
-  // and second navigations.
-  mojo::Remote<blink::mojom::BrowserInterfaceBroker> interface_broker_1;
-  mojo::Remote<blink::mojom::BrowserInterfaceBroker> interface_broker_2;
-
-  base::RunLoop wait_until_connection_error_loop_1;
-  base::RunLoop wait_until_connection_error_loop_2;
-
-  {
-    ScopedFakeInterfaceBrokerRequestInjector injector(web_contents());
-    injector.set_fake_receiver_for_next_commit(
-        interface_broker_1.BindNewPipeAndPassReceiver());
-    interface_broker_1.set_disconnect_handler(
-        wait_until_connection_error_loop_1.QuitClosure());
-    ASSERT_TRUE(NavigateToURLAndDoNotWaitForLoadStop(shell(), kUrl1));
-  }
-
-  // The test below only makes sense for same-RFH navigations, so we need to
-  // ensure that we won't trigger a same-site cross-RFH navigation.
-  DisableProactiveBrowsingInstanceSwapFor(web_contents()->GetMainFrame());
-
-  {
-    ScopedFakeInterfaceBrokerRequestInjector injector(web_contents());
-    injector.set_fake_receiver_for_next_commit(
-        interface_broker_2.BindNewPipeAndPassReceiver());
-    interface_broker_2.set_disconnect_handler(
-        wait_until_connection_error_loop_2.QuitClosure());
-    ASSERT_TRUE(NavigateToURLAndDoNotWaitForLoadStop(shell(), kUrl2));
-  }
-
-  // Simulate two interface requests corresponding to the first navigation
-  // arrived after the second navigation was committed, hence were dropped.
-  interface_broker_1->GetInterface(
-      mojo::PendingReceiver<mojom::MojoWebTestHelper>(
-          CreateDisconnectedMessagePipeHandle()));
-  interface_broker_1->GetInterface(
-      mojo::PendingReceiver<mojom::MojoWebTestHelper>(
-          CreateDisconnectedMessagePipeHandle()));
-
-  // RFHI destroys the DroppedInterfaceRequestLogger from navigation `n` on
-  // navigation `n+2`. Histrograms are recorded on destruction, there should
-  // be a single sample indicating two requests having been dropped for the
-  // first URL.
-  {
-    base::HistogramTester histogram_tester;
-    ASSERT_TRUE(NavigateToURLAndDoNotWaitForLoadStop(shell(), kUrl3));
-    histogram_tester.ExpectUniqueSample(
-        "RenderFrameHostImpl.DroppedInterfaceRequests", 2, 1);
-    histogram_tester.ExpectUniqueSample(
-        "RenderFrameHostImpl.DroppedInterfaceRequestName",
-        kHashOfContentMojomMojoWebTestHelper, 2);
-  }
-
-  // Simulate one interface request dropped for the second URL.
-  interface_broker_2->GetInterface(
-      mojo::PendingReceiver<mojom::MojoWebTestHelper>(
-          CreateDisconnectedMessagePipeHandle()));
-
-  // A final navigation should record the sample from the second URL.
-  {
-    base::HistogramTester histogram_tester;
-    ASSERT_TRUE(NavigateToURLAndDoNotWaitForLoadStop(shell(), kUrl4));
-
-    histogram_tester.ExpectUniqueSample(
-        "RenderFrameHostImpl.DroppedInterfaceRequests", 1, 1);
-    histogram_tester.ExpectUniqueSample(
-        "RenderFrameHostImpl.DroppedInterfaceRequestName",
-        kHashOfContentMojomMojoWebTestHelper, 1);
-  }
-
-  // Both the DroppedInterfaceRequestLogger for the first and second URLs are
-  // destroyed -- even more interfacerequests should not cause any crashes.
-  interface_broker_1->GetInterface(
-      mojo::PendingReceiver<mojom::MojoWebTestHelper>(
-          CreateDisconnectedMessagePipeHandle()));
-  interface_broker_2->GetInterface(
-      mojo::PendingReceiver<mojom::MojoWebTestHelper>(
-          CreateDisconnectedMessagePipeHandle()));
-
-  // The interface connections should be broken.
-  wait_until_connection_error_loop_1.Run();
-  wait_until_connection_error_loop_2.Run();
-}
-
 // Regression test for https://crbug.com/852350
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
                        GetCanonicalUrlAfterRendererCrash) {
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index ca8535d..646a7d2 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -3368,6 +3368,7 @@
     switches::kLogFile,
     switches::kLoggingLevel,
     switches::kMaxActiveWebGLContexts,
+    switches::kMaxDecodedImageSizeMb,
     switches::kMaxWebMediaPlayerCount,
     switches::kMSEAudioBufferSizeLimitMb,
     switches::kMSEVideoBufferSizeLimitMb,
diff --git a/content/browser/renderer_host/renderer_sandboxed_process_launcher_delegate_unittest.cc b/content/browser/renderer_host/renderer_sandboxed_process_launcher_delegate_unittest.cc
index 9c0f6382..f5a739ae 100644
--- a/content/browser/renderer_host/renderer_sandboxed_process_launcher_delegate_unittest.cc
+++ b/content/browser/renderer_host/renderer_sandboxed_process_launcher_delegate_unittest.cc
@@ -88,10 +88,7 @@
               policy->GetAppContainer()->GetAppContainerType());
 
     ::sandbox::policy::EqualSidList(
-        static_cast<::sandbox::PolicyBase*>(policy.get())
-            ->GetAppContainerBase()
-            ->GetCapabilities(),
-        {});
+        policy->GetAppContainer()->GetCapabilities(), {});
   } else {
     EXPECT_EQ(policy->GetAppContainer().get(), nullptr);
   }
diff --git a/content/browser/service_worker/service_worker_container_host.cc b/content/browser/service_worker/service_worker_container_host.cc
index 346744e..42470968 100644
--- a/content/browser/service_worker/service_worker_container_host.cc
+++ b/content/browser/service_worker/service_worker_container_host.cc
@@ -385,10 +385,12 @@
     ChildProcessSecurityPolicyImpl* policy =
         ChildProcessSecurityPolicyImpl::GetInstance();
     for (const auto& file : file_paths) {
-      if (!policy->CanReadFile(GetProcessId(), file))
+      if (!policy->CanReadFile(GetProcessId(), file)) {
         mojo::ReportBadMessage(
             "The renderer doesn't have access to the file "
             "but it tried to grant access to the controller.");
+        return;
+      }
 
       if (!policy->CanReadFile(controller_process_id, file))
         policy->GrantReadFile(controller_process_id, file);
diff --git a/content/browser/service_worker/service_worker_registry_unittest.cc b/content/browser/service_worker/service_worker_registry_unittest.cc
index f6b9981..27f1001 100644
--- a/content/browser/service_worker/service_worker_registry_unittest.cc
+++ b/content/browser/service_worker/service_worker_registry_unittest.cc
@@ -2440,10 +2440,8 @@
   // Promote the worker to active and add a controllee.
   registration_->SetActiveVersion(registration_->waiting_version());
   registration_->active_version()->SetStatus(ServiceWorkerVersion::ACTIVATED);
-  registry()->UpdateToActiveState(
-      registration_->id(),
-      blink::StorageKey(url::Origin::Create(registration_->scope())),
-      base::DoNothing());
+  registry()->UpdateToActiveState(registration_->id(), registration_->key(),
+                                  base::DoNothing());
   ServiceWorkerRemoteContainerEndpoint remote_endpoint;
   base::WeakPtr<ServiceWorkerContainerHost> container_host =
       CreateContainerHostForWindow(
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index 9c6f5d9..09a7b9c 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -205,8 +205,7 @@
     container_host->UpdateUrls(
         registration_->scope(),
         net::SiteForCookies::FromUrl(registration_->scope()),
-        url::Origin::Create(registration_->scope()),
-        blink::StorageKey(url::Origin::Create(registration_->scope())));
+        registration_->key().origin(), registration_->key());
     container_host->SetControllerRegistration(
         registration_, false /* notify_controllerchange */);
     EXPECT_TRUE(version_->HasControllee());
@@ -372,8 +371,7 @@
   absl::optional<blink::ServiceWorkerStatusCode> status;
   base::RunLoop run_loop;
   helper_->context()->registry()->DeleteRegistration(
-      registration_,
-      blink::StorageKey(url::Origin::Create(registration_->scope())),
+      registration_, registration_->key(),
       ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure()));
   run_loop.Run();
   ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, status.value());
@@ -463,8 +461,7 @@
   container_host->UpdateUrls(
       registration_->scope(),
       net::SiteForCookies::FromUrl(registration_->scope()),
-      url::Origin::Create(registration_->scope()),
-      blink::StorageKey(url::Origin::Create(registration_->scope())));
+      registration_->key().origin(), registration_->key());
   container_host->SetControllerRegistration(registration_, false);
   EXPECT_TRUE(version_->HasControllee());
   EXPECT_TRUE(container_host->controller());
@@ -1259,8 +1256,7 @@
   container_host->UpdateUrls(
       registration_->scope(),
       net::SiteForCookies::FromUrl(registration_->scope()),
-      url::Origin::Create(registration_->scope()),
-      blink::StorageKey(url::Origin::Create(registration_->scope())));
+      registration_->key().origin(), registration_->key());
   container_host->SetControllerRegistration(
       registration_, false /* notify_controllerchange */);
   EXPECT_TRUE(version_->HasControllee());
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index daec3291..b3c48fa 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -5828,6 +5828,7 @@
     RenderFrameHost* render_frame_host) {
   OPTIONAL_TRACE_EVENT0("content",
                         "WebContentsImpl::DocumentAvailableInMainFrame");
+  DCHECK(render_frame_host->IsInPrimaryMainFrame());
   SCOPED_UMA_HISTOGRAM_TIMER(
       "WebContentsObserver.DocumentAvailableInMainFrame");
   observers_.NotifyObservers(&WebContentsObserver::DocumentAvailableInMainFrame,
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index a0ebd6cd..dcc4fea4 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -700,6 +700,7 @@
     DropData* drop_data,
     const ui::OSExchangeData& data) const {
   drop_data->did_originate_from_renderer = data.DidOriginateFromRenderer();
+  drop_data->is_from_privileged = data.IsFromPrivileged();
 
   std::u16string plain_text;
   data.GetString(&plain_text);
@@ -1145,6 +1146,9 @@
           ? nullptr
           : std::make_unique<ui::DataTransferEndpoint>(
                 web_contents_->GetMainFrame()->GetLastCommittedOrigin()));
+  WebContentsDelegate* delegate = web_contents_->GetDelegate();
+  if (delegate && delegate->IsPrivileged())
+    data->MarkAsFromPrivileged();
 
   if (!image.isNull())
     data->provider().SetDragImage(image, image_offset);
@@ -1348,11 +1352,27 @@
   blink::DragOperationsMask op_mask =
       ConvertToDragOperationsMask(event.source_operations());
 
-  // Give the delegate an opportunity to cancel the drag.
-  if (web_contents_->GetDelegate() &&
-      !web_contents_->GetDelegate()->CanDragEnter(
-          web_contents_, *current_drop_data_.get(), op_mask)) {
-    current_drop_data_.reset(nullptr);
+  WebContentsDelegate* delegate = web_contents_->GetDelegate();
+
+  auto allow_drag = [&]() {
+    // We only allow drags from privileged WebContents to
+    // another privileged WebContents.
+    // Do not allow dragging privileged WebContents to
+    // non-priviledged WebContents or vice versa.
+    if (current_drop_data_->is_from_privileged !=
+        (delegate && delegate->IsPrivileged())) {
+      return false;
+    }
+
+    // Give the delegate an opportunity to cancel the drag
+    if (delegate && !delegate->CanDragEnter(web_contents_,
+                                            *current_drop_data_.get(), op_mask))
+      return false;
+    return true;
+  };
+
+  if (!allow_drag()) {
+    current_drop_data_ = nullptr;
     return;
   }
 
diff --git a/content/browser/web_contents/web_contents_view_aura.h b/content/browser/web_contents/web_contents_view_aura.h
index 7dd09f3..5909beb 100644
--- a/content/browser/web_contents/web_contents_view_aura.h
+++ b/content/browser/web_contents/web_contents_view_aura.h
@@ -112,6 +112,17 @@
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, StartDragging);
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, GetDropCallback_Run);
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, GetDropCallback_Cancelled);
+  FRIEND_TEST_ALL_PREFIXES(
+      WebContentsViewAuraTest,
+      RejectDragFromPrivilegedWebContentsToNonPrivilegedWebContents);
+  FRIEND_TEST_ALL_PREFIXES(
+      WebContentsViewAuraTest,
+      AcceptDragFromPrivilegedWebContentsToPrivilegedWebContents);
+  FRIEND_TEST_ALL_PREFIXES(
+      WebContentsViewAuraTest,
+      RejectDragFromNonPrivilegedWebContentsToPrivilegedWebContents);
+  FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest,
+                           StartDragFromPrivilegedWebContents);
 
   class WindowObserver;
 
diff --git a/content/browser/web_contents/web_contents_view_aura_unittest.cc b/content/browser/web_contents/web_contents_view_aura_unittest.cc
index ffcf0c0..17fd3b33 100644
--- a/content/browser/web_contents/web_contents_view_aura_unittest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_unittest.cc
@@ -57,10 +57,7 @@
 
 constexpr gfx::Rect kBounds = gfx::Rect(0, 0, 20, 20);
 constexpr gfx::PointF kClientPt = {5, 10};
-
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_WIN)
 constexpr gfx::PointF kScreenPt = {17, 3};
-#endif
 
 // Runs a specified callback when a ui::MouseEvent is received.
 class RunCallbackOnActivation : public WebContentsDelegate {
@@ -82,6 +79,12 @@
   base::OnceClosure closure_;
 };
 
+class PrivilegedWebContentsDelegate : public WebContentsDelegate {
+ public:
+  // WebContentsDelegate:
+  bool IsPrivileged() override { return true; }
+};
+
 class TestDragDropClient : public aura::client::DragDropClient {
  public:
   // aura::client::DragDropClient:
@@ -752,4 +755,71 @@
 
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+TEST_F(WebContentsViewAuraTest,
+       RejectDragFromPrivilegedWebContentsToNonPrivilegedWebContents) {
+  WebContentsViewAura* view = GetView();
+  auto data = std::make_unique<ui::OSExchangeData>();
+  data->MarkAsFromPrivileged();
+  ui::DropTargetEvent event(*data.get(), kClientPt, kScreenPt,
+                            ui::DragDropTypes::DRAG_MOVE);
+  // Simulate drag enter.
+  EXPECT_EQ(nullptr, view->current_drop_data_);
+  view->OnDragEntered(event);
+  ASSERT_EQ(nullptr, view->current_drop_data_);
+}
+
+TEST_F(WebContentsViewAuraTest,
+       AcceptDragFromPrivilegedWebContentsToPrivilegedWebContents) {
+  WebContentsViewAura* view = GetView();
+  PrivilegedWebContentsDelegate delegate;
+  web_contents()->SetDelegate(&delegate);
+  auto data = std::make_unique<ui::OSExchangeData>();
+  data->MarkAsFromPrivileged();
+  ui::DropTargetEvent event(*data.get(), kClientPt, kScreenPt,
+                            ui::DragDropTypes::DRAG_MOVE);
+  // Simulate drag enter.
+  EXPECT_EQ(nullptr, view->current_drop_data_);
+  view->OnDragEntered(event);
+  ASSERT_NE(nullptr, view->current_drop_data_);
+}
+
+TEST_F(WebContentsViewAuraTest,
+       RejectDragFromNonPrivilegedWebContentsToPrivilegedWebContents) {
+  WebContentsViewAura* view = GetView();
+  PrivilegedWebContentsDelegate delegate;
+  web_contents()->SetDelegate(&delegate);
+  auto data = std::make_unique<ui::OSExchangeData>();
+  ui::DropTargetEvent event(*data.get(), kClientPt, kScreenPt,
+                            ui::DragDropTypes::DRAG_MOVE);
+  // Simulate drag enter.
+  EXPECT_EQ(nullptr, view->current_drop_data_);
+  view->OnDragEntered(event);
+  ASSERT_EQ(nullptr, view->current_drop_data_);
+}
+
+TEST_F(WebContentsViewAuraTest, StartDragFromPrivilegedWebContents) {
+  TestDragDropClient drag_drop_client;
+  aura::client::SetDragDropClient(root_window(), &drag_drop_client);
+
+  // Mark the Web Contents as native UI.
+  WebContentsViewAura* view = GetView();
+  PrivilegedWebContentsDelegate delegate;
+  web_contents()->SetDelegate(&delegate);
+
+  // This condition is needed to avoid calling WebContentsViewAura::EndDrag
+  // which will result NOTREACHED being called in
+  // `RenderWidgetHostViewBase::TransformPointToCoordSpaceForView`.
+  view->drag_in_progress_ = true;
+
+  DropData drop_data;
+  view->StartDragging(drop_data, blink::DragOperationsMask::kDragOperationNone,
+                      gfx::ImageSkia(), gfx::Vector2d(),
+                      blink::mojom::DragEventSourceInfo(),
+                      RenderWidgetHostImpl::From(rvh()->GetWidget()));
+
+  ui::OSExchangeData* exchange_data = drag_drop_client.GetDragDropData();
+  EXPECT_TRUE(exchange_data);
+  EXPECT_TRUE(exchange_data->IsFromPrivileged());
+}
+
 }  // namespace content
diff --git a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
index fd7d2af..12708d7 100644
--- a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
+++ b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
@@ -326,8 +326,7 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerBrowserTest,
-                       DISABLED_Simple) {
+IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerBrowserTest, Simple) {
   InstallMockCert();
   InstallMockCertChainInterceptor();
 
@@ -405,8 +404,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerBrowserTest,
-                       DISABLED_VariantMatch) {
+IN_PROC_BROWSER_TEST_P(SignedExchangeRequestHandlerBrowserTest, VariantMatch) {
   SetAcceptLangs("en-US,fr");
   InstallUrlInterceptor(
       GURL("https://cert.example.org/cert.msg"),
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index ba648f7..f29f230f 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -7,6 +7,8 @@
 #include "base/callback.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/string_piece.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
 #include "content/browser/bad_message.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/webid/id_token_request_callback_data.h"
@@ -31,6 +33,8 @@
 namespace content {
 
 namespace {
+static constexpr base::TimeDelta kIdTokenRequestDelay = base::Seconds(3);
+
 std::string FormatRequestParams(const std::string& client_id,
                                 const std::string& nonce) {
   std::string query;
@@ -45,6 +49,36 @@
   }
   return query;
 }
+
+std::string FormatRequestParamsWithoutScope(const std::string& client_id,
+                                            const std::string& nonce,
+                                            const std::string& account_id,
+                                            bool is_sign_in) {
+  std::string query;
+  if (!client_id.empty())
+    query += "client_id=" + client_id;
+
+  if (!nonce.empty()) {
+    if (!query.empty())
+      query += "&";
+    query += "nonce=" + nonce;
+  }
+
+  if (!account_id.empty()) {
+    if (!query.empty())
+      query += "&";
+    query += "account_id=" + account_id;
+  }
+  // For returning users who are signing in instead of signing up, we do not
+  // show the privacy policy and terms of service on the consent sheet. This
+  // field indicates in the request that whether the user has granted consent
+  // after seeing the sheet with privacy policy and terms of service.
+  std::string consent_acquired = is_sign_in ? "false" : "true";
+  if (!query.empty())
+    query += "&consent_acquired=" + consent_acquired;
+  return query;
+}
+
 }  // namespace
 
 FederatedAuthRequestImpl::FederatedAuthRequestImpl(RenderFrameHost* host,
@@ -581,8 +615,8 @@
   }
 }
 
-void FederatedAuthRequestImpl::OnAccountSelected(
-    const std::string& account_id) {
+void FederatedAuthRequestImpl::OnAccountSelected(const std::string& account_id,
+                                                 bool is_sign_in) {
   // This could happen if user didn't select any accounts.
   if (account_id.empty()) {
     CompleteRequest(RequestIdTokenStatus::kError, "");
@@ -597,8 +631,11 @@
   }
 
   account_id_ = account_id;
+  id_token_request_time_ = base::TimeTicks::Now();
   network_manager_->SendTokenRequest(
-      endpoints_.token, account_id_, FormatRequestParams(client_id_, nonce_),
+      endpoints_.token, account_id_,
+      FormatRequestParamsWithoutScope(client_id_, nonce_, account_id,
+                                      is_sign_in),
       base::BindOnce(&FederatedAuthRequestImpl::OnTokenResponseReceived,
                      weak_ptr_factory_.GetWeakPtr()));
 }
@@ -606,6 +643,27 @@
 void FederatedAuthRequestImpl::OnTokenResponseReceived(
     IdpNetworkRequestManager::FetchStatus status,
     const std::string& id_token) {
+  // When fetching id tokens we show a "Verify" sheet to users in case fetching
+  // takes a long time due to latency etc.. In case that the fetching process is
+  // fast, we still want to show the "Verify" sheet for at least
+  // |kIdTokenRequestDelay| seconds for better UX.
+  base::TimeTicks response_receive_time = base::TimeTicks::Now();
+  base::TimeDelta fetch_time = response_receive_time - id_token_request_time_;
+  if (fetch_time >= kIdTokenRequestDelay) {
+    CompleteIdTokenRequest(status, id_token);
+    return;
+  }
+
+  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&FederatedAuthRequestImpl::CompleteIdTokenRequest,
+                     weak_ptr_factory_.GetWeakPtr(), status, id_token),
+      kIdTokenRequestDelay - fetch_time);
+}
+
+void FederatedAuthRequestImpl::CompleteIdTokenRequest(
+    IdpNetworkRequestManager::FetchStatus status,
+    const std::string& id_token) {
   switch (status) {
     case IdpNetworkRequestManager::FetchStatus::kHttpNotFoundError: {
       CompleteRequest(RequestIdTokenStatus::kErrorFetchingIdTokenHttpNotFound,
diff --git a/content/browser/webid/federated_auth_request_impl.h b/content/browser/webid/federated_auth_request_impl.h
index 36e7586..6b40dccf 100644
--- a/content/browser/webid/federated_auth_request_impl.h
+++ b/content/browser/webid/federated_auth_request_impl.h
@@ -90,7 +90,9 @@
       IdpNetworkRequestManager::FetchStatus status,
       IdpNetworkRequestManager::AccountList accounts,
       IdentityProviderMetadata idp_metadata);
-  void OnAccountSelected(const std::string& account_id);
+  void OnAccountSelected(const std::string& account_id, bool is_sign_in);
+  void CompleteIdTokenRequest(IdpNetworkRequestManager::FetchStatus status,
+                              const std::string& id_token);
   void OnTokenResponseReceived(IdpNetworkRequestManager::FetchStatus status,
                                const std::string& id_token);
   void DispatchOneLogout();
@@ -168,6 +170,7 @@
   // mediation flow.
   std::string account_id_;
   std::string id_token_;
+  base::TimeTicks id_token_request_time_;
   blink::mojom::FederatedAuthRequest::RequestIdTokenCallback
       auth_request_callback_;
 
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index c38042f4..9fe10ea 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -601,7 +601,8 @@
                       on_selected) {
                 displayed_accounts_ =
                     AccountList(accounts.begin(), accounts.end());
-                std::move(on_selected).Run(accounts[0].account_id);
+                std::move(on_selected)
+                    .Run(accounts[0].account_id, /*is_sign_in=*/false);
               }));
     }
 
@@ -615,6 +616,7 @@
                   IdpNetworkRequestManager::TokenRequestCallback callback) {
                 std::move(callback).Run(*conf.token_response, delivered_token);
               }));
+      task_environment()->FastForwardBy(base::Seconds(3));
     }
   }
 
@@ -972,7 +974,8 @@
                   on_selected) {
             EXPECT_EQ(sign_in_mode, SignInMode::kAuto);
             displayed_accounts = AccountList(accounts.begin(), accounts.end());
-            std::move(on_selected).Run(accounts[0].account_id);
+            std::move(on_selected)
+                .Run(accounts[0].account_id, /*is_sign_in=*/true);
           }));
 
   EXPECT_EQ(test_case.config.Mediated_conf.accounts.size(), 1u);
@@ -1002,7 +1005,8 @@
                   on_selected) {
             EXPECT_EQ(sign_in_mode, SignInMode::kExplicit);
             displayed_accounts = AccountList(accounts.begin(), accounts.end());
-            std::move(on_selected).Run(accounts[0].account_id);
+            std::move(on_selected)
+                .Run(accounts[0].account_id, /*is_sign_in=*/true);
           }));
 
   SetMockExpectations(test_case);
@@ -1052,7 +1056,8 @@
             // Auto sign in replaced by explicit sign in if screen reader is on.
             EXPECT_EQ(sign_in_mode, SignInMode::kExplicit);
             displayed_accounts = AccountList(accounts.begin(), accounts.end());
-            std::move(on_selected).Run(accounts[0].account_id);
+            std::move(on_selected)
+                .Run(accounts[0].account_id, /*is_sign_in=*/true);
           }));
 
   EXPECT_EQ(test_case.config.Mediated_conf.accounts.size(), 1u);
diff --git a/content/browser/webid/idp_network_request_manager.cc b/content/browser/webid/idp_network_request_manager.cc
index 3682234..f2246fe 100644
--- a/content/browser/webid/idp_network_request_manager.cc
+++ b/content/browser/webid/idp_network_request_manager.cc
@@ -56,13 +56,13 @@
 constexpr char kIdTokenKey[] = "id_token";
 
 // Token request body keys
-constexpr char kAccountKey[] = "sub";
+constexpr char kAccountKey[] = "account_id";
 constexpr char kRequestKey[] = "request";
 
 // Revoke request body keys.
 constexpr char kClientIdKey[] = "client_id";
 
-constexpr char kJSONMimeType[] = "application/json";
+constexpr char kRequestBodyContentType[] = "application/x-www-form-urlencoded";
 
 // 1 MiB is an arbitrary upper bound that should account for any reasonable
 // response size that is a part of this protocol.
@@ -113,7 +113,7 @@
   resource_request->url = target_url;
   resource_request->site_for_cookies = site_for_cookies;
   resource_request->headers.SetHeader(net::HttpRequestHeaders::kAccept,
-                                      kJSONMimeType);
+                                      kRequestBodyContentType);
 
   // Using a random 64-bit header value. This is just to keep service
   // implementations from assuming any particular static value.
@@ -412,15 +412,13 @@
   DCHECK(!token_request_callback_);
 
   token_request_callback_ = std::move(callback);
-
-  std::string token_request_body = CreateTokenRequestBody(account, request);
-  if (token_request_body.empty()) {
+  if (request.empty()) {
     std::move(token_request_callback_)
         .Run(FetchStatus::kInvalidRequestError, std::string());
     return;
   }
 
-  url_loader_ = CreateCredentialedUrlLoader(token_url, token_request_body);
+  url_loader_ = CreateCredentialedUrlLoader(token_url, request);
 
   url_loader_->DownloadToString(
       loader_factory_.get(),
@@ -463,8 +461,16 @@
 
   revoke_callback_ = std::move(callback);
 
-  std::string revoke_request_body =
-      CreateRevokeRequestBody(client_id, account_id);
+  std::string revoke_request_body;
+  if (!client_id.empty())
+    revoke_request_body += "client_id=" + client_id;
+
+  if (!account_id.empty()) {
+    if (!revoke_request_body.empty())
+      revoke_request_body += "&";
+    revoke_request_body += "account_id=" + account_id;
+  }
+
   if (revoke_request_body.empty()) {
     std::move(revoke_callback_).Run(RevokeResponse::kError);
     return;
@@ -828,7 +834,7 @@
   resource_request->credentials_mode =
       network::mojom::CredentialsMode::kInclude;
   resource_request->headers.SetHeader(net::HttpRequestHeaders::kAccept,
-                                      kJSONMimeType);
+                                      kRequestBodyContentType);
   // TODO(kenrb): Not following redirects is important for security because
   // this bypasses CORB. Ensure there is a test added.
   // https://crbug.com/1155312.
@@ -855,7 +861,7 @@
   if (request_body) {
     resource_request->method = net::HttpRequestHeaders::kPostMethod;
     resource_request->headers.SetHeader(net::HttpRequestHeaders::kContentType,
-                                        kJSONMimeType);
+                                        kRequestBodyContentType);
   }
 
   auto traffic_annotation = CreateTrafficAnnotation();
@@ -863,7 +869,7 @@
       network::SimpleURLLoader::Create(std::move(resource_request),
                                        traffic_annotation);
   if (request_body)
-    loader->AttachStringForUpload(*request_body, kJSONMimeType);
+    loader->AttachStringForUpload(*request_body, kRequestBodyContentType);
   return loader;
 }
 
diff --git a/content/browser/webid/idp_network_request_manager_unittest.cc b/content/browser/webid/idp_network_request_manager_unittest.cc
index 7aa629c..75c77bf 100644
--- a/content/browser/webid/idp_network_request_manager_unittest.cc
+++ b/content/browser/webid/idp_network_request_manager_unittest.cc
@@ -598,8 +598,7 @@
         ASSERT_EQ(network::DataElement::Tag::kBytes, elem.type());
         const network::DataElementBytes& byte_elem =
             elem.As<network::DataElementBytes>();
-        EXPECT_EQ("{\"request\":{\"client_id\":\"xxx\"},\"sub\":\"yyy\"}",
-                  byte_elem.AsStringPiece());
+        EXPECT_EQ("client_id=xxx&account_id=yyy", byte_elem.AsStringPiece());
       });
   test_url_loader_factory().SetInterceptor(interceptor);
   RevokeResponse status = SendRevokeRequestAndWaitForResponse("xxx", "yyy");
diff --git a/content/browser/webui/shared_resources_data_source.cc b/content/browser/webui/shared_resources_data_source.cc
index b24e927..0f768fd 100644
--- a/content/browser/webui/shared_resources_data_source.cc
+++ b/content/browser/webui/shared_resources_data_source.cc
@@ -104,15 +104,12 @@
   }
 }
 
-}  // namespace
-
-WebUIDataSource* CreateSharedResourcesDataSource() {
-  WebUIDataSource* source =
-      content::WebUIDataSource::Create(kChromeUIResourcesHost);
+void PopulateSharedResourcesDataSource(WebUIDataSource* source) {
   source->OverrideContentSecurityPolicy(
       network::mojom::CSPDirectiveName::WorkerSrc, "worker-src blob: 'self';");
-  // TODO(crbug.com/1098690): Trusted Type Polymer
-  source->DisableTrustedTypesCSP();
+
+  // Note: Don't put generated Mojo bindings here. Please explicitly add them to
+  // each WebUI's own data source.
 
   AddResources(GetContentResourceIds(), kContentResources,
                kContentResourcesSize, source);
@@ -138,28 +135,21 @@
 
   source->AddString("fontFamily", webui::GetFontFamily());
   source->AddString("fontSize", webui::GetFontSize());
+}
 
+}  // namespace
+
+WebUIDataSource* CreateSharedResourcesDataSource() {
+  WebUIDataSource* source =
+      content::WebUIDataSource::Create(kChromeUIResourcesHost);
+  PopulateSharedResourcesDataSource(source);
   return source;
 }
 
 WebUIDataSource* CreateUntrustedSharedResourcesDataSource() {
-  // This data source only serves resources used by all chrome-untrusted://
-  // WebUI pages.
-  //
-  // Don't put generated Mojo bindings here. Please explicitly add them to each
-  // WebUI's own data source.
   WebUIDataSource* source =
       content::WebUIDataSource::Create(kChromeUIUntrustedResourcesURL);
-
-  source->AddResourcePaths(
-      base::make_span(kMojoBindingsResources, kMojoBindingsResourcesSize));
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  // Expose a small subset of shared resources to chrome-untrusted://resources/
-  AddResources({IDR_WEBUI_JS_LOAD_TIME_DATA_M_JS}, kWebuiGeneratedResources,
-               kWebuiGeneratedResourcesSize, source);
-#endif
-
+  PopulateSharedResourcesDataSource(source);
   return source;
 }
 
diff --git a/content/browser/webui/web_ui_main_frame_observer.cc b/content/browser/webui/web_ui_main_frame_observer.cc
index 7cd821b..fd63709a0 100644
--- a/content/browser/webui/web_ui_main_frame_observer.cc
+++ b/content/browser/webui/web_ui_main_frame_observer.cc
@@ -74,7 +74,6 @@
     int32_t line_no,
     const std::u16string& source_id,
     const absl::optional<std::u16string>& untrusted_stack_trace) {
-  // TODO(iby) Change all VLOGs to DVLOGs once tast tests are stable.
   DVLOG(3) << "OnDidAddMessageToConsole called for " << message;
   if (untrusted_stack_trace) {
     DVLOG(3) << "stack is " << *untrusted_stack_trace;
@@ -134,8 +133,6 @@
   if (untrusted_stack_trace) {
     report.stack_trace = base::UTF16ToUTF8(*untrusted_stack_trace);
   }
-  report.send_to_production_servers =
-      features::kWebUIJavaScriptErrorReportsSendToProductionParam.Get();
 
   GURL page_url = source_frame->GetLastCommittedURL();
   if (page_url.is_valid()) {
@@ -149,12 +146,6 @@
 
 void WebUIMainFrameObserver::MaybeEnableWebUIJavaScriptErrorReporting(
     NavigationHandle* navigation_handle) {
-  if (!base::FeatureList::IsEnabled(
-          features::kSendWebUIJavaScriptErrorReports)) {
-    DVLOG(3) << "Experiment is off";
-    return;
-  }
-
   error_reporting_enabled_ =
       web_ui_->GetController()->IsJavascriptErrorReportingEnabled();
 
diff --git a/content/browser/webui/web_ui_main_frame_observer_unittest.cc b/content/browser/webui/web_ui_main_frame_observer_unittest.cc
index 3191c1e..5c839dc 100644
--- a/content/browser/webui/web_ui_main_frame_observer_unittest.cc
+++ b/content/browser/webui/web_ui_main_frame_observer_unittest.cc
@@ -9,13 +9,11 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/metrics/field_trial_params.h"
-#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "components/crash/content/browser/error_reporting/javascript_error_report.h"  // nogncheck
 #include "components/crash/content/browser/error_reporting/js_error_report_processor.h"  // nogncheck
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_ui_controller.h"
-#include "content/public/common/content_features.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_browser_context.h"
@@ -97,7 +95,6 @@
 class WebUIMainFrameObserverTest : public RenderViewHostTestHarness {
  public:
   void SetUp() override {
-    SetUpFeatureList();
     RenderViewHostTestHarness::SetUp();
     site_instance_ = SiteInstance::Create(browser_context());
     SetContents(TestWebContents::Create(browser_context(), site_instance_));
@@ -156,19 +153,11 @@
   }
 
  protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
   scoped_refptr<SiteInstance> site_instance_;
   std::unique_ptr<WebUIImpl> web_ui_;
   scoped_refptr<FakeJsErrorReportProcessor> processor_;
   scoped_refptr<JsErrorReportProcessor> previous_processor_;
 
-  virtual void SetUpFeatureList() {
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(
-        features::kSendWebUIJavaScriptErrorReports,
-        {{features::kSendWebUIJavaScriptErrorReportsSendToProductionVariation,
-          "true"}});
-  }
-
   static constexpr char kMessage8[] = "An Error Is Me";
   static constexpr char16_t kMessage16[] = u"An Error Is Me";
   static constexpr char kSourceURL8[] = "chrome://here.is.error/bad.js";
@@ -182,21 +171,6 @@
       u"at poorCaller (chrome://page/my.js:50:10)\n";
 };
 
-// Same as WebUIMainFrameObserverTest but with the
-// features::kSendWebUIJavaScriptErrorReports feature disabled.
-class WebUIMainFrameObserverFeatureDisabledTest
-    : public WebUIMainFrameObserverTest {
- public:
-  WebUIMainFrameObserverFeatureDisabledTest() = default;
-  ~WebUIMainFrameObserverFeatureDisabledTest() override = default;
-
- protected:
-  void SetUpFeatureList() override {
-    scoped_feature_list_.InitAndDisableFeature(
-        features::kSendWebUIJavaScriptErrorReports);
-  }
-};
-
 constexpr char WebUIMainFrameObserverTest::kMessage8[];
 constexpr char16_t WebUIMainFrameObserverTest::kMessage16[];
 constexpr char WebUIMainFrameObserverTest::kSourceURL8[];
@@ -262,15 +236,6 @@
   task_environment()->RunUntilIdle();
 }
 
-TEST_F(WebUIMainFrameObserverFeatureDisabledTest, NotSentIfFlagDisabled) {
-  NavigateToPage();
-  CallOnDidAddMessageToConsole(web_ui_->frame_host(),
-                               blink::mojom::ConsoleMessageLevel::kError,
-                               kMessage16, 5, kSourceURL16, kStackTrace16);
-  task_environment()->RunUntilIdle();
-  EXPECT_EQ(processor_->error_report_count(), 0);
-}
-
 TEST_F(WebUIMainFrameObserverTest, NotSentIfInvalidURL) {
   NavigateToPage();
   CallOnDidAddMessageToConsole(web_ui_->frame_host(),
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 454a6e41..b42a9bb8 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -382,7 +382,7 @@
           {"PartitionedCookies", net::features::kPartitionedCookies},
           {"PrefersColorSchemeClientHintHeader",
            blink::features::kPrefersColorSchemeClientHintHeader},
-          {"FirstPartySets", net::features::kFirstPartySets},
+          {"FirstPartySets", features::kFirstPartySets},
           {"SanitizerAPI", blink::features::kSanitizerAPI},
           {"StorageAccessAPI", blink::features::kStorageAccessAPI},
           {"TargetBlankImpliesNoOpener",
diff --git a/content/common/ax_serialization_utils.cc b/content/common/ax_serialization_utils.cc
index fca8978..3a4a289 100644
--- a/content/common/ax_serialization_utils.cc
+++ b/content/common/ax_serialization_utils.cc
@@ -9,7 +9,7 @@
 namespace content {
 
 bool AXShouldIncludePageScaleFactorInRoot() {
-#if !defined(OS_ANDROID) && !defined(OS_MAC)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_MAC)
   return true;
 #else
   return false;
diff --git a/content/common/child_process_host_impl.cc b/content/common/child_process_host_impl.cc
index 1a63031..efc2d939c 100644
--- a/content/common/child_process_host_impl.cc
+++ b/content/common/child_process_host_impl.cc
@@ -39,12 +39,12 @@
 #include "services/resource_coordinator/public/mojom/memory_instrumentation/constants.mojom.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include "base/linux_util.h"
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
 #include "base/mac/foundation_util.h"
 #include "content/common/mac_helpers.h"
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
 namespace {
 
@@ -71,7 +71,7 @@
   child_path = base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
       switches::kBrowserSubprocessPath);
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   // Use /proc/self/exe rather than our known binary path so updates
   // can't swap out the binary from underneath us.
   if (child_path.empty() && flags & CHILD_ALLOW_SELF)
@@ -83,7 +83,7 @@
   if (child_path.empty())
     base::PathService::Get(CHILD_PROCESS_EXE, &child_path);
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   std::string child_base_name = child_path.BaseName().value();
 
   if (flags != CHILD_NORMAL && base::mac::AmIBundled()) {
diff --git a/content/common/content_constants_internal.h b/content/common/content_constants_internal.h
index 2d41664..4a89c36 100644
--- a/content/common/content_constants_internal.h
+++ b/content/common/content_constants_internal.h
@@ -14,7 +14,7 @@
 
 namespace content {
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 // The mobile hang timer is shorter than the desktop hang timer because the
 // screen is smaller and more intimate, and therefore requires more nimbleness.
 constexpr base::TimeDelta kHungRendererDelay = base::Seconds(5);
diff --git a/content/common/content_message_generator.h b/content/common/content_message_generator.h
index 4547b1a..2370217 100644
--- a/content/common/content_message_generator.h
+++ b/content/common/content_message_generator.h
@@ -8,10 +8,10 @@
 #include "build/build_config.h"
 #include "media/media_buildflags.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #undef CONTENT_COMMON_GIN_JAVA_BRIDGE_MESSAGES_H_
 #include "content/common/gin_java_bridge_messages.h"
 #ifndef CONTENT_COMMON_GIN_JAVA_BRIDGE_MESSAGES_H_
 #error "Failed to include content/common/gin_java_bridge_messages.h"
 #endif
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
diff --git a/content/common/content_navigation_policy.cc b/content/common/content_navigation_policy.cc
index 0d5c2fd..65d1963 100644
--- a/content/common/content_navigation_policy.cc
+++ b/content/common/content_navigation_policy.cc
@@ -26,7 +26,7 @@
     // devices. The default threshold value is set to 1700 MB to account for all
     // 2GB devices which report lower RAM due to carveouts.
     int default_memory_threshold_mb =
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
         1700;
 #else
         // Desktop has lower memory limitations compared to Android allowing us
diff --git a/content/common/content_param_traits_macros.h b/content/common/content_param_traits_macros.h
index bbf4a222..7f2d105f 100644
--- a/content/common/content_param_traits_macros.h
+++ b/content/common/content_param_traits_macros.h
@@ -15,7 +15,7 @@
 #include "ipc/ipc_message_macros.h"
 #include "ui/native_theme/native_theme.h"
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 #include "third_party/blink/public/platform/mac/web_scrollbar_theme.h"
 #endif
 
@@ -25,7 +25,7 @@
 IPC_ENUM_TRAITS_MAX_VALUE(content::NavigationGesture,
                           content::NavigationGestureLast)
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 IPC_ENUM_TRAITS_MAX_VALUE(blink::ScrollerStyle, blink::kScrollerStyleOverlay)
 #endif
 
diff --git a/content/common/content_paths.cc b/content/common/content_paths.cc
index 6013ca8..e3fb6c2 100644
--- a/content/common/content_paths.cc
+++ b/content/common/content_paths.cc
@@ -8,7 +8,7 @@
 #include "base/path_service.h"
 #include "build/build_config.h"
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 #include "base/mac/bundle_locations.h"
 #endif
 
diff --git a/content/common/content_switches_internal.cc b/content/common/content_switches_internal.cc
index eb544f2..91b64e6 100644
--- a/content/common/content_switches_internal.cc
+++ b/content/common/content_switches_internal.cc
@@ -20,17 +20,17 @@
 #include "content/public/common/use_zoom_for_dsf_policy.h"
 #include "third_party/blink/public/mojom/v8_cache_options.mojom.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include "base/debug/debugger.h"
 #include "base/feature_list.h"
 #endif
 
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
+#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
 #include <signal.h>
 static void SigUSR1Handler(int signal) {}
 #endif
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "base/win/windows_version.h"
 
 #include <windows.h>
@@ -40,7 +40,7 @@
 
 namespace {
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 
 std::wstring ToNativeString(base::StringPiece string) {
   return base::ASCIIToWide(string);
@@ -50,7 +50,7 @@
   return base::WideToASCII(string);
 }
 
-#else  // defined(OS_WIN)
+#else  // BUILDFLAG(IS_WIN)
 
 std::string ToNativeString(const std::string& string) {
   return string;
@@ -60,7 +60,7 @@
   return string;
 }
 
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 }  // namespace
 
@@ -89,7 +89,7 @@
 }
 
 void WaitForDebugger(const std::string& label) {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
   std::string title = "Google Chrome";
 #else   // BUILDFLAG(CHROMIUM_BRANDING)
@@ -102,8 +102,8 @@
   message += base::NumberToString(base::GetCurrentProcId());
   ::MessageBox(NULL, base::UTF8ToWide(message).c_str(),
                base::UTF8ToWide(title).c_str(), MB_OK | MB_SETFOREGROUND);
-#elif defined(OS_POSIX)
-#if defined(OS_ANDROID)
+#elif BUILDFLAG(IS_POSIX)
+#if BUILDFLAG(IS_ANDROID)
   LOG(ERROR) << label << " waiting for GDB.";
   // Wait 24 hours for a debugger to be attached to the current process.
   base::debug::WaitForDebugger(24 * 60 * 60, true);
@@ -121,8 +121,8 @@
   sigaction(SIGUSR1, &sa, nullptr);
 
   pause();
-#endif  // defined(OS_ANDROID)
-#endif  // defined(OS_POSIX)
+#endif  // BUILDFLAG(IS_ANDROID)
+#endif  // BUILDFLAG(IS_POSIX)
 }
 
 std::vector<std::string> FeaturesFromSwitch(
diff --git a/content/common/cursors/webcursor_unittest.cc b/content/common/cursors/webcursor_unittest.cc
index 8f53ffa..9601f15a 100644
--- a/content/common/cursors/webcursor_unittest.cc
+++ b/content/common/cursors/webcursor_unittest.cc
@@ -14,7 +14,7 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/skia_conversions.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <windows.h>
 
 #include "ui/base/win/win_cursor.h"
@@ -192,7 +192,7 @@
 }
 #endif
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 void ScaleCursor(float scale, int hotspot_x, int hotspot_y) {
   ui::Cursor cursor(ui::mojom::CursorType::kCustom);
   cursor.set_custom_hotspot(gfx::Point(hotspot_x, hotspot_y));
diff --git a/content/common/drop_data_unittest.cc b/content/common/drop_data_unittest.cc
index 4d42e57f..f5a1742 100644
--- a/content/common/drop_data_unittest.cc
+++ b/content/common/drop_data_unittest.cc
@@ -11,7 +11,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "base/strings/utf_string_conversions.h"
 #define CONVERT_IF_NEEDED(x) base::UTF8ToWide((x))
 #else
diff --git a/content/common/external_ipc_dumper.cc b/content/common/external_ipc_dumper.cc
index f9ab7e69..563e421f 100644
--- a/content/common/external_ipc_dumper.cc
+++ b/content/common/external_ipc_dumper.cc
@@ -17,7 +17,7 @@
 const char kFilterEntryName[] = "GetFilter";
 const char kSetDumpDirectoryEntryName[] = "SetDumpDirectory";
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #define IPC_MESSAGE_DUMP_MODULE FILE_PATH_LITERAL("ipc_message_dump.dll")
 #else
 #define IPC_MESSAGE_DUMP_MODULE FILE_PATH_LITERAL("libipc_message_dump.so")
diff --git a/content/common/font_list_unittest.cc b/content/common/font_list_unittest.cc
index 7ea3a620b9..a9693b74 100644
--- a/content/common/font_list_unittest.cc
+++ b/content/common/font_list_unittest.cc
@@ -18,7 +18,7 @@
 
 namespace {
 
-#if !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
 bool HasFontWithName(const base::ListValue& list,
                      base::StringPiece expected_font_id,
                      base::StringPiece expected_display_name) {
@@ -32,11 +32,11 @@
 
   return false;
 }
-#endif  // !defined(OS_ANDROID) && !defined(OS_FUCHSI
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
 
 }  // namespace
 
-#if !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
 // GetFontList is not implemented on Android and Fuchsia.
 TEST(FontList, GetFontList) {
   base::test::TaskEnvironment task_environment;
@@ -47,11 +47,11 @@
             content::GetFontList_SlowBlocking();
         ASSERT_TRUE(fonts);
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
         EXPECT_TRUE(HasFontWithName(*fonts, "MS Gothic", "MS Gothic"));
         EXPECT_TRUE(HasFontWithName(*fonts, "Segoe UI", "Segoe UI"));
         EXPECT_TRUE(HasFontWithName(*fonts, "Verdana", "Verdana"));
-#elif defined(OS_LINUX) || defined(OS_CHROMEOS)
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
         EXPECT_TRUE(HasFontWithName(*fonts, "Arimo", "Arimo"));
 #else
         EXPECT_TRUE(HasFontWithName(*fonts, "Arial", "Arial"));
@@ -59,9 +59,9 @@
       }));
   task_environment.RunUntilIdle();
 }
-#endif  // !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 TEST(FontList, GetFontListLocalized) {
   base::i18n::SetICUDefaultLocale("ja-JP");
   std::unique_ptr<base::ListValue> ja_fonts =
@@ -75,9 +75,9 @@
   ASSERT_TRUE(ko_fonts);
   EXPECT_TRUE(HasFontWithName(*ko_fonts, "Malgun Gothic", "맑은 고딕"));
 }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 // On some macOS versions, CTFontManager returns LastResort and/or hidden fonts.
 // Ensure that someone (CTFontManager or our FontList code) filters these fonts
 // on all OS versions that we support.
@@ -95,4 +95,4 @@
         << font_id << " seems like a hidden font, which should be filtered";
   }
 }
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
diff --git a/content/common/input/synthetic_gesture_params.cc b/content/common/input/synthetic_gesture_params.cc
index e3bdc4f..c5a816f 100644
--- a/content/common/input/synthetic_gesture_params.cc
+++ b/content/common/input/synthetic_gesture_params.cc
@@ -25,7 +25,7 @@
 #if defined(USE_AURA)
   return gesture_source_type == mojom::GestureSourceType::kTouchInput ||
          gesture_source_type == mojom::GestureSourceType::kMouseInput;
-#elif defined(OS_ANDROID)
+#elif BUILDFLAG(IS_ANDROID)
   // Android supports mouse wheel events, but mouse drag is not yet
   // supported. See crbug.com/468806.
   return gesture_source_type == mojom::GestureSourceType::kTouchInput ||
diff --git a/content/common/mojo_core_library_support.cc b/content/common/mojo_core_library_support.cc
index daac629..11a6743 100644
--- a/content/common/mojo_core_library_support.cc
+++ b/content/common/mojo_core_library_support.cc
@@ -15,7 +15,7 @@
 }
 
 absl::optional<base::FilePath> GetMojoCoreSharedLibraryPath() {
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
   if (!command_line.HasSwitch(switches::kMojoCoreLibraryPath))
diff --git a/content/common/partition_alloc_support.cc b/content/common/partition_alloc_support.cc
index a5822bf2..db5b887 100644
--- a/content/common/partition_alloc_support.cc
+++ b/content/common/partition_alloc_support.cc
@@ -26,7 +26,7 @@
 #include "build/build_config.h"
 #include "content/public/common/content_switches.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include "base/system/sys_info.h"
 #endif
 
@@ -373,20 +373,20 @@
 
   base::allocator::StartThreadCachePeriodicPurge();
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // Lower thread cache limits to avoid stranding too much memory in the caches.
   if (base::SysInfo::IsLowEndDevice()) {
     base::internal::ThreadCacheRegistry::Instance().SetThreadCacheMultiplier(
         base::internal::ThreadCache::kDefaultMultiplier / 2.);
   }
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
   // Renderer processes are more performance-sensitive, increase thread cache
   // limits.
   if (process_type == switches::kRendererProcess &&
       base::FeatureList::IsEnabled(
           base::features::kPartitionAllocLargeThreadCacheSize)) {
-#if defined(OS_ANDROID) && !defined(ARCH_CPU_64_BITS)
+#if BUILDFLAG(IS_ANDROID) && !defined(ARCH_CPU_64_BITS)
     // Don't use a higher threshold on Android 32 bits, as long as memory usage
     // is not carefully tuned. Only control the threshold here to avoid changing
     // the rest of the code below.
@@ -398,7 +398,7 @@
         base::internal::ThreadCacheLimits::kLargeSizeThreshold;
     base::internal::ThreadCache::SetLargestCachedSize(
         base::internal::ThreadCacheLimits::kLargeSizeThreshold);
-#endif  // defined(OS_ANDROID) && !defined(ARCH_CPU_64_BITS)
+#endif  // BUILDFLAG(IS_ANDROID) && !defined(ARCH_CPU_64_BITS)
   }
 
 #endif  // defined(PA_THREAD_CACHE_SUPPORTED) &&
diff --git a/content/common/profiling_utils.cc b/content/common/profiling_utils.cc
index cd2916f..4b570c60 100644
--- a/content/common/profiling_utils.cc
+++ b/content/common/profiling_utils.cc
@@ -37,14 +37,14 @@
 
   // Android differs from the other platforms because it's not possible to
   // write in base::DIR_CURRENT and environment variables aren't well supported.
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   base::PathService::Get(base::DIR_TEMP, &path);
   path = path.Append("pgo_profiles/");
-#else  // !defined(OS_ANDROID)
+#else  // !BUILDFLAG(IS_ANDROID)
   std::string prof_template;
   std::unique_ptr<base::Environment> env(base::Environment::Create());
   if (env->GetVar("LLVM_PROFILE_FILE", &prof_template)) {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     path = base::FilePath(base::UTF8ToWide(prof_template)).DirName();
 #else
     path = base::FilePath(prof_template).DirName();
@@ -74,7 +74,7 @@
   int pool_index = base::RandInt(0, 3);
   std::string filename = base::StrCat(
       {"child_pool-", base::NumberToString(pool_index), ".profraw"});
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   path = path.Append(base::UTF8ToWide(filename));
 #else
   path = path.Append(filename);
diff --git a/content/common/set_process_title.cc b/content/common/set_process_title.cc
index 8b829a4..06cdab4 100644
--- a/content/common/set_process_title.cc
+++ b/content/common/set_process_title.cc
@@ -16,7 +16,7 @@
 
 #include "build/build_config.h"
 
-#if defined(OS_POSIX) && !defined(OS_MAC) && !defined(OS_SOLARIS)
+#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_SOLARIS)
 #include <limits.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -24,9 +24,9 @@
 #include <string>
 
 #include "base/command_line.h"
-#endif  // defined(OS_POSIX) && !defined(OS_MAC) && !defined(OS_SOLARIS)
+#endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_SOLARIS)
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include <errno.h>  // Get program_invocation_short_name declaration.
 #include <sys/prctl.h>
 
@@ -38,13 +38,13 @@
 #include "base/threading/platform_thread.h"
 // Linux/glibc doesn't natively have setproctitle().
 #include "content/common/set_process_title_linux.h"
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
 namespace content {
 
 // TODO(jrg): Find out if setproctitle or equivalent is available on Android.
-#if defined(OS_POSIX) && !defined(OS_MAC) && !defined(OS_SOLARIS) && \
-    !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_SOLARIS) && \
+    !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
 
 void SetProcessTitleFromCommandLine(const char** main_argv) {
   // Build a single string which consists of all the arguments separated
@@ -53,7 +53,7 @@
   std::string title;
   bool have_argv0 = false;
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   DCHECK_EQ(base::PlatformThread::CurrentId(), getpid());
 
   if (main_argv)
@@ -88,7 +88,7 @@
     *base_name_storage = std::move(base_name);
     program_invocation_short_name = &(*base_name_storage)[0];
   }
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
   const base::CommandLine* command_line =
       base::CommandLine::ForCurrentProcess();
diff --git a/content/common/skia_utils.cc b/content/common/skia_utils.cc
index a6b0433..cea09c2 100644
--- a/content/common/skia_utils.cc
+++ b/content/common/skia_utils.cc
@@ -35,7 +35,7 @@
 
   const int kMB = 1024 * 1024;
   size_t font_cache_limit;
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   font_cache_limit = base::SysInfo::IsLowEndDevice() ? kMB : 8 * kMB;
   SkGraphics::SetFontCacheLimit(font_cache_limit);
 #else
diff --git a/content/common/url_schemes.cc b/content/common/url_schemes.cc
index bff5378..e594401 100644
--- a/content/common/url_schemes.cc
+++ b/content/common/url_schemes.cc
@@ -99,7 +99,7 @@
   for (auto& scheme : schemes.empty_document_schemes)
     url::AddEmptyDocumentScheme(scheme.c_str());
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   if (schemes.allow_non_standard_schemes_in_origins)
     url::EnableNonStandardSchemesForAndroidWebView();
 #endif
diff --git a/content/common/url_utils.cc b/content/common/url_utils.cc
index 7ed7ecd..eae0e18 100644
--- a/content/common/url_utils.cc
+++ b/content/common/url_utils.cc
@@ -8,13 +8,14 @@
 
 #include "base/check_op.h"
 #include "base/strings/strcat.h"
+#include "build/build_config.h"
 #include "content/common/url_schemes.h"
 #include "content/public/common/url_constants.h"
 #include "url/gurl.h"
 
 namespace content {
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 url::Origin OriginFromAndroidPackageName(const std::string& package_name) {
   return url::Origin::Create(
       GURL(base::StrCat({kAndroidAppScheme, ":", package_name})));
@@ -22,7 +23,7 @@
 #endif
 
 bool IsAndroidAppOrigin(const absl::optional<url::Origin>& origin) {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   return origin && origin->scheme() == kAndroidAppScheme;
 #else
   return false;
diff --git a/content/common/url_utils.h b/content/common/url_utils.h
index 27c474c..f690856 100644
--- a/content/common/url_utils.h
+++ b/content/common/url_utils.h
@@ -12,7 +12,7 @@
 
 namespace content {
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 CONTENT_EXPORT url::Origin OriginFromAndroidPackageName(
     const std::string& package_name);
 #endif
diff --git a/content/common/url_utils_unittest.cc b/content/common/url_utils_unittest.cc
index 5efdae4..64bce54 100644
--- a/content/common/url_utils_unittest.cc
+++ b/content/common/url_utils_unittest.cc
@@ -69,7 +69,7 @@
 #endif
   EXPECT_FALSE(
       IsSafeRedirectTarget(GURL(), CreateValidURL("blob:https://foo.com/bar")));
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   EXPECT_FALSE(
       IsSafeRedirectTarget(GURL(), CreateValidURL("content://foo.bar")));
 #endif
diff --git a/content/common/user_agent.cc b/content/common/user_agent.cc
index 2fd614f..0975ef2 100644
--- a/content/common/user_agent.cc
+++ b/content/common/user_agent.cc
@@ -14,13 +14,13 @@
 #include "build/build_config.h"
 #include "build/util/chromium_git_revision.h"
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 #include "base/mac/mac_util.h"
 #endif
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "base/win/windows_version.h"
-#elif defined(OS_POSIX) && !defined(OS_MAC)
+#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC)
 #include <sys/utsname.h>
 #endif
 
@@ -29,19 +29,19 @@
 namespace {
 
 std::string GetUserAgentPlatform() {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   return "";
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   return "Macintosh; ";
 #elif defined(USE_OZONE)
   return "X11; ";  // strange, but that's what Firefox uses
-#elif defined(OS_ANDROID)
+#elif BUILDFLAG(IS_ANDROID)
   return "Linux; ";
-#elif defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_FUCHSIA)
   // TODO(https://crbug.com/1225812): Determine what to report for Fuchsia,
   // considering both backwards compatibility and User-Agent Reduction.
   return "X11; ";
-#elif defined(OS_POSIX)
+#elif BUILDFLAG(IS_POSIX)
   return "Unknown; ";
 #endif
 }
@@ -49,13 +49,13 @@
 }  // namespace
 
 std::string GetUnifiedPlatform() {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   return frozen_user_agent_strings::kUnifiedPlatformAndroid;
-#elif defined(OS_CHROMEOS)
+#elif BUILDFLAG(IS_CHROMEOS)
   return frozen_user_agent_strings::kUnifiedPlatformCrOS;
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   return frozen_user_agent_strings::kUnifiedPlatformMacOS;
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   return frozen_user_agent_strings::kUnifiedPlatformWindows;
 #else
   return frozen_user_agent_strings::kUnifiedPlatformLinux;
@@ -74,9 +74,9 @@
 std::string BuildCpuInfo() {
   std::string cpuinfo;
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   cpuinfo = "Intel";
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
   if (os_info->IsWowX86OnAMD64()) {
     cpuinfo = "WOW64";
@@ -88,7 +88,7 @@
     else if (windows_architecture == base::win::OSInfo::IA64_ARCHITECTURE)
       cpuinfo = "Win64; IA64";
   }
-#elif defined(OS_POSIX) && !defined(OS_MAC)
+#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC)
   // Should work on any Posix system.
   struct utsname unixinfo;
   uname(&unixinfo);
@@ -108,7 +108,7 @@
 // Return the CPU architecture in Windows/Mac/POSIX and the empty string
 // elsewhere.
 std::string GetLowEntropyCpuArchitecture() {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   base::win::OSInfo::WindowsArchitecture windows_architecture =
       base::win::OSInfo::GetInstance()->GetArchitecture();
   base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
@@ -122,7 +122,7 @@
              (windows_architecture == base::win::OSInfo::X64_ARCHITECTURE)) {
     return "x86";
   }
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   base::mac::CPUType cpu_type = base::mac::GetCPUType();
   if (cpu_type == base::mac::CPUType::kIntel) {
     return "x86";
@@ -130,7 +130,7 @@
              cpu_type == base::mac::CPUType::kTranslatedIntel) {
     return "arm";
   }
-#elif defined(OS_POSIX) && !defined(OS_ANDROID)
+#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
   std::string cpu_info = BuildCpuInfo();
   if (base::StartsWith(cpu_info, "arm") ||
       base::StartsWith(cpu_info, "aarch")) {
@@ -145,14 +145,14 @@
 }
 
 std::string GetLowEntropyCpuBitness() {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   return (base::win::OSInfo::GetInstance()->GetArchitecture() ==
           base::win::OSInfo::X86_ARCHITECTURE)
              ? "32"
              : "64";
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   return "64";
-#elif defined(OS_POSIX) && !defined(OS_ANDROID)
+#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
   return base::Contains(BuildCpuInfo(), "64") ? "64" : "32";
 #else
   return std::string();
@@ -162,14 +162,14 @@
 std::string GetOSVersion(IncludeAndroidBuildNumber include_android_build_number,
                          IncludeAndroidModel include_android_model) {
   std::string os_version;
-#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
   int32_t os_major_version = 0;
   int32_t os_minor_version = 0;
   int32_t os_bugfix_version = 0;
   base::SysInfo::OperatingSystemVersionNumbers(
       &os_major_version, &os_minor_version, &os_bugfix_version);
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   // A significant amount of web content breaks if the reported "Mac
   // OS X" major version number is greater than 10. Continue to report
   // this as 10_15_7, the last dot release for that macOS version.
@@ -182,22 +182,22 @@
 
 #endif
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   std::string android_version_str = base::SysInfo::OperatingSystemVersion();
   std::string android_info_str =
       GetAndroidOSInfo(include_android_build_number, include_android_model);
 #endif
 
   base::StringAppendF(&os_version,
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
                       "%d.%d", os_major_version, os_minor_version
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
                       "%d_%d_%d", os_major_version, os_minor_version,
                       os_bugfix_version
-#elif defined(OS_CHROMEOS)
+#elif BUILDFLAG(IS_CHROMEOS)
                       "%d.%d.%d", os_major_version, os_minor_version,
                       os_bugfix_version
-#elif defined(OS_ANDROID)
+#elif BUILDFLAG(IS_ANDROID)
                       "%s%s", android_version_str.c_str(),
                       android_info_str.c_str()
 #else
@@ -219,13 +219,13 @@
                                                   const std::string& cpu_type) {
   std::string os_cpu;
 
-#if !defined(OS_ANDROID) && defined(OS_POSIX) && !defined(OS_MAC)
+#if !BUILDFLAG(IS_ANDROID) && BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC)
   // Should work on any Posix system.
   struct utsname unixinfo;
   uname(&unixinfo);
 #endif
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   if (!cpu_type.empty())
     base::StringAppendF(&os_cpu, "Windows NT %s; %s", os_version.c_str(),
                         cpu_type.c_str());
@@ -233,18 +233,18 @@
     base::StringAppendF(&os_cpu, "Windows NT %s", os_version.c_str());
 #else
   base::StringAppendF(&os_cpu,
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
                       "%s Mac OS X %s", cpu_type.c_str(), os_version.c_str()
-#elif defined(OS_CHROMEOS)
+#elif BUILDFLAG(IS_CHROMEOS)
                       "CrOS "
                       "%s %s",
                       cpu_type.c_str(),  // e.g. i686
                       os_version.c_str()
-#elif defined(OS_ANDROID)
+#elif BUILDFLAG(IS_ANDROID)
                       "Android %s", os_version.c_str()
-#elif defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_FUCHSIA)
                       "Fuchsia"
-#elif defined(OS_POSIX)
+#elif BUILDFLAG(IS_POSIX)
                       "%s %s",
                       unixinfo.sysname,  // e.g. Linux
                       cpu_type.c_str()   // e.g. i686
@@ -257,7 +257,7 @@
 
 std::string GetReducedUserAgent(bool mobile, std::string major_version) {
   std::string user_agent;
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   std::string device_compat;
   // Note: The extra space after Mobile is meaningful here, to avoid
   // "MobileSafari", but unneeded for non-mobile Android devices.
@@ -285,7 +285,7 @@
 
 std::string BuildModelInfo() {
   std::string model;
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // Only send the model information if on the release build of Android,
   // matching user agent behaviour.
   if (base::SysInfo::GetAndroidBuildCodename() == "REL")
@@ -294,7 +294,7 @@
   return model;
 }
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 std::string BuildUserAgentFromProductAndExtraOSInfo(
     const std::string& product,
     const std::string& extra_os_info,
@@ -334,7 +334,7 @@
 
   return android_info_str;
 }
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
 std::string BuildUserAgentFromOSAndProduct(const std::string& os_info,
                                            const std::string& product) {
diff --git a/content/common/user_agent_unittest.cc b/content/common/user_agent_unittest.cc
index 5a7bd7b8..441d252 100644
--- a/content/common/user_agent_unittest.cc
+++ b/content/common/user_agent_unittest.cc
@@ -22,7 +22,7 @@
 
 TEST(UserAgentStringTest, BuildOSCpuInfoFromOSVersionAndCpuType) {
   const BuildOSCpuInfoTestCases test_cases[] = {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     // On Windows, it's possible to have an empty string for CPU type.
     {
         /*os_version=*/"10.0",
@@ -56,7 +56,7 @@
         /*cpu_type=*/"CPU TYPE",
         /*expected_os_cpu_info=*/"Windows NT VERSION; CPU TYPE",
     },
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
     {
         /*os_version=*/"10_15_4",
         /*cpu_type=*/"Intel",
@@ -92,7 +92,7 @@
         /*cpu_type=*/"CPU TYPE",
         /*expected_os_cpu_info=*/"CrOS CPU TYPE VERSION",
     },
-#elif defined(OS_ANDROID)
+#elif BUILDFLAG(IS_ANDROID)
     {
         /*os_version=*/"7.1.1",
         /*cpu_type=*/"UNUSED",
@@ -110,7 +110,7 @@
         /*cpu_type=*/"CPU TYPE",
         /*expected_os_cpu_info=*/"Android VERSION",
     },
-#elif defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_FUCHSIA)
     {
         /*os_version=*/"VERSION",
         /*cpu_type=*/"CPU TYPE",
@@ -129,8 +129,8 @@
 TEST(UserAgentStringTest, LowEntropyCpuArchitecture) {
   std::string arch = GetLowEntropyCpuArchitecture();
 
-#if defined(OS_WIN) || defined(OS_MAC) || \
-    (defined(OS_POSIX) && !defined(OS_ANDROID))
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || \
+    (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID))
   EXPECT_TRUE("arm" == arch || "x86" == arch);
 #else
   EXPECT_EQ("", arch);
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityNodeInfoUtils.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityNodeInfoUtils.java
index b3eb99b..e24db5f 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityNodeInfoUtils.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityNodeInfoUtils.java
@@ -94,6 +94,10 @@
         if (node.getError() != null) {
             builder.append(" error:\"").append(node.getError()).append("\"");
         }
+        if (node.getStateDescription() != null
+                && !node.getStateDescription().toString().isEmpty()) {
+            builder.append(" stateDescription:\"").append(node.getStateDescription()).append("\"");
+        }
 
         // Boolean properties - Only print when set to true except for enabled and visibleToUser,
         // which are both mostly true, so only print when they are false.
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
index c99cef7..2475d9ef 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
@@ -756,8 +756,9 @@
         if (!isAccessibilityEnabled()) return;
 
         // Update the AXMode based on screen reader status.
-        WebContentsAccessibilityImplJni.get().setAXMode(
-                mNativeObj, WebContentsAccessibilityImpl.this, newScreenReaderEnabledState);
+        WebContentsAccessibilityImplJni.get().setAXMode(mNativeObj,
+                WebContentsAccessibilityImpl.this, newScreenReaderEnabledState,
+                /* isAccessibilityEnabled= */ true);
 
         // Update the list of events we dispatch to enabled services.
         if (ContentFeatureList.isEnabled(ContentFeatureList.ON_DEMAND_ACCESSIBILITY_EVENTS)) {
@@ -1074,6 +1075,14 @@
         }
     }
 
+    public void updateAXModeFromNativeAccessibilityState() {
+        if (!isNativeInitialized()) return;
+        // Update the AXMode based on screen reader status.
+        WebContentsAccessibilityImplJni.get().setAXMode(mNativeObj,
+                WebContentsAccessibilityImpl.this, BrowserAccessibilityState.screenReaderMode(),
+                isAccessibilityEnabled());
+    }
+
     // Returns true if the hover event is to be consumed by accessibility feature.
     @CalledByNative
     private boolean onHoverEvent(int action) {
@@ -1839,9 +1848,9 @@
         CharSequence computedText = computeText(
                 text, annotateAsLink, language, suggestionStarts, suggestionEnds, suggestions);
 
-        // For pre-Android R, we add stateDescription to text for backwards compatibility.
+        // We add the stateDescription attribute when it is non-null and not empty.
         if (stateDescription != null && !stateDescription.isEmpty()) {
-            computedText = computedText + ", " + stateDescription;
+            node.setStateDescription(stateDescription);
         }
 
         // We expose the nested structure of links, which results in the roles of all nested nodes
@@ -1851,20 +1860,6 @@
         } else {
             node.setText(computedText);
         }
-
-        // TODO(mschillaci): In a follow-up CL, match these across Android versions and fix tests.
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
-            // For Android R and higher, we will not rely on concatenating text and
-            // stateDescription, and will instead revert text to original content and set
-            // stateDescription separately.
-            if (stateDescription != null && !stateDescription.isEmpty()) {
-                CharSequence originalText = computeText(text, annotateAsLink, language,
-                        suggestionStarts, suggestionEnds, suggestions);
-
-                node.setText(originalText);
-                node.setStateDescription(stateDescription);
-            }
-        }
     }
 
     protected boolean areInlineTextBoxesLoaded(int virtualViewId) {
@@ -2325,7 +2320,8 @@
         void enable(long nativeWebContentsAccessibilityAndroid, WebContentsAccessibilityImpl caller,
                 boolean screenReaderMode);
         void setAXMode(long nativeWebContentsAccessibilityAndroid,
-                WebContentsAccessibilityImpl caller, boolean screenReaderMode);
+                WebContentsAccessibilityImpl caller, boolean screenReaderMode,
+                boolean isAccessibilityEnabled);
         boolean areInlineTextBoxesLoaded(long nativeWebContentsAccessibilityAndroid,
                 WebContentsAccessibilityImpl caller, int id);
         void loadInlineTextBoxes(long nativeWebContentsAccessibilityAndroid,
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
index 299162a..e89ef21 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
@@ -41,6 +41,7 @@
 import org.chromium.content.browser.framehost.RenderFrameHostImpl;
 import org.chromium.content.browser.selection.SelectionPopupControllerImpl;
 import org.chromium.content_public.browser.ChildProcessImportance;
+import org.chromium.content_public.browser.ContentFeatureList;
 import org.chromium.content_public.browser.GlobalRenderFrameHostId;
 import org.chromium.content_public.browser.ImageDownloadCallback;
 import org.chromium.content_public.browser.JavaScriptCallback;
@@ -572,7 +573,12 @@
     public void onShow() {
         checkNotDestroyed();
         WebContentsAccessibilityImpl wcax = WebContentsAccessibilityImpl.fromWebContents(this);
-        if (wcax != null) wcax.refreshState();
+        if (wcax != null) {
+            wcax.refreshState();
+            if (ContentFeatureList.isEnabled(ContentFeatureList.AUTO_DISABLE_ACCESSIBILITY)) {
+                wcax.updateAXModeFromNativeAccessibilityState();
+            }
+        }
         SelectionPopupControllerImpl controller = getSelectionPopupController();
         if (controller != null) controller.restoreSelectionPopupsIfNecessary();
         WebContentsImplJni.get().onShow(mNativeWebContentsAndroid, WebContentsImpl.this);
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java b/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
index 92af8d0..7538582a 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/ContentFeatureList.java
@@ -28,6 +28,8 @@
     // Alphabetical:
     public static final String ACCESSIBILITY_PAGE_ZOOM = "AccessibilityPageZoom";
 
+    public static final String AUTO_DISABLE_ACCESSIBILITY = "AutoDisableAccessibility";
+
     public static final String BACKGROUND_MEDIA_RENDERER_HAS_MODERATE_BINDING =
             "BackgroundMediaRendererHasModerateBinding";
 
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
index 54686b30..bf7b35df 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
@@ -21,7 +21,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.Batch;
-import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.content_public.browser.test.ContentJUnit4ClassRunner;
@@ -207,7 +206,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaButton() {
         performAriaTest("aria-button.html");
     }
@@ -220,14 +218,12 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaCheckbox() {
         performAriaTest("aria-checkbox.html");
     }
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaChecked() {
         performAriaTest("aria-checked.html");
     }
@@ -264,7 +260,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaCombobox() {
         performAriaTest("aria-combobox.html");
     }
@@ -277,7 +272,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaComboboxUneditable() {
         performAriaTest("aria-combobox-uneditable.html");
     }
@@ -302,7 +296,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaCurrent() {
         performAriaTest("aria-current.html");
     }
@@ -411,7 +404,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaGridcell() {
         performAriaTest("aria-gridcell.html");
     }
@@ -454,7 +446,7 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1282189")
+    @DisabledTest(message = "https://crbug.com/1286036")
     public void test_ariaHiddenIframe() {
         performAriaTest("aria-hidden-iframe.html");
     }
@@ -467,7 +459,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaIllegalVal() {
         performAriaTest("aria-illegal-val.html");
     }
@@ -516,21 +507,18 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaListboxAriaSelected() {
         performAriaTest("aria-listbox-aria-selected.html");
     }
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaListboxDisabled() {
         performAriaTest("aria-listbox-disabled.html");
     }
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaListbox() {
         performAriaTest("aria-listbox.html");
     }
@@ -597,7 +585,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaMenuitemcheckbox() {
         performAriaTest("aria-menuitemcheckbox.html");
     }
@@ -616,7 +603,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaMenuitemradio() {
         performAriaTest("aria-menuitemradio.html");
     }
@@ -641,7 +627,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaMultiselectable() {
         performAriaTest("aria-multiselectable.html");
     }
@@ -666,14 +651,12 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaOptionComplexChildren() {
         performAriaTest("aria-option-complex-children.html");
     }
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaOption() {
         performAriaTest("aria-option.html");
     }
@@ -722,7 +705,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaPressed() {
         performAriaTest("aria-pressed.html");
     }
@@ -813,7 +795,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaSelected() {
         performAriaTest("aria-selected.html");
     }
@@ -826,7 +807,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaSetsize() {
         performAriaTest("aria-setsize.html");
     }
@@ -953,7 +933,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaTogglebutton() {
         performAriaTest("aria-togglebutton.html");
     }
@@ -984,7 +963,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_ariaTree() {
         performAriaTest("aria-tree.html");
     }
@@ -1057,7 +1035,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279785")
     public void test_toggleButtonExpandCollapse() {
         performAriaTest("toggle-button-expand-collapse.html");
     }
@@ -1180,7 +1157,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1281847")
     public void test_br() {
         performHtmlTest("br.html");
     }
@@ -1193,10 +1169,7 @@
 
     @Test
     @SmallTest
-    @DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.Q,
-            message = "Fails on Android 11: https://crbug.com/1280713")
-    public void
-    test_buttonWithListboxPopup() {
+    public void test_buttonWithListboxPopup() {
         performHtmlTest("button-with-listbox-popup.html");
     }
 
@@ -1286,7 +1259,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1281847")
     public void test_contenteditableWithEmbeddedContenteditables() {
         performHtmlTest("contenteditable-with-embedded-contenteditables.html");
     }
@@ -1419,7 +1391,7 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1280734")
+    @DisabledTest(message = "https://crbug.com/1286036")
     public void test_frameset() {
         performHtmlTest("frameset.html");
     }
@@ -1480,7 +1452,7 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1281847")
+    @DisabledTest(message = "https://crbug.com/1286036")
     public void test_iframeCoordinatesCrossProcess() {
         performHtmlTest("iframe-coordinates-cross-process.html");
     }
@@ -1505,14 +1477,14 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1281797")
+    @DisabledTest(message = "https://crbug.com/1286036")
     public void test_iframePresentational() {
         performHtmlTest("iframe-presentational.html");
     }
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279960")
+    @DisabledTest(message = "https://crbug.com/1286036")
     public void test_iframeTransform() {
         performHtmlTest("iframe-transform.html");
     }
@@ -1543,7 +1515,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1279723")
     public void test_img() {
         performHtmlTest("img.html");
     }
@@ -1664,7 +1635,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1281847")
     public void test_inputRadio() {
         performHtmlTest("input-radio.html");
     }
@@ -1773,7 +1743,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1281847")
     public void test_landmark() {
         performHtmlTest("landmark.html");
     }
@@ -2026,10 +1995,8 @@
 
     @Test
     @SmallTest
-    @DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.Q,
-            message = "Fails on Android 11: https://crbug.com/1280713")
-    public void
-    test_selectmenu() {
+    @DisabledTest(message = "https://crbug.com/1280713")
+    public void test_selectmenu() {
         performHtmlTest("selectmenu.html");
     }
 
diff --git a/content/public/browser/ax_inspect_factory_win.cc b/content/public/browser/ax_inspect_factory_win.cc
index e371880f..c51aa83 100644
--- a/content/public/browser/ax_inspect_factory_win.cc
+++ b/content/public/browser/ax_inspect_factory_win.cc
@@ -8,11 +8,11 @@
 #include "base/notreached.h"
 #include "base/win/com_init_util.h"
 #include "content/browser/accessibility/accessibility_event_recorder_uia_win.h"
-#include "content/browser/accessibility/accessibility_event_recorder_win.h"
 #include "content/browser/accessibility/accessibility_tree_formatter_blink.h"
 #include "content/browser/accessibility/accessibility_tree_formatter_uia_win.h"
 #include "content/browser/accessibility/accessibility_tree_formatter_win.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "ui/accessibility/platform/inspect/ax_event_recorder_win.h"
 
 namespace content {
 
@@ -71,9 +71,17 @@
   }
 
   switch (type) {
-    case ui::AXApiType::kWinIA2:
-      return std::make_unique<AccessibilityEventRecorderWin>(manager, pid,
-                                                             selector);
+    case ui::AXApiType::kWinIA2: {
+      // For now, just use out of context events when running as a utility to
+      // watch events (no BrowserAccessibilityManager), because otherwise Chrome
+      // events are not getting reported. Being in context is better so that for
+      // TEXT_REMOVED and TEXT_INSERTED events, we can query the text that was
+      // inserted or removed and include that in the log.
+      return std::make_unique<ui::AXEventRecorderWin>(
+          pid, selector,
+          manager ? ui::AXEventRecorderWin::kSync
+                  : ui::AXEventRecorderWin::kAsync);
+    }
     case ui::AXApiType::kWinUIA:
       return std::make_unique<AccessibilityEventRecorderUia>(manager, pid,
                                                              selector.pattern);
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index f2048ed..633fd857 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -46,7 +46,6 @@
 #include "content/public/common/url_utils.h"
 #include "media/audio/audio_manager.h"
 #include "media/mojo/mojom/media_service.mojom.h"
-#include "net/base/features.h"
 #include "net/cookies/site_for_cookies.h"
 #include "net/ssl/client_cert_identity.h"
 #include "net/ssl/client_cert_store.h"
@@ -1306,7 +1305,7 @@
 }
 
 bool ContentBrowserClient::IsFirstPartySetsEnabled() {
-  return base::FeatureList::IsEnabled(net::features::kFirstPartySets);
+  return base::FeatureList::IsEnabled(features::kFirstPartySets);
 }
 
 }  // namespace content
diff --git a/content/public/browser/cookie_store_factory.h b/content/public/browser/cookie_store_factory.h
index 05122b9..052030e 100644
--- a/content/public/browser/cookie_store_factory.h
+++ b/content/public/browser/cookie_store_factory.h
@@ -38,13 +38,14 @@
   // created using this config.
   CookieStoreConfig(const base::FilePath& path,
                     bool restore_old_session_cookies,
-                    bool persist_session_cookies);
+                    bool persist_session_cookies,
+                    bool first_party_sets_enabled);
   ~CookieStoreConfig();
 
   const base::FilePath path;
   const bool restore_old_session_cookies;
   const bool persist_session_cookies;
-
+  const bool first_party_sets_enabled;
   // The following are infrequently used cookie store parameters.
   // Rather than clutter the constructor API, these are assigned a default
   // value on CookieStoreConfig construction. Clients should then override
diff --git a/content/public/browser/identity_request_dialog_controller.h b/content/public/browser/identity_request_dialog_controller.h
index 8c21396..8a3a573 100644
--- a/content/public/browser/identity_request_dialog_controller.h
+++ b/content/public/browser/identity_request_dialog_controller.h
@@ -101,7 +101,8 @@
   using InitialApprovalCallback = base::OnceCallback<void(UserApproval)>;
   using IdProviderWindowClosedCallback = base::OnceCallback<void()>;
   using TokenExchangeApprovalCallback = base::OnceCallback<void(UserApproval)>;
-  using AccountSelectionCallback = base::OnceCallback<void(const std::string&)>;
+  using AccountSelectionCallback =
+      base::OnceCallback<void(const std::string&, bool)>;
 
   IdentityRequestDialogController() = default;
 
diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc
index 2c87b89..d2d21e96 100644
--- a/content/public/browser/web_contents_delegate.cc
+++ b/content/public/browser/web_contents_delegate.cc
@@ -377,4 +377,8 @@
   return nullptr;
 }
 
+bool WebContentsDelegate::IsPrivileged() {
+  return false;
+}
+
 }  // namespace content
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index f04506b..9024cf96 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -754,6 +754,11 @@
   // Returns a weak ptr to the web contents delegate.
   virtual base::WeakPtr<WebContentsDelegate> GetDelegateWeakPtr();
 
+  // Whether the WebContents is privileged.
+  // It's used to prevent drag and drop between privileged and non-privileged
+  // WebContents.
+  virtual bool IsPrivileged();
+
  protected:
   virtual ~WebContentsDelegate();
 
diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h
index c193932e..2557875 100644
--- a/content/public/browser/web_contents_observer.h
+++ b/content/public/browser/web_contents_observer.h
@@ -352,10 +352,30 @@
   // (nothing loaded) to 1.0 (page fully loaded).
   virtual void LoadProgressChanged(double progress) {}
 
-  // This method is invoked once the window.document object of the main frame
-  // was created. Since the WebContents could be hosting more than one main
-  // frame (e.g. prerendered page), the |render_frame_host| represents the frame
-  // where the event happened.
+  // This method is invoked once the window.document element of the primary main
+  // frame's current document (i.e., |render_frame_host|) is ready. This happens
+  // when the document's main HTML resource has finished parsing. Here
+  // document element refers to DOMDocument, which is different from browser
+  // implementation of blink::Document in DocumentUserData/DocumentService which
+  // are typically created when navigation commits.
+  //
+  // Note that DocumentAvailableInMainFrame should be used when the observers
+  // which send IPCs to the renderer want to ensure that window.document is
+  // non-null. For for the comment cases like observing primary document/URL
+  // changes in the omnibox due to navigation
+  // WebContentsObserver::PrimaryPageChanged should be used and to observe fully
+  // loaded signal WebContentsObserver::DidFinishLoad can be used.
+  //
+  // This event is dispatched once in the document's lifetime, which means it's
+  // not dispatched after navigation that restores a Back/Forward Cache page.
+  // For prerendering, this signal is dispatched when the main document element
+  // is available and the document is shown to the user (i.e., after the
+  // activation).
+  //
+  // TODO(crbug.com/1257140): Rename DocumentAvailableInMainFrame to
+  // PrimaryMainDocumentElementAvailable to capture the new behaviour. Remove
+  // RenderFrameHost* argument and directly use
+  // WebContents::GetPrimaryMainFrame().
   virtual void DocumentAvailableInMainFrame(
       RenderFrameHost* render_frame_host) {}
 
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 98857a4..446a4339 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -326,6 +326,27 @@
     "ExtraSafelistedRequestHeadersForOutOfBlinkCors",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables JavaScript API to intermediate federated identity requests.
+const base::Feature kFedCm{"FedCm", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Field trial boolean parameter which indicates whether FedCM HTTP filtering is
+// enabled.
+const char kFedCmInterceptionFieldTrialParamName[] = "Interception";
+
+// Enables usage of First Party Sets to determine cookie availability.
+constexpr base::Feature kFirstPartySets{"FirstPartySets",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Controls whether the client is considered a dogfooder for the FirstPartySets
+// feature.
+const base::FeatureParam<bool> kFirstPartySetsIsDogfooder{
+    &kFirstPartySets, "FirstPartySetsIsDogfooder", false};
+
+// When enabled, the client will opt in to the V2 component format for the
+// First-Party Sets component.
+const base::Feature kFirstPartySetsV2ComponentFormat{
+    "FirstPartySetsV2ComponentFormat", base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Whether to initialize the font manager when the renderer starts on a
 // background thread.
 const base::Feature kFontManagerEarlyInit {
@@ -356,13 +377,6 @@
 };
 #endif
 
-// Enables JavaScript API to intermediate federated identity requests.
-const base::Feature kFedCm{"FedCm", base::FEATURE_DISABLED_BY_DEFAULT};
-
-// Field trial boolean parameter which indicates whether FedCM HTTP filtering is
-// enabled.
-const char kFedCmInterceptionFieldTrialParamName[] = "Interception";
-
 // Enables scrollers inside Blink to store scroll offsets in fractional
 // floating-point numbers rather than truncating to integers.
 const base::Feature kFractionalScrollOffsets{"FractionalScrollOffsets",
@@ -884,6 +898,11 @@
 const base::Feature kSyntheticPointerActions{"SyntheticPointerActions",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Whether optimizations controlled by kNavigationThreadingOptimizations are
+// moved to the IO thread or a separate background thread.
+const base::Feature kThreadingOptimizationsOnIO{
+    "ThreadingOptimizationsOnIO", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables async touchpad pinch zoom events. We check the ACK of the first
 // synthetic wheel event in a pinch sequence, then send the rest of the
 // synthetic wheel events of the pinch sequence as non-blocking if the first
@@ -1178,23 +1197,6 @@
 
 #endif  // defined(OS_MAC)
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
-// If the JavaScript on a WebUI page has an error (such as an unhandled
-// exception), report that error back the crash reporting infrastructure, same
-// as we do for program crashes.
-const base::Feature kSendWebUIJavaScriptErrorReports{
-    "SendWebUIJavaScriptErrorReports", base::FEATURE_ENABLED_BY_DEFAULT};
-// Parameter: Should we send the error reports to the production server? If
-// false, we send to the staging server, which is useful for developers (doesn't
-// pollute the report database).
-const char kSendWebUIJavaScriptErrorReportsSendToProductionVariation[] =
-    "send_webui_js_errors_to_production";
-const base::FeatureParam<bool>
-    kWebUIJavaScriptErrorReportsSendToProductionParam{
-        &kSendWebUIJavaScriptErrorReports,
-        kSendWebUIJavaScriptErrorReportsSendToProductionVariation, true};
-#endif
-
 #if defined(WEBRTC_USE_PIPEWIRE)
 // Controls whether the PipeWire support for screen capturing is enabled on the
 // Wayland display server.
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index af6d6dfb..f42fe32 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -84,14 +84,17 @@
     kExperimentalContentSecurityPolicyFeatures;
 CONTENT_EXPORT extern const base::Feature
     kExtraSafelistedRequestHeadersForOutOfBlinkCors;
+CONTENT_EXPORT extern const base::Feature kFedCm;
+CONTENT_EXPORT extern const char kFedCmInterceptionFieldTrialParamName[];
+CONTENT_EXPORT extern const base::Feature kFirstPartySets;
+CONTENT_EXPORT extern const base::FeatureParam<bool> kFirstPartySetsIsDogfooder;
+CONTENT_EXPORT extern const base::Feature kFirstPartySetsV2ComponentFormat;
 CONTENT_EXPORT extern const base::Feature kFontManagerEarlyInit;
 CONTENT_EXPORT extern const base::Feature kFontSrcLocalMatching;
 #if !defined(OS_ANDROID)
 CONTENT_EXPORT extern const base::Feature
     kForwardMemoryPressureEventsToGpuProcess;
 #endif
-CONTENT_EXPORT extern const base::Feature kFedCm;
-CONTENT_EXPORT extern const char kFedCmInterceptionFieldTrialParamName[];
 CONTENT_EXPORT extern const base::Feature kFractionalScrollOffsets;
 CONTENT_EXPORT extern const base::Feature kGreaseUACH;
 CONTENT_EXPORT extern const base::Feature kHistoryPreventSandboxedNavigation;
@@ -230,6 +233,7 @@
 CONTENT_EXPORT extern const base::Feature
     kSuppressDifferentOriginSubframeJSDialogs;
 CONTENT_EXPORT extern const base::Feature kSyntheticPointerActions;
+CONTENT_EXPORT extern const base::Feature kThreadingOptimizationsOnIO;
 CONTENT_EXPORT extern const base::Feature kTouchpadAsyncPinchEvents;
 CONTENT_EXPORT extern const base::Feature kTouchpadOverscrollHistoryNavigation;
 CONTENT_EXPORT extern const base::Feature kTrustedDOMTypes;
@@ -304,14 +308,6 @@
 CONTENT_EXPORT extern const base::Feature kRetryGetVideoCaptureDeviceInfos;
 #endif  // defined(OS_MAC)
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
-CONTENT_EXPORT extern const base::Feature kSendWebUIJavaScriptErrorReports;
-CONTENT_EXPORT extern const char
-    kSendWebUIJavaScriptErrorReportsSendToProductionVariation[];
-CONTENT_EXPORT extern const base::FeatureParam<bool>
-    kWebUIJavaScriptErrorReportsSendToProductionParam;
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
-
 #if defined(WEBRTC_USE_PIPEWIRE)
 CONTENT_EXPORT extern const base::Feature kWebRtcPipeWireCapturer;
 #endif  // defined(WEBRTC_USE_PIPEWIRE)
diff --git a/content/public/common/content_switch_dependent_feature_overrides.cc b/content/public/common/content_switch_dependent_feature_overrides.cc
index 9c0dae5..a44da8a 100644
--- a/content/public/common/content_switch_dependent_feature_overrides.cc
+++ b/content/public/common/content_switch_dependent_feature_overrides.cc
@@ -121,8 +121,7 @@
      std::cref(network::features::kCrossOriginOpenerPolicyByDefault),
      base::FeatureList::OVERRIDE_ENABLE_FEATURE},
 
-    {network::switches::kUseFirstPartySet,
-     std::cref(net::features::kFirstPartySets),
+    {network::switches::kUseFirstPartySet, std::cref(features::kFirstPartySets),
      base::FeatureList::OVERRIDE_ENABLE_FEATURE},
 
     // Overrides for headless
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 943a1df7..7e3b255 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -221,6 +221,9 @@
 // Disables using CODECAPI_AVLowLatencyMode when creating DXVA decoders.
 const char kDisableLowLatencyDxva[]         = "disable-low-latency-dxva";
 
+// Disables Mojo broker capabilities in the browser during Mojo initialization.
+const char kDisableMojoBroker[] = "disable-mojo-broker";
+
 // Disables clearing the rendering output of a renderer when it didn't commit
 // new output for a while after a top-frame navigation.
 const char kDisableNewContentRenderingTimeout[] =
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index eb6561f..6030a46 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -71,6 +71,7 @@
 CONTENT_EXPORT extern const char kDisableKillAfterBadIPC[];
 CONTENT_EXPORT extern const char kDisableLocalStorage[];
 CONTENT_EXPORT extern const char kDisableLogging[];
+CONTENT_EXPORT extern const char kDisableMojoBroker[];
 CONTENT_EXPORT extern const char kDisableNewContentRenderingTimeout[];
 CONTENT_EXPORT extern const char kDisableNotifications[];
 CONTENT_EXPORT extern const char kDisableOriginTrialControlledBlinkFeatures[];
diff --git a/content/public/common/drop_data.h b/content/public/common/drop_data.h
index 75564bb..434f4ff 100644
--- a/content/public/common/drop_data.h
+++ b/content/public/common/drop_data.h
@@ -80,6 +80,9 @@
   // Whether this drag originated from a renderer.
   bool did_originate_from_renderer;
 
+  // Whether this drag is from a privileged Web Contents.
+  bool is_from_privileged = false;
+
   // User is dragging a link or image.
   GURL url;
   std::u16string url_title;  // The title associated with |url|.
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 4a82485..a38f0d9 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -4765,14 +4765,18 @@
   params->app_history_key = item.GetAppHistoryKey().Utf8();
 
   // Note that the value of `referrer` will be overwritten in the browser with a
-  // browser-calculated value, except for renderer-initated same-document
-  // navigations and the synchronous about:blank commit (because the browser
-  // doesn't know anything about those navigations).
+  // browser-calculated value in most cases. The exceptions are
+  // renderer-initated same-document navigations and the synchronous about:blank
+  // commit (because the browser doesn't know anything about those navigations).
+  // In those cases, the referrer policy component will still be overwritten in
+  // the browser, because this navigation won't change it and the browser
+  // already had access to the previous one. Send ReferrerPolicy::kDefault as a
+  // placeholder.
   // TODO(https://crbug.com/1131832): Remove `referrer` from
   // DidCommitProvisionalLoadParams.
   params->referrer = blink::mojom::Referrer::New(
       blink::WebStringToGURL(document_loader->Referrer()),
-      document_loader->GetReferrerPolicy());
+      network::mojom::ReferrerPolicy::kDefault);
 
   if (!frame_->Parent()) {
     // Top-level navigation.
diff --git a/content/services/auction_worklet/auction_worklet_service_impl.cc b/content/services/auction_worklet/auction_worklet_service_impl.cc
index d9c2ce3..b098588 100644
--- a/content/services/auction_worklet/auction_worklet_service_impl.cc
+++ b/content/services/auction_worklet/auction_worklet_service_impl.cc
@@ -31,12 +31,22 @@
     bool pause_for_debugger_on_start,
     mojo::PendingRemote<network::mojom::URLLoaderFactory>
         pending_url_loader_factory,
-    mojom::BiddingInterestGroupPtr bidding_interest_group) {
-  bidder_worklets_.Add(std::make_unique<BidderWorklet>(
-                           auction_v8_helper_, pause_for_debugger_on_start,
-                           std::move(pending_url_loader_factory),
-                           std::move(bidding_interest_group)),
-                       std::move(bidder_worklet_receiver));
+    const GURL& script_source_url,
+    const absl::optional<GURL>& wasm_helper_url,
+    const absl::optional<GURL>& trusted_bidding_signals_url,
+    const url::Origin& top_window_origin) {
+  auto bidder_worklet = std::make_unique<BidderWorklet>(
+      auction_v8_helper_, pause_for_debugger_on_start,
+      std::move(pending_url_loader_factory), script_source_url, wasm_helper_url,
+      trusted_bidding_signals_url, top_window_origin);
+  auto* bidder_worklet_ptr = bidder_worklet.get();
+
+  mojo::ReceiverId receiver_id = bidder_worklets_.Add(
+      std::move(bidder_worklet), std::move(bidder_worklet_receiver));
+
+  bidder_worklet_ptr->set_close_pipe_callback(
+      base::BindOnce(&AuctionWorkletServiceImpl::DisconnectBidderWorklet,
+                     base::Unretained(this), receiver_id));
 }
 
 void AuctionWorkletServiceImpl::LoadSellerWorklet(
@@ -57,4 +67,11 @@
       std::move(seller_worklet_receiver));
 }
 
+void AuctionWorkletServiceImpl::DisconnectBidderWorklet(
+    mojo::ReceiverId receiver_id,
+    const std::string& reason) {
+  bidder_worklets_.RemoveWithReason(receiver_id, /*custom_reason_code=*/0,
+                                    reason);
+}
+
 }  // namespace auction_worklet
diff --git a/content/services/auction_worklet/auction_worklet_service_impl.h b/content/services/auction_worklet/auction_worklet_service_impl.h
index b8fbb96..a277463d 100644
--- a/content/services/auction_worklet/auction_worklet_service_impl.h
+++ b/content/services/auction_worklet/auction_worklet_service_impl.h
@@ -47,7 +47,10 @@
       bool pause_for_debugger_on_start,
       mojo::PendingRemote<network::mojom::URLLoaderFactory>
           pending_url_loader_factory,
-      mojom::BiddingInterestGroupPtr bidding_interest_group) override;
+      const GURL& script_source_url,
+      const absl::optional<GURL>& wasm_helper_url,
+      const absl::optional<GURL>& trusted_bidding_signals_url,
+      const url::Origin& top_window_origin) override;
   void LoadSellerWorklet(
       mojo::PendingReceiver<mojom::SellerWorklet> seller_worklet_receiver,
       bool pause_for_debugger_on_start,
@@ -59,6 +62,9 @@
       LoadSellerWorkletCallback load_seller_worklet_callback) override;
 
  private:
+  void DisconnectBidderWorklet(mojo::ReceiverId receiver_id,
+                               const std::string& reason);
+
   mojo::Receiver<mojom::AuctionWorkletService> receiver_;
 
   scoped_refptr<AuctionV8Helper> auction_v8_helper_;
diff --git a/content/services/auction_worklet/bidder_worklet.cc b/content/services/auction_worklet/bidder_worklet.cc
index c22a752..bd73deb4 100644
--- a/content/services/auction_worklet/bidder_worklet.cc
+++ b/content/services/auction_worklet/bidder_worklet.cc
@@ -150,29 +150,26 @@
     bool pause_for_debugger_on_start,
     mojo::PendingRemote<network::mojom::URLLoaderFactory>
         pending_url_loader_factory,
-    mojom::BiddingInterestGroupPtr bidding_interest_group)
+    const GURL& script_source_url,
+    const absl::optional<GURL>& wasm_helper_url,
+    const absl::optional<GURL>& trusted_bidding_signals_url,
+    const url::Origin& top_window_origin)
     : v8_runner_(v8_helper->v8_runner()),
       v8_helper_(v8_helper),
       debug_id_(
           base::MakeRefCounted<AuctionV8Helper::DebugId>(v8_helper.get())),
-      // TODO(mmenke): Remove up the value_or() for script_source_url_; auction
-      // worklets shouldn't be created when there's no bidding URL.
-      script_source_url_(
-          bidding_interest_group->group.bidding_url.value_or(GURL())),
-      wasm_helper_url_(bidding_interest_group->group.bidding_wasm_helper_url),
-      trusted_bidding_signals_url_(
-          bidding_interest_group->group.trusted_bidding_signals_url),
-      trusted_bidding_signals_keys_(
-          bidding_interest_group->group.trusted_bidding_signals_keys),
+      script_source_url_(script_source_url),
+      wasm_helper_url_(wasm_helper_url),
+      trusted_bidding_signals_url_(trusted_bidding_signals_url),
+      top_window_origin_(top_window_origin),
       v8_state_(nullptr, base::OnTaskRunnerDeleter(v8_runner_)) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(user_sequence_checker_);
 
   url_loader_factory_.Bind(std::move(pending_url_loader_factory));
 
   v8_state_ = std::unique_ptr<V8State, base::OnTaskRunnerDeleter>(
-      new V8State(v8_helper, debug_id_, script_source_url_,
-                  weak_ptr_factory_.GetWeakPtr(),
-                  std::move(bidding_interest_group)),
+      new V8State(v8_helper, debug_id_, script_source_url_, top_window_origin_,
+                  weak_ptr_factory_.GetWeakPtr()),
       base::OnTaskRunnerDeleter(v8_runner_));
 
   paused_ = pause_for_debugger_on_start;
@@ -182,13 +179,6 @@
 
 BidderWorklet::~BidderWorklet() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(user_sequence_checker_);
-  // Invoke any pending callbacks, since destroying uninvoked Mojo callbacks can
-  // sometimes cause issues if the pipe they're over is still open. This is not
-  // strictly necessary here, since the BidderWorklet's received pipe is
-  // destroyed before the BidderWorklet itself is, but makes the class safer
-  // against refactors of the Mojo API.
-  FailAllPendingTasks();
-
   debug_id_->AbortDebuggerPauses();
 }
 
@@ -197,41 +187,40 @@
 }
 
 void BidderWorklet::GenerateBid(
+    mojom::BidderWorkletNonSharedParamsPtr bidder_worklet_non_shared_params,
     const absl::optional<std::string>& auction_signals_json,
     const absl::optional<std::string>& per_buyer_signals_json,
-    const url::Origin& top_window_origin,
     const url::Origin& seller_origin,
+    mojom::BiddingBrowserSignalsPtr bidding_browser_signals,
     base::Time auction_start_time,
     GenerateBidCallback generate_bid_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(user_sequence_checker_);
 
   generate_bid_tasks_.emplace_front();
   auto generate_bid_task = generate_bid_tasks_.begin();
+  generate_bid_task->bidder_worklet_non_shared_params =
+      std::move(bidder_worklet_non_shared_params);
   generate_bid_task->auction_signals_json = auction_signals_json;
   generate_bid_task->per_buyer_signals_json = per_buyer_signals_json;
-  generate_bid_task->top_window_origin = top_window_origin;
   generate_bid_task->seller_origin = seller_origin;
+  generate_bid_task->bidding_browser_signals =
+      std::move(bidding_browser_signals);
   generate_bid_task->auction_start_time = auction_start_time;
   generate_bid_task->callback = std::move(generate_bid_callback);
 
-  // If worklet script failed to load, or WASM was requested and failed, fail
-  // and exit early.
-  if (CodeLoadState() == LoadState::kFailure) {
-    DeliverBidCallbackOnUserThread(generate_bid_task,
-                                   mojom::BidderWorkletBidPtr(),
-                                   /*error_msgs=*/std::vector<std::string>());
-    return;
-  }
-
+  const auto& trusted_bidding_signals_keys =
+      generate_bid_task->bidder_worklet_non_shared_params
+          ->trusted_bidding_signals_keys;
   if (trusted_bidding_signals_url_.has_value() &&
-      trusted_bidding_signals_keys_.has_value() &&
-      !trusted_bidding_signals_keys_->empty()) {
+      trusted_bidding_signals_keys.has_value() &&
+      !trusted_bidding_signals_keys->empty()) {
     generate_bid_task->trusted_bidding_signals =
         TrustedSignals::LoadBiddingSignals(
             url_loader_factory_.get(),
-            std::set<std::string>(trusted_bidding_signals_keys_->begin(),
-                                  trusted_bidding_signals_keys_->end()),
-            top_window_origin.host(), *trusted_bidding_signals_url_, v8_helper_,
+            std::set<std::string>(trusted_bidding_signals_keys->begin(),
+                                  trusted_bidding_signals_keys->end()),
+            top_window_origin_.host(), *trusted_bidding_signals_url_,
+            v8_helper_,
             base::BindOnce(&BidderWorklet::OnTrustedBiddingSignalsDownloaded,
                            base::Unretained(this), generate_bid_task));
     return;
@@ -241,39 +230,30 @@
 }
 
 void BidderWorklet::ReportWin(
+    const std::string& interest_group_name,
     const absl::optional<std::string>& auction_signals_json,
     const absl::optional<std::string>& per_buyer_signals_json,
-    const url::Origin& top_window_origin,
     const std::string& seller_signals_json,
     const GURL& browser_signal_render_url,
     double browser_signal_bid,
     const url::Origin& browser_signal_seller_origin,
-    ReportWinCallback callback) {
+    ReportWinCallback report_win_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(user_sequence_checker_);
 
   report_win_tasks_.emplace_front();
   auto report_win_task = report_win_tasks_.begin();
+  report_win_task->interest_group_name = interest_group_name;
   report_win_task->auction_signals_json = auction_signals_json;
   report_win_task->per_buyer_signals_json = per_buyer_signals_json;
-  report_win_task->top_window_origin = top_window_origin;
   report_win_task->seller_signals_json = seller_signals_json;
   report_win_task->browser_signal_render_url = browser_signal_render_url;
   report_win_task->browser_signal_bid = browser_signal_bid;
   report_win_task->browser_signal_seller_origin = browser_signal_seller_origin;
-  report_win_task->callback = std::move(callback);
+  report_win_task->callback = std::move(report_win_callback);
 
-  // If worklet script and, if requested, WASM, aren't loaded, can't run script
-  // immediately.
-  LoadState code_load_state = CodeLoadState();
-  if (code_load_state != LoadState::kSuccess) {
-    // If required files failed to load, fail and exit early.
-    if (code_load_state == LoadState::kFailure) {
-      DeliverReportWinOnUserThread(report_win_task,
-                                   /*report_url=*/absl::optional<GURL>(),
-                                   /*errors=*/std::vector<std::string>());
-    }
+  // If not yet ready, need to wait for load to complete.
+  if (!IsCodeReady())
     return;
-  }
 
   RunReportWin(report_win_task);
 }
@@ -297,14 +277,14 @@
     scoped_refptr<AuctionV8Helper> v8_helper,
     scoped_refptr<AuctionV8Helper::DebugId> debug_id,
     const GURL& script_source_url,
-    base::WeakPtr<BidderWorklet> parent,
-    mojom::BiddingInterestGroupPtr bidding_interest_group)
+    const url::Origin& top_window_origin,
+    base::WeakPtr<BidderWorklet> parent)
     : v8_helper_(std::move(v8_helper)),
       debug_id_(std::move(debug_id)),
       parent_(std::move(parent)),
       user_thread_(base::SequencedTaskRunnerHandle::Get()),
-      bidding_interest_group_(std::move(bidding_interest_group)),
-      script_source_url_(std::move(script_source_url)) {
+      script_source_url_(std::move(script_source_url)),
+      top_window_origin_(top_window_origin) {
   DETACH_FROM_SEQUENCE(v8_sequence_checker_);
   v8_helper_->v8_runner()->PostTask(
       FROM_HERE, base::BindOnce(&V8State::FinishInit, base::Unretained(this)));
@@ -323,9 +303,9 @@
 }
 
 void BidderWorklet::V8State::ReportWin(
+    const std::string& interest_group_name,
     const absl::optional<std::string>& auction_signals_json,
     const absl::optional<std::string>& per_buyer_signals_json,
-    const url::Origin& browser_signal_top_window_origin,
     const std::string& seller_signals_json,
     const GURL& browser_signal_render_url,
     double browser_signal_bid,
@@ -360,12 +340,11 @@
   v8::Local<v8::Object> browser_signals = v8::Object::New(isolate);
   gin::Dictionary browser_signals_dict(isolate, browser_signals);
   if (!browser_signals_dict.Set("topWindowHostname",
-                                browser_signal_top_window_origin.host()) ||
+                                top_window_origin_.host()) ||
       !browser_signals_dict.Set(
           "interestGroupOwner",
-          bidding_interest_group_->group.owner.Serialize()) ||
-      !browser_signals_dict.Set("interestGroupName",
-                                bidding_interest_group_->group.name) ||
+          url::Origin::Create(script_source_url_).Serialize()) ||
+      !browser_signals_dict.Set("interestGroupName", interest_group_name) ||
       !browser_signals_dict.Set("renderUrl",
                                 browser_signal_render_url.spec()) ||
       !browser_signals_dict.Set("bid", browser_signal_bid) ||
@@ -400,18 +379,18 @@
 }
 
 void BidderWorklet::V8State::GenerateBid(
+    mojom::BidderWorkletNonSharedParamsPtr bidder_worklet_non_shared_params,
     const absl::optional<std::string>& auction_signals_json,
     const absl::optional<std::string>& per_buyer_signals_json,
-    const url::Origin& browser_signal_top_window_origin,
     const url::Origin& browser_signal_seller_origin,
+    mojom::BiddingBrowserSignalsPtr bidding_browser_signals,
     base::Time auction_start_time,
     scoped_refptr<TrustedSignals::Result> trusted_bidding_signals_result,
     GenerateBidCallbackInternal callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(v8_sequence_checker_);
 
-  const blink::InterestGroup& interest_group = bidding_interest_group_->group;
   // Can't make a bid without any ads.
-  if (!interest_group.ads) {
+  if (!bidder_worklet_non_shared_params->ads) {
     PostErrorBidCallbackToUserThread(std::move(callback));
     return;
   }
@@ -428,27 +407,32 @@
   std::vector<v8::Local<v8::Value>> args;
   v8::Local<v8::Object> interest_group_object = v8::Object::New(isolate);
   gin::Dictionary interest_group_dict(isolate, interest_group_object);
-  if (!interest_group_dict.Set("owner", interest_group.owner.Serialize()) ||
-      !interest_group_dict.Set("name", interest_group.name) ||
-      (interest_group.user_bidding_signals &&
-       !v8_helper_->InsertJsonValue(context, "userBiddingSignals",
-                                    *interest_group.user_bidding_signals,
-                                    interest_group_object))) {
+  if (!interest_group_dict.Set(
+          "owner", url::Origin::Create(script_source_url_).Serialize()) ||
+      !interest_group_dict.Set("name",
+                               bidder_worklet_non_shared_params->name) ||
+      (bidder_worklet_non_shared_params->user_bidding_signals &&
+       !v8_helper_->InsertJsonValue(
+           context, "userBiddingSignals",
+           *bidder_worklet_non_shared_params->user_bidding_signals,
+           interest_group_object))) {
     PostErrorBidCallbackToUserThread(std::move(callback));
     return;
   }
 
   v8::Local<v8::Value> ads;
-  if (!CreateAdVector(v8_helper_.get(), context, *interest_group.ads, ads) ||
+  if (!CreateAdVector(v8_helper_.get(), context,
+                      *bidder_worklet_non_shared_params->ads, ads) ||
       !v8_helper_->InsertValue("ads", std::move(ads), interest_group_object)) {
     PostErrorBidCallbackToUserThread(std::move(callback));
     return;
   }
 
-  if (interest_group.ad_components) {
+  if (bidder_worklet_non_shared_params->ad_components) {
     v8::Local<v8::Value> ad_components;
     if (!CreateAdVector(v8_helper_.get(), context,
-                        *interest_group.ad_components, ad_components) ||
+                        *bidder_worklet_non_shared_params->ad_components,
+                        ad_components) ||
         !v8_helper_->InsertValue("adComponents", std::move(ad_components),
                                  interest_group_object)) {
       PostErrorBidCallbackToUserThread(std::move(callback));
@@ -472,20 +456,20 @@
   } else {
     trusted_signals = trusted_bidding_signals_result->GetBiddingSignals(
         v8_helper_.get(), context,
-        *interest_group.trusted_bidding_signals_keys);
+        *bidder_worklet_non_shared_params->trusted_bidding_signals_keys);
   }
   args.push_back(trusted_signals);
 
   v8::Local<v8::Object> browser_signals = v8::Object::New(isolate);
   gin::Dictionary browser_signals_dict(isolate, browser_signals);
   if (!browser_signals_dict.Set("topWindowHostname",
-                                browser_signal_top_window_origin.host()) ||
+                                top_window_origin_.host()) ||
       !browser_signals_dict.Set("seller",
                                 browser_signal_seller_origin.Serialize()) ||
       !browser_signals_dict.Set("joinCount",
-                                bidding_interest_group_->signals->join_count) ||
+                                bidding_browser_signals->join_count) ||
       !browser_signals_dict.Set("bidCount",
-                                bidding_interest_group_->signals->bid_count)) {
+                                bidding_browser_signals->bid_count)) {
     PostErrorBidCallbackToUserThread(std::move(callback));
     return;
   }
@@ -505,7 +489,7 @@
 
   v8::Local<v8::Value> prev_wins;
   if (!CreatePrevWinsArray(v8_helper_.get(), context, auction_start_time,
-                           bidding_interest_group_->signals->prev_wins)
+                           bidding_browser_signals->prev_wins)
            .ToLocal(&prev_wins)) {
     PostErrorBidCallbackToUserThread(std::move(callback));
     return;
@@ -569,7 +553,7 @@
 
   GURL render_url(render_url_string);
   if (!IsAllowedAdUrl(render_url, script_source_url_, "render",
-                      *interest_group.ads, errors_out)) {
+                      *bidder_worklet_non_shared_params->ads, errors_out)) {
     PostErrorBidCallbackToUserThread(std::move(callback),
                                      std::move(errors_out));
     return;
@@ -579,7 +563,7 @@
   v8::Local<v8::Value> ad_components;
   if (result_dict.Get("adComponents", &ad_components) &&
       !ad_components->IsNullOrUndefined()) {
-    if (!interest_group.ad_components) {
+    if (!bidder_worklet_non_shared_params->ad_components) {
       errors_out.push_back(
           base::StrCat({script_source_url_.spec(),
                         " generateBid() return value contains adComponents but "
@@ -625,7 +609,8 @@
 
       GURL ad_component_url(url_string);
       if (!IsAllowedAdUrl(ad_component_url, script_source_url_, "adComponents",
-                          *interest_group.ad_components, errors_out)) {
+                          *bidder_worklet_non_shared_params->ad_components,
+                          errors_out)) {
         PostErrorBidCallbackToUserThread(std::move(callback),
                                          std::move(errors_out));
         return;
@@ -714,9 +699,6 @@
         debug_id_,
         base::BindOnce(&BidderWorklet::OnWasmDownloaded,
                        base::Unretained(this)));
-  } else {
-    // WASM helper is optional if not specified.
-    worklet_wasm_load_state_ = LoadState::kSuccess;
   }
 }
 
@@ -726,16 +708,18 @@
 
   worklet_loader_.reset();
 
-  // Fail all pending tasks if the script failed to load.
+  // On failure, close pipe and delete `this`, as it can't do anything without a
+  // loaded script.
   if (!worklet_script.success()) {
-    worklet_js_load_state_ = LoadState::kFailure;
-    if (error_msg.has_value())
-      load_code_error_msgs_.push_back(std::move(error_msg.value()));
-    FailAllPendingTasks();
+    std::move(close_pipe_callback_)
+        .Run(error_msg ? error_msg.value() : std::string());
+    // `this` should be deleted at this point.
     return;
   }
 
-  worklet_js_load_state_ = LoadState::kSuccess;
+  if (error_msg.has_value())
+    load_code_error_msgs_.push_back(std::move(error_msg.value()));
+
   v8_runner_->PostTask(FROM_HERE,
                        base::BindOnce(&BidderWorklet::V8State::SetWorkletScript,
                                       base::Unretained(v8_state_.get()),
@@ -750,16 +734,19 @@
 
   wasm_loader_.reset();
 
-  // If the WASM helper is actually requested, its failure fails the bid.
+  // If the WASM helper is actually requested, delete `this` and inform the
+  // browser process of the failure. ReportWin() calls would theoretically still
+  // be allowed, but that adds a lot more complexity around BidderWorklet reuse.
   if (!wasm_helper.success()) {
-    worklet_wasm_load_state_ = LoadState::kFailure;
-    if (error_msg.has_value())
-      load_code_error_msgs_.push_back(std::move(error_msg.value()));
-    FailAllPendingTasks();
+    std::move(close_pipe_callback_)
+        .Run(error_msg ? error_msg.value() : std::string());
+    // `this` should be deleted at this point.
     return;
   }
 
-  worklet_wasm_load_state_ = LoadState::kSuccess;
+  if (error_msg.has_value())
+    load_code_error_msgs_.push_back(std::move(error_msg.value()));
+
   v8_runner_->PostTask(FROM_HERE,
                        base::BindOnce(&BidderWorklet::V8State::SetWasmHelper,
                                       base::Unretained(v8_state_.get()),
@@ -801,20 +788,21 @@
 
 void BidderWorklet::GenerateBidIfReady(GenerateBidTaskList::iterator task) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(user_sequence_checker_);
-  // Script or WASM load failure should abort all tasks before getting here.
-  LoadState code_load_state = CodeLoadState();
-  DCHECK_NE(code_load_state, LoadState::kFailure);
-  if (task->trusted_bidding_signals || code_load_state != LoadState::kSuccess) {
+  if (task->trusted_bidding_signals || !IsCodeReady())
     return;
-  }
 
+  // Other than the callback field, no fields of `task` are needed after this
+  // point, so can consume them instead of copying them.
   v8_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
           &BidderWorklet::V8State::GenerateBid,
-          base::Unretained(v8_state_.get()), task->auction_signals_json,
-          task->per_buyer_signals_json, task->top_window_origin,
-          task->seller_origin, task->auction_start_time,
+          base::Unretained(v8_state_.get()),
+          std::move(task->bidder_worklet_non_shared_params),
+          std::move(task->auction_signals_json),
+          std::move(task->per_buyer_signals_json),
+          std::move(task->seller_origin),
+          std::move(task->bidding_browser_signals), task->auction_start_time,
           std::move(task->trusted_bidding_signals_result),
           base::BindOnce(&BidderWorklet::DeliverBidCallbackOnUserThread,
                          weak_ptr_factory_.GetWeakPtr(), task)));
@@ -823,32 +811,23 @@
 void BidderWorklet::RunReportWin(ReportWinTaskList::iterator task) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(user_sequence_checker_);
 
+  // Other than the callback field, no fields of `task` are needed after this
+  // point, so can consume them instead of copying them.
   v8_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
           &BidderWorklet::V8State::ReportWin, base::Unretained(v8_state_.get()),
-          task->auction_signals_json, task->per_buyer_signals_json,
-          task->top_window_origin, task->seller_signals_json,
-          task->browser_signal_render_url, task->browser_signal_bid,
-          task->browser_signal_seller_origin,
+          std::move(task->interest_group_name),
+          std::move(task->auction_signals_json),
+          std::move(task->per_buyer_signals_json),
+          std::move(task->seller_signals_json),
+          std::move(task->browser_signal_render_url),
+          std::move(task->browser_signal_bid),
+          std::move(task->browser_signal_seller_origin),
           base::BindOnce(&BidderWorklet::DeliverReportWinOnUserThread,
                          weak_ptr_factory_.GetWeakPtr(), task)));
 }
 
-void BidderWorklet::FailAllPendingTasks() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(user_sequence_checker_);
-  while (!generate_bid_tasks_.empty()) {
-    DeliverBidCallbackOnUserThread(generate_bid_tasks_.begin(),
-                                   mojom::BidderWorkletBidPtr(),
-                                   /*error_msgs=*/std::vector<std::string>());
-  }
-  while (!report_win_tasks_.empty()) {
-    DeliverReportWinOnUserThread(report_win_tasks_.begin(),
-                                 /*report_url=*/absl::nullopt,
-                                 /*errors=*/std::vector<std::string>());
-  }
-}
-
 void BidderWorklet::DeliverBidCallbackOnUserThread(
     GenerateBidTaskList::iterator task,
     mojom::BidderWorkletBidPtr bid,
@@ -876,19 +855,11 @@
   report_win_tasks_.erase(task);
 }
 
-BidderWorklet::LoadState BidderWorklet::CodeLoadState() const {
-  if (worklet_js_load_state_ == LoadState::kFailure ||
-      worklet_wasm_load_state_ == LoadState::kFailure) {
-    return LoadState::kFailure;
-  }
-
-  if (worklet_js_load_state_ == LoadState::kLoading ||
-      worklet_wasm_load_state_ == LoadState::kLoading) {
-    return LoadState::kLoading;
-  }
-  DCHECK_EQ(worklet_js_load_state_, LoadState::kSuccess);
-  DCHECK_EQ(worklet_wasm_load_state_, LoadState::kSuccess);
-  return LoadState::kSuccess;
+bool BidderWorklet::IsCodeReady() const {
+  // If `paused_`, loading hasn't started yet. Otherwise, null loaders indicate
+  // the worklet script has loaded successfully, and there's no WASM helper, or
+  // it has also loaded successfully.
+  return !paused_ && !worklet_loader_ && !wasm_loader_;
 }
 
 }  // namespace auction_worklet
diff --git a/content/services/auction_worklet/bidder_worklet.h b/content/services/auction_worklet/bidder_worklet.h
index 19bec47..db4b1ca6 100644
--- a/content/services/auction_worklet/bidder_worklet.h
+++ b/content/services/auction_worklet/bidder_worklet.h
@@ -53,6 +53,11 @@
 // interest group in different auctions.
 class BidderWorklet : public mojom::BidderWorklet {
  public:
+  // Deletes the worklet immediately and resets the BidderWorklet's Mojo pipe
+  // with the provided description. See mojo::Receiver::ResetWithReason().
+  using ClosePipeCallback =
+      base::OnceCallback<void(const std::string& description)>;
+
   // Starts loading the worklet script on construction, as well as the trusted
   // bidding data, if necessary. Will then call the script's generateBid()
   // function and invoke the callback with the results. Callback will always be
@@ -64,28 +69,42 @@
                 bool pause_for_debugger_on_start,
                 mojo::PendingRemote<network::mojom::URLLoaderFactory>
                     pending_url_loader_factory,
-                mojom::BiddingInterestGroupPtr bidding_interest_group);
+                const GURL& script_source_url,
+                const absl::optional<GURL>& bidding_wasm_helper_url,
+                const absl::optional<GURL>& trusted_bidding_signals_url,
+                const url::Origin& top_window_origin);
   explicit BidderWorklet(const BidderWorklet&) = delete;
   ~BidderWorklet() override;
   BidderWorklet& operator=(const BidderWorklet&) = delete;
 
+  // Sets the callback to be invoked on errors which require closing the pipe.
+  // Callback will also immediately delete `this`. Not an argument to
+  // constructor because the Mojo ReceiverId needs to be bound to the callback,
+  // but can only get that after creating the worklet. Must be called
+  // immediately after creating a BidderWorklet.
+  void set_close_pipe_callback(ClosePipeCallback close_pipe_callback) {
+    close_pipe_callback_ = std::move(close_pipe_callback);
+  }
+
   int context_group_id_for_testing() const;
 
   // mojom::BidderWorklet implementation:
-  void GenerateBid(const absl::optional<std::string>& auction_signals_json,
-                   const absl::optional<std::string>& per_buyer_signals_json,
-                   const url::Origin& top_window_origin,
-                   const url::Origin& seller_origin,
-                   base::Time auction_start_time,
-                   GenerateBidCallback generate_bid_callback) override;
-  void ReportWin(const absl::optional<std::string>& auction_signals_json,
+  void GenerateBid(
+      mojom::BidderWorkletNonSharedParamsPtr bidder_worklet_non_shared_params,
+      const absl::optional<std::string>& auction_signals_json,
+      const absl::optional<std::string>& per_buyer_signals_json,
+      const url::Origin& seller_origin,
+      mojom::BiddingBrowserSignalsPtr bidding_browser_signals,
+      base::Time auction_start_time,
+      GenerateBidCallback generate_bid_callback) override;
+  void ReportWin(const std::string& interest_group_name,
+                 const absl::optional<std::string>& auction_signals_json,
                  const absl::optional<std::string>& per_buyer_signals_json,
-                 const url::Origin& top_window_origin,
                  const std::string& seller_signals_json,
                  const GURL& browser_signal_render_url,
                  double browser_signal_bid,
                  const url::Origin& browser_signal_seller_origin,
-                 ReportWinCallback callback) override;
+                 ReportWinCallback report_win_callback) override;
   void ConnectDevToolsAgent(
       mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent) override;
 
@@ -94,10 +113,11 @@
     GenerateBidTask();
     ~GenerateBidTask();
 
+    mojom::BidderWorkletNonSharedParamsPtr bidder_worklet_non_shared_params;
     absl::optional<std::string> auction_signals_json;
     absl::optional<std::string> per_buyer_signals_json;
-    url::Origin top_window_origin;
     url::Origin seller_origin;
+    mojom::BiddingBrowserSignalsPtr bidding_browser_signals;
     base::Time auction_start_time;
 
     // Set while loading is in progress.
@@ -118,9 +138,9 @@
     ReportWinTask();
     ~ReportWinTask();
 
+    std::string interest_group_name;
     absl::optional<std::string> auction_signals_json;
     absl::optional<std::string> per_buyer_signals_json;
-    url::Origin top_window_origin;
     std::string seller_signals_json;
     GURL browser_signal_render_url;
     double browser_signal_bid;
@@ -138,8 +158,8 @@
     V8State(scoped_refptr<AuctionV8Helper> v8_helper,
             scoped_refptr<AuctionV8Helper::DebugId> debug_id,
             const GURL& script_source_url,
-            base::WeakPtr<BidderWorklet> parent,
-            mojom::BiddingInterestGroupPtr bidding_interest_group);
+            const url::Origin& top_window_origin,
+            base::WeakPtr<BidderWorklet> parent);
 
     void SetWorkletScript(WorkletLoader::Result worklet_script);
     void SetWasmHelper(WorkletWasmLoader::Result wasm_helper);
@@ -154,9 +174,9 @@
         base::OnceCallback<void(absl::optional<GURL> report_url,
                                 std::vector<std::string> errors)>;
 
-    void ReportWin(const absl::optional<std::string>& auction_signals_json,
+    void ReportWin(const std::string& interest_group_name,
+                   const absl::optional<std::string>& auction_signals_json,
                    const absl::optional<std::string>& per_buyer_signals_json,
-                   const url::Origin& browser_signal_top_window_origin,
                    const std::string& seller_signals_json,
                    const GURL& browser_signal_render_url,
                    double browser_signal_bid,
@@ -164,10 +184,11 @@
                    ReportWinCallbackInternal callback);
 
     void GenerateBid(
+        mojom::BidderWorkletNonSharedParamsPtr bidder_worklet_non_shared_params,
         const absl::optional<std::string>& auction_signals_json,
         const absl::optional<std::string>& per_buyer_signals_json,
-        const url::Origin& browser_signal_top_window_origin,
         const url::Origin& browser_signal_seller_origin,
+        mojom::BiddingBrowserSignalsPtr bidding_browser_signals,
         base::Time auction_start_time,
         scoped_refptr<TrustedSignals::Result> trusted_bidding_signals_result,
         GenerateBidCallbackInternal callback);
@@ -198,7 +219,7 @@
     const base::WeakPtr<BidderWorklet> parent_;
     const scoped_refptr<base::SequencedTaskRunner> user_thread_;
 
-    const mojom::BiddingInterestGroupPtr bidding_interest_group_;
+    const url::Origin owner_;
 
     // Compiled script, not bound to any context. Can be repeatedly bound to
     // different context and executed, without persisting any state.
@@ -208,12 +229,11 @@
     WorkletWasmLoader::Result wasm_helper_;
 
     const GURL script_source_url_;
+    const url::Origin top_window_origin_;
 
     SEQUENCE_CHECKER(v8_sequence_checker_);
   };
 
-  enum class LoadState { kLoading, kSuccess, kFailure };
-
   void ResumeIfPaused();
   void Start();
 
@@ -238,10 +258,6 @@
 
   void RunReportWin(ReportWinTaskList::iterator task);
 
-  // Fails all pending GenerateBid() and ReportWin() tasks, removing all tasks
-  // from both lists.
-  void FailAllPendingTasks();
-
   // Invokes the `callback` of `task` with the provided values, and removes
   // `task` from `generate_bid_tasks_`.
   void DeliverBidCallbackOnUserThread(GenerateBidTaskList::iterator task,
@@ -254,8 +270,9 @@
                                     absl::optional<GURL> report_url,
                                     std::vector<std::string> errors);
 
-  // Combines `worklet_js_load_state_` and `worklet_wasm_load_state_`.
-  LoadState CodeLoadState() const;
+  // Returns true if unpaused and the script and WASM helper (if needed) have
+  // loaded.
+  bool IsCodeReady() const;
 
   scoped_refptr<base::SequencedTaskRunner> v8_runner_;
 
@@ -265,18 +282,18 @@
 
   bool paused_;
 
+  // Values shared by all interest groups that the BidderWorklet can be used
+  // for.
   const GURL script_source_url_;
-
-  std::unique_ptr<WorkletLoader> worklet_loader_;
-  LoadState worklet_js_load_state_ = LoadState::kLoading;
-
   absl::optional<GURL> wasm_helper_url_;
-  std::unique_ptr<WorkletWasmLoader> wasm_loader_;
-  LoadState worklet_wasm_load_state_ = LoadState::kLoading;
-
-  // Values copied from the interest group used to create the BidderWorklet.
   const absl::optional<GURL> trusted_bidding_signals_url_;
-  const absl::optional<std::vector<std::string>> trusted_bidding_signals_keys_;
+
+  // Top window origin for the auctions sharing this BidderWorklet.
+  const url::Origin top_window_origin_;
+
+  // These are deleted once each resource is loaded.
+  std::unique_ptr<WorkletLoader> worklet_loader_;
+  std::unique_ptr<WorkletWasmLoader> wasm_loader_;
 
   // Lives on `v8_runner_`. Since it's deleted there via DeleteSoon, tasks can
   // be safely posted from main thread to it with an Unretained pointer.
@@ -288,6 +305,8 @@
   GenerateBidTaskList generate_bid_tasks_;
   ReportWinTaskList report_win_tasks_;
 
+  ClosePipeCallback close_pipe_callback_;
+
   // Errors that occurred while loading the code, if any.
   std::vector<std::string> load_code_error_msgs_;
 
diff --git a/content/services/auction_worklet/bidder_worklet_unittest.cc b/content/services/auction_worklet/bidder_worklet_unittest.cc
index 22d6ace..4901246 100644
--- a/content/services/auction_worklet/bidder_worklet_unittest.cc
+++ b/content/services/auction_worklet/bidder_worklet_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/cxx17_backports.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
@@ -103,7 +104,6 @@
   // but test that set these can use this to reset values to default after each
   // test.
   void SetDefaultParameters() {
-    interest_group_owner_ = url::Origin::Create(GURL("https://foo.test"));
     interest_group_name_ = "Fred";
     interest_group_user_bidding_signals_ = absl::nullopt;
 
@@ -125,8 +125,7 @@
 
     auction_signals_ = "[\"auction_signals\"]";
     per_buyer_signals_ = "[\"per_buyer_signals\"]";
-    browser_signal_top_window_origin_ =
-        url::Origin::Create(GURL("https://top.window.test/"));
+    top_window_origin_ = url::Origin::Create(GURL("https://top.window.test/"));
     browser_signal_seller_origin_ =
         url::Origin::Create(GURL("https://browser.signal.seller.test/"));
     seller_signals_ = "[\"seller_signals\"]";
@@ -212,7 +211,7 @@
       const std::vector<std::string>& expected_errors,
       base::OnceClosure done_closure) {
     bidder_worklet->ReportWin(
-        auction_signals_, per_buyer_signals_, browser_signal_top_window_origin_,
+        interest_group_name_, auction_signals_, per_buyer_signals_,
         seller_signals_, browser_signal_render_url_, browser_signal_bid_,
         browser_signal_seller_origin_,
         base::BindOnce(
@@ -243,29 +242,20 @@
     run_loop.Run();
   }
 
-  // Creates a BiddingInterestGroup based on test fixture configuration.
-  mojom::BiddingInterestGroupPtr CreateBiddingInterestGroup(const GURL& url) {
-    blink::InterestGroup interest_group;
-    interest_group.owner = interest_group_owner_;
-    interest_group.name = interest_group_name_;
-    interest_group.bidding_url = url;
-    interest_group.bidding_wasm_helper_url = interest_group_wasm_url_;
-    interest_group.user_bidding_signals = interest_group_user_bidding_signals_;
-    interest_group.trusted_bidding_signals_url =
-        interest_group_trusted_bidding_signals_url_;
-    interest_group.trusted_bidding_signals_keys =
-        interest_group_trusted_bidding_signals_keys_;
-    interest_group.ads = interest_group_ads_;
-    interest_group.ad_components = interest_group_ad_components_;
+  // Creates a BidderWorkletNonSharedParams based on test fixture
+  // configuration.
+  mojom::BidderWorkletNonSharedParamsPtr CreateBidderWorkletNonSharedParams() {
+    return mojom::BidderWorkletNonSharedParams::New(
+        interest_group_name_, interest_group_trusted_bidding_signals_keys_,
+        interest_group_user_bidding_signals_, interest_group_ads_,
+        interest_group_ad_components_);
+  }
 
-    mojom::BiddingBrowserSignalsPtr bidding_browser_signals =
-        mojom::BiddingBrowserSignals::New(
-            browser_signal_join_count_, browser_signal_bid_count_,
-            CloneWinList(browser_signal_prev_wins_));
-    mojom::BiddingInterestGroupPtr bidding_interest_group =
-        mojom::BiddingInterestGroup::New(std::move(interest_group),
-                                         std::move(bidding_browser_signals));
-    return bidding_interest_group;
+  // Creates a BiddingBrowserSignals based on test fixture configuration.
+  mojom::BiddingBrowserSignalsPtr CreateBiddingBrowserSignals() {
+    return mojom::BiddingBrowserSignals::New(
+        browser_signal_join_count_, browser_signal_bid_count_,
+        CloneWinList(browser_signal_prev_wins_));
   }
 
   // Create a BidderWorklet, returning the remote. If `out_bidder_worklet_impl`
@@ -283,25 +273,47 @@
 
     auto bidder_worklet_impl = std::make_unique<BidderWorklet>(
         v8_helper_, pause_for_debugger_on_start, std::move(url_loader_factory),
-        CreateBiddingInterestGroup(url.is_empty() ? interest_group_bidding_url_
-                                                  : url));
-    if (out_bidder_worklet_impl)
-      *out_bidder_worklet_impl = bidder_worklet_impl.get();
-
+        url.is_empty() ? interest_group_bidding_url_ : url,
+        interest_group_wasm_url_, interest_group_trusted_bidding_signals_url_,
+        top_window_origin_);
+    auto* bidder_worklet_ptr = bidder_worklet_impl.get();
     mojo::Remote<mojom::BidderWorklet> bidder_worklet;
-    mojo::MakeSelfOwnedReceiver(std::move(bidder_worklet_impl),
-                                bidder_worklet.BindNewPipeAndPassReceiver());
+    mojo::ReceiverId receiver_id =
+        bidder_worklets_.Add(std::move(bidder_worklet_impl),
+                             bidder_worklet.BindNewPipeAndPassReceiver());
+    bidder_worklet_ptr->set_close_pipe_callback(
+        base::BindOnce(&BidderWorkletTest::ClosePipeCallback,
+                       base::Unretained(this), receiver_id));
+    bidder_worklet.set_disconnect_with_reason_handler(base::BindRepeating(
+        &BidderWorkletTest::OnDisconnectWithReason, base::Unretained(this)));
+
+    if (out_bidder_worklet_impl)
+      *out_bidder_worklet_impl = bidder_worklet_ptr;
     return bidder_worklet;
   }
 
   void GenerateBid(mojom::BidderWorklet* bidder_worklet) {
     bidder_worklet->GenerateBid(
-        auction_signals_, per_buyer_signals_, browser_signal_top_window_origin_,
-        browser_signal_seller_origin_, auction_start_time_,
+        CreateBidderWorkletNonSharedParams(), auction_signals_,
+        per_buyer_signals_, browser_signal_seller_origin_,
+        CreateBiddingBrowserSignals(), auction_start_time_,
         base::BindOnce(&BidderWorkletTest::GenerateBidCallback,
                        base::Unretained(this)));
   }
 
+  // Calls GenerateBid(), expecting the callback never to be invoked.
+  void GenerateBidExpectingCallbackNotInvoked(
+      mojom::BidderWorklet* bidder_worklet) {
+    bidder_worklet->GenerateBid(
+        CreateBidderWorkletNonSharedParams(), auction_signals_,
+        per_buyer_signals_, browser_signal_seller_origin_,
+        CreateBiddingBrowserSignals(), auction_start_time_,
+        base::BindOnce([](mojom::BidderWorkletBidPtr bid,
+                          const std::vector<std::string>& errors) {
+          ADD_FAILURE() << "Callback should not be invoked.";
+        }));
+  }
+
   // Create a BidderWorklet and invokes GenerateBid(), waiting for the
   // GenerateBid() callback to be invoked. Returns a null Remote on failure.
   mojo::Remote<mojom::BidderWorklet> CreateWorkletAndGenerateBid() {
@@ -326,7 +338,39 @@
     load_script_run_loop_->Quit();
   }
 
+  // Waits for OnDisconnectWithReason() to be invoked, if it hasn't been
+  // already, and returns the error string it was invoked with.
+  std::string WaitForDisconnect() {
+    DCHECK(!disconnect_run_loop_);
+
+    if (!disconnect_reason_) {
+      disconnect_run_loop_ = std::make_unique<base::RunLoop>();
+      disconnect_run_loop_->Run();
+      disconnect_run_loop_.reset();
+    }
+
+    DCHECK(disconnect_reason_);
+    std::string disconnect_reason = std::move(disconnect_reason_).value();
+    disconnect_reason_.reset();
+    return disconnect_reason;
+  }
+
  protected:
+  void ClosePipeCallback(mojo::ReceiverId receiver_id,
+                         const std::string& description) {
+    bidder_worklets_.RemoveWithReason(receiver_id, /*custom_reason_code=*/0,
+                                      description);
+  }
+
+  void OnDisconnectWithReason(uint32_t custom_reason,
+                              const std::string& description) {
+    DCHECK(!disconnect_reason_);
+
+    disconnect_reason_ = description;
+    if (disconnect_run_loop_)
+      disconnect_run_loop_->Quit();
+  }
+
   std::vector<mojo::StructPtr<mojom::PreviousWin>> CloneWinList(
       const std::vector<mojo::StructPtr<mojom::PreviousWin>>& prev_win_list) {
     std::vector<mojo::StructPtr<mojom::PreviousWin>> out;
@@ -340,9 +384,8 @@
 
   // Values used to construct the BiddingInterestGroup passed to the
   // BidderWorklet.
-  url::Origin interest_group_owner_;
   std::string interest_group_name_;
-  const GURL interest_group_bidding_url_ = GURL("https://url.test/");
+  GURL interest_group_bidding_url_ = GURL("https://url.test/");
   absl::optional<GURL> interest_group_wasm_url_;
   absl::optional<std::string> interest_group_user_bidding_signals_;
   std::vector<blink::InterestGroup::Ad> interest_group_ads_;
@@ -357,7 +400,7 @@
 
   absl::optional<std::string> auction_signals_;
   absl::optional<std::string> per_buyer_signals_;
-  url::Origin browser_signal_top_window_origin_;
+  url::Origin top_window_origin_;
   url::Origin browser_signal_seller_origin_;
   std::string seller_signals_;
   GURL browser_signal_render_url_;
@@ -378,6 +421,15 @@
 
   network::TestURLLoaderFactory url_loader_factory_;
   scoped_refptr<AuctionV8Helper> v8_helper_;
+
+  // Reuseable run loop for disconnection errors.
+  std::unique_ptr<base::RunLoop> disconnect_run_loop_;
+  absl::optional<std::string> disconnect_reason_;
+
+  // Owns all created BidderWorklets - having a ReceiverSet allows them to have
+  // a ClosePipeCallback which behaves just like the one in
+  // AuctionWorkletServiceImpl, to better match production behavior.
+  mojo::UniqueReceiverSet<mojom::BidderWorklet> bidder_worklets_;
 };
 
 // Test the case the BidderWorklet pipe is closed before invoking the
@@ -387,31 +439,34 @@
 // invoking it.
 TEST_F(BidderWorkletTest, PipeClosed) {
   auto bidder_worklet = CreateWorklet();
-  GenerateBid(bidder_worklet.get());
+  GenerateBidExpectingCallbackNotInvoked(bidder_worklet.get());
   bidder_worklet.reset();
+  EXPECT_FALSE(bidder_worklets_.empty());
 
   // This should not result in a Mojo crash.
   task_environment_.RunUntilIdle();
+  EXPECT_TRUE(bidder_worklets_.empty());
 }
 
 TEST_F(BidderWorkletTest, NetworkError) {
   url_loader_factory_.AddResponse(interest_group_bidding_url_.spec(),
                                   CreateBasicGenerateBidScript(),
                                   net::HTTP_NOT_FOUND);
-  RunGenerateBidExpectingResult(
-      mojom::BidderWorkletBidPtr() /* expected_bid */,
-      {"Failed to load https://url.test/ HTTP status = 404 Not Found."});
+  auto bidder_worklet = CreateWorklet();
+  GenerateBidExpectingCallbackNotInvoked(bidder_worklet.get());
+  EXPECT_EQ("Failed to load https://url.test/ HTTP status = 404 Not Found.",
+            WaitForDisconnect());
 }
 
 TEST_F(BidderWorkletTest, CompileError) {
   AddJavascriptResponse(&url_loader_factory_, interest_group_bidding_url_,
                         "Invalid Javascript");
-  EXPECT_FALSE(CreateWorkletAndGenerateBid());
+  auto bidder_worklet = CreateWorklet();
+  GenerateBidExpectingCallbackNotInvoked(bidder_worklet.get());
 
-  EXPECT_FALSE(bid());
-  ASSERT_EQ(1u, bid_errors().size());
-  EXPECT_THAT(bid_errors()[0], StartsWith("https://url.test/:1 "));
-  EXPECT_THAT(bid_errors()[0], HasSubstr("SyntaxError"));
+  std::string error = WaitForDisconnect();
+  EXPECT_THAT(error, StartsWith("https://url.test/:1 "));
+  EXPECT_THAT(error, HasSubstr("SyntaxError"));
 }
 
 // Test parsing of return values.
@@ -899,9 +954,10 @@
     for (size_t i = 0; i < kNumGenerateBidCalls; ++i) {
       size_t bid_value = i + 1;
       bidder_worklet->GenerateBid(
+          CreateBidderWorkletNonSharedParams(),
           /*auction_signals_json=*/base::NumberToString(bid_value),
-          per_buyer_signals_, browser_signal_top_window_origin_,
-          browser_signal_seller_origin_, auction_start_time_,
+          per_buyer_signals_, browser_signal_seller_origin_,
+          CreateBiddingBrowserSignals(), auction_start_time_,
           base::BindLambdaForTesting(
               [&run_loop, &num_generate_bid_calls, bid_value](
                   mojom::BidderWorkletBidPtr bid,
@@ -935,58 +991,21 @@
 }
 
 // Test multiple GenerateBid calls on a single worklet, in parallel, in the case
-// the script fails to load. Do this twice, once before the worklet has
-// encountered a network error, and once after, to make sure both cases work.
-TEST_F(BidderWorkletTest, GenerateBidNetworkErrorParallel) {
+// the script fails to load.
+TEST_F(BidderWorkletTest, GenerateBidParallelLoadFails) {
   auto bidder_worklet = CreateWorklet();
 
-  // For the first loop iteration, call GenerateBid repeatedly and only then
-  // provide the network error. For the second loop iteration, reuse the bidder
-  // worklet from the first iteration, so the Javascript is loaded from the
-  // start.
-  for (bool generate_bid_invoked_before_network_error : {false, true}) {
-    SCOPED_TRACE(generate_bid_invoked_before_network_error);
-
-    base::RunLoop run_loop;
-    const size_t kNumGenerateBidCalls = 10;
-    size_t num_generate_bid_calls = 0;
-    for (size_t i = 0; i < kNumGenerateBidCalls; ++i) {
-      bidder_worklet->GenerateBid(
-          auction_signals_, per_buyer_signals_,
-          browser_signal_top_window_origin_, browser_signal_seller_origin_,
-          auction_start_time_,
-          base::BindLambdaForTesting(
-              [&run_loop, &num_generate_bid_calls](
-                  mojom::BidderWorkletBidPtr bid,
-                  const std::vector<std::string>& errors) {
-                EXPECT_FALSE(bid);
-                EXPECT_EQ(errors, std::vector<std::string>{
-                                      "Failed to load https://url.test/ HTTP "
-                                      "status = 404 Not Found."});
-                ++num_generate_bid_calls;
-                if (num_generate_bid_calls == kNumGenerateBidCalls)
-                  run_loop.Quit();
-              }));
-    }
-
-    // If this is the first loop iteration, wait for all the Mojo calls to
-    // settle, and then provide the network error.
-    if (generate_bid_invoked_before_network_error == false) {
-      // Since the script hasn't loaded yet, or failed to load, no bids should
-      // be generated.
-      task_environment_.RunUntilIdle();
-      EXPECT_FALSE(run_loop.AnyQuitCalled());
-      EXPECT_EQ(0u, num_generate_bid_calls);
-
-      // Script fails to load.
-      url_loader_factory_.AddResponse(interest_group_bidding_url_.spec(),
-                                      CreateBasicGenerateBidScript(),
-                                      net::HTTP_NOT_FOUND);
-    }
-
-    run_loop.Run();
-    EXPECT_EQ(kNumGenerateBidCalls, num_generate_bid_calls);
+  for (size_t i = 0; i < 10; ++i) {
+    GenerateBidExpectingCallbackNotInvoked(bidder_worklet.get());
   }
+
+  // Script fails to load.
+  url_loader_factory_.AddResponse(interest_group_bidding_url_.spec(),
+                                  CreateBasicGenerateBidScript(),
+                                  net::HTTP_NOT_FOUND);
+
+  EXPECT_EQ("Failed to load https://url.test/ HTTP status = 404 Not Found.",
+            WaitForDisconnect());
 }
 
 // Test multiple GenerateBid calls on a single worklet, in parallel, in the case
@@ -1017,13 +1036,14 @@
   size_t num_generate_bid_calls = 0;
   for (size_t i = 0; i < kNumGenerateBidCalls; ++i) {
     size_t bid_value = i + 1;
-    // Use a different origin for each GenerateBid call, since it's the only
-    // GenerateBid parameter that affects the URL of bidding signals requests.
-    url::Origin top_window_origin =
-        url::Origin::Create(GURL(base::StringPrintf("https://%zu.test", i)));
+    // Append a different key for each request, so they request distinct URLs.
+    auto interest_group_fields = CreateBidderWorkletNonSharedParams();
+    interest_group_fields->trusted_bidding_signals_keys->push_back(
+        base::NumberToString(i));
     bidder_worklet->GenerateBid(
-        auction_signals_, per_buyer_signals_, top_window_origin,
-        browser_signal_seller_origin_, auction_start_time_,
+        std::move(interest_group_fields), auction_signals_, per_buyer_signals_,
+        browser_signal_seller_origin_, CreateBiddingBrowserSignals(),
+        auction_start_time_,
         base::BindLambdaForTesting(
             [&run_loop, &num_generate_bid_calls, bid_value](
                 mojom::BidderWorkletBidPtr bid,
@@ -1056,10 +1076,11 @@
 
   // 3) The trusted bidding signals are loaded.
   for (size_t i = 0; i < kNumGenerateBidCalls; ++i) {
-    AddJsonResponse(&url_loader_factory_,
-                    GURL(base::StringPrintf(
-                        "https://signals.test/?hostname=%zu.test&keys=key", i)),
-                    base::StringPrintf(R"({"key":%zu})", i + 1));
+    AddJsonResponse(
+        &url_loader_factory_,
+        GURL(base::StringPrintf(
+            "https://signals.test/?hostname=top.window.test&keys=%zu,key", i)),
+        base::StringPrintf(R"({"key":%zu})", i + 1));
   }
 
   // The worklets can now generate bids.
@@ -1095,13 +1116,14 @@
   size_t num_generate_bid_calls = 0;
   for (size_t i = 0; i < kNumGenerateBidCalls; ++i) {
     size_t bid_value = i + 1;
-    // Use a different origin for each GenerateBid call, since it's the only
-    // GenerateBid parameter that affects the URL of bidding signals requests.
-    url::Origin top_window_origin =
-        url::Origin::Create(GURL(base::StringPrintf("https://%zu.test", i)));
+    // Append a different key for each request, so they request distinct URLs.
+    auto interest_group_fields = CreateBidderWorkletNonSharedParams();
+    interest_group_fields->trusted_bidding_signals_keys->push_back(
+        base::NumberToString(i));
     bidder_worklet->GenerateBid(
-        auction_signals_, per_buyer_signals_, top_window_origin,
-        browser_signal_seller_origin_, auction_start_time_,
+        std::move(interest_group_fields), auction_signals_, per_buyer_signals_,
+        browser_signal_seller_origin_, CreateBiddingBrowserSignals(),
+        auction_start_time_,
         base::BindLambdaForTesting(
             [&run_loop, &num_generate_bid_calls, bid_value](
                 mojom::BidderWorkletBidPtr bid,
@@ -1125,10 +1147,11 @@
 
   // 2) The trusted bidding signals are loaded.
   for (size_t i = 0; i < kNumGenerateBidCalls; ++i) {
-    AddJsonResponse(&url_loader_factory_,
-                    GURL(base::StringPrintf(
-                        "https://signals.test/?hostname=%zu.test&keys=key", i)),
-                    base::StringPrintf(R"({"key":%zu})", i + 1));
+    AddJsonResponse(
+        &url_loader_factory_,
+        GURL(base::StringPrintf(
+            "https://signals.test/?hostname=top.window.test&keys=%zu,key", i)),
+        base::StringPrintf(R"({"key":%zu})", i + 1));
   }
 
   // No callbacks should have been invoked, since the worklet script hasn't
@@ -1179,13 +1202,14 @@
   size_t num_generate_bid_calls = 0;
   for (size_t i = 0; i < kNumGenerateBidCalls; ++i) {
     size_t bid_value = i + 1;
-    // Use a different origin for each GenerateBid call, since it's the only
-    // GenerateBid parameter that affects the URL of bidding signals requests.
-    url::Origin top_window_origin =
-        url::Origin::Create(GURL(base::StringPrintf("https://%zu.test", i)));
+    // Append a different key for each request, so they request distinct URLs.
+    auto interest_group_fields = CreateBidderWorkletNonSharedParams();
+    interest_group_fields->trusted_bidding_signals_keys->push_back(
+        base::NumberToString(i));
     bidder_worklet->GenerateBid(
-        auction_signals_, per_buyer_signals_, top_window_origin,
-        browser_signal_seller_origin_, auction_start_time_,
+        std::move(interest_group_fields), auction_signals_, per_buyer_signals_,
+        browser_signal_seller_origin_, CreateBiddingBrowserSignals(),
+        auction_start_time_,
         base::BindLambdaForTesting(
             [&run_loop, &num_generate_bid_calls, bid_value](
                 mojom::BidderWorkletBidPtr bid,
@@ -1208,10 +1232,11 @@
 
   // 3) The trusted bidding signals are loaded.
   for (size_t i = 0; i < kNumGenerateBidCalls; ++i) {
-    AddJsonResponse(&url_loader_factory_,
-                    GURL(base::StringPrintf(
-                        "https://signals.test/?hostname=%zu.test&keys=key", i)),
-                    base::StringPrintf(R"({"key":%zu})", i + 1));
+    AddJsonResponse(
+        &url_loader_factory_,
+        GURL(base::StringPrintf(
+            "https://signals.test/?hostname=top.window.test&keys=%zu,key", i)),
+        base::StringPrintf(R"({"key":%zu})", i + 1));
   }
 
   // The worklets can now generate bids.
@@ -1309,14 +1334,14 @@
 }
 
 TEST_F(BidderWorkletTest, GenerateBidInterestGroupOwner) {
-  interest_group_owner_ = url::Origin::Create(GURL("https://foo.test/"));
+  interest_group_bidding_url_ = GURL("https://foo.test/bar");
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: interestGroup.owner, bid:1, render:"https://response.test/"})",
       mojom::BidderWorkletBid::New(
           R"("https://foo.test")", 1, GURL("https://response.test/"),
           /*ad_components=*/absl::nullopt, base::TimeDelta()));
 
-  interest_group_owner_ = url::Origin::Create(GURL("https://[::1]:40000/"));
+  interest_group_bidding_url_ = GURL("https://[::1]:40000/");
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: interestGroup.owner, bid:1, render:"https://response.test/"})",
       mojom::BidderWorkletBid::New(
@@ -1343,8 +1368,7 @@
 }
 
 TEST_F(BidderWorkletTest, GenerateBidBrowserSignalTopWindowOrigin) {
-  browser_signal_top_window_origin_ =
-      url::Origin::Create(GURL("https://top.window.test/"));
+  top_window_origin_ = url::Origin::Create(GURL("https://top.window.test/"));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: browserSignals.topWindowHostname, bid:1, render:"https://response.test/"})",
       mojom::BidderWorkletBid::New(
@@ -1466,11 +1490,16 @@
               /*charset=*/absl::nullopt, "Error 404", kAllowFledgeHeader,
               net::HTTP_NOT_FOUND);
 
-  RunGenerateBidWithJavascriptExpectingResult(
-      CreateBasicGenerateBidScript(),
-      /*expected_bid=*/mojom::BidderWorkletBidPtr(),
-      {"Failed to load https://foo.test/helper.wasm "
-       "HTTP status = 404 Not Found."});
+  // The Javascript request receives valid JS.
+  AddJavascriptResponse(&url_loader_factory_, interest_group_bidding_url_,
+                        CreateBasicGenerateBidScript());
+
+  auto bidder_worklet = CreateWorklet();
+  GenerateBidExpectingCallbackNotInvoked(bidder_worklet.get());
+  EXPECT_EQ(
+      "Failed to load https://foo.test/helper.wasm "
+      "HTTP status = 404 Not Found.",
+      WaitForDisconnect());
 }
 
 TEST_F(BidderWorkletTest, GenerateBidWasmFailure) {
@@ -1480,12 +1509,17 @@
               kWasmMimeType,
               /*charset=*/absl::nullopt, CreateBasicGenerateBidScript());
 
-  RunGenerateBidWithJavascriptExpectingResult(
-      CreateBasicGenerateBidScript(),
-      /*expected_bid=*/mojom::BidderWorkletBidPtr(),
-      {"https://foo.test/helper.wasm Uncaught CompileError: "
-       "WasmModuleObject::Compile(): expected magic word 00 61 73 6d, found "
-       "0a 20 20 20 @+0."});
+  // The Javascript request receives valid JS.
+  AddJavascriptResponse(&url_loader_factory_, interest_group_bidding_url_,
+                        CreateBasicGenerateBidScript());
+
+  auto bidder_worklet = CreateWorklet();
+  GenerateBidExpectingCallbackNotInvoked(bidder_worklet.get());
+  EXPECT_EQ(
+      "https://foo.test/helper.wasm Uncaught CompileError: "
+      "WasmModuleObject::Compile(): expected magic word 00 61 73 6d, found "
+      "0a 20 20 20 @+0.",
+      WaitForDisconnect());
 }
 
 TEST_F(BidderWorkletTest, GenerateBidWasm) {
@@ -1521,9 +1555,8 @@
 
   base::RunLoop run_loop;
   bidder_worklet->ReportWin(
-      /*auction_signals_json=*/"0", per_buyer_signals_,
-      browser_signal_top_window_origin_, seller_signals_,
-      browser_signal_render_url_, browser_signal_bid_,
+      interest_group_name_, /*auction_signals_json=*/"0", per_buyer_signals_,
+      seller_signals_, browser_signal_render_url_, browser_signal_bid_,
       browser_signal_seller_origin_,
       base::BindLambdaForTesting(
           [&run_loop](const absl::optional<GURL>& report_url,
@@ -1565,8 +1598,14 @@
     url_loader_factory_.ClearResponses();
 
     mojo::Remote<mojom::BidderWorklet> bidder_worklet = CreateWorklet();
-    load_script_run_loop_ = std::make_unique<base::RunLoop>();
-    GenerateBid(bidder_worklet.get());
+    if (test.expect_success) {
+      // On success, callback should be invoked.
+      load_script_run_loop_ = std::make_unique<base::RunLoop>();
+      GenerateBid(bidder_worklet.get());
+    } else {
+      // On error, the pipe is closed without invoking the callback.
+      GenerateBidExpectingCallbackNotInvoked(bidder_worklet.get());
+    }
 
     for (Event ev : test.events) {
       switch (ev) {
@@ -1595,9 +1634,16 @@
       task_environment_.RunUntilIdle();
     }
 
-    load_script_run_loop_->Run();
-    load_script_run_loop_.reset();
-    EXPECT_EQ(bid_.is_null(), !test.expect_success);
+    if (test.expect_success) {
+      // On success, the callback is invoked.
+      load_script_run_loop_->Run();
+      load_script_run_loop_.reset();
+      EXPECT_TRUE(bid_);
+    } else {
+      // On failure, the pipe is closed with a non-empty error message, without
+      // invoking the callback.
+      EXPECT_FALSE(WaitForDisconnect().empty());
+    }
   }
 }
 
@@ -1808,7 +1854,7 @@
 
   base::WaitableEvent* event_handle = WedgeV8Thread(v8_helper_.get());
   bidder_worklet->ReportWin(
-      auction_signals_, per_buyer_signals_, browser_signal_top_window_origin_,
+      interest_group_name_, auction_signals_, per_buyer_signals_,
       seller_signals_, browser_signal_render_url_, browser_signal_bid_,
       browser_signal_seller_origin_,
       base::BindOnce([](const absl::optional<GURL>& report_url,
@@ -1844,9 +1890,9 @@
     size_t num_report_win_calls = 0;
     for (size_t i = 0; i < kNumReportWinCalls; ++i) {
       bidder_worklet->ReportWin(
+          interest_group_name_,
           /*auction_signals_json=*/base::NumberToString(i), per_buyer_signals_,
-          browser_signal_top_window_origin_, seller_signals_,
-          browser_signal_render_url_, browser_signal_bid_,
+          seller_signals_, browser_signal_render_url_, browser_signal_bid_,
           browser_signal_seller_origin_,
           base::BindLambdaForTesting(
               [&run_loop, &num_report_win_calls, i](
@@ -1876,54 +1922,28 @@
 }
 
 // Test multiple ReportWin calls on a single worklet, in parallel, in the case
-// the worklet script fails to load. Do this twice, once before the worklet
-// script has received an error, and once after, to make sure both cases work.
-TEST_F(BidderWorkletTest, ReportWinNetworkErrorParallel) {
+// the worklet script fails to load.
+TEST_F(BidderWorkletTest, ReportWinParallelLoadFails) {
   auto bidder_worklet = CreateWorklet();
 
-  // For the first loop iteration, call ReportWin repeatedly before providing
-  // the network error script, then provide the network error. For the second
-  // loop iteration, reuse the bidder worklet from the first iteration, so the
-  // network error is present from the start.
-  for (bool report_win_invoked_before_worklet_script_loaded : {false, true}) {
-    SCOPED_TRACE(report_win_invoked_before_worklet_script_loaded);
-
-    base::RunLoop run_loop;
-    const size_t kNumReportWinCalls = 10;
-    size_t num_report_win_calls = 0;
-    for (size_t i = 0; i < kNumReportWinCalls; ++i) {
-      bidder_worklet->ReportWin(
-          /*auction_signals_json=*/base::NumberToString(i), per_buyer_signals_,
-          browser_signal_top_window_origin_, seller_signals_,
-          browser_signal_render_url_, browser_signal_bid_,
-          browser_signal_seller_origin_,
-          base::BindLambdaForTesting(
-              [&run_loop, &num_report_win_calls](
-                  const absl::optional<GURL>& report_url,
-                  const std::vector<std::string>& errors) {
-                EXPECT_FALSE(report_url);
-                EXPECT_EQ(errors, std::vector<std::string>{
-                                      "Failed to load https://url.test/ "
-                                      "HTTP status = 404 Not Found."});
-                ++num_report_win_calls;
-                if (num_report_win_calls == kNumReportWinCalls)
-                  run_loop.Quit();
-              }));
-    }
-
-    // If this is the first loop iteration, wait for all the Mojo calls to
-    // settle, and then provide the Javascript response body.
-    if (report_win_invoked_before_worklet_script_loaded == false) {
-      task_environment_.RunUntilIdle();
-      EXPECT_FALSE(run_loop.AnyQuitCalled());
-      url_loader_factory_.AddResponse(interest_group_bidding_url_.spec(),
-                                      CreateBasicGenerateBidScript(),
-                                      net::HTTP_NOT_FOUND);
-    }
-
-    run_loop.Run();
-    EXPECT_EQ(kNumReportWinCalls, num_report_win_calls);
+  for (size_t i = 0; i < 10; ++i) {
+    bidder_worklet->ReportWin(
+        interest_group_name_,
+        /*auction_signals_json=*/base::NumberToString(i), per_buyer_signals_,
+        seller_signals_, browser_signal_render_url_, browser_signal_bid_,
+        browser_signal_seller_origin_,
+        base::BindOnce([](const absl::optional<GURL>& report_url,
+                          const std::vector<std::string>& errors) {
+          ADD_FAILURE() << "Callback should not be invoked.";
+        }));
   }
+
+  url_loader_factory_.AddResponse(interest_group_bidding_url_.spec(),
+                                  CreateBasicGenerateBidScript(),
+                                  net::HTTP_NOT_FOUND);
+
+  EXPECT_EQ("Failed to load https://url.test/ HTTP status = 404 Not Found.",
+            WaitForDisconnect());
 }
 
 // Make sure Date() is not available when running reportWin().
@@ -1992,7 +2012,7 @@
 }
 
 TEST_F(BidderWorkletTest, ReportWinInterestGroupOwner) {
-  interest_group_owner_ = url::Origin::Create(GURL("https://foo.test/"));
+  interest_group_bidding_url_ = GURL("https://foo.test/bar");
   // Add an extra ".test" because origin's shouldn't have a terminal slash,
   // unlike URLs. If an extra slash were added to the origin, this would end up
   // as https://foo.test/.test, instead.
@@ -2000,15 +2020,14 @@
       R"(sendReportTo(browserSignals.interestGroupOwner + ".test"))",
       GURL("https://foo.test.test/"));
 
-  interest_group_owner_ = url::Origin::Create(GURL("https://[::1]:40000/"));
+  interest_group_bidding_url_ = GURL("https://[::1]:40000/");
   RunReportWinWithFunctionBodyExpectingResult(
       R"(sendReportTo(browserSignals.interestGroupOwner))",
       GURL("https://[::1]:40000/"));
 }
 
 TEST_F(BidderWorkletTest, ReportWinBrowserSignalTopWindowOrigin) {
-  browser_signal_top_window_origin_ =
-      url::Origin::Create(GURL("https://top.window.test/"));
+  top_window_origin_ = url::Origin::Create(GURL("https://top.window.test/"));
   RunReportWinWithFunctionBodyExpectingResult(
       R"(sendReportTo("https://" + browserSignals.topWindowHostname))",
       GURL("https://top.window.test/"));
@@ -2078,7 +2097,7 @@
   for (int i = 0; i < 3; ++i) {
     base::RunLoop run_loop;
     bidder_worklet->ReportWin(
-        auction_signals_, per_buyer_signals_, browser_signal_top_window_origin_,
+        interest_group_name_, auction_signals_, per_buyer_signals_,
         seller_signals_, browser_signal_render_url_, browser_signal_bid_,
         browser_signal_seller_origin_,
         base::BindLambdaForTesting(
@@ -2277,7 +2296,7 @@
   auto worklet =
       CreateWorklet(interest_group_bidding_url_,
                     /*pause_for_debugger_on_start=*/true, &worklet_impl);
-  GenerateBid(worklet.get());
+  GenerateBidExpectingCallbackNotInvoked(worklet.get());
   int id = worklet_impl->context_group_id_for_testing();
   TestChannel* channel = inspector_support.ConnectDebuggerSession(id);
 
@@ -2288,14 +2307,13 @@
       R"({"id":2,"method":"Debugger.enable","params":{}})");
 
   // Unpause execution.
-  load_script_run_loop_ = std::make_unique<base::RunLoop>();
   channel->RunCommandAndWaitForResult(
       3, "Runtime.runIfWaitingForDebugger",
       R"({"id":3,"method":"Runtime.runIfWaitingForDebugger","params":{}})");
-  load_script_run_loop_->Run();
-  load_script_run_loop_.reset();
 
-  EXPECT_FALSE(bid_);
+  // Worklet should disconnect with an error message.
+  EXPECT_FALSE(WaitForDisconnect().empty());
+
   // Should have gotten a parse error notification.
   TestChannel::Event parse_error =
       channel->WaitForMethodNotification("Debugger.scriptFailedToParse");
diff --git a/content/services/auction_worklet/public/mojom/auction_worklet_service.mojom b/content/services/auction_worklet/public/mojom/auction_worklet_service.mojom
index ebd268a..59ab1de8 100644
--- a/content/services/auction_worklet/public/mojom/auction_worklet_service.mojom
+++ b/content/services/auction_worklet/public/mojom/auction_worklet_service.mojom
@@ -24,16 +24,24 @@
 [ServiceSandbox=sandbox.mojom.Sandbox.kServiceWithJit]
 interface AuctionWorkletService {
   // Attempts to load Javascript at the specified URL and create a BidderWorklet
-  // from the response body. There is no return value - any error loading the
-  // worklet script may only be learned from the BidderWorklet API itself, when
-  // trying to run a script. This reduces roundtrips, and allows fetching
-  // per-auction bidding signals URL in parallel with fetching the bidding
-  // worklet script. It also makes the API more robust, not having any
-  // invocation ordering requirements.
+  // from the response body. A single BidderWorklet object can be used for
+  // multiple different InterestGroups, as long as they share the `bidding_url`
+  // `wasm_helper_url`, and `trusted_scoring_signals_url` that were passed in
+  // when creating the BidderWorklet. A single BidderWorklet can be used in
+  // multiple auctions as long as they share a `top_window_origin`. In practice,
+  // the DevTools hooks restrict sharing to auctions within a single
+  // RenderFrame.
+  //
+  // All methods may be invoked immediately upon creation. If there's a pending
+  // load of the script, callbacks will be delayed until it completes.
+  //
+  // On load error, the worklet will close its pipe with a reason string. The
+  // reason string, and worklet errors messages more generally, are considered
+  // privileged and should not be passed to renderer processes.
   //
   // Arguments:
   // `bidder_worklet` The pipe to communicate with the BidderWorklet. Closing
-  // the pipe will abort any in-progress loads and destroy the worklet.
+  //  the pipe will abort any in-progress loads and destroy the worklet.
   //
   // `pause_for_debugger_on_start` If this is true, the worklet should not
   //  commence any work until it gets a Runtime.runIfWaitingForDebugger debugger
@@ -44,15 +52,24 @@
   //  restricted to exactly those URLs (keeping in mind query parameter usage
   //  for trusted bidding signals and the allowed coalescing).
   //
-  // `bidding_interest_group` Definition of the interest group to fetch and
-  //  execute the script of for an ad auction (initially added by client JS in
-  //  the renderer, but managed by the browser's interest group store), as well
-  //  as some bidding history collected by the interest group store.
+  // `script_source_url` The URL of the seller worklet script.
+  //
+  // `wasm_helper_url` The URL of the wasm helper to load.
+  //
+  // `trusted_bidding_signals_url` The trusted bidding signals URL to fetch
+  //  for any any `trusted_bidding_signals_keys` provided to the BidderWorklet's
+  //  GenerateBid() method.
+  //
+  // `top_window_origin` The origin of the top-level window running the
+  //  auction(s) the BidderWorklet will be used in.
   LoadBidderWorklet(
       pending_receiver<BidderWorklet> bidder_worklet,
       bool pause_for_debugger_on_start,
       pending_remote<network.mojom.URLLoaderFactory> url_loader_factory,
-      BiddingInterestGroup bidding_interest_group);
+      url.mojom.Url script_source_url,
+      url.mojom.Url? wasm_helper_url,
+      url.mojom.Url? trusted_bidding_signals_url,
+      url.mojom.Origin top_window_origin);
 
   // Attempts to load Javascript at the specified URL and loads a SellerWorklet.
   // While a single SellerWorklet object can be used in auctions with different
@@ -60,7 +77,9 @@
   // object must share a `script_source_url` and `trusted_scoring_signals_url`,
   // which match the ones passed in when creating the SellerWorklet. All
   // auctions sharing a SellerWorklet must also have the same
-  // `top_window_origin`.
+  // `top_window_origin`. In practice, the DevTools hooks and URLLoaderFactory
+  // hooks (which make script fetches act as if they were made from the parent
+  // frame) restrict sharing to auctions within a single RenderFrame.
   //
   // Arguments:
   // `seller_worklet` The pipe to communicate with the SellerWorklet. Closing
diff --git a/content/services/auction_worklet/public/mojom/bidder_worklet.mojom b/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
index 180cd3e0..3ff08d6 100644
--- a/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
+++ b/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
@@ -27,8 +27,25 @@
   string ad_json;
 };
 
-// Browser signals passed to the BidderWorklet's generateBid() method. Some
-// fields are cached to pass to the reportWin() method as well.
+// Subset of blink.mojom.InterestGroup that is used by GenerateBid() and can
+// vary between InterestGroups that can use the same BidderWorklet (so it's
+// "not shared" between InterestGroups that share the same BidderWorklet).
+// See AuctionWorkletService::LoadBidderWorklet for parameters that must be the
+// same between InterestGroups.
+//
+// See blink.mojom.InterestGroup for documentation on individual fields.
+struct BidderWorkletNonSharedParams {
+  string name;
+  array<string>? trusted_bidding_signals_keys;
+  string? user_bidding_signals;
+  array<blink.mojom.InterestGroupAd>? ads;
+  array<blink.mojom.InterestGroupAd>? ad_components;
+};
+
+// Browser signals passed to the BidderWorklet's generateBid() method that are
+// stored on disk and updated by the browser, as opposed to coming from the
+// frame running the auction, or from the definition of the InterestGroup taking
+// part in an auction.
 struct BiddingBrowserSignals {
   // How many times this interest group has been joined in the period history
   // is maintained.
@@ -41,11 +58,6 @@
   array<PreviousWin> prev_wins;
 };
 
-struct BiddingInterestGroup {
-  blink.mojom.InterestGroup group;  // User JS specified, stored by browser.
-  BiddingBrowserSignals signals;  // Collected by browser.
-};
-
 // The results of running a FLEDGE generateBid() script.
 struct BidderWorkletBid {
   // JSON string to be passed to the scoring function.
@@ -86,6 +98,10 @@
   // needed.
   //
   // Arguments:
+  // `bidder_worklet_non_shared_params` values that can vary in the
+  //  InterestGroup definitions of the InterestGroups that can share this
+  //  BidderWorklet.
+  //
   // `auction_signals_json` The JSON representation of the auction signals for
   //  the auction, specified by the publisher page and provided to bidder
   //  worklets competing in an auction.
@@ -95,12 +111,11 @@
   //  publisher page and provided to all interest groups with the same owner
   //  as the one specified `interest_group`.
   //
-  // `browser_signal_top_window_origin` The origin of the top-level frame
-  //  where the auction is running.
-  //
   // `browser_signal_seller_origin` The origin of the seller script running
   //  the auction. Typically a valid, non-opaque HTTPS origin.
   //
+  // `bidding_browser_signals` See BiddingBrowserSignals.
+  //
   // `auction_start_time` The time the auction started, used to ensure the
   //  last win times provided to all worklets are relative to the same time.
   //
@@ -112,18 +127,23 @@
   //  sensitive for the renderer to see. There may be errors even when a bid
   //  is offered, and there may be no errors when there's no bid. Includes
   //  errors from failing to load the worklet script.
-  GenerateBid(string? auction_signals_json,
-              string? per_buyer_signals_json,
-              url.mojom.Origin browser_signal_top_window_origin,
-              url.mojom.Origin browser_signal_seller_origin,
-              mojo_base.mojom.Time auction_start_time) => (
-                  BidderWorkletBid? bid,
-                  array<string> errors);
+  GenerateBid(
+      BidderWorkletNonSharedParams bidder_worklet_non_shared_params,
+      string? auction_signals_json,
+      string? per_buyer_signals_json,
+      url.mojom.Origin browser_signal_seller_origin,
+      BiddingBrowserSignals bidding_browser_signals,
+      mojo_base.mojom.Time auction_start_time) => (
+          BidderWorkletBid? bid,
+          array<string> errors);
 
   // Calls the worklet's reportWin() method. Waits for the worklet script to
   // be loaded first, if needed.
   //
   // Arguments:
+  // `interest_group_name` Name of the winning interest group that ReportWin()
+  //  is being invoked on behalf of.
+  //
   // `auction_signals_json` The JSON representation of the auction signals for
   //  the auction, if specified by the publisher page and provided to bidder
   //  worklets competing in an auction. Null if not provided by the  publisher
@@ -135,9 +155,6 @@
   //  as the one specified `interest_group`. Null if not provided by the
   //  publisher page. Null will be passed to the worklet in that case.
   //
-  // `browser_signal_top_window_origin` The origin of the top-level frame
-  //  where the auction is running.
-  //
   // `seller_signals_json` is a JSON representation of the object returned by
   //  the seller worklet's ReportResult method.
   //
@@ -158,9 +175,9 @@
   //  the renderer to see. There may be errors even when a `report_url` is
   //  provided, and there may be no errors when there's no `report_url`.
   //  Includes errors from failing to load the worklet script.
-  ReportWin(string? auction_signals_json,
+  ReportWin(string interest_group_name,
+            string? auction_signals_json,
             string? per_buyer_signals_json,
-            url.mojom.Origin browser_signal_top_window_origin,
             string seller_signals_json,
             url.mojom.Url browser_signal_render_url,
             double browser_signal_bid,
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 397db537..2634826 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1867,6 +1867,8 @@
   }
 
   if (is_fuchsia) {
+    use_test_server = true
+
     additional_manifest_fragments = [
       "//build/config/fuchsia/test/audio_capabilities.test-cmx",
       "//build/config/fuchsia/test/font_capabilities.test-cmx",
diff --git a/content/test/data/accessibility/aria/aria-button-expected-android-external.txt b/content/test/data/accessibility/aria/aria-button-expected-android-external.txt
index be08f6c0..ff74cb4 100644
--- a/content/test/data/accessibility/aria/aria-button-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-button-expected-android-external.txt
@@ -1,11 +1,11 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++Button text:"Button1" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
-++ToggleButton text:"Button2, On" checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
-++ToggleButton text:"Button3, Off" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Button2" stateDescription:"On" checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Button3" stateDescription:"Off" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
 ++View text:"Button4" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="popUpButton", roleDescription="menu pop up button"]
 ++Button text:"Button5" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
 ++Button text:"Complex button " clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
-++ToggleButton text:"Complex toggle button , On" checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Complex toggle button " stateDescription:"On" checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
 ++++TextView text:"Complex toggle button " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
 ++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
 ++View text:"Complex pop up button " canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="popUpButton", roleDescription="menu pop up button"]
diff --git a/content/test/data/accessibility/aria/aria-checkbox-expected-android-external.txt b/content/test/data/accessibility/aria/aria-checkbox-expected-android-external.txt
index 7f3ec79..f50d173 100644
--- a/content/test/data/accessibility/aria/aria-checkbox-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-checkbox-expected-android-external.txt
@@ -1,7 +1,7 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++CheckBox text:"CheckBox1" checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
 ++CheckBox text:"CheckBox2" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
-++CheckBox text:"CheckBox3, Partially Checked" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
+++CheckBox text:"CheckBox3" stateDescription:"Partially Checked" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
 ++CheckBox text:"CheckBox4" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
 ++CheckBox text:"Complex checkbox" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
 ++++TextView text:"Complex " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
diff --git a/content/test/data/accessibility/aria/aria-checked-expected-android-external.txt b/content/test/data/accessibility/aria/aria-checked-expected-android-external.txt
index e93056b..5dcd28a1 100644
--- a/content/test/data/accessibility/aria/aria-checked-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-checked-expected-android-external.txt
@@ -1,4 +1,4 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++CheckBox checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
 ++CheckBox checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
-++CheckBox text:", Partially Checked" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
\ No newline at end of file
+++CheckBox stateDescription:"Partially Checked" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-combobox-expected-android-external.txt b/content/test/data/accessibility/aria/aria-combobox-expected-android-external.txt
index 96bec0f..5887fbd 100644
--- a/content/test/data/accessibility/aria/aria-combobox-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-combobox-expected-android-external.txt
@@ -1,6 +1,6 @@
 WebView focusable scrollable actions:[FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++TextView text:"State" viewIdResName:"state_label" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
 ++EditText canOpenPopUp clickable editable disabled focusable focused inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[CLEAR_FOCUS, CLICK, AX_FOCUS, COLLAPSE] bundle:[chromeRole="textFieldWithComboBox", clickableScore="300", hint="State"]
-++ListView text:", 2 items" viewIdResName:"state_list" clickable CollectionInfo:[rows=2, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
-++++View text:"Alabama, in list, item 1 of 2" viewIdResName:"state1" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Alaska, in list, item 2 of 2" viewIdResName:"state2" clickable focusable selected CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
+++ListView viewIdResName:"state_list" stateDescription:"2 items" clickable CollectionInfo:[rows=2, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"Alabama" viewIdResName:"state1" stateDescription:"in list, item 1 of 2" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Alaska" viewIdResName:"state2" stateDescription:"in list, item 2 of 2" clickable focusable selected CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-android-external.txt b/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-android-external.txt
index d1229b8..778c3c96 100644
--- a/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-android-external.txt
@@ -1,7 +1,7 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++TextView text:"Choose a fruit, with text content" viewIdResName:"combo1-label" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
 ++Spinner text:"Choose a fruit, with text content" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="comboBoxMenuButton"]
-++ListView text:", 3 items" viewIdResName:"listbox1" clickable CollectionInfo:[rows=3, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
-++++View text:"Apple, in list, item 1 of 3" viewIdResName:"combo1-0" clickable focusable selected CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Banana, in list, item 2 of 3" viewIdResName:"combo1-1" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Cherry, in list, item 3 of 3" viewIdResName:"combo1-2" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
+++ListView viewIdResName:"listbox1" stateDescription:"3 items" clickable CollectionInfo:[rows=3, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"Apple" viewIdResName:"combo1-0" stateDescription:"in list, item 1 of 3" clickable focusable selected CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Banana" viewIdResName:"combo1-1" stateDescription:"in list, item 2 of 3" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Cherry" viewIdResName:"combo1-2" stateDescription:"in list, item 3 of 3" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-current-expected-android-external.txt b/content/test/data/accessibility/aria/aria-current-expected-android-external.txt
index 7c0260e..3339b26 100644
--- a/content/test/data/accessibility/aria/aria-current-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-current-expected-android-external.txt
@@ -5,7 +5,7 @@
 ++View text:"null" contentDescription:"Section two" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-current.html#section2"]
 ++++TextView text:"Section two" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
 ++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
-++View text:"null" contentDescription:"Section three, current location" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-current.html#section3"]
+++View text:"null" contentDescription:"Section three" stateDescription:"current location" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-current.html#section3"]
 ++++TextView text:"Section three" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
 ++View text:"\n" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="lineBreak"]
 ++TextView text:"Section one heading" viewIdResName:"section1" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 1"]
@@ -15,14 +15,14 @@
 ++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
 ++++TextView text:"Span 1" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
 ++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
-++++TextView text:"Span 2, current item" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++++TextView text:"Span 2" stateDescription:"current item" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
 ++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
 ++++TextView text:"Span 3" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
-++TextView text:"aria-current is true, current item" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++TextView text:"aria-current is true" stateDescription:"current item" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
 ++TextView text:"aria-current is false" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
-++TextView text:"aria-current is time, current time" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
-++TextView text:"aria-current is date, current date" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
-++TextView text:"aria-current is location, current location" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
-++TextView text:"aria-current is step, current step" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
-++TextView text:"aria-current is page, current page" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++TextView text:"aria-current is time" stateDescription:"current time" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++TextView text:"aria-current is date" stateDescription:"current date" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++TextView text:"aria-current is location" stateDescription:"current location" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++TextView text:"aria-current is step" stateDescription:"current step" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++TextView text:"aria-current is page" stateDescription:"current page" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
 ++TextView text:"aria-current is empty string" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-gridcell-expected-android-external.txt b/content/test/data/accessibility/aria/aria-gridcell-expected-android-external.txt
index 85816008..ac3172f7 100644
--- a/content/test/data/accessibility/aria/aria-gridcell-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-gridcell-expected-android-external.txt
@@ -1,5 +1,5 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
-++GridView text:", multiselectable, none selected." CollectionInfo:[rows=2, cols=2] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++GridView stateDescription:"multiselectable, none selected." CollectionInfo:[rows=2, cols=2] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
 ++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
 ++++++View text:"Browser" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
 ++++++View text:"Rendering Engine" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
diff --git a/content/test/data/accessibility/aria/aria-illegal-val-expected-android-external.txt b/content/test/data/accessibility/aria/aria-illegal-val-expected-android-external.txt
index 0f3453a..f788cab 100644
--- a/content/test/data/accessibility/aria/aria-illegal-val-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-illegal-val-expected-android-external.txt
@@ -3,7 +3,7 @@
 ++EditText clickable editable textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hint="Autocomplete illegal"]
 ++View text:"Busy illegal" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="log", roleDescription="log"]
 ++View text:"Checked illegal" checkable checked clickable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
-++View text:"Current illegal, current item" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
+++View text:"Current illegal" stateDescription:"current item" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
 ++View text:"Disabled illegal" disabled actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
 ++View text:"Dropeffect illegal" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
 ++View text:"Expanded illegal" clickable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COLLAPSE] bundle:[chromeRole="treeItem", roleDescription="tree item"]
@@ -13,13 +13,13 @@
 ++View text:"Live illegal" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="log", roleDescription="log"]
 ++Dialog text:"Modal illegal" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="dialog", roleDescription="dialog"]
 ++EditText clickable editable multiLine textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hint="Multiline illegal"]
-++GridView text:"Multiselectable illegal, multiselectable, none selected." CollectionInfo:[rows=0, cols=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="grid", roleDescription="table"]
+++GridView text:"Multiselectable illegal" stateDescription:"multiselectable, none selected." CollectionInfo:[rows=0, cols=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="grid", roleDescription="table"]
 ++SeekBar text:"50, Orientation illegal" clickable RangeInfo:[current=50.0, min=0.0, max=100.0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="slider", roleDescription="slider"]
-++ToggleButton text:"Pressed illegal, On" checkable checked clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Pressed illegal" stateDescription:"On" checkable checked clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
 ++EditText clickable editable disabled textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS] bundle:[chromeRole="textField", hint="Readonly illegal"]
 ++View text:"Relevant illegal" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="log", roleDescription="log"]
 ++EditText clickable editable textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hint="Required illegal"]
-++View text:", multiselectable, 1 of 1 selected." CollectionInfo:[hierarchical, rows=1, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="tree", roleDescription="tree"]
+++View stateDescription:"multiselectable, 1 of 1 selected." CollectionInfo:[hierarchical, rows=1, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="tree", roleDescription="tree"]
 ++++View text:"Selected illegal" clickable selected CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
 ++GridView CollectionInfo:[rows=0, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
 ++++View text:"Sort illegal" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-listbox-aria-selected-expected-android-external.txt b/content/test/data/accessibility/aria/aria-listbox-aria-selected-expected-android-external.txt
index ecd1ff9..f9e43f6 100644
--- a/content/test/data/accessibility/aria/aria-listbox-aria-selected-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-listbox-aria-selected-expected-android-external.txt
@@ -1,7 +1,7 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
-++ListView text:", multiselectable, 2 of 5 selected." clickable CollectionInfo:[rows=5, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"]
-++++View text:"Item 1, in list, item 1 of 5" viewIdResName:"it1" focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
-++++View text:"Item 2, in list, item 2 of 5" viewIdResName:"it2" focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
-++++View text:"Item 3, in list, item 3 of 5" viewIdResName:"it3" focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
-++++View text:"Item 4, in list, item 4 of 5" viewIdResName:"it4" focusable selected CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
-++++View text:"Item 5, in list, item 5 of 5" viewIdResName:"it5" focusable selected CollectionItemInfo:[rowIndex=4, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
\ No newline at end of file
+++ListView stateDescription:"multiselectable, 2 of 5 selected." clickable CollectionInfo:[rows=5, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"]
+++++View text:"Item 1" viewIdResName:"it1" stateDescription:"in list, item 1 of 5" focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++View text:"Item 2" viewIdResName:"it2" stateDescription:"in list, item 2 of 5" focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++View text:"Item 3" viewIdResName:"it3" stateDescription:"in list, item 3 of 5" focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++View text:"Item 4" viewIdResName:"it4" stateDescription:"in list, item 4 of 5" focusable selected CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++View text:"Item 5" viewIdResName:"it5" stateDescription:"in list, item 5 of 5" focusable selected CollectionItemInfo:[rowIndex=4, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-listbox-disabled-expected-android-external.txt b/content/test/data/accessibility/aria/aria-listbox-disabled-expected-android-external.txt
index 0c7f460..e26de34 100644
--- a/content/test/data/accessibility/aria/aria-listbox-disabled-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-listbox-disabled-expected-android-external.txt
@@ -1,8 +1,8 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++TextView text:"Start of test: striped should have selected state" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 1"]
-++ListView text:", 4 items" clickable disabled CollectionInfo:[rows=4, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"]
-++++View text:"Orange, in list, item 1 of 4" disabled focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
-++++View text:"Striped, in list, item 2 of 4" disabled focusable selected CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
-++++View text:"Calico, in list, item 3 of 4" disabled focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
-++++View text:"Black, in list, item 4 of 4" disabled focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++ListView stateDescription:"4 items" clickable disabled CollectionInfo:[rows=4, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"]
+++++View text:"Orange" stateDescription:"in list, item 1 of 4" disabled focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++View text:"Striped" stateDescription:"in list, item 2 of 4" disabled focusable selected CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++View text:"Calico" stateDescription:"in list, item 3 of 4" disabled focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++View text:"Black" stateDescription:"in list, item 4 of 4" disabled focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
 ++TextView text:"End of test" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-listbox-expected-android-external.txt b/content/test/data/accessibility/aria/aria-listbox-expected-android-external.txt
index ce971b0..ae3d4440 100644
--- a/content/test/data/accessibility/aria/aria-listbox-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-listbox-expected-android-external.txt
@@ -1,7 +1,7 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
-++ListView text:", 4 items" clickable CollectionInfo:[rows=4, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
-++++View text:"Item 1, in list, item 1 of 4" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Item 2, in list, item 2 of 4" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++ListView stateDescription:"4 items" clickable CollectionInfo:[rows=4, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"Item 1" stateDescription:"in list, item 1 of 4" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 2" stateDescription:"in list, item 2 of 4" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
 ++++View actions:[AX_FOCUS] bundle:[chromeRole="splitter", clickableScore="100", roleDescription="splitter"]
-++++View text:"Second group item 1, in list, item 3 of 4" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Second group item 2, in list, item 4 of 4" clickable focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
+++++View text:"Second group item 1" stateDescription:"in list, item 3 of 4" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Second group item 2" stateDescription:"in list, item 4 of 4" clickable focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-menuitemcheckbox-expected-android-external.txt b/content/test/data/accessibility/aria/aria-menuitemcheckbox-expected-android-external.txt
index 9e19ead..1b51862 100644
--- a/content/test/data/accessibility/aria/aria-menuitemcheckbox-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-menuitemcheckbox-expected-android-external.txt
@@ -2,4 +2,4 @@
 ++View actions:[AX_FOCUS] bundle:[chromeRole="menu", roleDescription="menu"]
 ++++MenuItem text:"Menu item 1" checkable clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemCheckBox", roleDescription="checkbox"]
 ++++MenuItem text:"Menu item 2" checkable checked clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemCheckBox", roleDescription="checkbox"]
-++++MenuItem text:"Menu item 3, Partially Checked" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemCheckBox", roleDescription="checkbox"]
\ No newline at end of file
+++++MenuItem text:"Menu item 3" stateDescription:"Partially Checked" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemCheckBox", roleDescription="checkbox"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-menuitemradio-expected-android-external.txt b/content/test/data/accessibility/aria/aria-menuitemradio-expected-android-external.txt
index d7501dd4..8b166e9 100644
--- a/content/test/data/accessibility/aria/aria-menuitemradio-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-menuitemradio-expected-android-external.txt
@@ -2,4 +2,4 @@
 ++View actions:[AX_FOCUS] bundle:[chromeRole="menu", roleDescription="menu"]
 ++++MenuItem text:"Menu item 1" checkable clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemRadio", roleDescription="radio button"]
 ++++MenuItem text:"Menu item 2" checkable checked clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemRadio", roleDescription="radio button"]
-++++MenuItem text:"Menu item 3, Partially Checked" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemRadio", roleDescription="radio button"]
\ No newline at end of file
+++++MenuItem text:"Menu item 3" stateDescription:"Partially Checked" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemRadio", roleDescription="radio button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-multiselectable-expected-android-external.txt b/content/test/data/accessibility/aria/aria-multiselectable-expected-android-external.txt
index cae65089..604241d 100644
--- a/content/test/data/accessibility/aria/aria-multiselectable-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-multiselectable-expected-android-external.txt
@@ -1,11 +1,11 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
-++ListView text:"My Listbox, multiselectable, none selected. 4 items" clickable focusable CollectionInfo:[rows=4, cols=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
-++++View text:"Example 1, in list, item 1 of 4" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Example 2, in list, item 2 of 4" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Example 3, in list, item 3 of 4" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Example 4, in list, item 4 of 4" clickable focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++ListView text:"My Listbox, multiselectable, 2 of 4 selected." clickable focusable CollectionInfo:[rows=4, cols=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
-++++View text:"Example 1, in list, item 1 of 4" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Example 2, in list, item 2 of 4" clickable focusable selected CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Example 3, in list, item 3 of 4" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Example 4, in list, item 4 of 4" clickable focusable selected CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
+++ListView text:"My Listbox" stateDescription:"multiselectable, none selected. 4 items" clickable focusable CollectionInfo:[rows=4, cols=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"Example 1" stateDescription:"in list, item 1 of 4" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Example 2" stateDescription:"in list, item 2 of 4" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Example 3" stateDescription:"in list, item 3 of 4" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Example 4" stateDescription:"in list, item 4 of 4" clickable focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++ListView text:"My Listbox" stateDescription:"multiselectable, 2 of 4 selected." clickable focusable CollectionInfo:[rows=4, cols=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"Example 1" stateDescription:"in list, item 1 of 4" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Example 2" stateDescription:"in list, item 2 of 4" clickable focusable selected CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Example 3" stateDescription:"in list, item 3 of 4" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Example 4" stateDescription:"in list, item 4 of 4" clickable focusable selected CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-option-complex-children-expected-android-external.txt b/content/test/data/accessibility/aria/aria-option-complex-children-expected-android-external.txt
index ebb65f1..24434a81 100644
--- a/content/test/data/accessibility/aria/aria-option-complex-children-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-option-complex-children-expected-android-external.txt
@@ -1,10 +1,10 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
-++ListView text:", 2 items" clickable CollectionInfo:[rows=2, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"]
-++++View text:"label-WAI-ARIA 1.1, in list, item 1 of 2" focusable selected CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++ListView stateDescription:"2 items" clickable CollectionInfo:[rows=2, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"]
+++++View text:"label-WAI-ARIA 1.1" stateDescription:"in list, item 1 of 2" focusable selected CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
 ++++++View text:"null" contentDescription:"href-WAI-ARIA 1.1" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", hint="title-WAI-ARIA 1.1", roleDescription="link", targetUrl="https://www.w3.org/TR/wai-aria-practices-1.1/"]
 ++++++++TextView text:"href-WAI-ARIA 1.1" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
 ++++++TextView text:"title-https://www.w3.org/TR/wai-aria-practices-1.1/" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
 ++++++Button text:"Close" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
-++++View text:"1234567, in list, item 2 of 2" focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++View text:"1234567" stateDescription:"in list, item 2 of 2" focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
 ++++++TextView text:"href-WAI-ARIA 1.1 " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
 ++++++Button text:"Close" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-option-expected-android-external.txt b/content/test/data/accessibility/aria/aria-option-expected-android-external.txt
index b334b0e..ee38387 100644
--- a/content/test/data/accessibility/aria/aria-option-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-option-expected-android-external.txt
@@ -1,7 +1,7 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
-++ListView text:", 5 items" clickable CollectionInfo:[rows=5, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
-++++View text:"option 1, in list, item 1 of 5" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"label 2, in list, item 2 of 5" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"caterpillar, in list, item 3 of 5" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"catfish, in list, item 4 of 5" clickable focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"medusa, in list, item 5 of 5" clickable focusable CollectionItemInfo:[rowIndex=4, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
+++ListView stateDescription:"5 items" clickable CollectionInfo:[rows=5, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"option 1" stateDescription:"in list, item 1 of 5" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"label 2" stateDescription:"in list, item 2 of 5" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"caterpillar" stateDescription:"in list, item 3 of 5" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"catfish" stateDescription:"in list, item 4 of 5" clickable focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"medusa" stateDescription:"in list, item 5 of 5" clickable focusable CollectionItemInfo:[rowIndex=4, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-pressed-expected-android-external.txt b/content/test/data/accessibility/aria/aria-pressed-expected-android-external.txt
index 2479c55b..c3f49aea 100644
--- a/content/test/data/accessibility/aria/aria-pressed-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-pressed-expected-android-external.txt
@@ -1,5 +1,5 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++Button text:"Regular button" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
-++ToggleButton text:"Toggle button unpressed, Off" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
-++ToggleButton text:"Toggle button pressed, On" checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
-++ToggleButton text:"Toggle button mixed, Partially Checked" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
\ No newline at end of file
+++ToggleButton text:"Toggle button unpressed" stateDescription:"Off" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Toggle button pressed" stateDescription:"On" checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Toggle button mixed" stateDescription:"Partially Checked" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-region-expected-android-external.txt b/content/test/data/accessibility/aria/aria-region-expected-android-external.txt
index cbf1880..2c083bc 100644
--- a/content/test/data/accessibility/aria/aria-region-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-region-expected-android-external.txt
@@ -5,4 +5,4 @@
 ++View text:"Named ARIA region#2 gets the region role." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="region", roleDescription="region"]
 ++++TextView text:"Named ARIA region#2 gets the region role." viewIdResName:"region-name" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
 ++View text:"Named region" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="region", roleDescription="region"]
-++View text:"An aria-rolescription works on a nameless role=region." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="region", roleDescription="region"]
\ No newline at end of file
+++View text:"An aria-rolescription works on a nameless role=region." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="region", roleDescription="Regioneque region"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-region-expected-android.txt b/content/test/data/accessibility/aria/aria-region-expected-android.txt
index 74b5fde..b9fe622 100644
--- a/content/test/data/accessibility/aria/aria-region-expected-android.txt
+++ b/content/test/data/accessibility/aria/aria-region-expected-android.txt
@@ -5,4 +5,4 @@
 ++android.view.View role_description='region' name='Named ARIA region#2 gets the region role.'
 ++++android.widget.TextView name='Named ARIA region#2 gets the region role.'
 ++android.view.View role_description='region' name='Named region'
-++android.view.View role_description='region' name='An aria-rolescription works on a nameless role=region.'
\ No newline at end of file
+++android.view.View role_description='Regioneque region' name='An aria-rolescription works on a nameless role=region.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-roledescription-expected-android-external.txt b/content/test/data/accessibility/aria/aria-roledescription-expected-android-external.txt
index fc1ef7e9..8db1948 100644
--- a/content/test/data/accessibility/aria/aria-roledescription-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-roledescription-expected-android-external.txt
@@ -1,7 +1,7 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++Button text:"Native button" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
 ++Button text:"ARIA button" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
-++Button text:"Clicky button" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
+++Button text:"Clicky button" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="Clicky"]
 ++TextView text:"foo" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
 ++TextView text:"bar" focusable actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
-++TextView text:"baz" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
\ No newline at end of file
+++TextView text:"baz" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph", roleDescription="Texty"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-selected-expected-android-external.txt b/content/test/data/accessibility/aria/aria-selected-expected-android-external.txt
index 904a40e..f20cd5e 100644
--- a/content/test/data/accessibility/aria/aria-selected-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-selected-expected-android-external.txt
@@ -1,4 +1,4 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
-++ListView text:", 2 items" clickable CollectionInfo:[rows=2, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
-++++View text:"1, in list, item 1 of 2" clickable focusable selected CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"2, in list, item 2 of 2" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
+++ListView stateDescription:"2 items" clickable CollectionInfo:[rows=2, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"1" stateDescription:"in list, item 1 of 2" clickable focusable selected CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"2" stateDescription:"in list, item 2 of 2" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-setsize-expected-android-external.txt b/content/test/data/accessibility/aria/aria-setsize-expected-android-external.txt
index b8159d8..a1909b6 100644
--- a/content/test/data/accessibility/aria/aria-setsize-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-setsize-expected-android-external.txt
@@ -1,12 +1,12 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
-++ListView text:", 4 items" clickable CollectionInfo:[rows=4, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
-++++View text:"Item 1, in list, item 1 of 4" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Item 2, in list, item 2 of 4" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Item 3, in list, item 3 of 4" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Item 4, in list, item 4 of 4" clickable focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++ListView text:", 5 items" clickable CollectionInfo:[rows=5, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
-++++View text:"Item 1, in list, item 1 of 5" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Item 2, in list, item 2 of 5" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Item 3, in list, item 3 of 5" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Item 4, in list, item 4 of 5" clickable focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++View text:"Item 5, in list, item 5 of 5" clickable focusable CollectionItemInfo:[rowIndex=4, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
+++ListView stateDescription:"4 items" clickable CollectionInfo:[rows=4, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"Item 1" stateDescription:"in list, item 1 of 4" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 2" stateDescription:"in list, item 2 of 4" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 3" stateDescription:"in list, item 3 of 4" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 4" stateDescription:"in list, item 4 of 4" clickable focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++ListView stateDescription:"5 items" clickable CollectionInfo:[rows=5, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"Item 1" stateDescription:"in list, item 1 of 5" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 2" stateDescription:"in list, item 2 of 5" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 3" stateDescription:"in list, item 3 of 5" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 4" stateDescription:"in list, item 4 of 5" clickable focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 5" stateDescription:"in list, item 5 of 5" clickable focusable CollectionItemInfo:[rowIndex=4, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-togglebutton-expected-android-external.txt b/content/test/data/accessibility/aria/aria-togglebutton-expected-android-external.txt
index 97cd51d..5e629fb 100644
--- a/content/test/data/accessibility/aria/aria-togglebutton-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-togglebutton-expected-android-external.txt
@@ -1,5 +1,5 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++Button text:"Regular button" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
-++ToggleButton text:"Toggle button, Off" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
-++ToggleButton text:"Toggle button, On" checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
-++ToggleButton text:"Toggle button, Partially Checked" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
\ No newline at end of file
+++ToggleButton text:"Toggle button" stateDescription:"Off" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Toggle button" stateDescription:"On" checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Toggle button" stateDescription:"Partially Checked" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tree-expected-android-external.txt b/content/test/data/accessibility/aria/aria-tree-expected-android-external.txt
index 7e74163a..7a6a34c 100644
--- a/content/test/data/accessibility/aria/aria-tree-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/aria-tree-expected-android-external.txt
@@ -1,6 +1,6 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++View CollectionInfo:[hierarchical, rows=2, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="tree", roleDescription="tree"]
-++++View text:"Animals, Partially Checked" clickable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
+++++View text:"Animals" stateDescription:"Partially Checked" clickable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
 ++++++View text:"null" contentDescription:"Animals" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-tree.html#animals"]
 ++++++++TextView text:"Animals" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
 ++++++View actions:[AX_FOCUS] bundle:[chromeRole="group"]
diff --git a/content/test/data/accessibility/aria/toggle-button-expand-collapse-expected-android-external.txt b/content/test/data/accessibility/aria/toggle-button-expand-collapse-expected-android-external.txt
index 48ab484..e3e5432 100644
--- a/content/test/data/accessibility/aria/toggle-button-expand-collapse-expected-android-external.txt
+++ b/content/test/data/accessibility/aria/toggle-button-expand-collapse-expected-android-external.txt
@@ -1,5 +1,5 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
-++ToggleButton text:"Toggle button, pressed, collapsed, On" canOpenPopUp checkable checked clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
-++ToggleButton text:"Toggle button, not pressed, collapsed, Off" canOpenPopUp checkable clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
-++ToggleButton text:"Toggle button, pressed, expanded, On" canOpenPopUp checkable checked clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COLLAPSE] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
-++ToggleButton text:"Toggle button, not pressed, collapsed", Off" canOpenPopUp checkable clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COLLAPSE] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
\ No newline at end of file
+++ToggleButton text:"Toggle button, pressed, collapsed" stateDescription:"On" canOpenPopUp checkable checked clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Toggle button, not pressed, collapsed" stateDescription:"Off" canOpenPopUp checkable clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Toggle button, pressed, expanded" stateDescription:"On" canOpenPopUp checkable checked clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COLLAPSE] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Toggle button, not pressed, collapsed"" stateDescription:"Off" canOpenPopUp checkable clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COLLAPSE] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/css/background-color-transparent-expected-win.txt b/content/test/data/accessibility/css/background-color-transparent-expected-win.txt
index 05a2c85..6d08aae6 100644
--- a/content/test/data/accessibility/css/background-color-transparent-expected-win.txt
+++ b/content/test/data/accessibility/css/background-color-transparent-expected-win.txt
@@ -1,11 +1,11 @@
-ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE offset:0 background-color:rgb(255\,255\,255) offset:1 background-color:rgb(0\,0\,255) offset:2 background-color:rgb(0\,128\,0) offset:3 background-color:rgb(191\,191\,191) offset:4 background-color:rgb(159\,159\,159) ia2_hypertext='<obj0><obj1><obj2><obj3><obj4>'
-++IA2_ROLE_SECTION offset:0 background-color:rgb(255\,255\,255) ia2_hypertext='Document base color.'
-++++ROLE_SYSTEM_STATICTEXT name='Document base color.' offset:0 background-color:rgb(255\,255\,255) ia2_hypertext='Document base color.'
-++IA2_ROLE_PARAGRAPH offset:0 background-color:rgb(0\,0\,255) ia2_hypertext='Transparent color.'
-++++ROLE_SYSTEM_STATICTEXT name='Transparent color.' offset:0 background-color:rgb(0\,0\,255) ia2_hypertext='Transparent color.'
-++IA2_ROLE_PARAGRAPH offset:0 background-color:rgb(0\,128\,0) ia2_hypertext='Obscuring background.'
-++++ROLE_SYSTEM_STATICTEXT name='Obscuring background.' offset:0 background-color:rgb(0\,128\,0) ia2_hypertext='Obscuring background.'
-++IA2_ROLE_PARAGRAPH offset:0 background-color:rgb(159\,159\,159) ia2_hypertext='Blended with document base color.'
-++++ROLE_SYSTEM_STATICTEXT name='Blended with document base color.' offset:0 background-color:rgb(159\,159\,159) ia2_hypertext='Blended with document base color.'
-++IA2_ROLE_PARAGRAPH offset:0 background-color:rgb(143\,143\,143) ia2_hypertext='Blended with background color.'
-++++ROLE_SYSTEM_STATICTEXT name='Blended with background color.' offset:0 background-color:rgb(143\,143\,143) ia2_hypertext='Blended with background color.'
+ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE offset:0 background-color:rgb(255\,255\,255) color:rgb(0\,0\,0) offset:1 background-color:rgb(0\,0\,255) color:rgb(0\,0\,0) offset:2 background-color:rgb(0\,128\,0) color:rgb(0\,0\,0) offset:3 background-color:rgb(191\,191\,191) color:rgb(0\,0\,0) offset:4 background-color:rgb(159\,159\,159) color:rgb(0\,0\,0) ia2_hypertext='<obj0><obj1><obj2><obj3><obj4>'
+++IA2_ROLE_SECTION offset:0 background-color:rgb(255\,255\,255) color:rgb(0\,0\,0) ia2_hypertext='Document base color.'
+++++ROLE_SYSTEM_STATICTEXT name='Document base color.' offset:0 background-color:rgb(255\,255\,255) color:rgb(0\,0\,0) ia2_hypertext='Document base color.'
+++IA2_ROLE_PARAGRAPH offset:0 background-color:rgb(0\,0\,255) color:rgb(0\,0\,0) ia2_hypertext='Transparent color.'
+++++ROLE_SYSTEM_STATICTEXT name='Transparent color.' offset:0 background-color:rgb(0\,0\,255) color:rgb(0\,0\,0) ia2_hypertext='Transparent color.'
+++IA2_ROLE_PARAGRAPH offset:0 background-color:rgb(0\,128\,0) color:rgb(0\,0\,0) ia2_hypertext='Obscuring background.'
+++++ROLE_SYSTEM_STATICTEXT name='Obscuring background.' offset:0 background-color:rgb(0\,128\,0) color:rgb(0\,0\,0) ia2_hypertext='Obscuring background.'
+++IA2_ROLE_PARAGRAPH offset:0 background-color:rgb(159\,159\,159) color:rgb(0\,0\,0) ia2_hypertext='Blended with document base color.'
+++++ROLE_SYSTEM_STATICTEXT name='Blended with document base color.' offset:0 background-color:rgb(159\,159\,159) color:rgb(0\,0\,0) ia2_hypertext='Blended with document base color.'
+++IA2_ROLE_PARAGRAPH offset:0 background-color:rgb(143\,143\,143) color:rgb(0\,0\,0) ia2_hypertext='Blended with background color.'
+++++ROLE_SYSTEM_STATICTEXT name='Blended with background color.' offset:0 background-color:rgb(143\,143\,143) color:rgb(0\,0\,0) ia2_hypertext='Blended with background color.'
diff --git a/content/test/data/accessibility/css/color-expected-auralinux.txt b/content/test/data/accessibility/css/color-expected-auralinux.txt
index 758eb86..84f2eea 100644
--- a/content/test/data/accessibility/css/color-expected-auralinux.txt
+++ b/content/test/data/accessibility/css/color-expected-auralinux.txt
@@ -1,9 +1,9 @@
-[document web] offset=0 bg-color=0,0,255 fg-color=255,0,0 offset=1 bg-color=255,255,255
+[document web] offset=0 bg-color=0,0,255 fg-color=255,0,0 offset=1 bg-color=255,255,255 fg-color=0,0,0
 ++[paragraph] offset=0 bg-color=0,0,255 fg-color=255,0,0
 ++++[static] name='Red on blue.' offset=0 bg-color=0,0,255 fg-color=255,0,0
-++[section] selectable-text offset=0 bg-color=255,255,255 offset=9 bg-color=0,0,255 offset=25 bg-color=255,255,255 offset=26 bg-color=255,255,255 fg-color=0,255,0
-++++[static] name='Default.' offset=0 bg-color=255,255,255
-++++[static] name=' ' offset=0 bg-color=255,255,255
-++++[static] name='Blue background.' offset=0 bg-color=0,0,255
-++++[static] name=' ' offset=0 bg-color=255,255,255
+++[section] selectable-text offset=0 bg-color=255,255,255 fg-color=0,0,0 offset=9 bg-color=0,0,255 fg-color=0,0,0 offset=25 bg-color=255,255,255 fg-color=0,0,0 offset=26 bg-color=255,255,255 fg-color=0,255,0
+++++[static] name='Default.' offset=0 bg-color=255,255,255 fg-color=0,0,0
+++++[static] name=' ' offset=0 bg-color=255,255,255 fg-color=0,0,0
+++++[static] name='Blue background.' offset=0 bg-color=0,0,255 fg-color=0,0,0
+++++[static] name=' ' offset=0 bg-color=255,255,255 fg-color=0,0,0
 ++++[static] name='Green text.' offset=0 bg-color=255,255,255 fg-color=0,255,0
diff --git a/content/test/data/accessibility/css/color-expected-win.txt b/content/test/data/accessibility/css/color-expected-win.txt
index b826bd9..90942ec 100644
--- a/content/test/data/accessibility/css/color-expected-win.txt
+++ b/content/test/data/accessibility/css/color-expected-win.txt
@@ -1,9 +1,9 @@
-ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE offset:0 background-color:rgb(0\,0\,255) color:rgb(255\,0\,0) offset:1 background-color:rgb(255\,255\,255) ia2_hypertext='<obj0><obj1>'
+ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE offset:0 background-color:rgb(0\,0\,255) color:rgb(255\,0\,0) offset:1 background-color:rgb(255\,255\,255) color:rgb(0\,0\,0) ia2_hypertext='<obj0><obj1>'
 ++IA2_ROLE_PARAGRAPH offset:0 background-color:rgb(0\,0\,255) color:rgb(255\,0\,0) ia2_hypertext='Red on blue.'
 ++++ROLE_SYSTEM_STATICTEXT name='Red on blue.' offset:0 background-color:rgb(0\,0\,255) color:rgb(255\,0\,0) ia2_hypertext='Red on blue.'
-++IA2_ROLE_SECTION value='Default. Blue background. Green text.' FOCUSABLE IA2_STATE_MULTI_LINE offset:0 background-color:rgb(255\,255\,255) offset:9 background-color:rgb(0\,0\,255) offset:25 background-color:rgb(255\,255\,255) offset:26 background-color:rgb(255\,255\,255) color:rgb(0\,255\,0) ia2_hypertext='Default. Blue background. Green text.'
-++++ROLE_SYSTEM_STATICTEXT name='Default.' offset:0 background-color:rgb(255\,255\,255) ia2_hypertext='Default.'
-++++ROLE_SYSTEM_STATICTEXT name=' ' offset:0 background-color:rgb(255\,255\,255) ia2_hypertext=' '
-++++ROLE_SYSTEM_STATICTEXT name='Blue background.' offset:0 background-color:rgb(0\,0\,255) ia2_hypertext='Blue background.'
-++++ROLE_SYSTEM_STATICTEXT name=' ' offset:0 background-color:rgb(255\,255\,255) ia2_hypertext=' '
+++IA2_ROLE_SECTION value='Default. Blue background. Green text.' FOCUSABLE IA2_STATE_MULTI_LINE offset:0 background-color:rgb(255\,255\,255) color:rgb(0\,0\,0) offset:9 background-color:rgb(0\,0\,255) color:rgb(0\,0\,0) offset:25 background-color:rgb(255\,255\,255) color:rgb(0\,0\,0) offset:26 background-color:rgb(255\,255\,255) color:rgb(0\,255\,0) ia2_hypertext='Default. Blue background. Green text.'
+++++ROLE_SYSTEM_STATICTEXT name='Default.' offset:0 background-color:rgb(255\,255\,255) color:rgb(0\,0\,0) ia2_hypertext='Default.'
+++++ROLE_SYSTEM_STATICTEXT name=' ' offset:0 background-color:rgb(255\,255\,255) color:rgb(0\,0\,0) ia2_hypertext=' '
+++++ROLE_SYSTEM_STATICTEXT name='Blue background.' offset:0 background-color:rgb(0\,0\,255) color:rgb(0\,0\,0) ia2_hypertext='Blue background.'
+++++ROLE_SYSTEM_STATICTEXT name=' ' offset:0 background-color:rgb(255\,255\,255) color:rgb(0\,0\,0) ia2_hypertext=' '
 ++++ROLE_SYSTEM_STATICTEXT name='Green text.' offset:0 background-color:rgb(255\,255\,255) color:rgb(0\,255\,0) ia2_hypertext='Green text.'
diff --git a/content/test/data/accessibility/html/a-expected-fuchsia.txt b/content/test/data/accessibility/html/a-expected-fuchsia.txt
index 8b74e3f..f099e63 100644
--- a/content/test/data/accessibility/html/a-expected-fuchsia.txt
+++ b/content/test/data/accessibility/html/a-expected-fuchsia.txt
@@ -1,4 +1,6 @@
 UNKNOWN focusable
-++UNKNOWN
-++++LINK focusable label='normal link'
-++++++STATIC_TEXT label='normal link'
+++UNKNOWN hidden
+++++UNKNOWN
+++++++LINK focusable label='normal link' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='normal link'
+++++++++++UNKNOWN label='normal link'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/a-name-calc-expected-fuchsia.txt b/content/test/data/accessibility/html/a-name-calc-expected-fuchsia.txt
new file mode 100644
index 0000000..a925883
--- /dev/null
+++ b/content/test/data/accessibility/html/a-name-calc-expected-fuchsia.txt
@@ -0,0 +1,24 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++LINK focusable label='InnerText0' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='InnerText0'
+++++++++++UNKNOWN label='InnerText0'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++LINK focusable label='InnerText1' actions='{DEFAULT}' secondary_label='Title1'
+++++++++STATIC_TEXT label='InnerText1'
+++++++++++UNKNOWN label='InnerText1'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++LINK focusable label='Title2' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='InnerText2'
+++++++++++UNKNOWN label='InnerText2'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++LINK focusable label='LabelledBy3' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='InnerText3'
+++++++++++UNKNOWN label='InnerText3'
+++++++LINK focusable label='Title4' actions='{DEFAULT}'
+++++++LINK focusable label='Label5' actions='{DEFAULT}'
+++++++LINK focusable label='LabelledBy6' actions='{DEFAULT}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/a-name-expected-fuchsia.txt b/content/test/data/accessibility/html/a-name-expected-fuchsia.txt
new file mode 100644
index 0000000..219dd4a
--- /dev/null
+++ b/content/test/data/accessibility/html/a-name-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++STATIC_TEXT label='named anchor'
+++++++++++UNKNOWN label='named anchor'
+++++++LINK focusable label='both a named anchor and a link' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='both a named anchor and a link'
+++++++++++UNKNOWN label='both a named anchor and a link'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/a-nested-structure-expected-fuchsia.txt b/content/test/data/accessibility/html/a-nested-structure-expected-fuchsia.txt
new file mode 100644
index 0000000..7267c97
--- /dev/null
+++ b/content/test/data/accessibility/html/a-nested-structure-expected-fuchsia.txt
@@ -0,0 +1,23 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++LINK focusable label='Header 1 List element 1 List element 2 List element 3' actions='{DEFAULT}'
+++++++++UNKNOWN label='Header 1' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Header 1'
+++++++++++++UNKNOWN label='Header 1'
+++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++UNKNOWN label='%E2%80%A2 ' actions='{DEFAULT}'
+++++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++++STATIC_TEXT label='List element 1'
+++++++++++++++UNKNOWN label='List element 1'
+++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++UNKNOWN label='%E2%80%A2 ' actions='{DEFAULT}'
+++++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++++STATIC_TEXT label='List element 2'
+++++++++++++++UNKNOWN label='List element 2'
+++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++UNKNOWN label='%E2%80%A2 ' actions='{DEFAULT}'
+++++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++++STATIC_TEXT label='List element 3'
+++++++++++++++UNKNOWN label='List element 3'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/a-no-text-expected-fuchsia.txt b/content/test/data/accessibility/html/a-no-text-expected-fuchsia.txt
new file mode 100644
index 0000000..8b56bf0
--- /dev/null
+++ b/content/test/data/accessibility/html/a-no-text-expected-fuchsia.txt
@@ -0,0 +1,25 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++LINK focusable actions='{DEFAULT}'
+++++++++IMAGE actions='{DEFAULT}'
+++++++LINK focusable actions='{DEFAULT}'
+++++++++IMAGE actions='{DEFAULT}'
+++++++LINK focusable actions='{DEFAULT}'
+++++++++IMAGE actions='{DEFAULT}'
+++++++LINK focusable actions='{DEFAULT}'
+++++++++IMAGE actions='{DEFAULT}'
+++++++LINK focusable actions='{DEFAULT}'
+++++++++IMAGE actions='{DEFAULT}'
+++++++LINK focusable actions='{DEFAULT}'
+++++++++IMAGE actions='{DEFAULT}'
+++++++LINK focusable actions='{DEFAULT}'
+++++++++IMAGE actions='{DEFAULT}'
+++++++LINK focusable actions='{DEFAULT}'
+++++++++IMAGE actions='{DEFAULT}'
+++++++LINK focusable actions='{DEFAULT}'
+++++++++IMAGE actions='{DEFAULT}'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++LINK focusable actions='{DEFAULT}'
+++++++++IMAGE actions='{DEFAULT}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/a-onclick-expected-fuchsia.txt b/content/test/data/accessibility/html/a-onclick-expected-fuchsia.txt
new file mode 100644
index 0000000..8051da0
--- /dev/null
+++ b/content/test/data/accessibility/html/a-onclick-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++LINK label='link with no href but onclick' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='link with no href but onclick'
+++++++++++UNKNOWN label='link with no href but onclick'
+++++++LINK label='link with no href and click handler added via script' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='link with no href and click handler added via script'
+++++++++++UNKNOWN label='link with no href and click handler added via script'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/a-with-before-expected-fuchsia.txt b/content/test/data/accessibility/html/a-with-before-expected-fuchsia.txt
new file mode 100644
index 0000000..98ad485
--- /dev/null
+++ b/content/test/data/accessibility/html/a-with-before-expected-fuchsia.txt
@@ -0,0 +1,13 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++STATIC_TEXT label='Here is a '
+++++++++UNKNOWN label='Here is a '
+++++++LINK focusable label='[linked] link' actions='{DEFAULT}'
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT label='[linked] '
+++++++++++++UNKNOWN label='[linked] '
+++++++++STATIC_TEXT label='link'
+++++++++++UNKNOWN label='link'
+++++++STATIC_TEXT label='.'
+++++++++UNKNOWN label='.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/a-with-img-expected-fuchsia.txt b/content/test/data/accessibility/html/a-with-img-expected-fuchsia.txt
new file mode 100644
index 0000000..4112cdc
--- /dev/null
+++ b/content/test/data/accessibility/html/a-with-img-expected-fuchsia.txt
@@ -0,0 +1,29 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++LINK focusable label='Link with image at start.' actions='{DEFAULT}'
+++++++++IMAGE label='Link' actions='{DEFAULT}'
+++++++++STATIC_TEXT label=' with image at start.'
+++++++++++UNKNOWN label=' with image at start.'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++LINK focusable label='Link with image in the middle.' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Link with '
+++++++++++UNKNOWN label='Link with '
+++++++++IMAGE label='image' actions='{DEFAULT}'
+++++++++STATIC_TEXT label=' in the middle.'
+++++++++++UNKNOWN label=' in the middle.'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++LINK focusable label='Link with broken in the middle.' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Link with '
+++++++++++UNKNOWN label='Link with '
+++++++++IMAGE label='broken' actions='{DEFAULT}'
+++++++++STATIC_TEXT label=' in the middle.'
+++++++++++UNKNOWN label=' in the middle.'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++LINK focusable label='Link with image at the end' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Link with image at the '
+++++++++++UNKNOWN label='Link with image at the '
+++++++++IMAGE label='end' actions='{DEFAULT}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/abbr-expected-fuchsia.txt b/content/test/data/accessibility/html/abbr-expected-fuchsia.txt
new file mode 100644
index 0000000..34a9098
--- /dev/null
+++ b/content/test/data/accessibility/html/abbr-expected-fuchsia.txt
@@ -0,0 +1,11 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='The '
+++++++++++UNKNOWN label='The '
+++++++++UNKNOWN label='World Health Organization'
+++++++++++STATIC_TEXT label='WHO'
+++++++++++++UNKNOWN label='WHO'
+++++++++STATIC_TEXT label=' was founded in 1948.'
+++++++++++UNKNOWN label=' was founded in 1948.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/action-verbs-expected-fuchsia.txt b/content/test/data/accessibility/html/action-verbs-expected-fuchsia.txt
new file mode 100644
index 0000000..30178b81
--- /dev/null
+++ b/content/test/data/accessibility/html/action-verbs-expected-fuchsia.txt
@@ -0,0 +1,77 @@
+UNKNOWN focusable label='Action verbs'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Generic div'
+++++++++++UNKNOWN label='Generic div'
+++++++UNKNOWN label='Heading'
+++++++++STATIC_TEXT label='Heading'
+++++++++++UNKNOWN label='Heading'
+++++++BUTTON focusable label='Button' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Button'
+++++++++++UNKNOWN label='Button'
+++++++LINK focusable label='Link' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Link'
+++++++++++UNKNOWN label='Link'
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN
+++++++SEARCH_BOX focusable actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}'
+++++++CHECK_BOX focusable actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++CHECK_BOX focusable actions='{DEFAULT}' checked_state='CHECKED'
+++++++RADIO_BUTTON focusable actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++UNKNOWN focusable label='ARIA Switch' actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++STATIC_TEXT label='ARIA Switch'
+++++++++++UNKNOWN label='ARIA Switch'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable label='Summary' actions='{DEFAULT}'
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT hidden label='%E2%96%B8 '
+++++++++++++STATIC_TEXT label='Summary'
+++++++++++++++UNKNOWN label='Summary'
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='Details'
+++++++UNKNOWN focusable actions='{DEFAULT}' value='Pop-up button'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable selected label='Pop-up button' actions='{DEFAULT}'
+++++++UNKNOWN actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Div with click handler'
+++++++++++UNKNOWN label='Div with click handler'
+++++++UNKNOWN actions='{DEFAULT}'
+++++++++PARAGRAPH actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Paragraph with click handler on parent'
+++++++++++++UNKNOWN label='Paragraph with click handler on parent'
+++++++UNKNOWN
+++++++++UNKNOWN label='Menu item 1' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Menu item 1'
+++++++++++++UNKNOWN label='Menu item 1'
+++++++++UNKNOWN label='Menu item 2' actions='{DEFAULT}' checked_state='CHECKED'
+++++++++++STATIC_TEXT label='Menu item 2'
+++++++++++++UNKNOWN label='Menu item 2'
+++++++++UNKNOWN label='Menu item 3' actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++++STATIC_TEXT label='Menu item 3'
+++++++++++++UNKNOWN label='Menu item 3'
+++++++BUTTON label='ARIA Button' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='ARIA Button'
+++++++++++UNKNOWN label='ARIA Button'
+++++++BUTTON focusable label='ARIA button with tab index' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='ARIA button with tab index'
+++++++++++UNKNOWN label='ARIA button with tab index'
+++++++BUTTON focusable label='ARIA button with negative tab index' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='ARIA button with negative tab index'
+++++++++++UNKNOWN label='ARIA button with negative tab index'
+++++++UNKNOWN actions='{DEFAULT}'
+++++++++BUTTON focusable label='ARIA button that is an active descendant' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='ARIA button that is an active descendant'
+++++++++++++UNKNOWN label='ARIA button that is an active descendant'
+++++++UNKNOWN actions='{DEFAULT}'
+++++++++UNKNOWN label='ARIA switch in clickable container' actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++++STATIC_TEXT label='ARIA switch in clickable container'
+++++++++++++UNKNOWN label='ARIA switch in clickable container'
+++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Generic in clickable container'
+++++++++++++UNKNOWN label='Generic in clickable container'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/actions-expected-fuchsia.txt b/content/test/data/accessibility/html/actions-expected-fuchsia.txt
new file mode 100644
index 0000000..c1079f0
--- /dev/null
+++ b/content/test/data/accessibility/html/actions-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable label='Actions'
+++UNKNOWN hidden
+++++UNKNOWN
+++++++SLIDER focusable actions='{SET_VALUE}'
+++++++TEXT_FIELD focusable label='Test textfield' actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/address-expected-fuchsia.txt b/content/test/data/accessibility/html/address-expected-fuchsia.txt
new file mode 100644
index 0000000..c11b78da
--- /dev/null
+++ b/content/test/data/accessibility/html/address-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Please contact John Citizen for more information.'
+++++++++++UNKNOWN label='Please contact John Citizen for more information.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/area-expected-fuchsia.txt b/content/test/data/accessibility/html/area-expected-fuchsia.txt
new file mode 100644
index 0000000..b1fe2e1
--- /dev/null
+++ b/content/test/data/accessibility/html/area-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++IMAGE label='pipe'
+++++++++LINK focusable label='pipe1' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='pipe2' actions='{DEFAULT}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/article-expected-fuchsia.txt b/content/test/data/accessibility/html/article-expected-fuchsia.txt
new file mode 100644
index 0000000..7332c27
--- /dev/null
+++ b/content/test/data/accessibility/html/article-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is an article element.'
+++++++++++UNKNOWN label='This is an article element.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/aside-expected-fuchsia.txt b/content/test/data/accessibility/html/aside-expected-fuchsia.txt
new file mode 100644
index 0000000..d77f1e9
--- /dev/null
+++ b/content/test/data/accessibility/html/aside-expected-fuchsia.txt
@@ -0,0 +1,13 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='The aside tag defines some content aside from the content it is placed in.'
+++++++++++UNKNOWN label='The aside tag defines some content aside from the content it is placed in.'
+++++++UNKNOWN
+++++++++UNKNOWN label='Aside tag'
+++++++++++STATIC_TEXT label='Aside tag'
+++++++++++++UNKNOWN label='Aside tag'
+++++++++PARAGRAPH
+++++++++++STATIC_TEXT label='The aside content should be related to the surrounding content.'
+++++++++++++UNKNOWN label='The aside content should be related to the surrounding content.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/b-expected-fuchsia.txt b/content/test/data/accessibility/html/b-expected-fuchsia.txt
new file mode 100644
index 0000000..7fc531be
--- /dev/null
+++ b/content/test/data/accessibility/html/b-expected-fuchsia.txt
@@ -0,0 +1,10 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='Some '
+++++++++++UNKNOWN label='Some '
+++++++++STATIC_TEXT label='bold'
+++++++++++UNKNOWN label='bold'
+++++++++STATIC_TEXT label=' text'
+++++++++++UNKNOWN label=' text'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/bdo-expected-fuchsia.txt b/content/test/data/accessibility/html/bdo-expected-fuchsia.txt
new file mode 100644
index 0000000..00f52212
--- /dev/null
+++ b/content/test/data/accessibility/html/bdo-expected-fuchsia.txt
@@ -0,0 +1,11 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++STATIC_TEXT label='Some LTR text'
+++++++++UNKNOWN label='Some LTR text'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++STATIC_TEXT label='Some RTL text '
+++++++++UNKNOWN label='Some RTL text '
+++++++STATIC_TEXT label='with some LTR text embedded'
+++++++++UNKNOWN label='with some LTR text embedded'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/blockquote-expected-fuchsia.txt b/content/test/data/accessibility/html/blockquote-expected-fuchsia.txt
new file mode 100644
index 0000000..0c4aaae
--- /dev/null
+++ b/content/test/data/accessibility/html/blockquote-expected-fuchsia.txt
@@ -0,0 +1,10 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++PARAGRAPH
+++++++++++STATIC_TEXT label='First blockquote has a child element.'
+++++++++++++UNKNOWN label='First blockquote has a child element.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Second blockquote has no child.'
+++++++++++UNKNOWN label='Second blockquote has no child.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/blockquote-levels-expected-fuchsia.txt b/content/test/data/accessibility/html/blockquote-levels-expected-fuchsia.txt
new file mode 100644
index 0000000..fb1fc77
--- /dev/null
+++ b/content/test/data/accessibility/html/blockquote-levels-expected-fuchsia.txt
@@ -0,0 +1,13 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN hidden
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Top level'
+++++++++++++UNKNOWN label='Top level'
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='Sub'
+++++++++++++++UNKNOWN label='Sub'
+++++++++++++UNKNOWN
+++++++++++++++STATIC_TEXT label='Sub-sub'
+++++++++++++++++UNKNOWN label='Sub-sub'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/body-expected-fuchsia.txt b/content/test/data/accessibility/html/body-expected-fuchsia.txt
new file mode 100644
index 0000000..8aebafc
--- /dev/null
+++ b/content/test/data/accessibility/html/body-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='This test is for body tag'
+++++++++++UNKNOWN label='This test is for body tag'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/br-expected-fuchsia.txt b/content/test/data/accessibility/html/br-expected-fuchsia.txt
new file mode 100644
index 0000000..7021052
--- /dev/null
+++ b/content/test/data/accessibility/html/br-expected-fuchsia.txt
@@ -0,0 +1,16 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN label='<newline>'
+++++++++UNKNOWN label='<newline>'
+++++++STATIC_TEXT label='Text line 1'
+++++++++UNKNOWN label='Text line 1'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='Text line 2'
+++++++++++UNKNOWN label='Text line 2'
+++++++++UNKNOWN label='<newline>'
+++++++++++UNKNOWN label='<newline>'
+++++++++STATIC_TEXT label='Text line 3'
+++++++++++UNKNOWN label='Text line 3'
+++++++UNKNOWN label='<newline>'
+++++++++UNKNOWN label='<newline>'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/button-expected-fuchsia.txt b/content/test/data/accessibility/html/button-expected-fuchsia.txt
new file mode 100644
index 0000000..b8b2017
--- /dev/null
+++ b/content/test/data/accessibility/html/button-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++BUTTON focusable label='Click me!' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Click me!'
+++++++++++UNKNOWN label='Click me!'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/button-name-calc-expected-fuchsia.txt b/content/test/data/accessibility/html/button-name-calc-expected-fuchsia.txt
new file mode 100644
index 0000000..ef90ba5
--- /dev/null
+++ b/content/test/data/accessibility/html/button-name-calc-expected-fuchsia.txt
@@ -0,0 +1,40 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++BUTTON focusable label='InnerText0' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='InnerText0'
+++++++++++UNKNOWN label='InnerText0'
+++++++BUTTON focusable label='InnerText1' actions='{DEFAULT}' secondary_label='Title1'
+++++++++STATIC_TEXT label='InnerText1'
+++++++++++UNKNOWN label='InnerText1'
+++++++BUTTON focusable label='AriaLabel2' actions='{DEFAULT}' secondary_label='Title2'
+++++++++STATIC_TEXT label='InnerText2'
+++++++++++UNKNOWN label='InnerText2'
+++++++BUTTON focusable label='LabelledBy3' actions='{DEFAULT}' secondary_label='Title3'
+++++++++STATIC_TEXT label='InnerText3'
+++++++++++UNKNOWN label='InnerText3'
+++++++BUTTON focusable label='LabelledBy4' actions='{DEFAULT}' secondary_label='DescribedBy4'
+++++++++STATIC_TEXT label='InnerText4'
+++++++++++UNKNOWN label='InnerText4'
+++++++BUTTON focusable label='InnerText5' actions='{DEFAULT}' secondary_label='DescribedBy5'
+++++++++STATIC_TEXT label='InnerText5'
+++++++++++UNKNOWN label='InnerText5'
+++++++BUTTON focusable label='Outer inner' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Outer'
+++++++++++UNKNOWN label='Outer'
+++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='inner'
+++++++++++++UNKNOWN label='inner'
+++++++BUTTON focusable label='Outer inner1' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Outer'
+++++++++++UNKNOWN label='Outer'
+++++++++UNKNOWN label='inner1' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='inner2'
+++++++++++++UNKNOWN label='inner2'
+++++++BUTTON focusable label='Outer grandchild' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Outer'
+++++++++++UNKNOWN label='Outer'
+++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++STATIC_TEXT label='grandchild'
+++++++++++++++UNKNOWN label='grandchild'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/button-with-listbox-popup-expected-android-external.txt b/content/test/data/accessibility/html/button-with-listbox-popup-expected-android-external.txt
index 7dbf46b5..d561d20 100644
--- a/content/test/data/accessibility/html/button-with-listbox-popup-expected-android-external.txt
+++ b/content/test/data/accessibility/html/button-with-listbox-popup-expected-android-external.txt
@@ -2,7 +2,7 @@
 ++View viewIdResName:"test1" actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
 ++++TextView text:"Choose one:" viewIdResName:"span" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
 ++++View text:"Choose one: Foo" viewIdResName:"test" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="popUpButton", clickableScore="300", roleDescription="pop up button"]
-++++ListView text:"Choose one:, 3 items" viewIdResName:"options" clickable focusable CollectionInfo:[rows=3, cols=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
-++++++View text:"Baz, in list, item 1 of 3" viewIdResName:"option1" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++++View text:"Bar, in list, item 2 of 3" viewIdResName:"option2" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++++View text:"Foo, in list, item 3 of 3" viewIdResName:"option3" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
+++++ListView text:"Choose one:" viewIdResName:"options" stateDescription:"3 items" clickable focusable CollectionInfo:[rows=3, cols=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++++View text:"Baz" viewIdResName:"option1" stateDescription:"in list, item 1 of 3" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++++View text:"Bar" viewIdResName:"option2" stateDescription:"in list, item 2 of 3" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++++View text:"Foo" viewIdResName:"option3" stateDescription:"in list, item 3 of 3" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/button-with-listbox-popup-expected-fuchsia.txt b/content/test/data/accessibility/html/button-with-listbox-popup-expected-fuchsia.txt
new file mode 100644
index 0000000..d05a8d6
--- /dev/null
+++ b/content/test/data/accessibility/html/button-with-listbox-popup-expected-fuchsia.txt
@@ -0,0 +1,29 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Choose one:'
+++++++++++++UNKNOWN label='Choose one:'
+++++++++UNKNOWN focusable label='Choose one: Foo' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Foo'
+++++++++++++UNKNOWN label='Foo'
+++++++++UNKNOWN focusable label='Choose one:' actions='{DEFAULT}'
+++++++++++UNKNOWN focusable label='Baz' actions='{DEFAULT}'
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT label='%E2%80%A2 '
+++++++++++++++++UNKNOWN label='%E2%80%A2 '
+++++++++++++STATIC_TEXT label='Baz'
+++++++++++++++UNKNOWN label='Baz'
+++++++++++UNKNOWN focusable label='Bar' actions='{DEFAULT}'
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT label='%E2%80%A2 '
+++++++++++++++++UNKNOWN label='%E2%80%A2 '
+++++++++++++STATIC_TEXT label='Bar'
+++++++++++++++UNKNOWN label='Bar'
+++++++++++UNKNOWN focusable label='Foo' actions='{DEFAULT}'
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT label='%E2%80%A2 '
+++++++++++++++++UNKNOWN label='%E2%80%A2 '
+++++++++++++STATIC_TEXT label='Foo'
+++++++++++++++UNKNOWN label='Foo'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/canvas-expected-fuchsia.txt b/content/test/data/accessibility/html/canvas-expected-fuchsia.txt
new file mode 100644
index 0000000..31a7f33
--- /dev/null
+++ b/content/test/data/accessibility/html/canvas-expected-fuchsia.txt
@@ -0,0 +1,8 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Static fallback'
+++++++UNKNOWN
+++++++++LINK focusable label='Interactive fallback' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Interactive fallback'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/canvas-fallback-expected-fuchsia.txt b/content/test/data/accessibility/html/canvas-fallback-expected-fuchsia.txt
new file mode 100644
index 0000000..4024992
--- /dev/null
+++ b/content/test/data/accessibility/html/canvas-fallback-expected-fuchsia.txt
@@ -0,0 +1,22 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Static fallback'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='<newline>    '
+++++++++PARAGRAPH secondary_label='Aria hidden paragraph in fallback content'
+++++++++++STATIC_TEXT label='Line breaking content in a fallback'
+++++++++STATIC_TEXT label='<newline>    '
+++++++++PARAGRAPH secondary_label='Visibility hidden paragraph in fallback content'
+++++++++++STATIC_TEXT label='This is another paragraph in fallback'
+++++++++STATIC_TEXT label='<newline>    '
+++++++++UNKNOWN hidden label='Aria hidden paragraph in fallback content'
+++++++++++STATIC_TEXT hidden label='Aria hidden paragraph in fallback content'
+++++++++STATIC_TEXT label='<newline>    '
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT label='Display none text in fallback content '
+++++++++STATIC_TEXT label='<newline>    '
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT label='Visibility hidden paragraph in fallback content'
+++++++++STATIC_TEXT label='<newline>  '
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/caption-expected-fuchsia.txt b/content/test/data/accessibility/html/caption-expected-fuchsia.txt
new file mode 100644
index 0000000..deca86f
--- /dev/null
+++ b/content/test/data/accessibility/html/caption-expected-fuchsia.txt
@@ -0,0 +1,42 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TABLE label='Browser and Engine' number_of_rows=3 number_of_columns=2
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Browser and Engine'
+++++++++++++UNKNOWN label='Browser and '
+++++++++++++UNKNOWN label='Engine'
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW
+++++++++++++COLUMN_HEADER label='Browser' cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Browser'
+++++++++++++++++UNKNOWN label='Browser'
+++++++++++++COLUMN_HEADER label='Engine' cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Engine'
+++++++++++++++++UNKNOWN label='Engine'
+++++++++++TABLE_ROW row_index=1
+++++++++++++CELL label='Chrome' cell_row_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Chrome'
+++++++++++++++++UNKNOWN label='Chrome'
+++++++++++++CELL label='Blink' cell_row_index=1 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Blink'
+++++++++++++++++UNKNOWN label='Blink'
+++++++++++TABLE_ROW row_index=2
+++++++++++++CELL label='Safari' cell_row_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Safari'
+++++++++++++++++UNKNOWN label='Safari'
+++++++++++++CELL label='WebKit' cell_row_index=2 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='WebKit'
+++++++++++++++++UNKNOWN label='WebKit'
+++++++TABLE label='Name' secondary_label='Description' number_of_rows=1 number_of_columns=2
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Description'
+++++++++++++UNKNOWN label='Description'
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW
+++++++++++++CELL label='A' cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='A'
+++++++++++++++++UNKNOWN label='A'
+++++++++++++CELL label='B' cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='B'
+++++++++++++++++UNKNOWN label='B'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/character-locations-expected-fuchsia.txt b/content/test/data/accessibility/html/character-locations-expected-fuchsia.txt
new file mode 100644
index 0000000..fb4ded36
--- /dev/null
+++ b/content/test/data/accessibility/html/character-locations-expected-fuchsia.txt
@@ -0,0 +1,23 @@
+UNKNOWN focusable label='Character Locations'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN label='Heading'
+++++++++STATIC_TEXT label='Heading'
+++++++++++UNKNOWN label='Heading'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='Paragraph'
+++++++++++UNKNOWN label='Paragraph'
+++++++BUTTON focusable label='Button' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Button'
+++++++++++UNKNOWN label='Button'
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='Input'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Input'
+++++++++++++UNKNOWN label='Input'
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='Textarea'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Textarea'
+++++++++++++UNKNOWN label='Textarea'
+++++++IMAGE label='Image with alt text'
+++++++LINK focusable label='Image inside link' actions='{DEFAULT}'
+++++++++IMAGE label='Image inside link' actions='{DEFAULT}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/checkbox-name-calc-expected-fuchsia.txt b/content/test/data/accessibility/html/checkbox-name-calc-expected-fuchsia.txt
new file mode 100644
index 0000000..cb97bf4f
--- /dev/null
+++ b/content/test/data/accessibility/html/checkbox-name-calc-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++CHECK_BOX focusable label='Title0' actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++CHECK_BOX focusable label='Label1' actions='{DEFAULT}' secondary_label='Title1' checked_state='UNCHECKED'
+++++++CHECK_BOX focusable label='AriaLabel2' actions='{DEFAULT}' secondary_label='Title2' checked_state='UNCHECKED'
+++++++CHECK_BOX focusable label='LabelledBy3' actions='{DEFAULT}' secondary_label='Title3' checked_state='UNCHECKED'
+++++++CHECK_BOX focusable label='LabelledBy4' actions='{DEFAULT}' secondary_label='DescribedBy4' checked_state='UNCHECKED'
+++++++CHECK_BOX focusable actions='{DEFAULT}' secondary_label='DescribedBy5' checked_state='UNCHECKED'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/cite-expected-fuchsia.txt b/content/test/data/accessibility/html/cite-expected-fuchsia.txt
new file mode 100644
index 0000000..fb3e6ff
--- /dev/null
+++ b/content/test/data/accessibility/html/cite-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++IMAGE label='Pipe'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='The pipe'
+++++++++++UNKNOWN label='The pipe'
+++++++++STATIC_TEXT label=' clicked by SomeOne.'
+++++++++++UNKNOWN label=' clicked by SomeOne.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/clickable-ancestor-expected-fuchsia.txt b/content/test/data/accessibility/html/clickable-ancestor-expected-fuchsia.txt
new file mode 100644
index 0000000..9fd5e21
--- /dev/null
+++ b/content/test/data/accessibility/html/clickable-ancestor-expected-fuchsia.txt
@@ -0,0 +1,16 @@
+UNKNOWN focusable label='Checking nodes marked as clickable'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Generic div'
+++++++++++UNKNOWN label='Generic div'
+++++++UNKNOWN label='Heading'
+++++++++STATIC_TEXT label='Heading'
+++++++++++UNKNOWN label='Heading'
+++++++BUTTON focusable label='Button' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Button'
+++++++++++UNKNOWN label='Button'
+++++++UNKNOWN actions='{DEFAULT}'
+++++++++PARAGRAPH actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Paragraph with click handler on parent and should be not marked as clickable'
+++++++++++++UNKNOWN label='Paragraph with click handler on parent and should be not marked as clickable'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/code-expected-fuchsia.txt b/content/test/data/accessibility/html/code-expected-fuchsia.txt
new file mode 100644
index 0000000..30a063d8
--- /dev/null
+++ b/content/test/data/accessibility/html/code-expected-fuchsia.txt
@@ -0,0 +1,5 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++STATIC_TEXT label='A piece of computer code'
+++++++++UNKNOWN label='A piece of computer code'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/col-expected-fuchsia.txt b/content/test/data/accessibility/html/col-expected-fuchsia.txt
new file mode 100644
index 0000000..67040dc
--- /dev/null
+++ b/content/test/data/accessibility/html/col-expected-fuchsia.txt
@@ -0,0 +1,19 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TABLE number_of_rows=2 number_of_columns=2
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW
+++++++++++++COLUMN_HEADER label='Browser' cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Browser'
+++++++++++++++++UNKNOWN label='Browser'
+++++++++++++COLUMN_HEADER label='Rendering Engine' cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Rendering Engine'
+++++++++++++++++UNKNOWN label='Rendering Engine'
+++++++++++TABLE_ROW row_index=1
+++++++++++++CELL label='Chrome' cell_row_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Chrome'
+++++++++++++++++UNKNOWN label='Chrome'
+++++++++++++CELL label='Blink' cell_row_index=1 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Blink'
+++++++++++++++++UNKNOWN label='Blink'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/colgroup-expected-fuchsia.txt b/content/test/data/accessibility/html/colgroup-expected-fuchsia.txt
new file mode 100644
index 0000000..ddfaf7e
--- /dev/null
+++ b/content/test/data/accessibility/html/colgroup-expected-fuchsia.txt
@@ -0,0 +1,19 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TABLE number_of_rows=2 number_of_columns=2
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW
+++++++++++++COLUMN_HEADER label='Single' cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Single'
+++++++++++++++++UNKNOWN label='Single'
+++++++++++++COLUMN_HEADER label='Pair' cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Pair'
+++++++++++++++++UNKNOWN label='Pair'
+++++++++++TABLE_ROW row_index=1
+++++++++++++CELL label='A' cell_row_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='A'
+++++++++++++++++UNKNOWN label='A'
+++++++++++++CELL label='AA' cell_row_index=1 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='AA'
+++++++++++++++++UNKNOWN label='AA'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/combobox-optgroup-expected-fuchsia.txt b/content/test/data/accessibility/html/combobox-optgroup-expected-fuchsia.txt
new file mode 100644
index 0000000..17cf420
--- /dev/null
+++ b/content/test/data/accessibility/html/combobox-optgroup-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable actions='{DEFAULT}' value='Mercedes Label'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden focusable label='Volvo Label' actions='{DEFAULT}'
+++++++++++UNKNOWN hidden focusable label='Saab Label' actions='{DEFAULT}'
+++++++++++UNKNOWN focusable selected label='Mercedes Label' actions='{DEFAULT}'
+++++++++++UNKNOWN hidden focusable label='Audi Label' actions='{DEFAULT}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/contenteditable-descendants-expected-fuchsia.txt b/content/test/data/accessibility/html/contenteditable-descendants-expected-fuchsia.txt
new file mode 100644
index 0000000..39c9052
--- /dev/null
+++ b/content/test/data/accessibility/html/contenteditable-descendants-expected-fuchsia.txt
@@ -0,0 +1,38 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN focusable actions='{DEFAULT}' value='A contenteditable with a link and an  and a .<newline><newline>Always expose editable tables as tables.<newline>Editable list item.'
+++++++++PARAGRAPH actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='A contenteditable with a '
+++++++++++++UNKNOWN label='A contenteditable with a '
+++++++++++LINK label='link' actions='{DEFAULT}'
+++++++++++++STATIC_TEXT label='link'
+++++++++++++++UNKNOWN label='link'
+++++++++++STATIC_TEXT label=' and an '
+++++++++++++UNKNOWN label=' and an '
+++++++++++IMAGE label='Image' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label=' and a '
+++++++++++++UNKNOWN label=' and a '
+++++++++++BUTTON focusable label='Button' actions='{DEFAULT}'
+++++++++++++STATIC_TEXT label='Button'
+++++++++++++++UNKNOWN label='Button'
+++++++++++STATIC_TEXT label='.'
+++++++++++++UNKNOWN label='.'
+++++++++TABLE actions='{DEFAULT}' number_of_rows=1 number_of_columns=1
+++++++++++ROW_GROUP hidden
+++++++++++++TABLE_ROW actions='{DEFAULT}'
+++++++++++++++CELL label='Always expose editable tables as tables.' actions='{DEFAULT}' cell_row_span=1 cell_column_span=1
+++++++++++++++++STATIC_TEXT label='Always expose editable tables as tables.'
+++++++++++++++++++UNKNOWN label='Always expose editable tables as tables.'
+++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++UNKNOWN label='1. ' actions='{DEFAULT}'
+++++++++++++++STATIC_TEXT hidden label='1. '
+++++++++++++STATIC_TEXT label='Editable list item.'
+++++++++++++++UNKNOWN label='Editable list item.'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='Non-editable paragraph.'
+++++++++++UNKNOWN label='Non-editable paragraph.'
+++++++PARAGRAPH focusable actions='{DEFAULT}' value='Should keep the role but change the state.'
+++++++++STATIC_TEXT label='Should keep the role but change the state.'
+++++++++++UNKNOWN label='Should keep the role but change the state.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-fuchsia.txt b/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-fuchsia.txt
new file mode 100644
index 0000000..987765f
--- /dev/null
+++ b/content/test/data/accessibility/html/contenteditable-descendants-with-selection-expected-fuchsia.txt
@@ -0,0 +1,32 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN focusable actions='{DEFAULT}' value='A contenteditable with a link and an  and a .<newline><newline>Always expose editable tables as tables.<newline>Editable list item.'
+++++++++PARAGRAPH actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='A contenteditable with a '
+++++++++++++UNKNOWN label='A contenteditable with a '
+++++++++++LINK label='link' actions='{DEFAULT}'
+++++++++++++STATIC_TEXT label='link'
+++++++++++++++UNKNOWN label='link'
+++++++++++STATIC_TEXT label=' and an '
+++++++++++++UNKNOWN label=' and an '
+++++++++++IMAGE label='Image' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label=' and a '
+++++++++++++UNKNOWN label=' and a '
+++++++++++BUTTON focusable label='Button' actions='{DEFAULT}'
+++++++++++++STATIC_TEXT label='Button'
+++++++++++++++UNKNOWN label='Button'
+++++++++++STATIC_TEXT label='.'
+++++++++++++UNKNOWN label='.'
+++++++++TABLE actions='{DEFAULT}' number_of_rows=1 number_of_columns=1
+++++++++++ROW_GROUP hidden
+++++++++++++TABLE_ROW actions='{DEFAULT}'
+++++++++++++++CELL label='Always expose editable tables as tables.' actions='{DEFAULT}' cell_row_span=1 cell_column_span=1
+++++++++++++++++STATIC_TEXT label='Always expose editable tables as tables.'
+++++++++++++++++++UNKNOWN label='Always expose editable tables as tables.'
+++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++UNKNOWN label='1. ' actions='{DEFAULT}'
+++++++++++++++STATIC_TEXT hidden label='1. '
+++++++++++++STATIC_TEXT label='Editable list item.'
+++++++++++++++UNKNOWN label='Editable list item.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-fuchsia.txt b/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-fuchsia.txt
new file mode 100644
index 0000000..089f01d
--- /dev/null
+++ b/content/test/data/accessibility/html/contenteditable-with-embedded-contenteditables-expected-fuchsia.txt
@@ -0,0 +1,18 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN focusable actions='{DEFAULT}' value='This is editable.<newline><newline>This is not editable.<newline><newline><newline>But this one is.<newline><newline>So is this one.'
+++++++++PARAGRAPH actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='This is editable.'
+++++++++++++UNKNOWN label='This is editable.'
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT label='This is not editable.'
+++++++++++++UNKNOWN label='This is not editable.'
+++++++++++UNKNOWN label='<newline>' actions='{DEFAULT}'
+++++++++++++UNKNOWN label='<newline>'
+++++++++++PARAGRAPH focusable actions='{DEFAULT}' value='But this one is.'
+++++++++++++STATIC_TEXT label='But this one is.'
+++++++++++++++UNKNOWN label='But this one is.'
+++++++++PARAGRAPH actions='{DEFAULT}' value='So is this one.'
+++++++++++STATIC_TEXT label='So is this one.'
+++++++++++++UNKNOWN label='So is this one.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-fuchsia.txt b/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-fuchsia.txt
new file mode 100644
index 0000000..083fbd3
--- /dev/null
+++ b/content/test/data/accessibility/html/contenteditable-with-no-descendants-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN focusable label='label' actions='{DEFAULT}'
+++++++UNKNOWN focusable actions='{DEFAULT}' secondary_label='description'
+++++++UNKNOWN focusable label='title' actions='{DEFAULT}'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='description'
+++++++++++UNKNOWN label='description'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/dd-expected-fuchsia.txt b/content/test/data/accessibility/html/dd-expected-fuchsia.txt
new file mode 100644
index 0000000..2fef7ac6
--- /dev/null
+++ b/content/test/data/accessibility/html/dd-expected-fuchsia.txt
@@ -0,0 +1,10 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Coffee'
+++++++++++++UNKNOWN label='Coffee'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Black hot drink'
+++++++++++++UNKNOWN label='Black hot drink'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/del-expected-fuchsia.txt b/content/test/data/accessibility/html/del-expected-fuchsia.txt
new file mode 100644
index 0000000..cd2f9ac0
--- /dev/null
+++ b/content/test/data/accessibility/html/del-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='I am '
+++++++++++UNKNOWN label='I am '
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='vegetarian'
+++++++++++++UNKNOWN label='vegetarian'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/details-expected-fuchsia.txt b/content/test/data/accessibility/html/details-expected-fuchsia.txt
new file mode 100644
index 0000000..f8da101
--- /dev/null
+++ b/content/test/data/accessibility/html/details-expected-fuchsia.txt
@@ -0,0 +1,27 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable label='details tag' actions='{DEFAULT}'
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT hidden label='%E2%96%B8 '
+++++++++++++STATIC_TEXT label='details tag'
+++++++++++++++UNKNOWN label='details tag'
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='<newline>    '
+++++++++++STATIC_TEXT hidden label='<newline>    '
+++++++++++PARAGRAPH hidden
+++++++++++++STATIC_TEXT hidden label='The details tag specifies additional details that the user can view or hide on demand.'
+++++++++++STATIC_TEXT hidden label='<newline>  '
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable label='details tag open' actions='{DEFAULT}'
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT hidden label='%E2%96%BE '
+++++++++++++STATIC_TEXT label='details tag open'
+++++++++++++++UNKNOWN label='details tag open'
+++++++++UNKNOWN hidden
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='The details tag with open specifies that the details should be visible (open) to the user.'
+++++++++++++++UNKNOWN label='The details tag with open specifies that the details should be visible (open) to the user.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/dfn-expected-fuchsia.txt b/content/test/data/accessibility/html/dfn-expected-fuchsia.txt
new file mode 100644
index 0000000..5d2783fc
--- /dev/null
+++ b/content/test/data/accessibility/html/dfn-expected-fuchsia.txt
@@ -0,0 +1,8 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++STATIC_TEXT label='Web Browser'
+++++++++UNKNOWN label='Web Browser'
+++++++STATIC_TEXT label=' A computer program with a graphical user interface for displaying HTML files, used to navigate the World Wide Web.'
+++++++++UNKNOWN label=' A computer program with a graphical user interface for displaying HTML files, used to navigate '
+++++++++UNKNOWN label='the World Wide Web.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/dialog-expected-fuchsia.txt b/content/test/data/accessibility/html/dialog-expected-fuchsia.txt
new file mode 100644
index 0000000..2c618bf3
--- /dev/null
+++ b/content/test/data/accessibility/html/dialog-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Text in dialog'
+++++++++++UNKNOWN label='Text in dialog'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/div-expected-fuchsia.txt b/content/test/data/accessibility/html/div-expected-fuchsia.txt
new file mode 100644
index 0000000..ef5dd24
--- /dev/null
+++ b/content/test/data/accessibility/html/div-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Unfocusable div'
+++++++++++UNKNOWN label='Unfocusable div'
+++++++UNKNOWN focusable label='Focusable div'
+++++++++STATIC_TEXT label='Focusable div'
+++++++++++UNKNOWN label='Focusable div'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/dl-expected-fuchsia.txt b/content/test/data/accessibility/html/dl-expected-fuchsia.txt
new file mode 100644
index 0000000..7c6c446c
--- /dev/null
+++ b/content/test/data/accessibility/html/dl-expected-fuchsia.txt
@@ -0,0 +1,13 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Term'
+++++++++++++UNKNOWN label='Term'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Description'
+++++++++++++UNKNOWN label='Description'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Definition'
+++++++++++UNKNOWN label='Definition'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/dt-expected-fuchsia.txt b/content/test/data/accessibility/html/dt-expected-fuchsia.txt
new file mode 100644
index 0000000..2fef7ac6
--- /dev/null
+++ b/content/test/data/accessibility/html/dt-expected-fuchsia.txt
@@ -0,0 +1,10 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Coffee'
+++++++++++++UNKNOWN label='Coffee'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Black hot drink'
+++++++++++++UNKNOWN label='Black hot drink'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/element-class-id-src-attr-expected-fuchsia.txt b/content/test/data/accessibility/html/element-class-id-src-attr-expected-fuchsia.txt
new file mode 100644
index 0000000..7e9173d
--- /dev/null
+++ b/content/test/data/accessibility/html/element-class-id-src-attr-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN label='Image'
+++++++++STATIC_TEXT label='Image'
+++++++++++UNKNOWN label='Image'
+++++++IMAGE label='ImageAlt'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/em-expected-fuchsia.txt b/content/test/data/accessibility/html/em-expected-fuchsia.txt
new file mode 100644
index 0000000..e308f723
--- /dev/null
+++ b/content/test/data/accessibility/html/em-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++STATIC_TEXT label='One word is '
+++++++++UNKNOWN label='One word is '
+++++++STATIC_TEXT label='emphasized'
+++++++++UNKNOWN label='emphasized'
+++++++STATIC_TEXT label='.'
+++++++++UNKNOWN label='.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/embed-expected-fuchsia.txt b/content/test/data/accessibility/html/embed-expected-fuchsia.txt
new file mode 100644
index 0000000..4e79b7a
--- /dev/null
+++ b/content/test/data/accessibility/html/embed-expected-fuchsia.txt
@@ -0,0 +1,4 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/fieldset-expected-fuchsia.txt b/content/test/data/accessibility/html/fieldset-expected-fuchsia.txt
new file mode 100644
index 0000000..19bbd0c
--- /dev/null
+++ b/content/test/data/accessibility/html/fieldset-expected-fuchsia.txt
@@ -0,0 +1,13 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN label='Browser Engines:'
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='Browser Engines:'
+++++++++++++++UNKNOWN label='Browser Engines:'
+++++++UNKNOWN label='Which cake do you prefer?'
+++++++++UNKNOWN
+++++++++++UNKNOWN label='Which cake do you prefer?'
+++++++++++++STATIC_TEXT label='Which cake do you prefer?'
+++++++++++++++UNKNOWN label='Which cake do you prefer?'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/figcaption-expected-fuchsia.txt b/content/test/data/accessibility/html/figcaption-expected-fuchsia.txt
new file mode 100644
index 0000000..635f7870
--- /dev/null
+++ b/content/test/data/accessibility/html/figcaption-expected-fuchsia.txt
@@ -0,0 +1,8 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN label='Fig.1 - A green Box'
+++++++++IMAGE label='This is a green box.'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Fig.1 - A green Box'
+++++++++++++UNKNOWN label='Fig.1 - A green Box'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/figure-expected-fuchsia.txt b/content/test/data/accessibility/html/figure-expected-fuchsia.txt
new file mode 100644
index 0000000..0ff28b087
--- /dev/null
+++ b/content/test/data/accessibility/html/figure-expected-fuchsia.txt
@@ -0,0 +1,5 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++IMAGE label='Sunspots'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/footer-expected-fuchsia.txt b/content/test/data/accessibility/html/footer-expected-fuchsia.txt
new file mode 100644
index 0000000..9c2b792
--- /dev/null
+++ b/content/test/data/accessibility/html/footer-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Footer element'
+++++++++++UNKNOWN label='Footer element'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/footer-inside-other-section-expected-fuchsia.txt b/content/test/data/accessibility/html/footer-inside-other-section-expected-fuchsia.txt
new file mode 100644
index 0000000..6aafa42
--- /dev/null
+++ b/content/test/data/accessibility/html/footer-inside-other-section-expected-fuchsia.txt
@@ -0,0 +1,18 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='footer inside article.'
+++++++++++++++UNKNOWN label='footer inside article.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='footer inside section.'
+++++++++++++++UNKNOWN label='footer inside section.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='footer inside main.'
+++++++++++++++UNKNOWN label='footer inside main.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/form-expected-fuchsia.txt b/content/test/data/accessibility/html/form-expected-fuchsia.txt
new file mode 100644
index 0000000..0093440
--- /dev/null
+++ b/content/test/data/accessibility/html/form-expected-fuchsia.txt
@@ -0,0 +1,11 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++BUTTON focusable label='Submit' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Submit'
+++++++++++++UNKNOWN label='Submit'
+++++++UNKNOWN label='Name'
+++++++++BUTTON focusable label='Submit' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Submit'
+++++++++++++UNKNOWN label='Submit'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/form-validation-message-expected-fuchsia.txt b/content/test/data/accessibility/html/form-validation-message-expected-fuchsia.txt
new file mode 100644
index 0000000..2068b285
--- /dev/null
+++ b/content/test/data/accessibility/html/form-validation-message-expected-fuchsia.txt
@@ -0,0 +1,10 @@
+UNKNOWN focusable
+++UNKNOWN label='Please enter pet name'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Pet name:'
+++++++++++++UNKNOWN label='Pet name:'
+++++++++TEXT_FIELD focusable label='Pet name:' actions='{DEFAULT, SET_VALUE}'
+++++++++++UNKNOWN
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/frameset-expected-fuchsia.txt b/content/test/data/accessibility/html/frameset-expected-fuchsia.txt
new file mode 100644
index 0000000..eacd27e
--- /dev/null
+++ b/content/test/data/accessibility/html/frameset-expected-fuchsia.txt
@@ -0,0 +1,32 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN focusable
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++PARAGRAPH
+++++++++++++++++STATIC_TEXT label='My favorite browser is '
+++++++++++++++++++UNKNOWN label='My favorite browser is '
+++++++++++++++++UNKNOWN
+++++++++++++++++++STATIC_TEXT label='ABC'
+++++++++++++++++++++UNKNOWN label='ABC'
+++++++++++++++++STATIC_TEXT label=' '
+++++++++++++++++++UNKNOWN label=' '
+++++++++++++++++UNKNOWN
+++++++++++++++++++STATIC_TEXT label='Chrome'
+++++++++++++++++++++UNKNOWN label='Chrome'
+++++++++++++++++STATIC_TEXT label='!'
+++++++++++++++++++UNKNOWN label='!'
+++++++UNKNOWN focusable
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++PARAGRAPH
+++++++++++++++++STATIC_TEXT label='This test is to check '
+++++++++++++++++++UNKNOWN label='This test is to check '
+++++++++++++++++UNKNOWN
+++++++++++++++++++STATIC_TEXT label='mark tag'
+++++++++++++++++++++UNKNOWN label='mark tag'
+++++++++++++++++STATIC_TEXT label='.'
+++++++++++++++++++UNKNOWN label='.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/header-expected-fuchsia.txt b/content/test/data/accessibility/html/header-expected-fuchsia.txt
new file mode 100644
index 0000000..7f4467c
--- /dev/null
+++ b/content/test/data/accessibility/html/header-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++HEADER
+++++++++STATIC_TEXT label='Chromium Browser'
+++++++++++UNKNOWN label='Chromium Browser'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/header-inside-other-section-expected-fuchsia.txt b/content/test/data/accessibility/html/header-inside-other-section-expected-fuchsia.txt
new file mode 100644
index 0000000..b6dbf5e
--- /dev/null
+++ b/content/test/data/accessibility/html/header-inside-other-section-expected-fuchsia.txt
@@ -0,0 +1,18 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='Header inside article.'
+++++++++++++++UNKNOWN label='Header inside article.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='Header inside section.'
+++++++++++++++UNKNOWN label='Header inside section.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='Header inside main.'
+++++++++++++++UNKNOWN label='Header inside main.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/heading-expected-fuchsia.txt b/content/test/data/accessibility/html/heading-expected-fuchsia.txt
new file mode 100644
index 0000000..ea1e8b06
--- /dev/null
+++ b/content/test/data/accessibility/html/heading-expected-fuchsia.txt
@@ -0,0 +1,21 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN label='Heading 1'
+++++++++STATIC_TEXT label='Heading 1'
+++++++++++UNKNOWN label='Heading 1'
+++++++UNKNOWN label='Heading 2'
+++++++++STATIC_TEXT label='Heading 2'
+++++++++++UNKNOWN label='Heading 2'
+++++++UNKNOWN label='Heading 3'
+++++++++STATIC_TEXT label='Heading 3'
+++++++++++UNKNOWN label='Heading 3'
+++++++UNKNOWN label='Heading 4'
+++++++++STATIC_TEXT label='Heading 4'
+++++++++++UNKNOWN label='Heading 4'
+++++++UNKNOWN label='Heading 5'
+++++++++STATIC_TEXT label='Heading 5'
+++++++++++UNKNOWN label='Heading 5'
+++++++UNKNOWN label='Heading 6'
+++++++++STATIC_TEXT label='Heading 6'
+++++++++++UNKNOWN label='Heading 6'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/heading-with-tabIndex-expected-fuchsia.txt b/content/test/data/accessibility/html/heading-with-tabIndex-expected-fuchsia.txt
new file mode 100644
index 0000000..e2ba592
--- /dev/null
+++ b/content/test/data/accessibility/html/heading-with-tabIndex-expected-fuchsia.txt
@@ -0,0 +1,15 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN label='Heading 2, no tabIndex'
+++++++++STATIC_TEXT label='Heading 2, no tabIndex'
+++++++++++UNKNOWN label='Heading 2, no tabIndex'
+++++++UNKNOWN focusable label='Heading 1, tabIndex of negative 1'
+++++++++STATIC_TEXT label='Heading 1, tabIndex of negative 1'
+++++++++++UNKNOWN label='Heading 1, tabIndex of negative 1'
+++++++UNKNOWN focusable label='Heading 3, tabIndex of 0'
+++++++++STATIC_TEXT label='Heading 3, tabIndex of 0'
+++++++++++UNKNOWN label='Heading 3, tabIndex of 0'
+++++++UNKNOWN focusable label='Heading 4, tabIndex of 1'
+++++++++STATIC_TEXT label='Heading 4, tabIndex of 1'
+++++++++++UNKNOWN label='Heading 4, tabIndex of 1'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/hr-expected-fuchsia.txt b/content/test/data/accessibility/html/hr-expected-fuchsia.txt
new file mode 100644
index 0000000..0fb864c
--- /dev/null
+++ b/content/test/data/accessibility/html/hr-expected-fuchsia.txt
@@ -0,0 +1,14 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='Before.'
+++++++++++UNKNOWN label='Before.'
+++++++UNKNOWN label='Dividing line' actions='{SET_VALUE}'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='Middle.'
+++++++++++UNKNOWN label='Middle.'
+++++++UNKNOWN actions='{SET_VALUE}'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='After.'
+++++++++++UNKNOWN label='After.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/html-expected-fuchsia.txt b/content/test/data/accessibility/html/html-expected-fuchsia.txt
new file mode 100644
index 0000000..6c011d9
--- /dev/null
+++ b/content/test/data/accessibility/html/html-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable label='HTML element'
+++UNKNOWN hidden
+++++UNKNOWN label='BODY element'
+++++++BUTTON focusable label='Button element' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Button'
+++++++++++UNKNOWN label='Button'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/i-expected-fuchsia.txt b/content/test/data/accessibility/html/i-expected-fuchsia.txt
new file mode 100644
index 0000000..b191ed2
--- /dev/null
+++ b/content/test/data/accessibility/html/i-expected-fuchsia.txt
@@ -0,0 +1,10 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='This is to check '
+++++++++++UNKNOWN label='This is to check '
+++++++++STATIC_TEXT label='italic property'
+++++++++++UNKNOWN label='italic property'
+++++++++STATIC_TEXT label=' using i tag.'
+++++++++++UNKNOWN label=' using i tag.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/id-expected-fuchsia.txt b/content/test/data/accessibility/html/id-expected-fuchsia.txt
new file mode 100644
index 0000000..ca5490bf
--- /dev/null
+++ b/content/test/data/accessibility/html/id-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='No id'
+++++++++++UNKNOWN label='No id'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='Has id'
+++++++++++UNKNOWN label='Has id'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/iframe-coordinates-expected-fuchsia.txt b/content/test/data/accessibility/html/iframe-coordinates-expected-fuchsia.txt
new file mode 100644
index 0000000..5e1ca77
--- /dev/null
+++ b/content/test/data/accessibility/html/iframe-coordinates-expected-fuchsia.txt
@@ -0,0 +1,29 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++BUTTON focusable label='Button' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Button'
+++++++++++++UNKNOWN label='Button'
+++++++UNKNOWN
+++++++++BUTTON focusable label='Button' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Button'
+++++++++++++UNKNOWN label='Button'
+++++++UNKNOWN
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN focusable
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN
+++++++++++++++++++BUTTON focusable label='Ordinary Button' actions='{DEFAULT}'
+++++++++++++++++++++STATIC_TEXT label='Ordinary Button'
+++++++++++++++++++++++UNKNOWN label='Ordinary Button'
+++++++UNKNOWN
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN focusable
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN
+++++++++++++++++++BUTTON focusable label='Scrolled Button' actions='{DEFAULT}'
+++++++++++++++++++++STATIC_TEXT label='Scrolled Button'
+++++++++++++++++++++++UNKNOWN label='Scrolled Button'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/iframe-create-empty-expected-fuchsia.txt b/content/test/data/accessibility/html/iframe-create-empty-expected-fuchsia.txt
new file mode 100644
index 0000000..7da02c9
--- /dev/null
+++ b/content/test/data/accessibility/html/iframe-create-empty-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/iframe-create-expected-fuchsia.txt b/content/test/data/accessibility/html/iframe-create-expected-fuchsia.txt
new file mode 100644
index 0000000..0c28c613
--- /dev/null
+++ b/content/test/data/accessibility/html/iframe-create-expected-fuchsia.txt
@@ -0,0 +1,10 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN
+++++++++++++++LINK focusable label='done' actions='{DEFAULT}'
+++++++++++++++++STATIC_TEXT label='done'
+++++++++++++++++++UNKNOWN label='done'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/iframe-empty-positioned-expected-fuchsia.txt b/content/test/data/accessibility/html/iframe-empty-positioned-expected-fuchsia.txt
new file mode 100644
index 0000000..98f261e9
--- /dev/null
+++ b/content/test/data/accessibility/html/iframe-empty-positioned-expected-fuchsia.txt
@@ -0,0 +1,11 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN focusable
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++UNKNOWN focusable
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/iframe-expected-fuchsia.txt b/content/test/data/accessibility/html/iframe-expected-fuchsia.txt
new file mode 100644
index 0000000..a455b73a
--- /dev/null
+++ b/content/test/data/accessibility/html/iframe-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable label='Empty iframe'
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/iframe-presentational-expected-fuchsia.txt b/content/test/data/accessibility/html/iframe-presentational-expected-fuchsia.txt
new file mode 100644
index 0000000..7da02c9
--- /dev/null
+++ b/content/test/data/accessibility/html/iframe-presentational-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/iframe-scrollable-expected-fuchsia.txt b/content/test/data/accessibility/html/iframe-scrollable-expected-fuchsia.txt
new file mode 100644
index 0000000..e6b8f8ec
--- /dev/null
+++ b/content/test/data/accessibility/html/iframe-scrollable-expected-fuchsia.txt
@@ -0,0 +1,14 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable label='Scrollable iframe'
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN
+++++++++++++++++STATIC_TEXT label='visible text'
+++++++++++++++++++UNKNOWN label='visible text'
+++++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN
+++++++++++++++++STATIC_TEXT label='hidden from viewport'
+++++++++++++++++++UNKNOWN label='hidden from viewport'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/iframe-srcdoc-changed-expected-fuchsia.txt b/content/test/data/accessibility/html/iframe-srcdoc-changed-expected-fuchsia.txt
new file mode 100644
index 0000000..0219144
--- /dev/null
+++ b/content/test/data/accessibility/html/iframe-srcdoc-changed-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable label='done'
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/iframe-transform-expected-fuchsia.txt b/content/test/data/accessibility/html/iframe-transform-expected-fuchsia.txt
new file mode 100644
index 0000000..b43ec5bd4
--- /dev/null
+++ b/content/test/data/accessibility/html/iframe-transform-expected-fuchsia.txt
@@ -0,0 +1,13 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN focusable
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++IMAGE
+++++++UNKNOWN focusable
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++IMAGE
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/ignored-selection-between-text-expected-fuchsia.txt b/content/test/data/accessibility/html/ignored-selection-between-text-expected-fuchsia.txt
new file mode 100644
index 0000000..a4cbabe
--- /dev/null
+++ b/content/test/data/accessibility/html/ignored-selection-between-text-expected-fuchsia.txt
@@ -0,0 +1,19 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN hidden
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='before selection'
+++++++++++++UNKNOWN label='before selection'
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='Some ignored text'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='this text is not ignored'
+++++++++++++UNKNOWN label='this text is not ignored'
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='this text is ignored'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='after selection'
+++++++++++++UNKNOWN label='after selection'
+++++++STATIC_TEXT label='Done'
+++++++++UNKNOWN label='Done'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/ignored-selection-expected-fuchsia.txt b/content/test/data/accessibility/html/ignored-selection-expected-fuchsia.txt
new file mode 100644
index 0000000..1074784d
--- /dev/null
+++ b/content/test/data/accessibility/html/ignored-selection-expected-fuchsia.txt
@@ -0,0 +1,13 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH hidden
+++++++STATIC_TEXT label='this text is not ignored'
+++++++++UNKNOWN label='this text is not ignored'
+++++++UNKNOWN hidden
+++++++++STATIC_TEXT hidden label='this text is ignored'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='after selection'
+++++++++++UNKNOWN label='after selection'
+++++++STATIC_TEXT label='Done'
+++++++++UNKNOWN label='Done'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/ignored-selection-no-unignored-expected-fuchsia.txt b/content/test/data/accessibility/html/ignored-selection-no-unignored-expected-fuchsia.txt
new file mode 100644
index 0000000..f3c7bdb5
--- /dev/null
+++ b/content/test/data/accessibility/html/ignored-selection-no-unignored-expected-fuchsia.txt
@@ -0,0 +1,10 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='Some ignored text'
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='this text is also ignored'
+++++++STATIC_TEXT label='Done'
+++++++++UNKNOWN label='Done'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/img-empty-alt-expected-fuchsia.txt b/content/test/data/accessibility/html/img-empty-alt-expected-fuchsia.txt
new file mode 100644
index 0000000..71ad0b8
--- /dev/null
+++ b/content/test/data/accessibility/html/img-empty-alt-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++IMAGE
+++++++IMAGE
+++++++IMAGE label='full'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/img-expected-fuchsia.txt b/content/test/data/accessibility/html/img-expected-fuchsia.txt
new file mode 100644
index 0000000..5a97c2fb
--- /dev/null
+++ b/content/test/data/accessibility/html/img-expected-fuchsia.txt
@@ -0,0 +1,11 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++IMAGE label='pipe'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++IMAGE label='  '
+++++++STATIC_TEXT label=' '
+++++++IMAGE label='SVG face'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/img-link-empty-alt-expected-fuchsia.txt b/content/test/data/accessibility/html/img-link-empty-alt-expected-fuchsia.txt
new file mode 100644
index 0000000..920fbdea
--- /dev/null
+++ b/content/test/data/accessibility/html/img-link-empty-alt-expected-fuchsia.txt
@@ -0,0 +1,16 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++LINK focusable label='unread ' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='unread '
+++++++++++UNKNOWN label='unread '
+++++++LINK focusable label='read ' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='read '
+++++++++++UNKNOWN label='read '
+++++++LINK focusable label='read ' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='read '
+++++++++++UNKNOWN label='read '
+++++++LINK focusable label='read' actions='{DEFAULT}'
+++++++++IMAGE actions='{DEFAULT}'
+++++++++STATIC_TEXT label='read'
+++++++++++UNKNOWN label='read'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/in-page-links-expected-fuchsia.txt b/content/test/data/accessibility/html/in-page-links-expected-fuchsia.txt
new file mode 100644
index 0000000..0e12891
--- /dev/null
+++ b/content/test/data/accessibility/html/in-page-links-expected-fuchsia.txt
@@ -0,0 +1,54 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++LINK focusable label='Empty anchor' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Empty anchor'
+++++++++++UNKNOWN label='Empty anchor'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++LINK focusable label='Anchor with content' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Anchor with content'
+++++++++++UNKNOWN label='Anchor with content'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++LINK focusable label='Anchor with ID' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Anchor with ID'
+++++++++++UNKNOWN label='Anchor with ID'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++LINK focusable label='Empty span' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Empty span'
+++++++++++UNKNOWN label='Empty span'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++LINK focusable label='Span with content' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Span with content'
+++++++++++UNKNOWN label='Span with content'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++LINK focusable label='Paragraph with content' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Paragraph with content'
+++++++++++UNKNOWN label='Paragraph with content'
+++++++PARAGRAPH
+++++++++UNKNOWN
+++++++++STATIC_TEXT label='After empty anchor'
+++++++++++UNKNOWN label='After empty anchor'
+++++++PARAGRAPH
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Anchor with content'
+++++++++++++UNKNOWN label='Anchor with content'
+++++++PARAGRAPH
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Anchor with ID'
+++++++++++++UNKNOWN label='Anchor with ID'
+++++++PARAGRAPH
+++++++++UNKNOWN
+++++++++STATIC_TEXT label='After empty span'
+++++++++++UNKNOWN label='After empty span'
+++++++PARAGRAPH
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Span with content'
+++++++++++++UNKNOWN label='Span with content'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='Paragraph with content'
+++++++++++UNKNOWN label='Paragraph with content'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-button-expected-fuchsia.txt b/content/test/data/accessibility/html/input-button-expected-fuchsia.txt
new file mode 100644
index 0000000..8e26f60
--- /dev/null
+++ b/content/test/data/accessibility/html/input-button-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++BUTTON focusable label='Button' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Button'
+++++++++++UNKNOWN label='Button'
+++++++BUTTON focusable label='Name' actions='{DEFAULT}' secondary_label='Description'
+++++++++STATIC_TEXT label='Description'
+++++++++++UNKNOWN label='Description'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-checkbox-expected-fuchsia.txt b/content/test/data/accessibility/html/input-checkbox-expected-fuchsia.txt
new file mode 100644
index 0000000..532f051
--- /dev/null
+++ b/content/test/data/accessibility/html/input-checkbox-expected-fuchsia.txt
@@ -0,0 +1,18 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN hidden
+++++++++CHECK_BOX focusable label='Checkbox0' actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++STATIC_TEXT hidden label='Checkbox0'
+++++++UNKNOWN hidden
+++++++++CHECK_BOX focusable label='Checkbox1' actions='{DEFAULT}' checked_state='CHECKED'
+++++++++STATIC_TEXT hidden label='Checkbox1'
+++++++UNKNOWN hidden
+++++++++CHECK_BOX focusable label='Checkbox2' actions='{DEFAULT}' checked_state='CHECKED'
+++++++++STATIC_TEXT hidden label='Checkbox2'
+++++++UNKNOWN hidden
+++++++++CHECK_BOX focusable label='Checkbox3' actions='{DEFAULT}' checked_state='CHECKED'
+++++++++STATIC_TEXT hidden label='Checkbox3'
+++++++UNKNOWN hidden
+++++++++CHECK_BOX focusable label='Checkbox4' actions='{DEFAULT}' checked_state='MIXED'
+++++++++STATIC_TEXT hidden label='Checkbox4'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-checkbox-in-menu-expected-fuchsia.txt b/content/test/data/accessibility/html/input-checkbox-in-menu-expected-fuchsia.txt
new file mode 100644
index 0000000..2d38695
--- /dev/null
+++ b/content/test/data/accessibility/html/input-checkbox-in-menu-expected-fuchsia.txt
@@ -0,0 +1,17 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++CHECK_BOX focusable label='Checkbox1' actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++++STATIC_TEXT hidden label='Checkbox1'
+++++++++UNKNOWN hidden
+++++++++++CHECK_BOX focusable label='Checkbox2' actions='{DEFAULT}' checked_state='CHECKED'
+++++++++++STATIC_TEXT hidden label='Checkbox2'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++CHECK_BOX focusable label='Checkbox3' actions='{DEFAULT}' checked_state='CHECKED'
+++++++++++STATIC_TEXT hidden label='Checkbox3'
+++++++++UNKNOWN hidden
+++++++++++CHECK_BOX focusable label='Checkbox4' actions='{DEFAULT}' checked_state='MIXED'
+++++++++++STATIC_TEXT hidden label='Checkbox4'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-checkbox-label-expected-fuchsia.txt b/content/test/data/accessibility/html/input-checkbox-label-expected-fuchsia.txt
new file mode 100644
index 0000000..ba58697f
--- /dev/null
+++ b/content/test/data/accessibility/html/input-checkbox-label-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN hidden
+++++++++CHECK_BOX focusable label='Checkbox Title' actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++STATIC_TEXT hidden label=' Checkbox Title'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-color-expected-fuchsia.txt b/content/test/data/accessibility/html/input-color-expected-fuchsia.txt
new file mode 100644
index 0000000..68fe3d2
--- /dev/null
+++ b/content/test/data/accessibility/html/input-color-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable actions='{DEFAULT, SET_VALUE}' value='#ff9900'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-date-expected-fuchsia.txt b/content/test/data/accessibility/html/input-date-expected-fuchsia.txt
new file mode 100644
index 0000000..c051d56
--- /dev/null
+++ b/content/test/data/accessibility/html/input-date-expected-fuchsia.txt
@@ -0,0 +1,37 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable actions='{DEFAULT, SET_VALUE}' value='2008-09-01'
+++++++++UNKNOWN
+++++++++++UNKNOWN
+++++++++++++UNKNOWN focusable label='Month' actions='{SET_VALUE}' value='09'
+++++++++++++++STATIC_TEXT label='09'
+++++++++++++++++UNKNOWN label='09'
+++++++++++++STATIC_TEXT label='/'
+++++++++++++++UNKNOWN label='/'
+++++++++++++UNKNOWN focusable label='Day' actions='{SET_VALUE}' value='01'
+++++++++++++++STATIC_TEXT label='01'
+++++++++++++++++UNKNOWN label='01'
+++++++++++++STATIC_TEXT label='/'
+++++++++++++++UNKNOWN label='/'
+++++++++++++UNKNOWN focusable label='Year' actions='{SET_VALUE}' value='2008'
+++++++++++++++STATIC_TEXT label='2008'
+++++++++++++++++UNKNOWN label='2008'
+++++++++UNKNOWN focusable label='Show date picker'
+++++++UNKNOWN focusable label='When' actions='{DEFAULT, SET_VALUE}' value='2008-09-01'
+++++++++UNKNOWN
+++++++++++UNKNOWN
+++++++++++++UNKNOWN focusable label='Month When' actions='{SET_VALUE}' value='09'
+++++++++++++++STATIC_TEXT label='09'
+++++++++++++++++UNKNOWN label='09'
+++++++++++++STATIC_TEXT label='/'
+++++++++++++++UNKNOWN label='/'
+++++++++++++UNKNOWN focusable label='Day When' actions='{SET_VALUE}' value='01'
+++++++++++++++STATIC_TEXT label='01'
+++++++++++++++++UNKNOWN label='01'
+++++++++++++STATIC_TEXT label='/'
+++++++++++++++UNKNOWN label='/'
+++++++++++++UNKNOWN focusable label='Year When' actions='{SET_VALUE}' value='2008'
+++++++++++++++STATIC_TEXT label='2008'
+++++++++++++++++UNKNOWN label='2008'
+++++++++UNKNOWN focusable label='Show date picker'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-date-with-popup-open-expected-fuchsia.txt b/content/test/data/accessibility/html/input-date-with-popup-open-expected-fuchsia.txt
new file mode 100644
index 0000000..ad7feb5
--- /dev/null
+++ b/content/test/data/accessibility/html/input-date-with-popup-open-expected-fuchsia.txt
@@ -0,0 +1,146 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable actions='{DEFAULT, SET_VALUE}' value='2008-09-01'
+++++++++UNKNOWN
+++++++++++UNKNOWN
+++++++++++++UNKNOWN focusable label='Month' actions='{SET_VALUE}' value='09'
+++++++++++++++STATIC_TEXT label='09'
+++++++++++++++++UNKNOWN label='09'
+++++++++++++STATIC_TEXT label='/'
+++++++++++++++UNKNOWN label='/'
+++++++++++++UNKNOWN focusable label='Day' actions='{SET_VALUE}' value='01'
+++++++++++++++STATIC_TEXT label='01'
+++++++++++++++++UNKNOWN label='01'
+++++++++++++STATIC_TEXT label='/'
+++++++++++++++UNKNOWN label='/'
+++++++++++++UNKNOWN focusable label='Year' actions='{SET_VALUE}' value='2008'
+++++++++++++++STATIC_TEXT label='2008'
+++++++++++++++++UNKNOWN label='2008'
+++++++++UNKNOWN focusable label='Show date picker'
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++++++UNKNOWN hidden
+++++++++++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++++++++++BUTTON focusable label='Show month selection panel' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='September 2008'
+++++++++++++++++++++++++IMAGE actions='{DEFAULT}'
+++++++++++++++++++++BUTTON focusable label='Show previous month' actions='{DEFAULT}'
+++++++++++++++++++++++IMAGE actions='{DEFAULT}'
+++++++++++++++++++++BUTTON focusable label='Show next month' actions='{DEFAULT}'
+++++++++++++++++++++++IMAGE actions='{DEFAULT}'
+++++++++++++++++++GRID focusable actions='{DEFAULT}' number_of_rows=6 number_of_columns=7
+++++++++++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='S'
+++++++++++++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='M'
+++++++++++++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='T'
+++++++++++++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='W'
+++++++++++++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='T'
+++++++++++++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='F'
+++++++++++++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='S'
+++++++++++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++++++++++++TABLE_ROW actions='{DEFAULT}'
+++++++++++++++++++++++++++CELL focusable label='Sunday, August 31, 2008' actions='{DEFAULT}' cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='31'
+++++++++++++++++++++++++++CELL focusable selected label='Monday, September 1, 2008' actions='{DEFAULT}' cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='1'
+++++++++++++++++++++++++++CELL focusable label='Tuesday, September 2, 2008' actions='{DEFAULT}' cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='2'
+++++++++++++++++++++++++++CELL focusable label='Wednesday, September 3, 2008' actions='{DEFAULT}' cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='3'
+++++++++++++++++++++++++++CELL focusable label='Thursday, September 4, 2008' actions='{DEFAULT}' cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='4'
+++++++++++++++++++++++++++CELL focusable label='Friday, September 5, 2008' actions='{DEFAULT}' cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='5'
+++++++++++++++++++++++++++CELL focusable label='Saturday, September 6, 2008' actions='{DEFAULT}' cell_column_index=6 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='6'
+++++++++++++++++++++++++TABLE_ROW actions='{DEFAULT}' row_index=1
+++++++++++++++++++++++++++CELL focusable label='Sunday, September 7, 2008' actions='{DEFAULT}' cell_row_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='7'
+++++++++++++++++++++++++++CELL focusable label='Monday, September 8, 2008' actions='{DEFAULT}' cell_row_index=1 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='8'
+++++++++++++++++++++++++++CELL focusable label='Tuesday, September 9, 2008' actions='{DEFAULT}' cell_row_index=1 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='9'
+++++++++++++++++++++++++++CELL focusable label='Wednesday, September 10, 2008' actions='{DEFAULT}' cell_row_index=1 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='10'
+++++++++++++++++++++++++++CELL focusable label='Thursday, September 11, 2008' actions='{DEFAULT}' cell_row_index=1 cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='11'
+++++++++++++++++++++++++++CELL focusable label='Friday, September 12, 2008' actions='{DEFAULT}' cell_row_index=1 cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='12'
+++++++++++++++++++++++++++CELL focusable label='Saturday, September 13, 2008' actions='{DEFAULT}' cell_row_index=1 cell_column_index=6 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='13'
+++++++++++++++++++++++++TABLE_ROW actions='{DEFAULT}' row_index=2
+++++++++++++++++++++++++++CELL focusable label='Sunday, September 14, 2008' actions='{DEFAULT}' cell_row_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='14'
+++++++++++++++++++++++++++CELL focusable label='Monday, September 15, 2008' actions='{DEFAULT}' cell_row_index=2 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='15'
+++++++++++++++++++++++++++CELL focusable label='Tuesday, September 16, 2008' actions='{DEFAULT}' cell_row_index=2 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='16'
+++++++++++++++++++++++++++CELL focusable label='Wednesday, September 17, 2008' actions='{DEFAULT}' cell_row_index=2 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='17'
+++++++++++++++++++++++++++CELL focusable label='Thursday, September 18, 2008' actions='{DEFAULT}' cell_row_index=2 cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='18'
+++++++++++++++++++++++++++CELL focusable label='Friday, September 19, 2008' actions='{DEFAULT}' cell_row_index=2 cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='19'
+++++++++++++++++++++++++++CELL focusable label='Saturday, September 20, 2008' actions='{DEFAULT}' cell_row_index=2 cell_column_index=6 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='20'
+++++++++++++++++++++++++TABLE_ROW actions='{DEFAULT}' row_index=3
+++++++++++++++++++++++++++CELL focusable label='Sunday, September 21, 2008' actions='{DEFAULT}' cell_row_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='21'
+++++++++++++++++++++++++++CELL focusable label='Monday, September 22, 2008' actions='{DEFAULT}' cell_row_index=3 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='22'
+++++++++++++++++++++++++++CELL focusable label='Tuesday, September 23, 2008' actions='{DEFAULT}' cell_row_index=3 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='23'
+++++++++++++++++++++++++++CELL focusable label='Wednesday, September 24, 2008' actions='{DEFAULT}' cell_row_index=3 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='24'
+++++++++++++++++++++++++++CELL focusable label='Thursday, September 25, 2008' actions='{DEFAULT}' cell_row_index=3 cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='25'
+++++++++++++++++++++++++++CELL focusable label='Friday, September 26, 2008' actions='{DEFAULT}' cell_row_index=3 cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='26'
+++++++++++++++++++++++++++CELL focusable label='Saturday, September 27, 2008' actions='{DEFAULT}' cell_row_index=3 cell_column_index=6 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='27'
+++++++++++++++++++++++++TABLE_ROW actions='{DEFAULT}' row_index=4
+++++++++++++++++++++++++++CELL focusable label='Sunday, September 28, 2008' actions='{DEFAULT}' cell_row_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='28'
+++++++++++++++++++++++++++CELL focusable label='Monday, September 29, 2008' actions='{DEFAULT}' cell_row_index=4 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='29'
+++++++++++++++++++++++++++CELL focusable label='Tuesday, September 30, 2008' actions='{DEFAULT}' cell_row_index=4 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='30'
+++++++++++++++++++++++++++CELL focusable label='Wednesday, October 1, 2008' actions='{DEFAULT}' cell_row_index=4 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='1'
+++++++++++++++++++++++++++CELL focusable label='Thursday, October 2, 2008' actions='{DEFAULT}' cell_row_index=4 cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='2'
+++++++++++++++++++++++++++CELL focusable label='Friday, October 3, 2008' actions='{DEFAULT}' cell_row_index=4 cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='3'
+++++++++++++++++++++++++++CELL focusable label='Saturday, October 4, 2008' actions='{DEFAULT}' cell_row_index=4 cell_column_index=6 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='4'
+++++++++++++++++++++++++TABLE_ROW actions='{DEFAULT}' row_index=5
+++++++++++++++++++++++++++CELL focusable label='Sunday, October 5, 2008' actions='{DEFAULT}' cell_row_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='5'
+++++++++++++++++++++++++++CELL focusable label='Monday, October 6, 2008' actions='{DEFAULT}' cell_row_index=5 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='6'
+++++++++++++++++++++++++++CELL focusable label='Tuesday, October 7, 2008' actions='{DEFAULT}' cell_row_index=5 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='7'
+++++++++++++++++++++++++++CELL focusable label='Wednesday, October 8, 2008' actions='{DEFAULT}' cell_row_index=5 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='8'
+++++++++++++++++++++++++++CELL focusable label='Thursday, October 9, 2008' actions='{DEFAULT}' cell_row_index=5 cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='9'
+++++++++++++++++++++++++++CELL focusable label='Friday, October 10, 2008' actions='{DEFAULT}' cell_row_index=5 cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='10'
+++++++++++++++++++++++++++CELL focusable label='Saturday, October 11, 2008' actions='{DEFAULT}' cell_row_index=5 cell_column_index=6 cell_row_span=1 cell_column_span=1
+++++++++++++++++++++++++++++STATIC_TEXT label='11'
+++++++++++++++++++++BUTTON focusable label='Clear' actions='{DEFAULT}'
+++++++++++++++++++++++STATIC_TEXT label='Clear'
+++++++++++++++++++++BUTTON focusable label='Today' actions='{DEFAULT}'
+++++++++++++++++++++++STATIC_TEXT label='Today'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-datetime-expected-fuchsia.txt b/content/test/data/accessibility/html/input-datetime-expected-fuchsia.txt
new file mode 100644
index 0000000..cfa9f960
--- /dev/null
+++ b/content/test/data/accessibility/html/input-datetime-expected-fuchsia.txt
@@ -0,0 +1,11 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='1/1/2015 1:00AM'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='1/1/2015 1:00AM'
+++++++++++++UNKNOWN label='1/1/2015 1:00AM'
+++++++TEXT_FIELD focusable label='Launch' actions='{DEFAULT, SET_VALUE}' value='1/1/2015 1:00AM'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='1/1/2015 1:00AM'
+++++++++++++UNKNOWN label='1/1/2015 1:00AM'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-datetime-local-expected-fuchsia.txt b/content/test/data/accessibility/html/input-datetime-local-expected-fuchsia.txt
new file mode 100644
index 0000000..f7f3ccb
--- /dev/null
+++ b/content/test/data/accessibility/html/input-datetime-local-expected-fuchsia.txt
@@ -0,0 +1,35 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN
+++++++++++UNKNOWN
+++++++++++++UNKNOWN focusable label='Month' actions='{SET_VALUE}'
+++++++++++++++STATIC_TEXT label='mm'
+++++++++++++++++UNKNOWN label='mm'
+++++++++++++STATIC_TEXT label='/'
+++++++++++++++UNKNOWN label='/'
+++++++++++++UNKNOWN focusable label='Day' actions='{SET_VALUE}'
+++++++++++++++STATIC_TEXT label='dd'
+++++++++++++++++UNKNOWN label='dd'
+++++++++++++STATIC_TEXT label='/'
+++++++++++++++UNKNOWN label='/'
+++++++++++++UNKNOWN focusable label='Year' actions='{SET_VALUE}'
+++++++++++++++STATIC_TEXT label='yyyy'
+++++++++++++++++UNKNOWN label='yyyy'
+++++++++++++STATIC_TEXT label=', '
+++++++++++++++UNKNOWN label=', '
+++++++++++++UNKNOWN focusable label='Hours' actions='{SET_VALUE}'
+++++++++++++++STATIC_TEXT label='--'
+++++++++++++++++UNKNOWN label='--'
+++++++++++++STATIC_TEXT label=':'
+++++++++++++++UNKNOWN label=':'
+++++++++++++UNKNOWN focusable label='Minutes' actions='{SET_VALUE}'
+++++++++++++++STATIC_TEXT label='--'
+++++++++++++++++UNKNOWN label='--'
+++++++++++++STATIC_TEXT label=' '
+++++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN focusable label='AM/PM' actions='{SET_VALUE}'
+++++++++++++++STATIC_TEXT label='--'
+++++++++++++++++UNKNOWN label='--'
+++++++++UNKNOWN focusable label='Show local date and time picker'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-email-expected-fuchsia.txt b/content/test/data/accessibility/html/input-email-expected-fuchsia.txt
new file mode 100644
index 0000000..f08f096
--- /dev/null
+++ b/content/test/data/accessibility/html/input-email-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='someone@example.com'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='someone@example.com'
+++++++++++++UNKNOWN label='someone@example.com'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-file-expected-fuchsia.txt b/content/test/data/accessibility/html/input-file-expected-fuchsia.txt
new file mode 100644
index 0000000..4d78bf8
--- /dev/null
+++ b/content/test/data/accessibility/html/input-file-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++BUTTON focusable label='No file chosen, Choose File' actions='{DEFAULT}' value='No file chosen'
+++++++++BUTTON focusable label='Choose File' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Choose File'
+++++++++++++UNKNOWN label='Choose File'
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='No file chosen'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-image-expected-fuchsia.txt b/content/test/data/accessibility/html/input-image-expected-fuchsia.txt
new file mode 100644
index 0000000..270a6e9
--- /dev/null
+++ b/content/test/data/accessibility/html/input-image-expected-fuchsia.txt
@@ -0,0 +1,4 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++BUTTON focusable label='Submit' actions='{DEFAULT}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-list-expected-fuchsia.txt b/content/test/data/accessibility/html/input-list-expected-fuchsia.txt
new file mode 100644
index 0000000..77bbb16
--- /dev/null
+++ b/content/test/data/accessibility/html/input-list-expected-fuchsia.txt
@@ -0,0 +1,13 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Choose a pokemon '
+++++++++++UNKNOWN label='Choose a pokemon '
+++++++++TEXT_FIELD_WITH_COMBO_BOX focusable label='Choose a pokemon' actions='{DEFAULT, SET_VALUE}'
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++STATIC_TEXT hidden label='%E2%96%BE'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-month-expected-fuchsia.txt b/content/test/data/accessibility/html/input-month-expected-fuchsia.txt
new file mode 100644
index 0000000..5b405fc
--- /dev/null
+++ b/content/test/data/accessibility/html/input-month-expected-fuchsia.txt
@@ -0,0 +1,15 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN
+++++++++++UNKNOWN
+++++++++++++UNKNOWN focusable label='Month' actions='{SET_VALUE}'
+++++++++++++++STATIC_TEXT label='---------'
+++++++++++++++++UNKNOWN label='---------'
+++++++++++++STATIC_TEXT label=' '
+++++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN focusable label='Year' actions='{SET_VALUE}'
+++++++++++++++STATIC_TEXT label='----'
+++++++++++++++++UNKNOWN label='----'
+++++++++UNKNOWN focusable label='Show month picker'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-number-expected-fuchsia.txt b/content/test/data/accessibility/html/input-number-expected-fuchsia.txt
new file mode 100644
index 0000000..a10039a
--- /dev/null
+++ b/content/test/data/accessibility/html/input-number-expected-fuchsia.txt
@@ -0,0 +1,11 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable actions='{DEFAULT, SET_VALUE}' value='1'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='1'
+++++++++++++UNKNOWN label='1'
+++++++UNKNOWN focusable actions='{DEFAULT, SET_VALUE}' value='6'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='6'
+++++++++++++UNKNOWN label='6'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-password-expected-fuchsia.txt b/content/test/data/accessibility/html/input-password-expected-fuchsia.txt
new file mode 100644
index 0000000..9145b70
--- /dev/null
+++ b/content/test/data/accessibility/html/input-password-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2'
+++++++++++++UNKNOWN label='%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2%E2%80%A2'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-radio-expected-fuchsia.txt b/content/test/data/accessibility/html/input-radio-expected-fuchsia.txt
new file mode 100644
index 0000000..2718606a
--- /dev/null
+++ b/content/test/data/accessibility/html/input-radio-expected-fuchsia.txt
@@ -0,0 +1,26 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++RADIO_BUTTON focusable actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++STATIC_TEXT label='Radio1'
+++++++++++UNKNOWN label='Radio1'
+++++++++UNKNOWN label='<newline>'
+++++++++++UNKNOWN label='<newline>'
+++++++++RADIO_BUTTON focusable actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++STATIC_TEXT label='Radio2'
+++++++++++UNKNOWN label='Radio2'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++RADIO_BUTTON focusable label='Radio3' actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++++STATIC_TEXT hidden label='Radio3'
+++++++++UNKNOWN hidden
+++++++++++RADIO_BUTTON focusable label='Radio4' actions='{DEFAULT}' checked_state='CHECKED'
+++++++++++STATIC_TEXT hidden label='Radio4'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++RADIO_BUTTON focusable label='Radio5' actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++++STATIC_TEXT hidden label='Radio5'
+++++++++UNKNOWN hidden
+++++++++++RADIO_BUTTON focusable label='Radio6' actions='{DEFAULT}' checked_state='CHECKED'
+++++++++++STATIC_TEXT hidden label='Radio6'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-radio-in-menu-expected-fuchsia.txt b/content/test/data/accessibility/html/input-radio-in-menu-expected-fuchsia.txt
new file mode 100644
index 0000000..d99a4ec
--- /dev/null
+++ b/content/test/data/accessibility/html/input-radio-in-menu-expected-fuchsia.txt
@@ -0,0 +1,19 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++RADIO_BUTTON focusable actions='{DEFAULT}' checked_state='CHECKED'
+++++++++STATIC_TEXT label='Radio0 '
+++++++++++UNKNOWN label='Radio0 '
+++++++++RADIO_BUTTON focusable actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++STATIC_TEXT label='Radio1 '
+++++++++++UNKNOWN label='Radio1 '
+++++++++UNKNOWN hidden
+++++++++++RADIO_BUTTON focusable label='Radio2' actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++++STATIC_TEXT hidden label='Radio2'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++RADIO_BUTTON focusable label='Radio3' actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++++STATIC_TEXT hidden label='Radio3'
+++++++++RADIO_BUTTON focusable actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++RADIO_BUTTON focusable actions='{DEFAULT}' checked_state='CHECKED'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-radio-wrapped-label-expected-fuchsia.txt b/content/test/data/accessibility/html/input-radio-wrapped-label-expected-fuchsia.txt
new file mode 100644
index 0000000..207a1fc8b
--- /dev/null
+++ b/content/test/data/accessibility/html/input-radio-wrapped-label-expected-fuchsia.txt
@@ -0,0 +1,15 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN label='Appearance'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Appearance'
+++++++++++++UNKNOWN label='Appearance'
+++++++++UNKNOWN
+++++++++++RADIO_BUTTON focusable label='System' actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++++STATIC_TEXT label='System'
+++++++++++++UNKNOWN label='System'
+++++++++UNKNOWN
+++++++++++RADIO_BUTTON focusable label='Dark' actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++++++STATIC_TEXT label='Dark'
+++++++++++++UNKNOWN label='Dark'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-range-expected-fuchsia.txt b/content/test/data/accessibility/html/input-range-expected-fuchsia.txt
new file mode 100644
index 0000000..f76d448
--- /dev/null
+++ b/content/test/data/accessibility/html/input-range-expected-fuchsia.txt
@@ -0,0 +1,8 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++SLIDER focusable actions='{SET_VALUE}'
+++++++SLIDER focusable actions='{SET_VALUE}'
+++++++SLIDER focusable actions='{SET_VALUE}' value='Medium'
+++++++SLIDER focusable actions='{SET_VALUE}' value='Friday'
+++++++SLIDER focusable actions='{SET_VALUE}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-reset-expected-fuchsia.txt b/content/test/data/accessibility/html/input-reset-expected-fuchsia.txt
new file mode 100644
index 0000000..e732816e
--- /dev/null
+++ b/content/test/data/accessibility/html/input-reset-expected-fuchsia.txt
@@ -0,0 +1,8 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN
+++++++BUTTON focusable label='Reset' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Reset'
+++++++++++UNKNOWN label='Reset'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-search-expected-fuchsia.txt b/content/test/data/accessibility/html/input-search-expected-fuchsia.txt
new file mode 100644
index 0000000..0bd267c
--- /dev/null
+++ b/content/test/data/accessibility/html/input-search-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++SEARCH_BOX focusable actions='{DEFAULT, SET_VALUE}' value='Search terms'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Search terms'
+++++++++++++UNKNOWN label='Search terms'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-submit-expected-fuchsia.txt b/content/test/data/accessibility/html/input-submit-expected-fuchsia.txt
new file mode 100644
index 0000000..54b3f24
--- /dev/null
+++ b/content/test/data/accessibility/html/input-submit-expected-fuchsia.txt
@@ -0,0 +1,26 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}'
+++++++++++UNKNOWN
+++++++++BUTTON focusable label='First submit in a form is a valid default button' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='First submit  in a form is a valid default button'
+++++++++++++UNKNOWN label='First submit  in a form is a valid default button'
+++++++++BUTTON focusable label='Second submit in a form not a valid default button' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Second submit in a form not a valid default button'
+++++++++++++UNKNOWN label='Second submit in a form not a valid default button'
+++++++UNKNOWN
+++++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}'
+++++++++++UNKNOWN
+++++++++BUTTON focusable label='First image button in a form is a valid default button' actions='{DEFAULT}'
+++++++++++IMAGE
+++++++++++STATIC_TEXT label='First image button in a form is a valid default button'
+++++++++++++UNKNOWN label='First image button in a form is a valid default button'
+++++++++BUTTON focusable label='Second image button in a form not a valid default button' actions='{DEFAULT}'
+++++++++++IMAGE
+++++++++++STATIC_TEXT label='Second image button in a form not a valid default button'
+++++++++++++UNKNOWN label='Second image button in a form not a valid default button'
+++++++BUTTON focusable label='Submit outside of form not a valid default button' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Submit outside of form not a valid default button'
+++++++++++UNKNOWN label='Submit outside of form not a valid default button'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-suggestions-source-element-expected-fuchsia.txt b/content/test/data/accessibility/html/input-suggestions-source-element-expected-fuchsia.txt
new file mode 100644
index 0000000..ca1873c5
--- /dev/null
+++ b/content/test/data/accessibility/html/input-suggestions-source-element-expected-fuchsia.txt
@@ -0,0 +1,10 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD_WITH_COMBO_BOX focusable actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT hidden label='%E2%96%BE'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-tel-expected-fuchsia.txt b/content/test/data/accessibility/html/input-tel-expected-fuchsia.txt
new file mode 100644
index 0000000..6f98f1f
--- /dev/null
+++ b/content/test/data/accessibility/html/input-tel-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='123-456-7890'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='123-456-7890'
+++++++++++++UNKNOWN label='123-456-7890'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-text-expected-fuchsia.txt b/content/test/data/accessibility/html/input-text-expected-fuchsia.txt
new file mode 100644
index 0000000..30c878a
--- /dev/null
+++ b/content/test/data/accessibility/html/input-text-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD focusable label='Name' actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-text-name-calc-expected-fuchsia.txt b/content/test/data/accessibility/html/input-text-name-calc-expected-fuchsia.txt
new file mode 100644
index 0000000..4975138
--- /dev/null
+++ b/content/test/data/accessibility/html/input-text-name-calc-expected-fuchsia.txt
@@ -0,0 +1,27 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TEXT_FIELD focusable label='Title0' actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN
+++++++TEXT_FIELD focusable label='Label1' actions='{DEFAULT, SET_VALUE}' secondary_label='Title1'
+++++++++UNKNOWN
+++++++TEXT_FIELD focusable label='AriaLabel2' actions='{DEFAULT, SET_VALUE}' secondary_label='Title2'
+++++++++UNKNOWN
+++++++TEXT_FIELD focusable label='LabelledBy3' actions='{DEFAULT, SET_VALUE}' secondary_label='Title3'
+++++++++UNKNOWN
+++++++TEXT_FIELD focusable label='Placeholder4' actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN
+++++++TEXT_FIELD focusable label='ARIA Placeholder4a' actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN
+++++++TEXT_FIELD focusable label='Placeholder4b' actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN
+++++++TEXT_FIELD focusable label='Placeholder5' actions='{DEFAULT, SET_VALUE}' secondary_label='Title5'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN
+++++++TEXT_FIELD focusable label='LabelledBy6' actions='{DEFAULT, SET_VALUE}' secondary_label='DescribedBy6'
+++++++++UNKNOWN
+++++++TEXT_FIELD focusable label='AriaLabel7' actions='{DEFAULT, SET_VALUE}' secondary_label='DescribedBy7'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-text-read-only-expected-fuchsia.txt b/content/test/data/accessibility/html/input-text-read-only-expected-fuchsia.txt
new file mode 100644
index 0000000..a91449d
--- /dev/null
+++ b/content/test/data/accessibility/html/input-text-read-only-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD focusable label='Name' actions='{DEFAULT}'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-text-value-changed-expected-fuchsia.txt b/content/test/data/accessibility/html/input-text-value-changed-expected-fuchsia.txt
new file mode 100644
index 0000000..f232343
--- /dev/null
+++ b/content/test/data/accessibility/html/input-text-value-changed-expected-fuchsia.txt
@@ -0,0 +1,15 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='aaYYYYbb'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='aaYYYYbb'
+++++++++++++UNKNOWN label='aaYYYYbb'
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='aa'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='aa'
+++++++++++++UNKNOWN label='aa'
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='After'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='After'
+++++++++++++UNKNOWN label='After'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-text-value-expected-fuchsia.txt b/content/test/data/accessibility/html/input-text-value-expected-fuchsia.txt
new file mode 100644
index 0000000..0240224
--- /dev/null
+++ b/content/test/data/accessibility/html/input-text-value-expected-fuchsia.txt
@@ -0,0 +1,41 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++STATIC_TEXT label='l1'
+++++++++++UNKNOWN label='l1'
+++++++TEXT_FIELD focusable label='l1' actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN
+++++++UNKNOWN
+++++++++STATIC_TEXT label='l2'
+++++++++++UNKNOWN label='l2'
+++++++TEXT_FIELD focusable label='l2' actions='{DEFAULT, SET_VALUE}' value='value'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='value'
+++++++++++++UNKNOWN label='value'
+++++++TEXT_FIELD focusable label='l2' actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN
+++++++TEXT_FIELD focusable label='l2' actions='{DEFAULT, SET_VALUE}' value='value  *'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='value  *'
+++++++++++++UNKNOWN label='value  *'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Email'
+++++++++++UNKNOWN label='Email'
+++++++UNKNOWN
+++++++TEXT_FIELD focusable label='Email' actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN
+++++++TEXT_FIELD focusable label='Email' actions='{DEFAULT, SET_VALUE}' value='value'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='value'
+++++++++++++UNKNOWN label='value'
+++++++TEXT_FIELD focusable label='l5' actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN
+++++++TEXT_FIELD focusable label='l6' actions='{DEFAULT, SET_VALUE}' value='Value'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Value'
+++++++++++++UNKNOWN label='Value'
+++++++TEXT_FIELD focusable label='Name' actions='{DEFAULT, SET_VALUE}' secondary_label='Description' value='value'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='value'
+++++++++++++UNKNOWN label='value'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-text-with-selection-expected-fuchsia.txt b/content/test/data/accessibility/html/input-text-with-selection-expected-fuchsia.txt
new file mode 100644
index 0000000..6b29d41f
--- /dev/null
+++ b/content/test/data/accessibility/html/input-text-with-selection-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='Selection'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Selection'
+++++++++++++UNKNOWN label='Selection'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-time-expected-fuchsia.txt b/content/test/data/accessibility/html/input-time-expected-fuchsia.txt
new file mode 100644
index 0000000..74eee0c
--- /dev/null
+++ b/content/test/data/accessibility/html/input-time-expected-fuchsia.txt
@@ -0,0 +1,37 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable actions='{DEFAULT, SET_VALUE}' value='00:00:00'
+++++++++UNKNOWN
+++++++++++UNKNOWN
+++++++++++++UNKNOWN focusable label='Hours' actions='{SET_VALUE}' value='12'
+++++++++++++++STATIC_TEXT label='12'
+++++++++++++++++UNKNOWN label='12'
+++++++++++++STATIC_TEXT label=':'
+++++++++++++++UNKNOWN label=':'
+++++++++++++UNKNOWN focusable label='Minutes' actions='{SET_VALUE}' value='00'
+++++++++++++++STATIC_TEXT label='00'
+++++++++++++++++UNKNOWN label='00'
+++++++++++++STATIC_TEXT label=' '
+++++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN focusable label='AM/PM' actions='{SET_VALUE}' value='AM'
+++++++++++++++STATIC_TEXT label='AM'
+++++++++++++++++UNKNOWN label='AM'
+++++++++UNKNOWN focusable label='Show time picker'
+++++++UNKNOWN focusable label='Breakfast' actions='{DEFAULT, SET_VALUE}' value='00:00:00'
+++++++++UNKNOWN
+++++++++++UNKNOWN
+++++++++++++UNKNOWN focusable label='Hours Breakfast' actions='{SET_VALUE}' value='12'
+++++++++++++++STATIC_TEXT label='12'
+++++++++++++++++UNKNOWN label='12'
+++++++++++++STATIC_TEXT label=':'
+++++++++++++++UNKNOWN label=':'
+++++++++++++UNKNOWN focusable label='Minutes Breakfast' actions='{SET_VALUE}' value='00'
+++++++++++++++STATIC_TEXT label='00'
+++++++++++++++++UNKNOWN label='00'
+++++++++++++STATIC_TEXT label=' '
+++++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN focusable label='AM/PM Breakfast' actions='{SET_VALUE}' value='AM'
+++++++++++++++STATIC_TEXT label='AM'
+++++++++++++++++UNKNOWN label='AM'
+++++++++UNKNOWN focusable label='Show time picker'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-time-with-popup-open-expected-fuchsia.txt b/content/test/data/accessibility/html/input-time-with-popup-open-expected-fuchsia.txt
new file mode 100644
index 0000000..24fae91
--- /dev/null
+++ b/content/test/data/accessibility/html/input-time-with-popup-open-expected-fuchsia.txt
@@ -0,0 +1,329 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable actions='{DEFAULT, SET_VALUE}' value='13:50:02.922'
+++++++++UNKNOWN
+++++++++++UNKNOWN
+++++++++++++UNKNOWN focusable label='Hours' actions='{SET_VALUE}' value='01'
+++++++++++++++STATIC_TEXT label='01'
+++++++++++++++++UNKNOWN label='01'
+++++++++++++STATIC_TEXT label=':'
+++++++++++++++UNKNOWN label=':'
+++++++++++++UNKNOWN focusable label='Minutes' actions='{SET_VALUE}' value='50'
+++++++++++++++STATIC_TEXT label='50'
+++++++++++++++++UNKNOWN label='50'
+++++++++++++STATIC_TEXT label=':'
+++++++++++++++UNKNOWN label=':'
+++++++++++++UNKNOWN label='Seconds' value='02'
+++++++++++++++STATIC_TEXT label='02'
+++++++++++++++++UNKNOWN label='02'
+++++++++++++STATIC_TEXT label='.'
+++++++++++++++UNKNOWN label='.'
+++++++++++++UNKNOWN label='Milliseconds' value='922'
+++++++++++++++STATIC_TEXT label='922'
+++++++++++++++++UNKNOWN label='922'
+++++++++++++STATIC_TEXT label=' '
+++++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN focusable label='AM/PM' actions='{SET_VALUE}' value='PM'
+++++++++++++++STATIC_TEXT label='PM'
+++++++++++++++++UNKNOWN label='PM'
+++++++++UNKNOWN focusable label='Show time picker'
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN
+++++++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++++++UNKNOWN hidden
+++++++++++++++++++++UNKNOWN focusable label='Hours' actions='{DEFAULT}'
+++++++++++++++++++++++UNKNOWN focusable selected label='01' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='01'
+++++++++++++++++++++++UNKNOWN focusable label='02' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='02'
+++++++++++++++++++++++UNKNOWN focusable label='03' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='03'
+++++++++++++++++++++++UNKNOWN focusable label='04' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='04'
+++++++++++++++++++++++UNKNOWN focusable label='05' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='05'
+++++++++++++++++++++++UNKNOWN focusable label='06' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='06'
+++++++++++++++++++++++UNKNOWN focusable label='07' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='07'
+++++++++++++++++++++++UNKNOWN focusable label='08' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='08'
+++++++++++++++++++++++UNKNOWN focusable label='09' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='09'
+++++++++++++++++++++++UNKNOWN focusable label='10' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='10'
+++++++++++++++++++++++UNKNOWN focusable label='11' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='11'
+++++++++++++++++++++++UNKNOWN focusable label='12' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='12'
+++++++++++++++++++++UNKNOWN focusable label='Minutes' actions='{DEFAULT}'
+++++++++++++++++++++++UNKNOWN focusable label='01' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='01'
+++++++++++++++++++++++UNKNOWN focusable label='02' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='02'
+++++++++++++++++++++++UNKNOWN focusable label='03' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='03'
+++++++++++++++++++++++UNKNOWN focusable label='04' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='04'
+++++++++++++++++++++++UNKNOWN focusable label='05' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='05'
+++++++++++++++++++++++UNKNOWN focusable label='06' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='06'
+++++++++++++++++++++++UNKNOWN focusable label='07' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='07'
+++++++++++++++++++++++UNKNOWN focusable label='08' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='08'
+++++++++++++++++++++++UNKNOWN focusable label='09' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='09'
+++++++++++++++++++++++UNKNOWN focusable label='10' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='10'
+++++++++++++++++++++++UNKNOWN focusable label='11' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='11'
+++++++++++++++++++++++UNKNOWN focusable label='12' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='12'
+++++++++++++++++++++++UNKNOWN focusable label='13' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='13'
+++++++++++++++++++++++UNKNOWN focusable label='14' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='14'
+++++++++++++++++++++++UNKNOWN focusable label='15' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='15'
+++++++++++++++++++++++UNKNOWN focusable label='16' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='16'
+++++++++++++++++++++++UNKNOWN focusable label='17' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='17'
+++++++++++++++++++++++UNKNOWN focusable label='18' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='18'
+++++++++++++++++++++++UNKNOWN focusable label='19' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='19'
+++++++++++++++++++++++UNKNOWN focusable label='20' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='20'
+++++++++++++++++++++++UNKNOWN focusable label='21' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='21'
+++++++++++++++++++++++UNKNOWN focusable label='22' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='22'
+++++++++++++++++++++++UNKNOWN focusable label='23' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='23'
+++++++++++++++++++++++UNKNOWN focusable label='24' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='24'
+++++++++++++++++++++++UNKNOWN focusable label='25' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='25'
+++++++++++++++++++++++UNKNOWN focusable label='26' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='26'
+++++++++++++++++++++++UNKNOWN focusable label='27' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='27'
+++++++++++++++++++++++UNKNOWN focusable label='28' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='28'
+++++++++++++++++++++++UNKNOWN focusable label='29' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='29'
+++++++++++++++++++++++UNKNOWN focusable label='30' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='30'
+++++++++++++++++++++++UNKNOWN focusable label='31' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='31'
+++++++++++++++++++++++UNKNOWN focusable label='32' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='32'
+++++++++++++++++++++++UNKNOWN focusable label='33' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='33'
+++++++++++++++++++++++UNKNOWN focusable label='34' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='34'
+++++++++++++++++++++++UNKNOWN focusable label='35' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='35'
+++++++++++++++++++++++UNKNOWN focusable label='36' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='36'
+++++++++++++++++++++++UNKNOWN focusable label='37' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='37'
+++++++++++++++++++++++UNKNOWN focusable label='38' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='38'
+++++++++++++++++++++++UNKNOWN focusable label='39' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='39'
+++++++++++++++++++++++UNKNOWN focusable label='40' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='40'
+++++++++++++++++++++++UNKNOWN focusable label='41' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='41'
+++++++++++++++++++++++UNKNOWN focusable label='42' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='42'
+++++++++++++++++++++++UNKNOWN focusable label='43' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='43'
+++++++++++++++++++++++UNKNOWN focusable label='44' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='44'
+++++++++++++++++++++++UNKNOWN focusable label='45' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='45'
+++++++++++++++++++++++UNKNOWN focusable label='46' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='46'
+++++++++++++++++++++++UNKNOWN focusable label='47' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='47'
+++++++++++++++++++++++UNKNOWN focusable label='48' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='48'
+++++++++++++++++++++++UNKNOWN focusable label='49' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='49'
+++++++++++++++++++++++UNKNOWN focusable selected label='50' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='50'
+++++++++++++++++++++++UNKNOWN focusable label='51' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='51'
+++++++++++++++++++++++UNKNOWN focusable label='52' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='52'
+++++++++++++++++++++++UNKNOWN focusable label='53' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='53'
+++++++++++++++++++++++UNKNOWN focusable label='54' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='54'
+++++++++++++++++++++++UNKNOWN focusable label='55' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='55'
+++++++++++++++++++++++UNKNOWN focusable label='56' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='56'
+++++++++++++++++++++++UNKNOWN focusable label='57' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='57'
+++++++++++++++++++++++UNKNOWN focusable label='58' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='58'
+++++++++++++++++++++++UNKNOWN focusable label='59' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='59'
+++++++++++++++++++++++UNKNOWN focusable label='00' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='00'
+++++++++++++++++++++UNKNOWN focusable label='Seconds' actions='{DEFAULT}'
+++++++++++++++++++++++UNKNOWN focusable label='01' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='01'
+++++++++++++++++++++++UNKNOWN focusable selected label='02' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='02'
+++++++++++++++++++++++UNKNOWN focusable label='03' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='03'
+++++++++++++++++++++++UNKNOWN focusable label='04' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='04'
+++++++++++++++++++++++UNKNOWN focusable label='05' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='05'
+++++++++++++++++++++++UNKNOWN focusable label='06' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='06'
+++++++++++++++++++++++UNKNOWN focusable label='07' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='07'
+++++++++++++++++++++++UNKNOWN focusable label='08' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='08'
+++++++++++++++++++++++UNKNOWN focusable label='09' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='09'
+++++++++++++++++++++++UNKNOWN focusable label='10' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='10'
+++++++++++++++++++++++UNKNOWN focusable label='11' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='11'
+++++++++++++++++++++++UNKNOWN focusable label='12' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='12'
+++++++++++++++++++++++UNKNOWN focusable label='13' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='13'
+++++++++++++++++++++++UNKNOWN focusable label='14' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='14'
+++++++++++++++++++++++UNKNOWN focusable label='15' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='15'
+++++++++++++++++++++++UNKNOWN focusable label='16' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='16'
+++++++++++++++++++++++UNKNOWN focusable label='17' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='17'
+++++++++++++++++++++++UNKNOWN focusable label='18' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='18'
+++++++++++++++++++++++UNKNOWN focusable label='19' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='19'
+++++++++++++++++++++++UNKNOWN focusable label='20' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='20'
+++++++++++++++++++++++UNKNOWN focusable label='21' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='21'
+++++++++++++++++++++++UNKNOWN focusable label='22' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='22'
+++++++++++++++++++++++UNKNOWN focusable label='23' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='23'
+++++++++++++++++++++++UNKNOWN focusable label='24' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='24'
+++++++++++++++++++++++UNKNOWN focusable label='25' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='25'
+++++++++++++++++++++++UNKNOWN focusable label='26' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='26'
+++++++++++++++++++++++UNKNOWN focusable label='27' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='27'
+++++++++++++++++++++++UNKNOWN focusable label='28' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='28'
+++++++++++++++++++++++UNKNOWN focusable label='29' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='29'
+++++++++++++++++++++++UNKNOWN focusable label='30' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='30'
+++++++++++++++++++++++UNKNOWN focusable label='31' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='31'
+++++++++++++++++++++++UNKNOWN focusable label='32' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='32'
+++++++++++++++++++++++UNKNOWN focusable label='33' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='33'
+++++++++++++++++++++++UNKNOWN focusable label='34' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='34'
+++++++++++++++++++++++UNKNOWN focusable label='35' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='35'
+++++++++++++++++++++++UNKNOWN focusable label='36' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='36'
+++++++++++++++++++++++UNKNOWN focusable label='37' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='37'
+++++++++++++++++++++++UNKNOWN focusable label='38' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='38'
+++++++++++++++++++++++UNKNOWN focusable label='39' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='39'
+++++++++++++++++++++++UNKNOWN focusable label='40' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='40'
+++++++++++++++++++++++UNKNOWN focusable label='41' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='41'
+++++++++++++++++++++++UNKNOWN focusable label='42' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='42'
+++++++++++++++++++++++UNKNOWN focusable label='43' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='43'
+++++++++++++++++++++++UNKNOWN focusable label='44' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='44'
+++++++++++++++++++++++UNKNOWN focusable label='45' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='45'
+++++++++++++++++++++++UNKNOWN focusable label='46' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='46'
+++++++++++++++++++++++UNKNOWN focusable label='47' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='47'
+++++++++++++++++++++++UNKNOWN focusable label='48' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='48'
+++++++++++++++++++++++UNKNOWN focusable label='49' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='49'
+++++++++++++++++++++++UNKNOWN focusable label='50' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='50'
+++++++++++++++++++++++UNKNOWN focusable label='51' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='51'
+++++++++++++++++++++++UNKNOWN focusable label='52' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='52'
+++++++++++++++++++++++UNKNOWN focusable label='53' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='53'
+++++++++++++++++++++++UNKNOWN focusable label='54' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='54'
+++++++++++++++++++++++UNKNOWN focusable label='55' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='55'
+++++++++++++++++++++++UNKNOWN focusable label='56' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='56'
+++++++++++++++++++++++UNKNOWN focusable label='57' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='57'
+++++++++++++++++++++++UNKNOWN focusable label='58' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='58'
+++++++++++++++++++++++UNKNOWN focusable label='59' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='59'
+++++++++++++++++++++++UNKNOWN focusable label='00' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='00'
+++++++++++++++++++++UNKNOWN focusable label='Milliseconds' actions='{DEFAULT}'
+++++++++++++++++++++++UNKNOWN focusable label='100' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='100'
+++++++++++++++++++++++UNKNOWN focusable label='200' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='200'
+++++++++++++++++++++++UNKNOWN focusable label='300' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='300'
+++++++++++++++++++++++UNKNOWN focusable label='400' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='400'
+++++++++++++++++++++++UNKNOWN focusable label='500' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='500'
+++++++++++++++++++++++UNKNOWN focusable label='600' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='600'
+++++++++++++++++++++++UNKNOWN focusable label='700' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='700'
+++++++++++++++++++++++UNKNOWN focusable label='800' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='800'
+++++++++++++++++++++++UNKNOWN focusable selected label='922' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='922'
+++++++++++++++++++++++UNKNOWN focusable label='000' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='000'
+++++++++++++++++++++UNKNOWN focusable label='AM/PM' actions='{DEFAULT}'
+++++++++++++++++++++++UNKNOWN focusable selected label='PM' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='PM'
+++++++++++++++++++++++UNKNOWN focusable label='AM' actions='{DEFAULT}'
+++++++++++++++++++++++++STATIC_TEXT label='AM'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-types-expected-fuchsia.txt b/content/test/data/accessibility/html/input-types-expected-fuchsia.txt
new file mode 100644
index 0000000..2e5c48b
--- /dev/null
+++ b/content/test/data/accessibility/html/input-types-expected-fuchsia.txt
@@ -0,0 +1,91 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Default: '
+++++++++++UNKNOWN label='Default: '
+++++++++TEXT_FIELD focusable label='Default:' actions='{DEFAULT, SET_VALUE}'
+++++++++++UNKNOWN
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Button: '
+++++++++++UNKNOWN label='Button: '
+++++++++BUTTON focusable label='Button:' actions='{DEFAULT}'
+++++++UNKNOWN hidden
+++++++++STATIC_TEXT hidden label='Checkbox: '
+++++++++CHECK_BOX focusable label='Checkbox:' actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Color: '
+++++++++++UNKNOWN label='Color: '
+++++++++UNKNOWN focusable label='Color:' actions='{DEFAULT, SET_VALUE}' value='#000000'
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Email: '
+++++++++++UNKNOWN label='Email: '
+++++++++TEXT_FIELD focusable label='Email:' actions='{DEFAULT, SET_VALUE}'
+++++++++++UNKNOWN
+++++++UNKNOWN
+++++++++STATIC_TEXT label='File: '
+++++++++++UNKNOWN label='File: '
+++++++++BUTTON focusable label='File:' actions='{DEFAULT}' value='No file chosen'
+++++++++++BUTTON focusable label='Choose File' actions='{DEFAULT}'
+++++++++++++STATIC_TEXT label='Choose File'
+++++++++++++++UNKNOWN label='Choose File'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='No file chosen'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Image: '
+++++++++++UNKNOWN label='Image: '
+++++++++BUTTON focusable label='Image:' actions='{DEFAULT}'
+++++++++++IMAGE
+++++++++++STATIC_TEXT label='Submit'
+++++++++++++UNKNOWN label='Submit'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Number: '
+++++++++++UNKNOWN label='Number: '
+++++++++UNKNOWN focusable label='Number:' actions='{DEFAULT, SET_VALUE}'
+++++++++++UNKNOWN
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Password: '
+++++++++++UNKNOWN label='Password: '
+++++++++TEXT_FIELD focusable label='Password:' actions='{DEFAULT, SET_VALUE}'
+++++++++++UNKNOWN
+++++++UNKNOWN hidden
+++++++++STATIC_TEXT hidden label='Radio: '
+++++++++RADIO_BUTTON focusable label='Radio:' actions='{DEFAULT}' checked_state='UNCHECKED'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Range: '
+++++++++++UNKNOWN label='Range: '
+++++++++SLIDER focusable label='Range:' actions='{SET_VALUE}'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Reset: '
+++++++++++UNKNOWN label='Reset: '
+++++++++BUTTON focusable label='Reset:' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Reset'
+++++++++++++UNKNOWN label='Reset'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Search: '
+++++++++++UNKNOWN label='Search: '
+++++++++SEARCH_BOX focusable label='Search:' actions='{DEFAULT, SET_VALUE}'
+++++++++++UNKNOWN
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Submit: '
+++++++++++UNKNOWN label='Submit: '
+++++++++BUTTON focusable label='Submit:' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Submit'
+++++++++++++UNKNOWN label='Submit'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Tel: '
+++++++++++UNKNOWN label='Tel: '
+++++++++TEXT_FIELD focusable label='Tel:' actions='{DEFAULT, SET_VALUE}'
+++++++++++UNKNOWN
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Text: '
+++++++++++UNKNOWN label='Text: '
+++++++++TEXT_FIELD focusable label='Text:' actions='{DEFAULT, SET_VALUE}'
+++++++++++UNKNOWN
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Url: '
+++++++++++UNKNOWN label='Url: '
+++++++++TEXT_FIELD focusable label='Url:' actions='{DEFAULT, SET_VALUE}'
+++++++++++UNKNOWN
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-url-expected-fuchsia.txt b/content/test/data/accessibility/html/input-url-expected-fuchsia.txt
new file mode 100644
index 0000000..2cd17e3
--- /dev/null
+++ b/content/test/data/accessibility/html/input-url-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='example.com'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='example.com'
+++++++++++++UNKNOWN label='example.com'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/input-week-expected-fuchsia.txt b/content/test/data/accessibility/html/input-week-expected-fuchsia.txt
new file mode 100644
index 0000000..5b433ac
--- /dev/null
+++ b/content/test/data/accessibility/html/input-week-expected-fuchsia.txt
@@ -0,0 +1,17 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable actions='{DEFAULT, SET_VALUE}'
+++++++++UNKNOWN
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='Week '
+++++++++++++++UNKNOWN label='Week '
+++++++++++++UNKNOWN focusable label='Week' actions='{SET_VALUE}'
+++++++++++++++STATIC_TEXT label='--'
+++++++++++++++++UNKNOWN label='--'
+++++++++++++STATIC_TEXT label=', '
+++++++++++++++UNKNOWN label=', '
+++++++++++++UNKNOWN focusable label='Year' actions='{SET_VALUE}'
+++++++++++++++STATIC_TEXT label='----'
+++++++++++++++++UNKNOWN label='----'
+++++++++UNKNOWN focusable label='Show week picker'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/ins-expected-fuchsia.txt b/content/test/data/accessibility/html/ins-expected-fuchsia.txt
new file mode 100644
index 0000000..b285397
--- /dev/null
+++ b/content/test/data/accessibility/html/ins-expected-fuchsia.txt
@@ -0,0 +1,16 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='My favorite browser is '
+++++++++++UNKNOWN label='My favorite browser is '
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='ABC'
+++++++++++++UNKNOWN label='ABC'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Chrome'
+++++++++++++UNKNOWN label='Chrome'
+++++++++STATIC_TEXT label='!'
+++++++++++UNKNOWN label='!'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/isInteresting-expected-fuchsia.txt b/content/test/data/accessibility/html/isInteresting-expected-fuchsia.txt
new file mode 100644
index 0000000..68078784
--- /dev/null
+++ b/content/test/data/accessibility/html/isInteresting-expected-fuchsia.txt
@@ -0,0 +1,14 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN label='A non focusable child of a control should not be interesting on Android'
+++++++++STATIC_TEXT label='A non focusable child of a control should not be interesting on Android'
+++++++++++UNKNOWN label='A non focusable child of a control should not be '
+++++++++++UNKNOWN label='interesting on Android'
+++++++UNKNOWN actions='{DEFAULT}'
+++++++++STATIC_TEXT label='Div with click handler'
+++++++++++UNKNOWN label='Div with click handler'
+++++++BUTTON label='I am interesting' actions='{DEFAULT}'
+++++++++PARAGRAPH actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='I should not be interesting'
+++++++++++++UNKNOWN label='I should not be interesting'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/label-expected-fuchsia.txt b/content/test/data/accessibility/html/label-expected-fuchsia.txt
new file mode 100644
index 0000000..199e4215
--- /dev/null
+++ b/content/test/data/accessibility/html/label-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Label'
+++++++++++UNKNOWN label='Label'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/landmark-expected-fuchsia.txt b/content/test/data/accessibility/html/landmark-expected-fuchsia.txt
new file mode 100644
index 0000000..19f26bf
--- /dev/null
+++ b/content/test/data/accessibility/html/landmark-expected-fuchsia.txt
@@ -0,0 +1,264 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++HEADER
+++++++++STATIC_TEXT label='This is a header element.'
+++++++++++UNKNOWN label='This is a header element.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is an aside element.'
+++++++++++UNKNOWN label='This is an aside element.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is an address element.'
+++++++++++UNKNOWN label='This is an address element.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is a footer element.'
+++++++++++UNKNOWN label='This is a footer element.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is a form element.'
+++++++++++UNKNOWN label='This is a form element.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is a main element.'
+++++++++++UNKNOWN label='This is a main element.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is a nav element.'
+++++++++++UNKNOWN label='This is a nav element.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is an ARIA application landmark.'
+++++++++++UNKNOWN label='This is an ARIA application landmark.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is an ARIA banner landmark.'
+++++++++++UNKNOWN label='This is an ARIA banner landmark.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is an ARIA complementary landmark.'
+++++++++++UNKNOWN label='This is an ARIA complementary landmark.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is an ARIA contentinfo landmark.'
+++++++++++UNKNOWN label='This is an ARIA contentinfo landmark.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is an ARIA form landmark.'
+++++++++++UNKNOWN label='This is an ARIA form landmark.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is an ARIA main landmark.'
+++++++++++UNKNOWN label='This is an ARIA main landmark.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is an ARIA navigation landmark.'
+++++++++++UNKNOWN label='This is an ARIA navigation landmark.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is an ARIA search landmark.'
+++++++++++UNKNOWN label='This is an ARIA search landmark.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT banner role.'
+++++++++++++UNKNOWN label='This should NOT banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable label='Details' actions='{DEFAULT}'
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT hidden label='%E2%96%B8 '
+++++++++++++STATIC_TEXT label='Details'
+++++++++++++++UNKNOWN label='Details'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='This should NOT header role.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN
+++++++++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT banner role.'
+++++++++++++++UNKNOWN label='This should NOT banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable label='Details' actions='{DEFAULT}'
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT hidden label='%E2%96%B8 '
+++++++++++++STATIC_TEXT label='Details'
+++++++++++++++UNKNOWN label='Details'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT hidden label='This should NOT header role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN
+++++++++++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT have banner role.'
+++++++++++++++UNKNOWN label='This should NOT have banner role.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT footer role.'
+++++++++++++UNKNOWN label='This should NOT footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++UNKNOWN label='This should NOT have footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++UNKNOWN label='This should NOT have footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++UNKNOWN label='This should NOT have footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++UNKNOWN label='This should NOT have footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable label='Details' actions='{DEFAULT}'
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT hidden label='%E2%96%B8 '
+++++++++++++STATIC_TEXT label='Details'
+++++++++++++++UNKNOWN label='Details'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='This should NOT footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++UNKNOWN label='This should NOT have footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++UNKNOWN label='This should NOT have footer role.'
+++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN
+++++++++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++++++++UNKNOWN label='This should NOT have footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++UNKNOWN label='This should NOT have footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT footer role.'
+++++++++++++++UNKNOWN label='This should NOT footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++++UNKNOWN label='This should NOT have footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++++UNKNOWN label='This should NOT have footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++++UNKNOWN label='This should NOT have footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++++UNKNOWN label='This should NOT have footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable label='Details' actions='{DEFAULT}'
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT hidden label='%E2%96%B8 '
+++++++++++++STATIC_TEXT label='Details'
+++++++++++++++UNKNOWN label='Details'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT hidden label='This should NOT footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++++UNKNOWN label='This should NOT have footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++++UNKNOWN label='This should NOT have footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN
+++++++++++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++++++++++UNKNOWN label='This should NOT have footer role.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='This should NOT have footer role.'
+++++++++++++++UNKNOWN label='This should NOT have footer role.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/layout-table-in-button-expected-fuchsia.txt b/content/test/data/accessibility/html/layout-table-in-button-expected-fuchsia.txt
new file mode 100644
index 0000000..9f7e5e4e
--- /dev/null
+++ b/content/test/data/accessibility/html/layout-table-in-button-expected-fuchsia.txt
@@ -0,0 +1,10 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++BUTTON focusable label='Conversation with Foo. 0 unread messages.'
+++++++++UNKNOWN
+++++++++++UNKNOWN
+++++++++++++UNKNOWN label='Conversation with Foo. 0 unread messages.'
+++++++++++++++UNKNOWN label='Conversation with Foo. 0 unread messages.'
+++++++++++++++++STATIC_TEXT label='Foo'
+++++++++++++++++++UNKNOWN label='Foo'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/legend-expected-fuchsia.txt b/content/test/data/accessibility/html/legend-expected-fuchsia.txt
new file mode 100644
index 0000000..d8c321d
--- /dev/null
+++ b/content/test/data/accessibility/html/legend-expected-fuchsia.txt
@@ -0,0 +1,16 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN label='Browser Engines:'
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='Browser Engines:'
+++++++++++++++UNKNOWN label='Browser Engines:'
+++++++++++STATIC_TEXT label='Browser: '
+++++++++++++UNKNOWN label='Browser: '
+++++++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}'
+++++++++++++UNKNOWN
+++++++++++STATIC_TEXT label=' Rendering Engine: '
+++++++++++++UNKNOWN label=' Rendering Engine: '
+++++++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}'
+++++++++++++UNKNOWN
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/li-expected-fuchsia.txt b/content/test/data/accessibility/html/li-expected-fuchsia.txt
new file mode 100644
index 0000000..da713787
--- /dev/null
+++ b/content/test/data/accessibility/html/li-expected-fuchsia.txt
@@ -0,0 +1,19 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN label='Custom name'
+++++++++++UNKNOWN label='%E2%80%A2 '
+++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++STATIC_TEXT label='Item 1'
+++++++++++++UNKNOWN label='Item 1'
+++++++++UNKNOWN
+++++++++++UNKNOWN label='%E2%80%A2 '
+++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++STATIC_TEXT label='Item 2'
+++++++++++++UNKNOWN label='Item 2'
+++++++++UNKNOWN
+++++++++++UNKNOWN label='%E2%80%A2 '
+++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++STATIC_TEXT label='Item 3'
+++++++++++++UNKNOWN label='Item 3'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/link-expected-fuchsia.txt b/content/test/data/accessibility/html/link-expected-fuchsia.txt
new file mode 100644
index 0000000..ffbb452
--- /dev/null
+++ b/content/test/data/accessibility/html/link-expected-fuchsia.txt
@@ -0,0 +1,3 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/link-inside-heading-expected-fuchsia.txt b/content/test/data/accessibility/html/link-inside-heading-expected-fuchsia.txt
new file mode 100644
index 0000000..db88d49a
--- /dev/null
+++ b/content/test/data/accessibility/html/link-inside-heading-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN label='Link In Heading'
+++++++++LINK focusable label='Link In Heading' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Link In Heading'
+++++++++++++UNKNOWN label='Link In Heading'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/list-expected-fuchsia.txt b/content/test/data/accessibility/html/list-expected-fuchsia.txt
new file mode 100644
index 0000000..94aac5e
--- /dev/null
+++ b/content/test/data/accessibility/html/list-expected-fuchsia.txt
@@ -0,0 +1,33 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++UNKNOWN label='%E2%80%A2 '
+++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++STATIC_TEXT label='tic'
+++++++++++++UNKNOWN label='tic'
+++++++++UNKNOWN
+++++++++++UNKNOWN label='%E2%80%A2 '
+++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++STATIC_TEXT label='tac'
+++++++++++++UNKNOWN label='tac'
+++++++++UNKNOWN
+++++++++++UNKNOWN label='%E2%80%A2 '
+++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++STATIC_TEXT label='toe'
+++++++++++++UNKNOWN label='toe'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='tic'
+++++++++++++UNKNOWN label='tic'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='tac'
+++++++++++++UNKNOWN label='tac'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='toe'
+++++++++++++UNKNOWN label='toe'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/list-item-aria-setsize-unknown-expected-fuchsia.txt b/content/test/data/accessibility/html/list-item-aria-setsize-unknown-expected-fuchsia.txt
new file mode 100644
index 0000000..76c46d0
--- /dev/null
+++ b/content/test/data/accessibility/html/list-item-aria-setsize-unknown-expected-fuchsia.txt
@@ -0,0 +1,23 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Level 1, item 5 of set size unknown'
+++++++++++++UNKNOWN label='Level 1, item 5 of set size unknown'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Level 1, item 6 of set size unknown'
+++++++++++++UNKNOWN label='Level 1, item 6 of set size unknown'
+++++++++++UNKNOWN
+++++++++++++UNKNOWN
+++++++++++++++STATIC_TEXT label='Level 2, item 6 of set size unknown'
+++++++++++++++++UNKNOWN label='Level 2, item 6 of set size unknown'
+++++++++++++UNKNOWN
+++++++++++++++STATIC_TEXT label='Level 2, item 7 of set size unknown'
+++++++++++++++++UNKNOWN label='Level 2, item 7 of set size unknown'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Level 1, item 7 of set size 2'
+++++++++++++UNKNOWN label='Level 1, item 7 of set size 2'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Level 1, item 8 of set size 3'
+++++++++++++UNKNOWN label='Level 1, item 8 of set size 3'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/list-item-aria-setsize-unknown-flattened-expected-fuchsia.txt b/content/test/data/accessibility/html/list-item-aria-setsize-unknown-flattened-expected-fuchsia.txt
new file mode 100644
index 0000000..1fb0f7cc
--- /dev/null
+++ b/content/test/data/accessibility/html/list-item-aria-setsize-unknown-flattened-expected-fuchsia.txt
@@ -0,0 +1,44 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Level 1, item 1 of set size unknown'
+++++++++++++UNKNOWN label='Level 1, item 1 of set size unknown'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='++Level 2, item 1 of set size unknown'
+++++++++++++UNKNOWN label='++Level 2, item 1 of set size unknown'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='++Level 2, item 2 of set size unknown'
+++++++++++++UNKNOWN label='++Level 2, item 2 of set size unknown'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='++Level 2, item 3 of set size unknown'
+++++++++++++UNKNOWN label='++Level 2, item 3 of set size unknown'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='++++Level 3, item 1 of set size unknown'
+++++++++++++UNKNOWN label='++++Level 3, item 1 of set size unknown'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='++++Level 3, item 2 of set size unknown'
+++++++++++++UNKNOWN label='++++Level 3, item 2 of set size unknown'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Level 1, item 2 of set size unknown'
+++++++++++++UNKNOWN label='Level 1, item 2 of set size unknown'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Level 1, item 3 of set size unknown'
+++++++++++++UNKNOWN label='Level 1, item 3 of set size unknown'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='Level Unspecified, aria-setsize attribute does not exist, item 4 of set size 4'
+++++++++++++++UNKNOWN label='Level Unspecified, aria-setsize attribute does not exist, item 4 of set size 4'
+++++++++UNKNOWN
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='Level Unspecified, aria-setsize attribute does not exist, item 5 of set size 5'
+++++++++++++++UNKNOWN label='Level Unspecified, aria-setsize attribute does not exist, item 5 of set size 5'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/list-markers-expected-fuchsia.txt b/content/test/data/accessibility/html/list-markers-expected-fuchsia.txt
new file mode 100644
index 0000000..32059b7c
--- /dev/null
+++ b/content/test/data/accessibility/html/list-markers-expected-fuchsia.txt
@@ -0,0 +1,31 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++UNKNOWN label='%E2%80%A2 '
+++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++STATIC_TEXT label='First item properly groups itself despite '
+++++++++++++UNKNOWN label='First item properly groups itself despite '
+++++++++++STATIC_TEXT label='bolded'
+++++++++++++UNKNOWN label='bolded'
+++++++++++STATIC_TEXT label=' text.'
+++++++++++++UNKNOWN label=' text.'
+++++++++UNKNOWN
+++++++++++UNKNOWN label='%E2%80%A2 '
+++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++STATIC_TEXT label='This should also be '
+++++++++++++UNKNOWN label='This should also be '
+++++++++++STATIC_TEXT label='seen'
+++++++++++++UNKNOWN label='seen'
+++++++++++STATIC_TEXT label=' as a group.'
+++++++++++++UNKNOWN label=' as a group.'
+++++++++UNKNOWN
+++++++++++UNKNOWN label='%E2%80%A2 '
+++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++STATIC_TEXT label='Some '
+++++++++++++UNKNOWN label='Some '
+++++++++++STATIC_TEXT label='more'
+++++++++++++UNKNOWN label='more'
+++++++++++STATIC_TEXT label=' text.'
+++++++++++++UNKNOWN label=' text.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/main-expected-fuchsia.txt b/content/test/data/accessibility/html/main-expected-fuchsia.txt
new file mode 100644
index 0000000..201b02f
--- /dev/null
+++ b/content/test/data/accessibility/html/main-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is main element.'
+++++++++++UNKNOWN label='This is main element.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is an ARIA role main.'
+++++++++++UNKNOWN label='This is an ARIA role main.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/mark-expected-fuchsia.txt b/content/test/data/accessibility/html/mark-expected-fuchsia.txt
new file mode 100644
index 0000000..3ec89620
--- /dev/null
+++ b/content/test/data/accessibility/html/mark-expected-fuchsia.txt
@@ -0,0 +1,11 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='This test is to check '
+++++++++++UNKNOWN label='This test is to check '
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='mark tag'
+++++++++++++UNKNOWN label='mark tag'
+++++++++STATIC_TEXT label='.'
+++++++++++UNKNOWN label='.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/math-expected-fuchsia.txt b/content/test/data/accessibility/html/math-expected-fuchsia.txt
new file mode 100644
index 0000000..8fad83b
--- /dev/null
+++ b/content/test/data/accessibility/html/math-expected-fuchsia.txt
@@ -0,0 +1,22 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++UNKNOWN
+++++++++++++UNKNOWN
+++++++++++++++STATIC_TEXT label='%F0%9D%90%B4'
+++++++++++++++++UNKNOWN label='%F0%9D%90%B4'
+++++++++++++UNKNOWN
+++++++++++++++STATIC_TEXT label='2'
+++++++++++++++++UNKNOWN label='2'
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='+'
+++++++++++++++UNKNOWN label='+'
+++++++++++UNKNOWN
+++++++++++++UNKNOWN
+++++++++++++++STATIC_TEXT label='%F0%9D%90%B5'
+++++++++++++++++UNKNOWN label='%F0%9D%90%B5'
+++++++++++++UNKNOWN
+++++++++++++++STATIC_TEXT label='2'
+++++++++++++++++UNKNOWN label='2'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/menu-expected-fuchsia.txt b/content/test/data/accessibility/html/menu-expected-fuchsia.txt
new file mode 100644
index 0000000..64c1a59
--- /dev/null
+++ b/content/test/data/accessibility/html/menu-expected-fuchsia.txt
@@ -0,0 +1,14 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='Menu is deprecated, but it may still be used semantically with list item children. For more history, see crbug.com/87553.'
+++++++++++UNKNOWN label='Menu is deprecated, but it may still be used semantically with list item children. For more history, see '
+++++++++++UNKNOWN label='crbug.com/87553.'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Cats'
+++++++++++++UNKNOWN label='Cats'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Dogs'
+++++++++++++UNKNOWN label='Dogs'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/meta-expected-fuchsia.txt b/content/test/data/accessibility/html/meta-expected-fuchsia.txt
new file mode 100644
index 0000000..ffbb452
--- /dev/null
+++ b/content/test/data/accessibility/html/meta-expected-fuchsia.txt
@@ -0,0 +1,3 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/meter-expected-fuchsia.txt b/content/test/data/accessibility/html/meter-expected-fuchsia.txt
new file mode 100644
index 0000000..d5f531d
--- /dev/null
+++ b/content/test/data/accessibility/html/meter-expected-fuchsia.txt
@@ -0,0 +1,8 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++UNKNOWN
+++++++++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/modal-dialog-closed-expected-fuchsia.txt b/content/test/data/accessibility/html/modal-dialog-closed-expected-fuchsia.txt
new file mode 100644
index 0000000..45d87df4
--- /dev/null
+++ b/content/test/data/accessibility/html/modal-dialog-closed-expected-fuchsia.txt
@@ -0,0 +1,14 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++STATIC_TEXT label='Test that elements respawn in the accessibility tree after a modal dialog closes.'
+++++++++UNKNOWN label='Test that elements respawn in the accessibility tree after a modal dialog closes.'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='This dialog is closed and should not be in the tree'
+++++++++UNKNOWN focusable actions='{DEFAULT}' value='This should be in the tree.'
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN focusable selected label='This should be in the tree.' actions='{DEFAULT}'
+++++++UNKNOWN focusable actions='{DEFAULT, SET_VALUE}' value='#000000'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/modal-dialog-opened-expected-fuchsia.txt b/content/test/data/accessibility/html/modal-dialog-opened-expected-fuchsia.txt
new file mode 100644
index 0000000..b2f74c4
--- /dev/null
+++ b/content/test/data/accessibility/html/modal-dialog-opened-expected-fuchsia.txt
@@ -0,0 +1,15 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN hidden
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='The dialog subtree should be the only text content in the accessibility tree. '
+++++++++++++UNKNOWN label='The dialog subtree should be the only text content in the accessibility tree. '
+++++++++++LINK focusable label='Link inside the dialog.' actions='{DEFAULT}'
+++++++++++++STATIC_TEXT label='Link inside the dialog.'
+++++++++++++++UNKNOWN label='Link inside the dialog.'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++UNKNOWN hidden
+++++++++STATIC_TEXT hidden label='No file chosen'
+++++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/modal-dialog-stack-expected-blink.txt b/content/test/data/accessibility/html/modal-dialog-stack-expected-blink.txt
index c9779a5..e9d5f9d 100644
--- a/content/test/data/accessibility/html/modal-dialog-stack-expected-blink.txt
+++ b/content/test/data/accessibility/html/modal-dialog-stack-expected-blink.txt
@@ -3,11 +3,11 @@
 ++++genericContainer ignored invisible
 ++++++section ignored invisible
 ++++++++dialog ignored invisible
-++++++++++staticText ignored name='<newline>    This was the top dialog and should be marked ignored in the tree.<newline>  '
+++++++++++staticText ignored invisible name='<newline>    This was the top dialog and should be marked ignored in the tree.<newline>  '
 ++++++++menuListPopup ignored invisible
 ++++++++++menuListOption ignored
 ++++++dialog ignored invisible
-++++++++staticText ignored name='<newline>  This was the middle dialog and should be marked ignored in the tree.<newline>'
+++++++++staticText ignored invisible name='<newline>  This was the middle dialog and should be marked ignored in the tree.<newline>'
 ++++++dialog
 ++++++++staticText name='This is the now active dialog. Of course it should be in the tree. '
 ++++++++++inlineTextBox name='This is the now active dialog. Of course it should be in the tree. '
diff --git a/content/test/data/accessibility/html/modal-dialog-stack-expected-fuchsia.txt b/content/test/data/accessibility/html/modal-dialog-stack-expected-fuchsia.txt
new file mode 100644
index 0000000..b2f13928
--- /dev/null
+++ b/content/test/data/accessibility/html/modal-dialog-stack-expected-fuchsia.txt
@@ -0,0 +1,17 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='<newline>    This was the top dialog and should be marked ignored in the tree.<newline>  '
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++UNKNOWN hidden
+++++++++STATIC_TEXT hidden label='<newline>  This was the middle dialog and should be marked ignored in the tree.<newline>'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This is the now active dialog. Of course it should be in the tree. '
+++++++++++UNKNOWN label='This is the now active dialog. Of course it should be in the tree. '
+++++++++BUTTON focusable label='This is in the active dialog and should be in the tree.' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='This is in the active dialog and should be in the tree.'
+++++++++++++UNKNOWN label='This is in the active dialog and should be in the tree.'
+++++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/move-child-hypertext-2-expected-fuchsia.txt b/content/test/data/accessibility/html/move-child-hypertext-2-expected-fuchsia.txt
new file mode 100644
index 0000000..abef1dc
--- /dev/null
+++ b/content/test/data/accessibility/html/move-child-hypertext-2-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable label='done'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/move-child-hypertext-expected-fuchsia.txt b/content/test/data/accessibility/html/move-child-hypertext-expected-fuchsia.txt
new file mode 100644
index 0000000..928ef654
--- /dev/null
+++ b/content/test/data/accessibility/html/move-child-hypertext-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable label='done'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN hidden
+++++++++UNKNOWN focusable
+++++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/navigation-expected-fuchsia.txt b/content/test/data/accessibility/html/navigation-expected-fuchsia.txt
new file mode 100644
index 0000000..b31a284e8
--- /dev/null
+++ b/content/test/data/accessibility/html/navigation-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++LINK focusable label='Don't click on me' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Don't click on me'
+++++++++++++UNKNOWN label='Don't click on me'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/nestedlist-expected-fuchsia.txt b/content/test/data/accessibility/html/nestedlist-expected-fuchsia.txt
new file mode 100644
index 0000000..b4e5f4d
--- /dev/null
+++ b/content/test/data/accessibility/html/nestedlist-expected-fuchsia.txt
@@ -0,0 +1,50 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='This is a multi-leveled list'
+++++++++++UNKNOWN label='This is a multi-leveled list'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++UNKNOWN label='1. '
+++++++++++++STATIC_TEXT hidden label='1. '
+++++++++++STATIC_TEXT label='Sports'
+++++++++++++UNKNOWN label='Sports'
+++++++++++UNKNOWN
+++++++++++++UNKNOWN
+++++++++++++++UNKNOWN label='1. '
+++++++++++++++++STATIC_TEXT hidden label='1. '
+++++++++++++++STATIC_TEXT label='Tennis'
+++++++++++++++++UNKNOWN label='Tennis'
+++++++++++++UNKNOWN
+++++++++++++++UNKNOWN label='2. '
+++++++++++++++++STATIC_TEXT hidden label='2. '
+++++++++++++++STATIC_TEXT label='basketball'
+++++++++++++++++UNKNOWN label='basketball'
+++++++++UNKNOWN
+++++++++++UNKNOWN label='2. '
+++++++++++++STATIC_TEXT hidden label='2. '
+++++++++++STATIC_TEXT label='Books'
+++++++++++++UNKNOWN label='Books'
+++++++++++UNKNOWN
+++++++++++++UNKNOWN
+++++++++++++++UNKNOWN label='1. '
+++++++++++++++++STATIC_TEXT hidden label='1. '
+++++++++++++++STATIC_TEXT label='Oliver Twist'
+++++++++++++++++UNKNOWN label='Oliver Twist'
+++++++++++++UNKNOWN
+++++++++++++++UNKNOWN label='2. '
+++++++++++++++++STATIC_TEXT hidden label='2. '
+++++++++++++++STATIC_TEXT label='Kindred'
+++++++++++++++++UNKNOWN label='Kindred'
+++++++++++++UNKNOWN
+++++++++++++++UNKNOWN label='3. '
+++++++++++++++++STATIC_TEXT hidden label='3. '
+++++++++++++++STATIC_TEXT label='The Promise of Sleep'
+++++++++++++++++UNKNOWN label='The Promise of Sleep'
+++++++++UNKNOWN
+++++++++++UNKNOWN label='3. '
+++++++++++++STATIC_TEXT hidden label='3. '
+++++++++++STATIC_TEXT label='Recipes'
+++++++++++++UNKNOWN label='Recipes'
+++++++++UNKNOWN
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/no-source-video-expected-fuchsia.txt b/content/test/data/accessibility/html/no-source-video-expected-fuchsia.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/html/no-source-video-expected-fuchsia.txt
diff --git a/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-fuchsia.txt b/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-fuchsia.txt
new file mode 100644
index 0000000..e0c03c39
--- /dev/null
+++ b/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-fuchsia.txt
@@ -0,0 +1,5 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN focusable label='Done' actions='{DEFAULT}'
+++++++UNKNOWN actions='{DEFAULT}'
+++++++UNKNOWN actions='{DEFAULT}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/noscript-expected-fuchsia.txt b/content/test/data/accessibility/html/noscript-expected-fuchsia.txt
new file mode 100644
index 0000000..32f1554
--- /dev/null
+++ b/content/test/data/accessibility/html/noscript-expected-fuchsia.txt
@@ -0,0 +1,5 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN hidden
+++++++++STATIC_TEXT hidden label='Test to check noscript tag.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/object-expected-fuchsia.txt b/content/test/data/accessibility/html/object-expected-fuchsia.txt
new file mode 100644
index 0000000..4e79b7a
--- /dev/null
+++ b/content/test/data/accessibility/html/object-expected-fuchsia.txt
@@ -0,0 +1,4 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/offscreen-iframe-expected-fuchsia.txt b/content/test/data/accessibility/html/offscreen-iframe-expected-fuchsia.txt
new file mode 100644
index 0000000..dd66ed7
--- /dev/null
+++ b/content/test/data/accessibility/html/offscreen-iframe-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN label='iframe_onscreen'
+++++++++++++++UNKNOWN label='iframe_offscreen'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/offscreen-select-expected-fuchsia.txt b/content/test/data/accessibility/html/offscreen-select-expected-fuchsia.txt
new file mode 100644
index 0000000..52ff632a
--- /dev/null
+++ b/content/test/data/accessibility/html/offscreen-select-expected-fuchsia.txt
@@ -0,0 +1,14 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN focusable actions='{DEFAULT}' value='Onscreen 1'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable selected label='Onscreen 1' actions='{DEFAULT}'
+++++++++++UNKNOWN hidden focusable label='Onscreen 2' actions='{DEFAULT}'
+++++++++++UNKNOWN hidden focusable label='Onscreen 3' actions='{DEFAULT}'
+++++++UNKNOWN hidden
+++++++UNKNOWN focusable actions='{DEFAULT}' value='Offscreen 1'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable selected label='Offscreen 1' actions='{DEFAULT}'
+++++++++++UNKNOWN hidden focusable label='Offscreen 2' actions='{DEFAULT}'
+++++++++++UNKNOWN hidden focusable label='Offscreen 3' actions='{DEFAULT}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/ol-expected-fuchsia.txt b/content/test/data/accessibility/html/ol-expected-fuchsia.txt
new file mode 100644
index 0000000..51db3ec
--- /dev/null
+++ b/content/test/data/accessibility/html/ol-expected-fuchsia.txt
@@ -0,0 +1,35 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++UNKNOWN label='1. '
+++++++++++++STATIC_TEXT hidden label='1. '
+++++++++++STATIC_TEXT label='Chrome'
+++++++++++++UNKNOWN label='Chrome'
+++++++++UNKNOWN
+++++++++++UNKNOWN label='2. '
+++++++++++++STATIC_TEXT hidden label='2. '
+++++++++++STATIC_TEXT label='Safari'
+++++++++++++UNKNOWN label='Safari'
+++++++++UNKNOWN
+++++++++++UNKNOWN label='3. '
+++++++++++++STATIC_TEXT hidden label='3. '
+++++++++++STATIC_TEXT label='IE'
+++++++++++++UNKNOWN label='IE'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++UNKNOWN label='10. '
+++++++++++++STATIC_TEXT hidden label='10. '
+++++++++++STATIC_TEXT label='Android'
+++++++++++++UNKNOWN label='Android'
+++++++++UNKNOWN
+++++++++++UNKNOWN label='11. '
+++++++++++++STATIC_TEXT hidden label='11. '
+++++++++++STATIC_TEXT label='Mac'
+++++++++++++UNKNOWN label='Mac'
+++++++++UNKNOWN
+++++++++++UNKNOWN label='12. '
+++++++++++++STATIC_TEXT hidden label='12. '
+++++++++++STATIC_TEXT label='Windows'
+++++++++++++UNKNOWN label='Windows'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/optgroup-expected-fuchsia.txt b/content/test/data/accessibility/html/optgroup-expected-fuchsia.txt
new file mode 100644
index 0000000..88f4cb1
--- /dev/null
+++ b/content/test/data/accessibility/html/optgroup-expected-fuchsia.txt
@@ -0,0 +1,23 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable actions='{DEFAULT}'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN label='Enabled'
+++++++++++++++STATIC_TEXT label='Enabled'
+++++++++++++++++UNKNOWN label='Enabled'
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN focusable label='One' actions='{DEFAULT}'
+++++++++++++++UNKNOWN focusable label='Two' actions='{DEFAULT}'
+++++++++++++++UNKNOWN focusable label='Three' actions='{DEFAULT}'
+++++++++++++++UNKNOWN focusable label='Four' actions='{DEFAULT}'
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN label='Disabled'
+++++++++++++++STATIC_TEXT label='Disabled'
+++++++++++++++++UNKNOWN label='Disabled'
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN label='One' actions='{DEFAULT}'
+++++++++++++++UNKNOWN label='Two' actions='{DEFAULT}'
+++++++++++++++UNKNOWN label='Three' actions='{DEFAULT}'
+++++++++++++++UNKNOWN label='Four' actions='{DEFAULT}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/option-in-datalist-expected-fuchsia.txt b/content/test/data/accessibility/html/option-in-datalist-expected-fuchsia.txt
new file mode 100644
index 0000000..ffbb452
--- /dev/null
+++ b/content/test/data/accessibility/html/option-in-datalist-expected-fuchsia.txt
@@ -0,0 +1,3 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/output-expected-fuchsia.txt b/content/test/data/accessibility/html/output-expected-fuchsia.txt
new file mode 100644
index 0000000..0709aca7e
--- /dev/null
+++ b/content/test/data/accessibility/html/output-expected-fuchsia.txt
@@ -0,0 +1,13 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN focusable actions='{DEFAULT, SET_VALUE}'
+++++++++++UNKNOWN
+++++++++STATIC_TEXT label=' + '
+++++++++++UNKNOWN label=' + '
+++++++++UNKNOWN focusable actions='{DEFAULT, SET_VALUE}'
+++++++++++UNKNOWN
+++++++++STATIC_TEXT label=' ='
+++++++++++UNKNOWN label=' ='
+++++++++UNKNOWN
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/overflow-actions-expected-fuchsia.txt b/content/test/data/accessibility/html/overflow-actions-expected-fuchsia.txt
new file mode 100644
index 0000000..3ceedba
--- /dev/null
+++ b/content/test/data/accessibility/html/overflow-actions-expected-fuchsia.txt
@@ -0,0 +1,17 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN label='Example Paragraph 1 Example Paragraph 2'
+++++++++PARAGRAPH
+++++++++++STATIC_TEXT label='Example Paragraph 1'
+++++++++++++UNKNOWN label='Example Paragraph 1'
+++++++++PARAGRAPH
+++++++++++STATIC_TEXT label='Example Paragraph 2'
+++++++++++++UNKNOWN label='Example Paragraph 2'
+++++++UNKNOWN
+++++++++PARAGRAPH
+++++++++++STATIC_TEXT label='Example Paragraph 1'
+++++++++++++UNKNOWN label='Example Paragraph 1'
+++++++++PARAGRAPH
+++++++++++STATIC_TEXT label='Example Paragraph 2'
+++++++++++++UNKNOWN label='Example Paragraph 2'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/p-expected-fuchsia.txt b/content/test/data/accessibility/html/p-expected-fuchsia.txt
new file mode 100644
index 0000000..c2cf712
--- /dev/null
+++ b/content/test/data/accessibility/html/p-expected-fuchsia.txt
@@ -0,0 +1,10 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++STATIC_TEXT label='Before'
+++++++++UNKNOWN label='Before'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='Paragraph'
+++++++++++UNKNOWN label='Paragraph'
+++++++STATIC_TEXT label='After'
+++++++++UNKNOWN label='After'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/param-expected-fuchsia.txt b/content/test/data/accessibility/html/param-expected-fuchsia.txt
new file mode 100644
index 0000000..4e79b7a
--- /dev/null
+++ b/content/test/data/accessibility/html/param-expected-fuchsia.txt
@@ -0,0 +1,4 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/pre-expected-fuchsia.txt b/content/test/data/accessibility/html/pre-expected-fuchsia.txt
new file mode 100644
index 0000000..283683ce
--- /dev/null
+++ b/content/test/data/accessibility/html/pre-expected-fuchsia.txt
@@ -0,0 +1,21 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This test is to check   pre<newline>formatting.'
+++++++++++UNKNOWN label='This test is to check   pre'
+++++++++++UNKNOWN label='<newline>'
+++++++++++UNKNOWN label='formatting.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This test is to check   pre<newline>formatting'
+++++++++++UNKNOWN label='This test is to check   pre'
+++++++++++UNKNOWN label='<newline>'
+++++++++++UNKNOWN label='formatting'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This test is to check   pre<newline>formatting.'
+++++++++++UNKNOWN label='This test is to check   pre'
+++++++++++UNKNOWN label='<newline>'
+++++++++++UNKNOWN label='formatting.'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='This test is to check pre formatting.'
+++++++++++UNKNOWN label='This test is to check pre formatting.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/progress-expected-fuchsia.txt b/content/test/data/accessibility/html/progress-expected-fuchsia.txt
new file mode 100644
index 0000000..6f3cf19
--- /dev/null
+++ b/content/test/data/accessibility/html/progress-expected-fuchsia.txt
@@ -0,0 +1,5 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++UNKNOWN
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/q-expected-fuchsia.txt b/content/test/data/accessibility/html/q-expected-fuchsia.txt
new file mode 100644
index 0000000..834475c0
--- /dev/null
+++ b/content/test/data/accessibility/html/q-expected-fuchsia.txt
@@ -0,0 +1,19 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='This is '
+++++++++++UNKNOWN label='This is '
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT label='"'
+++++++++++++++++UNKNOWN label='"'
+++++++++++STATIC_TEXT label='Chromium Blink'
+++++++++++++UNKNOWN label='Chromium Blink'
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT label='"'
+++++++++++++++++UNKNOWN label='"'
+++++++++STATIC_TEXT label=' based browser.'
+++++++++++UNKNOWN label=' based browser.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/s-expected-fuchsia.txt b/content/test/data/accessibility/html/s-expected-fuchsia.txt
new file mode 100644
index 0000000..4afa26b
--- /dev/null
+++ b/content/test/data/accessibility/html/s-expected-fuchsia.txt
@@ -0,0 +1,5 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++STATIC_TEXT label='My car is blue.'
+++++++++UNKNOWN label='My car is blue.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/samp-expected-fuchsia.txt b/content/test/data/accessibility/html/samp-expected-fuchsia.txt
new file mode 100644
index 0000000..5658632
--- /dev/null
+++ b/content/test/data/accessibility/html/samp-expected-fuchsia.txt
@@ -0,0 +1,5 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++STATIC_TEXT label='Sample output from a computer program'
+++++++++UNKNOWN label='Sample output from a computer program'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/script-expected-fuchsia.txt b/content/test/data/accessibility/html/script-expected-fuchsia.txt
new file mode 100644
index 0000000..ffbb452
--- /dev/null
+++ b/content/test/data/accessibility/html/script-expected-fuchsia.txt
@@ -0,0 +1,3 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/scrollable-expected-fuchsia.txt b/content/test/data/accessibility/html/scrollable-expected-fuchsia.txt
new file mode 100644
index 0000000..492d047
--- /dev/null
+++ b/content/test/data/accessibility/html/scrollable-expected-fuchsia.txt
@@ -0,0 +1,10 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++STATIC_TEXT label='not scrollable'
+++++++++++UNKNOWN label='not scrollable'
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/scrollable-overflow-expected-fuchsia.txt b/content/test/data/accessibility/html/scrollable-overflow-expected-fuchsia.txt
new file mode 100644
index 0000000..5d677e71
--- /dev/null
+++ b/content/test/data/accessibility/html/scrollable-overflow-expected-fuchsia.txt
@@ -0,0 +1,64 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN hidden
+++++++++PARAGRAPH
+++++++++++STATIC_TEXT label='no overflow'
+++++++++++++UNKNOWN label='no overflow'
+++++++UNKNOWN hidden
+++++++++PARAGRAPH
+++++++++++STATIC_TEXT label='clipped large lots of text more text'
+++++++++++++UNKNOWN label='clipped large lots of text more text'
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='ab cd'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='ab cd'
+++++++++++++UNKNOWN label='ab'
+++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN label='cd'
+++++++UNKNOWN hidden
+++++++++UNKNOWN
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='tiny'
+++++++++++++++UNKNOWN label='tiny'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='x=hidden'
+++++++++++++++UNKNOWN label='x=hidden'
+++++++++UNKNOWN label='x=hidden'
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='x=hidden'
+++++++++++++++UNKNOWN label='x=hidden'
+++++++++UNKNOWN label='x=auto'
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='x=auto'
+++++++++++++++UNKNOWN label='x=auto'
+++++++++UNKNOWN label='x=scroll'
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='x=scroll'
+++++++++++++++UNKNOWN label='x=scroll'
+++++++++UNKNOWN hidden
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='x=visible'
+++++++++++++++UNKNOWN label='x=visible'
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='y=hidden'
+++++++++++++++UNKNOWN label='y=hidden'
+++++++++UNKNOWN label='y=hidden'
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='y=hidden'
+++++++++++++++UNKNOWN label='y=hidden'
+++++++++UNKNOWN label='y=auto'
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='y=auto'
+++++++++++++++UNKNOWN label='y=auto'
+++++++++UNKNOWN label='y=scroll'
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='y=scroll'
+++++++++++++++UNKNOWN label='y=scroll'
+++++++++UNKNOWN hidden
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='y=visible'
+++++++++++++++UNKNOWN label='y=visible'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/scrollable-textarea-expected-fuchsia.txt b/content/test/data/accessibility/html/scrollable-textarea-expected-fuchsia.txt
new file mode 100644
index 0000000..5c26e6f
--- /dev/null
+++ b/content/test/data/accessibility/html/scrollable-textarea-expected-fuchsia.txt
@@ -0,0 +1,33 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='little'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='little'
+++++++++++++UNKNOWN label='little'
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text'
+++++++++++++UNKNOWN label='lots+of+text+'
+++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN label='lots+of+text+'
+++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN label='lots+of+text+'
+++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN label='lots+of+text+'
+++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN label='lots+of+text+'
+++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN label='lots+of+text+'
+++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN label='lots+of+text+'
+++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN label='lots+of+text+'
+++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN label='lots+of+text+'
+++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN label='lots+of+text+'
+++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN label='lots+of+text+'
+++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN label='lots+of+text'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/section-expected-fuchsia.txt b/content/test/data/accessibility/html/section-expected-fuchsia.txt
new file mode 100644
index 0000000..93f1b341
--- /dev/null
+++ b/content/test/data/accessibility/html/section-expected-fuchsia.txt
@@ -0,0 +1,16 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++STATIC_TEXT label='An unnamed section element gets the section role.'
+++++++++++UNKNOWN label='An unnamed section element gets the section role.'
+++++++UNKNOWN label='name'
+++++++++STATIC_TEXT label='Named section element #1 gets the region role'
+++++++++++UNKNOWN label='Named section element #1 gets the region role'
+++++++UNKNOWN label='Named section element #2 gets the region role'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Named section element #2 gets the region role'
+++++++++++++UNKNOWN label='Named section element #2 gets the region role'
+++++++UNKNOWN label='name'
+++++++++STATIC_TEXT label='Named section element #3 gets the region role'
+++++++++++UNKNOWN label='Named section element #3 gets the region role'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/select-expected-fuchsia.txt b/content/test/data/accessibility/html/select-expected-fuchsia.txt
new file mode 100644
index 0000000..50ccb52
--- /dev/null
+++ b/content/test/data/accessibility/html/select-expected-fuchsia.txt
@@ -0,0 +1,28 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable actions='{DEFAULT}' value='Placeholder option'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable selected label='Placeholder option' actions='{DEFAULT}'
+++++++++++UNKNOWN hidden focusable label='Option 1' actions='{DEFAULT}'
+++++++++++UNKNOWN hidden focusable label='Option 2' actions='{DEFAULT}'
+++++++UNKNOWN focusable actions='{DEFAULT}' value='Option 2'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden focusable label='Option 1' actions='{DEFAULT}'
+++++++++++UNKNOWN focusable selected label='Option 2' actions='{DEFAULT}'
+++++++++++UNKNOWN hidden focusable label='Option 3' actions='{DEFAULT}'
+++++++UNKNOWN focusable actions='{DEFAULT}' value='Option 1'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable selected label='Option 1' actions='{DEFAULT}'
+++++++++++UNKNOWN hidden focusable label='Option 2' actions='{DEFAULT}'
+++++++++++UNKNOWN hidden focusable label='Option 3' actions='{DEFAULT}'
+++++++UNKNOWN focusable actions='{DEFAULT}'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable label='Option 1' actions='{DEFAULT}'
+++++++++++UNKNOWN focusable label='Option 2' actions='{DEFAULT}'
+++++++++++UNKNOWN focusable label='Option 3' actions='{DEFAULT}'
+++++++UNKNOWN focusable actions='{DEFAULT}'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable label='Option 1' actions='{DEFAULT}'
+++++++++++UNKNOWN focusable label='Option 2' actions='{DEFAULT}'
+++++++++++UNKNOWN focusable label='Option 3' actions='{DEFAULT}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/select-follows-focus-aria-selected-false-expected-fuchsia.txt b/content/test/data/accessibility/html/select-follows-focus-aria-selected-false-expected-fuchsia.txt
new file mode 100644
index 0000000..cdd0764
--- /dev/null
+++ b/content/test/data/accessibility/html/select-follows-focus-aria-selected-false-expected-fuchsia.txt
@@ -0,0 +1,12 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN focusable label='tab1' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='tab1'
+++++++++++++UNKNOWN label='tab1'
+++++++++UNKNOWN focusable label='tab2' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='tab2'
+++++++++++++UNKNOWN label='tab2'
+++++++STATIC_TEXT label='Done'
+++++++++UNKNOWN label='Done'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/select-follows-focus-expected-fuchsia.txt b/content/test/data/accessibility/html/select-follows-focus-expected-fuchsia.txt
new file mode 100644
index 0000000..7725fcdc
--- /dev/null
+++ b/content/test/data/accessibility/html/select-follows-focus-expected-fuchsia.txt
@@ -0,0 +1,15 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN focusable selected label='tab1' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='tab1'
+++++++++++++UNKNOWN label='tab1'
+++++++++UNKNOWN focusable selected label='tab2' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='tab2'
+++++++++++++UNKNOWN label='tab2'
+++++++++UNKNOWN focusable label='tab3' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='tab3'
+++++++++++++UNKNOWN label='tab3'
+++++++STATIC_TEXT label='Done'
+++++++++UNKNOWN label='Done'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/select-follows-focus-multiselect-expected-fuchsia.txt b/content/test/data/accessibility/html/select-follows-focus-multiselect-expected-fuchsia.txt
new file mode 100644
index 0000000..14e23f25
--- /dev/null
+++ b/content/test/data/accessibility/html/select-follows-focus-multiselect-expected-fuchsia.txt
@@ -0,0 +1,15 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN focusable label='tab1' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='tab1'
+++++++++++++UNKNOWN label='tab1'
+++++++++UNKNOWN focusable selected label='tab2' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='tab2'
+++++++++++++UNKNOWN label='tab2'
+++++++++UNKNOWN focusable label='tab3' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='tab3'
+++++++++++++UNKNOWN label='tab3'
+++++++STATIC_TEXT label='Done'
+++++++++UNKNOWN label='Done'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/selection-container-expected-fuchsia.txt b/content/test/data/accessibility/html/selection-container-expected-fuchsia.txt
new file mode 100644
index 0000000..47fa71ac
--- /dev/null
+++ b/content/test/data/accessibility/html/selection-container-expected-fuchsia.txt
@@ -0,0 +1,14 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable label='selection_list' actions='{DEFAULT}'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN label='Enabled'
+++++++++++++++STATIC_TEXT label='Enabled'
+++++++++++++++++UNKNOWN label='Enabled'
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN focusable label='One' actions='{DEFAULT}'
+++++++++++++++UNKNOWN focusable label='Two' actions='{DEFAULT}'
+++++++++++++++UNKNOWN focusable label='Three' actions='{DEFAULT}'
+++++++++++++++UNKNOWN focusable label='Four' actions='{DEFAULT}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/selectmenu-expected-android-external.txt b/content/test/data/accessibility/html/selectmenu-expected-android-external.txt
index d373a3e4..a6bd028b 100644
--- a/content/test/data/accessibility/html/selectmenu-expected-android-external.txt
+++ b/content/test/data/accessibility/html/selectmenu-expected-android-external.txt
@@ -1,11 +1,11 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
 ++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
 ++++View viewIdResName:"A" actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
-++++++View text:"Option 1, in list, item 1 of 0" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++++View text:"Option 2, in list, item 1 of 0" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++++View text:"Option 3, in list, item 1 of 0" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++++View text:"Option 1" stateDescription:"in list, item 1 of 0" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++++View text:"Option 2" stateDescription:"in list, item 1 of 0" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++++View text:"Option 3" stateDescription:"in list, item 1 of 0" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
 ++++View viewIdResName:"B" actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
 ++++++TextView text:"Custom selectmenu button" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
-++++++View text:"Option 1, in list, item 1 of 0" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++++View text:"Option 2, in list, item 1 of 0" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
-++++++View text:"Option 3, in list, item 1 of 0" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
+++++++View text:"Option 1" stateDescription:"in list, item 1 of 0" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++++View text:"Option 2" stateDescription:"in list, item 1 of 0" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++++View text:"Option 3" stateDescription:"in list, item 1 of 0" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/selectmenu-expected-blink.txt b/content/test/data/accessibility/html/selectmenu-expected-blink.txt
index 79f4d642..dd3f3e3e 100644
--- a/content/test/data/accessibility/html/selectmenu-expected-blink.txt
+++ b/content/test/data/accessibility/html/selectmenu-expected-blink.txt
@@ -3,7 +3,7 @@
 ++++genericContainer
 ++++++genericContainer
 ++++++++genericContainer ignored
-++++++++++popUpButton collapsed focusable name='Option 1' setSize=0 haspopup=menu
+++++++++++comboBoxMenuButton collapsed focusable value='Option 1' haspopup=listbox
 ++++++++++++genericContainer
 ++++++++++++++staticText name='Option 1'
 ++++++++++++++++inlineTextBox name='Option 1'
@@ -12,7 +12,7 @@
 ++++++++++listBox ignored invisible
 ++++++genericContainer
 ++++++++genericContainer ignored
-++++++++++popUpButton collapsed name='Custom selectmenu button' setSize=0 haspopup=menu
+++++++++++comboBoxMenuButton collapsed value='Custom selectmenu button' haspopup=listbox
 ++++++++++++staticText name='Custom selectmenu button'
 ++++++++++++++inlineTextBox name='Custom selectmenu button'
 ++++++++genericContainer ignored
diff --git a/content/test/data/accessibility/html/selectmenu-expected-fuchsia.txt b/content/test/data/accessibility/html/selectmenu-expected-fuchsia.txt
new file mode 100644
index 0000000..118f96d
--- /dev/null
+++ b/content/test/data/accessibility/html/selectmenu-expected-fuchsia.txt
@@ -0,0 +1,19 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable label='Option 1' actions='{DEFAULT}'
+++++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++++STATIC_TEXT label='Option 1'
+++++++++++++++++UNKNOWN label='Option 1'
+++++++++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN label='Custom selectmenu button' actions='{DEFAULT}'
+++++++++++++STATIC_TEXT label='Custom selectmenu button'
+++++++++++++++UNKNOWN label='Custom selectmenu button'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/selectmenu-expected-mac.txt b/content/test/data/accessibility/html/selectmenu-expected-mac.txt
index 3999c36..267c298 100644
--- a/content/test/data/accessibility/html/selectmenu-expected-mac.txt
+++ b/content/test/data/accessibility/html/selectmenu-expected-mac.txt
@@ -1,8 +1,9 @@
 AXWebArea AXRoleDescription='HTML content'
 ++AXGroup AXRoleDescription='group'
 ++++AXGroup AXRoleDescription='group'
-++++++AXPopUpButton AXRoleDescription='pop up button' AXTitle='Option 1'
+++++++AXComboBox AXRoleDescription='combo box' AXValue='Option 1'
 ++++++++AXGroup AXRoleDescription='group'
 ++++++++++AXStaticText AXRoleDescription='text' AXValue='Option 1'
 ++++AXGroup AXRoleDescription='group'
-++++++AXPopUpButton AXRoleDescription='pop up button' AXTitle='Custom selectmenu button'
+++++++AXComboBox AXRoleDescription='combo box' AXValue='Custom selectmenu button'
+++++++++AXStaticText AXRoleDescription='text' AXValue='Custom selectmenu button'
diff --git a/content/test/data/accessibility/html/selectmenu-expected-uia-win.txt b/content/test/data/accessibility/html/selectmenu-expected-uia-win.txt
index b5dad61..d87a7ea7 100644
--- a/content/test/data/accessibility/html/selectmenu-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/selectmenu-expected-uia-win.txt
@@ -1,8 +1,9 @@
 Document
 ++Group IsControlElement=false
 ++++Group IsControlElement=false
-++++++Button Name='Option 1' ExpandCollapse.ExpandCollapseState='Collapsed'
+++++++ComboBox ExpandCollapse.ExpandCollapseState='Collapsed' Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false Value.Value='Option 1'
 ++++++++Group IsControlElement=false
 ++++++++++Text Name='Option 1'
 ++++Group IsControlElement=false
-++++++Button Name='Custom selectmenu button' ExpandCollapse.ExpandCollapseState='Collapsed'
+++++++ComboBox ExpandCollapse.ExpandCollapseState='Collapsed' Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false Value.Value='Custom selectmenu button'
+++++++++Text Name='Custom selectmenu button'
diff --git a/content/test/data/accessibility/html/selectmenu-expected-win.txt b/content/test/data/accessibility/html/selectmenu-expected-win.txt
index 36680db..21778acb 100644
--- a/content/test/data/accessibility/html/selectmenu-expected-win.txt
+++ b/content/test/data/accessibility/html/selectmenu-expected-win.txt
@@ -1,8 +1,9 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0>'
 ++IA2_ROLE_SECTION ia2_hypertext='<obj0><obj1>'
 ++++IA2_ROLE_SECTION ia2_hypertext='<obj0>'
-++++++ROLE_SYSTEM_BUTTONMENU name='Option 1' COLLAPSED FOCUSABLE HASPOPUP haspopup:menu ia2_hypertext='<obj0>'
+++++++ROLE_SYSTEM_COMBOBOX value='Option 1' COLLAPSED FOCUSABLE HASPOPUP haspopup:listbox ia2_hypertext='<obj0>'
 ++++++++IA2_ROLE_SECTION ia2_hypertext='Option 1'
 ++++++++++ROLE_SYSTEM_STATICTEXT name='Option 1' ia2_hypertext='Option 1'
 ++++IA2_ROLE_SECTION ia2_hypertext='<obj0>'
-++++++ROLE_SYSTEM_BUTTONMENU name='Custom selectmenu button' COLLAPSED HASPOPUP haspopup:menu ia2_hypertext='Custom selectmenu button'
+++++++ROLE_SYSTEM_COMBOBOX value='Custom selectmenu button' COLLAPSED HASPOPUP haspopup:listbox ia2_hypertext='Custom selectmenu button'
+++++++++ROLE_SYSTEM_STATICTEXT name='Custom selectmenu button' ia2_hypertext='Custom selectmenu button'
diff --git a/content/test/data/accessibility/html/selectmenu-open-expected-blink.txt b/content/test/data/accessibility/html/selectmenu-open-expected-blink.txt
index bb50234..19d5f5ed 100644
--- a/content/test/data/accessibility/html/selectmenu-open-expected-blink.txt
+++ b/content/test/data/accessibility/html/selectmenu-open-expected-blink.txt
@@ -3,7 +3,7 @@
 ++++genericContainer
 ++++++genericContainer
 ++++++++genericContainer ignored
-++++++++++popUpButton name='Custom selectmenu button' setSize=0 haspopup=menu
+++++++++++comboBoxMenuButton value='Custom selectmenu button' haspopup=listbox
 ++++++++++++staticText name='Custom selectmenu button'
 ++++++++++++++inlineTextBox name='Custom selectmenu button'
 ++++++++genericContainer ignored
diff --git a/content/test/data/accessibility/html/selectmenu-open-expected-fuchsia.txt b/content/test/data/accessibility/html/selectmenu-open-expected-fuchsia.txt
new file mode 100644
index 0000000..a7a9a8b2
--- /dev/null
+++ b/content/test/data/accessibility/html/selectmenu-open-expected-fuchsia.txt
@@ -0,0 +1,13 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN label='Custom selectmenu button' actions='{DEFAULT}'
+++++++++++++STATIC_TEXT label='Custom selectmenu button'
+++++++++++++++UNKNOWN label='Custom selectmenu button'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++UNKNOWN focusable selected label='Option 1' actions='{DEFAULT}'
+++++++++++++UNKNOWN focusable label='Option 2' actions='{DEFAULT}'
+++++++++++++UNKNOWN focusable label='Option 3' actions='{DEFAULT}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/selectmenu-open-expected-mac.txt b/content/test/data/accessibility/html/selectmenu-open-expected-mac.txt
index 284f1d5..dbb743b 100644
--- a/content/test/data/accessibility/html/selectmenu-open-expected-mac.txt
+++ b/content/test/data/accessibility/html/selectmenu-open-expected-mac.txt
@@ -1,7 +1,8 @@
 AXWebArea AXRoleDescription='HTML content'
 ++AXGroup AXRoleDescription='group'
 ++++AXGroup AXRoleDescription='group'
-++++++AXPopUpButton AXRoleDescription='pop up button' AXTitle='Custom selectmenu button'
+++++++AXComboBox AXRoleDescription='combo box' AXValue='Custom selectmenu button'
+++++++++AXStaticText AXRoleDescription='text' AXValue='Custom selectmenu button'
 ++++++AXList AXRoleDescription='list'
 ++++++++AXStaticText AXRoleDescription='text' AXValue='Option 1'
 ++++++++AXStaticText AXRoleDescription='text' AXValue='Option 2'
diff --git a/content/test/data/accessibility/html/selectmenu-open-expected-uia-win.txt b/content/test/data/accessibility/html/selectmenu-open-expected-uia-win.txt
index 5789ea5..c762a2a 100644
--- a/content/test/data/accessibility/html/selectmenu-open-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/selectmenu-open-expected-uia-win.txt
@@ -1,7 +1,8 @@
 Document
 ++Group IsControlElement=false
 ++++Group IsControlElement=false
-++++++Button Name='Custom selectmenu button' ExpandCollapse.ExpandCollapseState='Expanded'
+++++++ComboBox ExpandCollapse.ExpandCollapseState='Expanded' Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false Value.Value='Custom selectmenu button'
+++++++++Text Name='Custom selectmenu button'
 ++++++List Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
 ++++++++ListItem Name='Option 1' SelectionItem.IsSelected=true
 ++++++++ListItem Name='Option 2' SelectionItem.IsSelected=false
diff --git a/content/test/data/accessibility/html/selectmenu-open-expected-win.txt b/content/test/data/accessibility/html/selectmenu-open-expected-win.txt
index a056d33a..b613866 100644
--- a/content/test/data/accessibility/html/selectmenu-open-expected-win.txt
+++ b/content/test/data/accessibility/html/selectmenu-open-expected-win.txt
@@ -1,7 +1,8 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE ia2_hypertext='<obj0>'
 ++IA2_ROLE_SECTION ia2_hypertext='<obj0>'
 ++++IA2_ROLE_SECTION ia2_hypertext='<obj0><obj1>'
-++++++ROLE_SYSTEM_BUTTONMENU name='Custom selectmenu button' EXPANDED HASPOPUP haspopup:menu ia2_hypertext='Custom selectmenu button'
+++++++ROLE_SYSTEM_COMBOBOX value='Custom selectmenu button' EXPANDED HASPOPUP haspopup:listbox ia2_hypertext='Custom selectmenu button'
+++++++++ROLE_SYSTEM_STATICTEXT name='Custom selectmenu button' ia2_hypertext='Custom selectmenu button'
 ++++++ROLE_SYSTEM_LIST ia2_hypertext='<obj0><obj1><obj2>'
 ++++++++ROLE_SYSTEM_LISTITEM name='Option 1' SELECTED FOCUSABLE SELECTABLE ia2_hypertext='Option 1'
 ++++++++ROLE_SYSTEM_LISTITEM name='Option 2' FOCUSABLE SELECTABLE ia2_hypertext='Option 2'
diff --git a/content/test/data/accessibility/html/small-expected-fuchsia.txt b/content/test/data/accessibility/html/small-expected-fuchsia.txt
new file mode 100644
index 0000000..949f9bf
--- /dev/null
+++ b/content/test/data/accessibility/html/small-expected-fuchsia.txt
@@ -0,0 +1,8 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='Chromium'
+++++++++++UNKNOWN label='Chromium'
+++++++++STATIC_TEXT label='open source project'
+++++++++++UNKNOWN label='open source project'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/source-expected-fuchsia.txt b/content/test/data/accessibility/html/source-expected-fuchsia.txt
new file mode 100644
index 0000000..bcc6143
--- /dev/null
+++ b/content/test/data/accessibility/html/source-expected-fuchsia.txt
@@ -0,0 +1,64 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable
+++++++++UNKNOWN
+++++++++++UNKNOWN hidden
+++++++++++++BUTTON hidden
+++++++++++UNKNOWN
+++++++++++++UNKNOWN
+++++++++++++++BUTTON focusable label='play' actions='{DEFAULT}'
+++++++++++++++++UNKNOWN
+++++++++++++++UNKNOWN label='elapsed time: 0:00'
+++++++++++++++++STATIC_TEXT label='0:00'
+++++++++++++++++++UNKNOWN label='0:00'
+++++++++++++++UNKNOWN label='total time: / 0:00'
+++++++++++++++++STATIC_TEXT label='/ 0:00'
+++++++++++++++++++UNKNOWN label='/ 0:00'
+++++++++++++++SLIDER focusable label='audio time scrubber 0:00 / 0:00' actions='{SET_VALUE}' value='elapsed time: 0:00'
+++++++++++++++UNKNOWN
+++++++++++++++++UNKNOWN
+++++++++++++++++SLIDER focusable label='volume' actions='{SET_VALUE}'
+++++++++++++++++BUTTON focusable label='mute' actions='{DEFAULT}'
+++++++++++++++BUTTON hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON focusable label='show more media controls' actions='{DEFAULT}' secondary_label='more options'
+++++++++++++++++UNKNOWN
+++++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Play'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Fullscreen'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Download'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Mute'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Cast'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Captions'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Playback speed'
diff --git a/content/test/data/accessibility/html/span-expected-fuchsia.txt b/content/test/data/accessibility/html/span-expected-fuchsia.txt
new file mode 100644
index 0000000..16282107
--- /dev/null
+++ b/content/test/data/accessibility/html/span-expected-fuchsia.txt
@@ -0,0 +1,120 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='This'
+++++++++++UNKNOWN label='This'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++STATIC_TEXT label='paragraph has '
+++++++++++UNKNOWN label='paragraph has '
+++++++++STATIC_TEXT label='text'
+++++++++++UNKNOWN label='text'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++STATIC_TEXT label='in'
+++++++++++UNKNOWN label='in'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++STATIC_TEXT label='spans'
+++++++++++UNKNOWN label='spans'
+++++++++STATIC_TEXT label='.'
+++++++++++UNKNOWN label='.'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='E1. Eat'
+++++++++++UNKNOWN label='E1. Eat'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++LINK focusable label='space' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='space'
+++++++++++++UNKNOWN label='space'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='E2. Eat'
+++++++++++UNKNOWN label='E2. Eat'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++LINK focusable label='space' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='space'
+++++++++++++UNKNOWN label='space'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='E3. Eat'
+++++++++++UNKNOWN label='E3. Eat'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++LINK focusable label='space' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='space'
+++++++++++++UNKNOWN label='space'
+++++++PARAGRAPH
+++++++++LINK focusable label='E4. Eat' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='E4. Eat'
+++++++++++++UNKNOWN label='E4. Eat'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++STATIC_TEXT label='space'
+++++++++++UNKNOWN label='space'
+++++++PARAGRAPH
+++++++++LINK focusable label='E5. Eat' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='E5. Eat'
+++++++++++++UNKNOWN label='E5. Eat'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++STATIC_TEXT label='space'
+++++++++++UNKNOWN label='space'
+++++++PARAGRAPH
+++++++++LINK focusable label='E6. Eat' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='E6. Eat'
+++++++++++++UNKNOWN label='E6. Eat'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++STATIC_TEXT label='space'
+++++++++++UNKNOWN label='space'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='K1. Keep'
+++++++++++UNKNOWN label='K1. Keep'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++STATIC_TEXT label='space'
+++++++++++UNKNOWN label='space'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='K2. Keep'
+++++++++++UNKNOWN label='K2. Keep'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++STATIC_TEXT label='space'
+++++++++++UNKNOWN label='space'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='K3. Keep'
+++++++++++UNKNOWN label='K3. Keep'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++STATIC_TEXT label='space'
+++++++++++UNKNOWN label='space'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='K4. Keep '
+++++++++++UNKNOWN label='K4. Keep '
+++++++++STATIC_TEXT label='space'
+++++++++++UNKNOWN label='space'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='K5. Keep'
+++++++++++UNKNOWN label='K5. Keep'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++STATIC_TEXT label='space'
+++++++++++UNKNOWN label='space'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='K6. Keep '
+++++++++++UNKNOWN label='K6. Keep '
+++++++++STATIC_TEXT label='space'
+++++++++++UNKNOWN label='space'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='K7. Keep'
+++++++++++UNKNOWN label='K7. Keep'
+++++++++STATIC_TEXT label=' space'
+++++++++++UNKNOWN label=' space'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='K8. Keep'
+++++++++++UNKNOWN label='K8. Keep'
+++++++++STATIC_TEXT label=' '
+++++++++++UNKNOWN label=' '
+++++++++STATIC_TEXT label='space'
+++++++++++UNKNOWN label='space'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/strong-expected-fuchsia.txt b/content/test/data/accessibility/html/strong-expected-fuchsia.txt
new file mode 100644
index 0000000..1d89d893
--- /dev/null
+++ b/content/test/data/accessibility/html/strong-expected-fuchsia.txt
@@ -0,0 +1,5 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++STATIC_TEXT label='Strong text'
+++++++++UNKNOWN label='Strong text'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/style-expected-fuchsia.txt b/content/test/data/accessibility/html/style-expected-fuchsia.txt
new file mode 100644
index 0000000..ffbb452
--- /dev/null
+++ b/content/test/data/accessibility/html/style-expected-fuchsia.txt
@@ -0,0 +1,3 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/sub-expected-fuchsia.txt b/content/test/data/accessibility/html/sub-expected-fuchsia.txt
new file mode 100644
index 0000000..50289fa
--- /dev/null
+++ b/content/test/data/accessibility/html/sub-expected-fuchsia.txt
@@ -0,0 +1,33 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='This text contains '
+++++++++++UNKNOWN label='This text contains '
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='subscript'
+++++++++++++UNKNOWN label='subscript'
+++++++++STATIC_TEXT label=' text.'
+++++++++++UNKNOWN label=' text.'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='H'
+++++++++++UNKNOWN label='H'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label=' '
+++++++++++++UNKNOWN label=' '
+++++++++++UNKNOWN
+++++++++++++UNKNOWN focusable label='3'
+++++++++++++++UNKNOWN label='oops'
+++++++++++++++++STATIC_TEXT label='3'
+++++++++++++++++++UNKNOWN label='3'
+++++++++++STATIC_TEXT label=' '
+++++++++++++UNKNOWN label=' '
+++++++++++UNKNOWN
+++++++++++++LINK focusable label='2' actions='{DEFAULT}'
+++++++++++++++UNKNOWN label='better' actions='{DEFAULT}'
+++++++++++++++++STATIC_TEXT label='2'
+++++++++++++++++++UNKNOWN label='2'
+++++++++++STATIC_TEXT label=' '
+++++++++++++UNKNOWN label=' '
+++++++++STATIC_TEXT label='O'
+++++++++++UNKNOWN label='O'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/summary-expected-fuchsia.txt b/content/test/data/accessibility/html/summary-expected-fuchsia.txt
new file mode 100644
index 0000000..f3f8287
--- /dev/null
+++ b/content/test/data/accessibility/html/summary-expected-fuchsia.txt
@@ -0,0 +1,29 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable label='details tag' actions='{DEFAULT}'
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT hidden label='%E2%96%B8 '
+++++++++++++STATIC_TEXT label='details tag'
+++++++++++++++UNKNOWN label='details tag'
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='<newline>    '
+++++++++++STATIC_TEXT hidden label='<newline>    '
+++++++++++PARAGRAPH hidden
+++++++++++++STATIC_TEXT hidden label='The details tag specifies additional details that the user can view or hide on demand.'
+++++++++++STATIC_TEXT hidden label='<newline>  '
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN focusable label='name' actions='{DEFAULT}' secondary_label='details #2'
+++++++++++++UNKNOWN hidden
+++++++++++++++STATIC_TEXT hidden label='%E2%96%B8 '
+++++++++++++STATIC_TEXT label='details #2'
+++++++++++++++UNKNOWN label='details #2'
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='<newline>    '
+++++++++++STATIC_TEXT hidden label='<newline>    '
+++++++++++PARAGRAPH hidden
+++++++++++++STATIC_TEXT hidden label='Paragraph#2'
+++++++++++STATIC_TEXT hidden label='<newline>  '
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/sup-expected-fuchsia.txt b/content/test/data/accessibility/html/sup-expected-fuchsia.txt
new file mode 100644
index 0000000..51f8f0d
--- /dev/null
+++ b/content/test/data/accessibility/html/sup-expected-fuchsia.txt
@@ -0,0 +1,11 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='This text contains'
+++++++++++UNKNOWN label='This text contains'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='superscript'
+++++++++++++UNKNOWN label='superscript'
+++++++++STATIC_TEXT label='text.'
+++++++++++UNKNOWN label='text.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-and-math-elements-expected-fuchsia.txt b/content/test/data/accessibility/html/svg-and-math-elements-expected-fuchsia.txt
new file mode 100644
index 0000000..cdd041df
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-and-math-elements-expected-fuchsia.txt
@@ -0,0 +1,13 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++IMAGE label='original'
+++++++++UNKNOWN label='created'
+++++++++UNKNOWN label='created'
+++++++UNKNOWN
+++++++++UNKNOWN label='original'
+++++++++UNKNOWN label='created'
+++++++++UNKNOWN label='created'
+++++++STATIC_TEXT label='Done'
+++++++++UNKNOWN label='Done'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-as-object-source-expected-fuchsia.txt b/content/test/data/accessibility/html/svg-as-object-source-expected-fuchsia.txt
new file mode 100644
index 0000000..341d789
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-as-object-source-expected-fuchsia.txt
@@ -0,0 +1,19 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN focusable label='object without space'
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN
+++++++++++++BUTTON label='face'
+++++++++++++BUTTON label='left-eye'
+++++++++++++BUTTON label='right-eye'
+++++++++++++BUTTON label='nose'
+++++++++++++BUTTON label='smile'
+++++++UNKNOWN focusable label='object with space'
+++++++++UNKNOWN focusable
+++++++++++UNKNOWN
+++++++++++++BUTTON label='face'
+++++++++++++BUTTON label='left-eye'
+++++++++++++BUTTON label='right-eye'
+++++++++++++BUTTON label='nose'
+++++++++++++BUTTON label='smile'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-child-of-button-expected-fuchsia.txt b/content/test/data/accessibility/html/svg-child-of-button-expected-fuchsia.txt
new file mode 100644
index 0000000..c805624
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-child-of-button-expected-fuchsia.txt
@@ -0,0 +1,5 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++BUTTON focusable label='Search' actions='{DEFAULT}'
+++++++++IMAGE actions='{DEFAULT}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-child-of-svg-expected-fuchsia.txt b/content/test/data/accessibility/html/svg-child-of-svg-expected-fuchsia.txt
new file mode 100644
index 0000000..08b88b08
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-child-of-svg-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++IMAGE label='Has two simple children'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++UNKNOWN label='Has two focusable children'
+++++++++UNKNOWN focusable
+++++++++UNKNOWN focusable
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-desc-in-group-expected-fuchsia.txt b/content/test/data/accessibility/html/svg-desc-in-group-expected-fuchsia.txt
new file mode 100644
index 0000000..71c0920
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-desc-in-group-expected-fuchsia.txt
@@ -0,0 +1,10 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++UNKNOWN secondary_label='Group with circle and text'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Group with circle and text'
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='hello world'
+++++++++++++++UNKNOWN label='hello world'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-elements-not-mapped-expected-fuchsia.txt b/content/test/data/accessibility/html/svg-elements-not-mapped-expected-fuchsia.txt
new file mode 100644
index 0000000..716bfbcd
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-elements-not-mapped-expected-fuchsia.txt
@@ -0,0 +1,109 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++IMAGE label='animate should not be exposed'
+++++++++UNKNOWN hidden
+++++++IMAGE label='animateMotion should not be exposed'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++IMAGE label='animateTransform should not be exposed'
+++++++++UNKNOWN hidden
+++++++IMAGE label='clipPath should not be exposed'
+++++++IMAGE label='cursor should not be exposed'
+++++++++UNKNOWN hidden
+++++++IMAGE label='defs should not be exposed'
+++++++++UNKNOWN hidden
+++++++IMAGE label='discard should not be exposed'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++IMAGE label='filter and filter effect elements should not be exposed'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++IMAGE label='hatch should not be exposed'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++IMAGE label='hatchpath should not be exposed'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++IMAGE label='image which lacks criteria for inclusion should not be exposed'
+++++++IMAGE label='linearGradient should not be exposed'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++IMAGE label='marker should not be exposed'
+++++++IMAGE label='mask should not be exposed'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++IMAGE label='metadata should not be exposed'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++IMAGE label='mpath should not be exposed'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++IMAGE label='pattern should not be exposed'
+++++++IMAGE label='radialGradient should not be exposed'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++IMAGE label='solidColor should not be exposed'
+++++++++UNKNOWN hidden
+++++++IMAGE label='stop should not be exposed'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++UNKNOWN label='style and set should not be exposed'
+++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++UNKNOWN hidden
+++++++UNKNOWN label='switch should not be exposed'
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='Ciao!'
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='Hola!'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Hey!'
+++++++++++++UNKNOWN label='Hey!'
+++++++IMAGE label='view should not be exposed'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++IMAGE label='desc should not be exposed as accessible object' secondary_label='abc'
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='abc'
+++++++IMAGE label='title should not be exposed as accessible object' secondary_label='abc'
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='abc'
+++++++IMAGE label='circle which lacks criteria for inclusion should not be exposed'
+++++++IMAGE label='ellipse which lacks criteria for inclusion should not be exposed'
+++++++UNKNOWN label='foreignObject which lacks criteria for inclusion should not be exposed'
+++++++++UNKNOWN hidden
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='Hello world'
+++++++++++++++UNKNOWN label='Hello '
+++++++++++++++UNKNOWN label='world'
+++++++IMAGE label='group which lacks criteria for inclusion should not be exposed'
+++++++++UNKNOWN hidden
+++++++IMAGE label='line which lacks criteria for inclusion should not be exposed'
+++++++IMAGE label='path which lacks criteria for inclusion should not be exposed'
+++++++IMAGE label='polygon which lacks criteria for inclusion should not be exposed'
+++++++IMAGE label='polyline which lacks criteria for inclusion should not be exposed'
+++++++IMAGE label='rect which lacks criteria for inclusion should not be exposed'
+++++++IMAGE label='symbol which lacks criteria for inclusion should not be exposed'
+++++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++UNKNOWN label='textPath which lacks criteria for inclusion should not be exposed'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Hello'
+++++++++++++UNKNOWN label='H'
+++++++++++++UNKNOWN label='e'
+++++++++++++UNKNOWN label='l'
+++++++++++++UNKNOWN label='l'
+++++++++++++UNKNOWN label='o'
+++++++UNKNOWN label='tspan which lacks criteria for inclusion should not be exposed'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Hello '
+++++++++++++UNKNOWN label='Hello '
+++++++++++STATIC_TEXT label='world'
+++++++++++++UNKNOWN label='world'
+++++++++++STATIC_TEXT label='!'
+++++++++++++UNKNOWN label='!'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-expected-fuchsia.txt b/content/test/data/accessibility/html/svg-expected-fuchsia.txt
new file mode 100644
index 0000000..f84238d
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN label='svg' secondary_label='SVG Title Tag'
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='SVG Title Tag'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Test'
+++++++++++++UNKNOWN label='Test'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-style-element-expected-fuchsia.txt b/content/test/data/accessibility/html/svg-style-element-expected-fuchsia.txt
new file mode 100644
index 0000000..ae25cd6
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-style-element-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++BUTTON focusable label='Kettle' actions='{DEFAULT}'
+++++++++++STATIC_TEXT label='Kettle'
+++++++++++++UNKNOWN label='Kettle'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-symbol-with-role-expected-android-external.txt b/content/test/data/accessibility/html/svg-symbol-with-role-expected-android-external.txt
index 0bb22b2..959ced7 100644
--- a/content/test/data/accessibility/html/svg-symbol-with-role-expected-android-external.txt
+++ b/content/test/data/accessibility/html/svg-symbol-with-role-expected-android-external.txt
@@ -1,5 +1,5 @@
 WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea", hasImage="true"]
 ++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer", hasImage="true"]
 ++++Image actions:[AX_FOCUS] bundle:[chromeRole="svgRoot", hasImage="true", roleDescription="graphic"]
-++++++Button text:"Click me!" viewIdResName:"myRect" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
-++++++Button text:"Click me!" viewIdResName:"myRect" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
\ No newline at end of file
+++++++Button text:"Click me!" viewIdResName:"myRect" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="fancy button"]
+++++++Button text:"Click me!" viewIdResName:"myRect" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="fancy button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-symbol-with-role-expected-fuchsia.txt b/content/test/data/accessibility/html/svg-symbol-with-role-expected-fuchsia.txt
new file mode 100644
index 0000000..c62d547
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-symbol-with-role-expected-fuchsia.txt
@@ -0,0 +1,12 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='Click me!'
+++++++++BUTTON label='Click me!'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Click me!'
+++++++++BUTTON label='Click me!'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Click me!'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-text-alternative-computation-expected-fuchsia.txt b/content/test/data/accessibility/html/svg-text-alternative-computation-expected-fuchsia.txt
new file mode 100644
index 0000000..ed56009b
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-text-alternative-computation-expected-fuchsia.txt
@@ -0,0 +1,90 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN hidden
+++++++++STATIC_TEXT hidden label='Link (from aria-labelledby)'
+++++++UNKNOWN hidden
+++++++++STATIC_TEXT hidden label='Circle (from aria-labelledby)'
+++++++UNKNOWN
+++++++++LINK focusable label='Link (from aria-labelledby)' actions='{DEFAULT}' secondary_label='Link (from title)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Link (from title)'
+++++++UNKNOWN
+++++++++LINK focusable label='Link (from aria-label)' actions='{DEFAULT}' secondary_label='Link (from title)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Link (from title)'
+++++++UNKNOWN
+++++++++LINK focusable label='Link (from title)' actions='{DEFAULT}' secondary_label='Link (from xlink:title)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Link (from title)'
+++++++UNKNOWN
+++++++++LINK focusable label='Link (from xlink:title)' actions='{DEFAULT}'
+++++++UNKNOWN
+++++++++UNKNOWN label='Circle (from aria-labelledby)'
+++++++UNKNOWN
+++++++++UNKNOWN label='Circle (from aria-label)'
+++++++UNKNOWN
+++++++++UNKNOWN label='Circle (from title)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Circle (from title)'
+++++++UNKNOWN
+++++++++UNKNOWN label='Circle (From first use element's title)'
+++++++++++UNKNOWN hidden
+++++++++UNKNOWN label='Circle (From second use element's title)'
+++++++++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='Rectangle Symbol (From symbol's title)'
+++++++++UNKNOWN label='Rectangle Symbol (From symbol's title)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Rectangle Symbol (From symbol's title)'
+++++++++UNKNOWN label='Rectangle Symbol (From symbol's title)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Rectangle Symbol (From symbol's title)'
+++++++UNKNOWN hidden
+++++++++STATIC_TEXT hidden label='Link (from aria-describedby)'
+++++++UNKNOWN
+++++++++LINK focusable label='Link (from title)' actions='{DEFAULT}' secondary_label='Link (from aria-describedby)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Link (from title)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Link (from desc)'
+++++++UNKNOWN
+++++++++LINK focusable label='Link (from title)' actions='{DEFAULT}' secondary_label='Link (from aria-description)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Link (from title)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Link (from desc)'
+++++++UNKNOWN
+++++++++LINK focusable label='Link (from title)' actions='{DEFAULT}' secondary_label='Link (from desc)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Link (from title)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Link (from desc)'
+++++++UNKNOWN
+++++++++LINK focusable label='Link (from aria-label)' actions='{DEFAULT}' secondary_label='Link (from title)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Link (from title)'
+++++++UNKNOWN
+++++++++LINK focusable label='Link (from title)' actions='{DEFAULT}' secondary_label='Link (from xlink:title)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Link (from title)'
+++++++UNKNOWN
+++++++++UNKNOWN secondary_label='Circle (From first use element's desc)'
+++++++++++UNKNOWN hidden
+++++++++UNKNOWN secondary_label='Circle (From second use element's desc)'
+++++++++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN label='Circle 1' secondary_label='Circle (From first use element's title)'
+++++++++++UNKNOWN hidden
+++++++++UNKNOWN label='Circle 2' secondary_label='Circle (From second use element's title)'
+++++++++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++STATIC_TEXT hidden label='Rectangle Symbol (From symbol's desc)'
+++++++++UNKNOWN secondary_label='Rectangle Symbol (From symbol's desc)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Rectangle Symbol (From symbol's desc)'
+++++++++UNKNOWN secondary_label='Rectangle Symbol (From symbol's desc)'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='Rectangle Symbol (From symbol's desc)'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-title-in-group-expected-fuchsia.txt b/content/test/data/accessibility/html/svg-title-in-group-expected-fuchsia.txt
new file mode 100644
index 0000000..c3b2a87
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-title-in-group-expected-fuchsia.txt
@@ -0,0 +1,10 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++IMAGE label='abcde'
+++++++++++UNKNOWN hidden
+++++++++++++STATIC_TEXT hidden label='abcde'
+++++++++++UNKNOWN
+++++++++++++STATIC_TEXT label='NL'
+++++++++++++++UNKNOWN label='NL'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-with-clickable-rect-expected-fuchsia.txt b/content/test/data/accessibility/html/svg-with-clickable-rect-expected-fuchsia.txt
new file mode 100644
index 0000000..c2ae7c7
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-with-clickable-rect-expected-fuchsia.txt
@@ -0,0 +1,6 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-with-foreign-object-expected-fuchsia.txt b/content/test/data/accessibility/html/svg-with-foreign-object-expected-fuchsia.txt
new file mode 100644
index 0000000..ae51abea
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-with-foreign-object-expected-fuchsia.txt
@@ -0,0 +1,9 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++PARAGRAPH
+++++++++++++STATIC_TEXT label='Hello world'
+++++++++++++++UNKNOWN label='Hello '
+++++++++++++++UNKNOWN label='world'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-with-link-to-document-expected-fuchsia.txt b/content/test/data/accessibility/html/svg-with-link-to-document-expected-fuchsia.txt
new file mode 100644
index 0000000..6fd37b8
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-with-link-to-document-expected-fuchsia.txt
@@ -0,0 +1,5 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN label='svg'
+++++++++LINK focusable actions='{DEFAULT}'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/svg-with-non-link-anchors-expected-fuchsia.txt b/content/test/data/accessibility/html/svg-with-non-link-anchors-expected-fuchsia.txt
new file mode 100644
index 0000000..93df4c32
--- /dev/null
+++ b/content/test/data/accessibility/html/svg-with-non-link-anchors-expected-fuchsia.txt
@@ -0,0 +1,14 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN label='Text descendants'
+++++++++UNKNOWN
+++++++++++UNKNOWN focusable label='focusable'
+++++++++++++STATIC_TEXT label='focusable'
+++++++++++++++UNKNOWN label='focusable'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='not focusable'
+++++++++++++UNKNOWN label='not focusable'
+++++++UNKNOWN label='Not text descendants'
+++++++++UNKNOWN focusable
+++++++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/tabindex-expose-children-expected-fuchsia.txt b/content/test/data/accessibility/html/tabindex-expose-children-expected-fuchsia.txt
new file mode 100644
index 0000000..3dfd290
--- /dev/null
+++ b/content/test/data/accessibility/html/tabindex-expose-children-expected-fuchsia.txt
@@ -0,0 +1,23 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN hidden
+++++++++UNKNOWN
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN
+++++++++++++++UNKNOWN label='1.'
+++++++++++++++++STATIC_TEXT label='1.'
+++++++++++++++++++UNKNOWN label='1.'
+++++++++++++++UNKNOWN label='2.'
+++++++++++++++++STATIC_TEXT label='2.'
+++++++++++++++++++UNKNOWN label='2.'
+++++++UNKNOWN focusable label='3. 4.'
+++++++++UNKNOWN
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN
+++++++++++++++UNKNOWN label='3.'
+++++++++++++++++STATIC_TEXT label='3.'
+++++++++++++++++++UNKNOWN label='3.'
+++++++++++++++UNKNOWN label='4.'
+++++++++++++++++STATIC_TEXT label='4.'
+++++++++++++++++++UNKNOWN label='4.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/table-column-remove-expected-fuchsia.txt b/content/test/data/accessibility/html/table-column-remove-expected-fuchsia.txt
new file mode 100644
index 0000000..363d6ea
--- /dev/null
+++ b/content/test/data/accessibility/html/table-column-remove-expected-fuchsia.txt
@@ -0,0 +1,17 @@
+UNKNOWN focusable label='done'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TABLE number_of_rows=2 number_of_columns=1
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW
+++++++++++++CELL hidden cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT hidden label='AB'
+++++++++++++CELL label='B' cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='B'
+++++++++++++++++UNKNOWN label='B'
+++++++++++TABLE_ROW row_index=1
+++++++++++++CELL hidden cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT hidden label='CD'
+++++++++++++CELL label='D' cell_row_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='D'
+++++++++++++++++UNKNOWN label='D'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/table-focusable-sections-expected-fuchsia.txt b/content/test/data/accessibility/html/table-focusable-sections-expected-fuchsia.txt
new file mode 100644
index 0000000..a566fcc5
--- /dev/null
+++ b/content/test/data/accessibility/html/table-focusable-sections-expected-fuchsia.txt
@@ -0,0 +1,35 @@
+UNKNOWN focusable label='Table example - focusable thead, tbody, tfoot'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TABLE number_of_rows=4 number_of_columns=2
+++++++++ROW_GROUP focusable
+++++++++++TABLE_ROW
+++++++++++++COLUMN_HEADER label='Sum' cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Sum'
+++++++++++++++++UNKNOWN label='Sum'
+++++++++++++COLUMN_HEADER label='Subtraction' cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Subtraction'
+++++++++++++++++UNKNOWN label='Subtraction'
+++++++++ROW_GROUP focusable
+++++++++++TABLE_ROW row_index=1
+++++++++++++CELL label='10' cell_row_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='10'
+++++++++++++++++UNKNOWN label='10'
+++++++++++++CELL label='7' cell_row_index=1 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='7'
+++++++++++++++++UNKNOWN label='7'
+++++++++++TABLE_ROW row_index=2
+++++++++++++CELL label='2' cell_row_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='2'
+++++++++++++++++UNKNOWN label='2'
+++++++++++++CELL label='4' cell_row_index=2 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='4'
+++++++++++++++++UNKNOWN label='4'
+++++++++ROW_GROUP focusable
+++++++++++TABLE_ROW row_index=3
+++++++++++++CELL label='12' cell_row_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='12'
+++++++++++++++++UNKNOWN label='12'
+++++++++++++CELL label='3' cell_row_index=3 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='3'
+++++++++++++++++UNKNOWN label='3'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/table-headers-empty-first-cell-expected-fuchsia.txt b/content/test/data/accessibility/html/table-headers-empty-first-cell-expected-fuchsia.txt
new file mode 100644
index 0000000..e0a38ac
--- /dev/null
+++ b/content/test/data/accessibility/html/table-headers-empty-first-cell-expected-fuchsia.txt
@@ -0,0 +1,101 @@
+UNKNOWN focusable label='Table example - headers with empty first cell'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TABLE label='Delivery slots:' number_of_rows=5 number_of_columns=6
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='Delivery slots:'
+++++++++++++UNKNOWN label='Delivery slots:'
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW
+++++++++++++CELL cell_row_span=1 cell_column_span=1
+++++++++++++COLUMN_HEADER label='Monday' cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Monday'
+++++++++++++++++UNKNOWN label='Monday'
+++++++++++++COLUMN_HEADER label='Tuesday' cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Tuesday'
+++++++++++++++++UNKNOWN label='Tuesday'
+++++++++++++COLUMN_HEADER label='Wednesday' cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Wednesday'
+++++++++++++++++UNKNOWN label='Wednesday'
+++++++++++++COLUMN_HEADER label='Thursday' cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Thursday'
+++++++++++++++++UNKNOWN label='Thursday'
+++++++++++++COLUMN_HEADER label='Friday' cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Friday'
+++++++++++++++++UNKNOWN label='Friday'
+++++++++++TABLE_ROW row_index=1
+++++++++++++UNKNOWN label='09:00 - 11:00' cell_row_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='09:00 - 11:00'
+++++++++++++++++UNKNOWN label='09:00 - 11:00'
+++++++++++++CELL label='Closed' cell_row_index=1 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Closed'
+++++++++++++++++UNKNOWN label='Closed'
+++++++++++++CELL label='Open' cell_row_index=1 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Open'
+++++++++++++++++UNKNOWN label='Open'
+++++++++++++CELL label='Open' cell_row_index=1 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Open'
+++++++++++++++++UNKNOWN label='Open'
+++++++++++++CELL label='Closed' cell_row_index=1 cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Closed'
+++++++++++++++++UNKNOWN label='Closed'
+++++++++++++CELL label='Closed' cell_row_index=1 cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Closed'
+++++++++++++++++UNKNOWN label='Closed'
+++++++++++TABLE_ROW row_index=2
+++++++++++++UNKNOWN label='11:00 - 13:00' cell_row_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='11:00 - 13:00'
+++++++++++++++++UNKNOWN label='11:00 - 13:00'
+++++++++++++CELL label='Open' cell_row_index=2 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Open'
+++++++++++++++++UNKNOWN label='Open'
+++++++++++++CELL label='Open' cell_row_index=2 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Open'
+++++++++++++++++UNKNOWN label='Open'
+++++++++++++CELL label='Closed' cell_row_index=2 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Closed'
+++++++++++++++++UNKNOWN label='Closed'
+++++++++++++CELL label='Closed' cell_row_index=2 cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Closed'
+++++++++++++++++UNKNOWN label='Closed'
+++++++++++++CELL label='Closed' cell_row_index=2 cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Closed'
+++++++++++++++++UNKNOWN label='Closed'
+++++++++++TABLE_ROW row_index=3
+++++++++++++UNKNOWN label='13:00 - 15:00' cell_row_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='13:00 - 15:00'
+++++++++++++++++UNKNOWN label='13:00 - 15:00'
+++++++++++++CELL label='Open' cell_row_index=3 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Open'
+++++++++++++++++UNKNOWN label='Open'
+++++++++++++CELL label='Open' cell_row_index=3 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Open'
+++++++++++++++++UNKNOWN label='Open'
+++++++++++++CELL label='Open' cell_row_index=3 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Open'
+++++++++++++++++UNKNOWN label='Open'
+++++++++++++CELL label='Closed' cell_row_index=3 cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Closed'
+++++++++++++++++UNKNOWN label='Closed'
+++++++++++++CELL label='Closed' cell_row_index=3 cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Closed'
+++++++++++++++++UNKNOWN label='Closed'
+++++++++++TABLE_ROW row_index=4
+++++++++++++UNKNOWN label='15:00 - 17:00' cell_row_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='15:00 - 17:00'
+++++++++++++++++UNKNOWN label='15:00 - 17:00'
+++++++++++++CELL label='Closed' cell_row_index=4 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Closed'
+++++++++++++++++UNKNOWN label='Closed'
+++++++++++++CELL label='Closed' cell_row_index=4 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Closed'
+++++++++++++++++UNKNOWN label='Closed'
+++++++++++++CELL label='Closed' cell_row_index=4 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Closed'
+++++++++++++++++UNKNOWN label='Closed'
+++++++++++++CELL label='Open' cell_row_index=4 cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Open'
+++++++++++++++++UNKNOWN label='Open'
+++++++++++++CELL label='Open' cell_row_index=4 cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Open'
+++++++++++++++++UNKNOWN label='Open'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/table-headers-on-all-sides-expected-fuchsia.txt b/content/test/data/accessibility/html/table-headers-on-all-sides-expected-fuchsia.txt
new file mode 100644
index 0000000..f13a144
--- /dev/null
+++ b/content/test/data/accessibility/html/table-headers-on-all-sides-expected-fuchsia.txt
@@ -0,0 +1,49 @@
+UNKNOWN focusable label='Table example - headers on all sides'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TABLE number_of_rows=4 number_of_columns=4
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW
+++++++++++++CELL cell_row_span=1 cell_column_span=1
+++++++++++++COLUMN_HEADER label='Red' cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Red'
+++++++++++++++++UNKNOWN label='Red'
+++++++++++++COLUMN_HEADER label='Green' cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Green'
+++++++++++++++++UNKNOWN label='Green'
+++++++++++++CELL cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++TABLE_ROW row_index=1
+++++++++++++UNKNOWN label='Fruit' cell_row_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Fruit'
+++++++++++++++++UNKNOWN label='Fruit'
+++++++++++++CELL label='strawberry' cell_row_index=1 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='strawberry'
+++++++++++++++++UNKNOWN label='strawberry'
+++++++++++++CELL label='lime' cell_row_index=1 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='lime'
+++++++++++++++++UNKNOWN label='lime'
+++++++++++++UNKNOWN label='Fruit' cell_row_index=1 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Fruit'
+++++++++++++++++UNKNOWN label='Fruit'
+++++++++++TABLE_ROW row_index=2
+++++++++++++UNKNOWN label='Veggies' cell_row_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Veggies'
+++++++++++++++++UNKNOWN label='Veggies'
+++++++++++++CELL label='radish' cell_row_index=2 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='radish'
+++++++++++++++++UNKNOWN label='radish'
+++++++++++++CELL label='spinach' cell_row_index=2 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='spinach'
+++++++++++++++++UNKNOWN label='spinach'
+++++++++++++UNKNOWN label='Veggies' cell_row_index=2 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Veggies'
+++++++++++++++++UNKNOWN label='Veggies'
+++++++++++TABLE_ROW row_index=3
+++++++++++++CELL cell_row_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++COLUMN_HEADER label='Red' cell_row_index=3 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Red'
+++++++++++++++++UNKNOWN label='Red'
+++++++++++++COLUMN_HEADER label='Green' cell_row_index=3 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Green'
+++++++++++++++++UNKNOWN label='Green'
+++++++++++++CELL cell_row_index=3 cell_column_index=3 cell_row_span=1 cell_column_span=1
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/table-layout-expected-fuchsia.txt b/content/test/data/accessibility/html/table-layout-expected-fuchsia.txt
new file mode 100644
index 0000000..e4135d13
--- /dev/null
+++ b/content/test/data/accessibility/html/table-layout-expected-fuchsia.txt
@@ -0,0 +1,35 @@
+UNKNOWN focusable label='Table example #2'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN
+++++++++++++UNKNOWN label='1'
+++++++++++++++STATIC_TEXT label='1'
+++++++++++++++++UNKNOWN label='1'
+++++++++++++UNKNOWN label='2'
+++++++++++++++STATIC_TEXT label='2'
+++++++++++++++++UNKNOWN label='2'
+++++++++++++UNKNOWN label='3'
+++++++++++++++STATIC_TEXT label='3'
+++++++++++++++++UNKNOWN label='3'
+++++++++++UNKNOWN
+++++++++++++UNKNOWN label='4'
+++++++++++++++STATIC_TEXT label='4'
+++++++++++++++++UNKNOWN label='4'
+++++++++++++UNKNOWN label='5'
+++++++++++++++STATIC_TEXT label='5'
+++++++++++++++++UNKNOWN label='5'
+++++++++++++UNKNOWN label='6'
+++++++++++++++STATIC_TEXT label='6'
+++++++++++++++++UNKNOWN label='6'
+++++++++++UNKNOWN
+++++++++++++UNKNOWN label='7'
+++++++++++++++STATIC_TEXT label='7'
+++++++++++++++++UNKNOWN label='7'
+++++++++++++UNKNOWN label='8'
+++++++++++++++STATIC_TEXT label='8'
+++++++++++++++++UNKNOWN label='8'
+++++++++++++UNKNOWN label='9'
+++++++++++++++STATIC_TEXT label='9'
+++++++++++++++++UNKNOWN label='9'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/table-multiple-row-and-column-headers-expected-fuchsia.txt b/content/test/data/accessibility/html/table-multiple-row-and-column-headers-expected-fuchsia.txt
new file mode 100644
index 0000000..9872751
--- /dev/null
+++ b/content/test/data/accessibility/html/table-multiple-row-and-column-headers-expected-fuchsia.txt
@@ -0,0 +1,96 @@
+UNKNOWN focusable label='Table example - multiple row and column headers'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TABLE number_of_rows=6 number_of_columns=6
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW
+++++++++++++CELL cell_row_span=2 cell_column_span=2
+++++++++++++COLUMN_HEADER label='Mars' cell_column_index=2 cell_row_span=1 cell_column_span=2
+++++++++++++++STATIC_TEXT label='Mars'
+++++++++++++++++UNKNOWN label='Mars'
+++++++++++++COLUMN_HEADER label='Venus' cell_column_index=4 cell_row_span=1 cell_column_span=2
+++++++++++++++STATIC_TEXT label='Venus'
+++++++++++++++++UNKNOWN label='Venus'
+++++++++++TABLE_ROW row_index=1
+++++++++++++COLUMN_HEADER label='Produced' cell_row_index=1 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Produced'
+++++++++++++++++UNKNOWN label='Produced'
+++++++++++++COLUMN_HEADER label='Sold' cell_row_index=1 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Sold'
+++++++++++++++++UNKNOWN label='Sold'
+++++++++++++COLUMN_HEADER label='Produced' cell_row_index=1 cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Produced'
+++++++++++++++++UNKNOWN label='Produced'
+++++++++++++COLUMN_HEADER label='Sold' cell_row_index=1 cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Sold'
+++++++++++++++++UNKNOWN label='Sold'
+++++++++++TABLE_ROW row_index=2
+++++++++++++UNKNOWN label='For Toddlers' cell_row_index=2 cell_row_span=2 cell_column_span=1
+++++++++++++++STATIC_TEXT label='For Toddlers'
+++++++++++++++++UNKNOWN label='For Toddlers'
+++++++++++++UNKNOWN label='Teddy Bears' cell_row_index=2 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Teddy Bears'
+++++++++++++++++UNKNOWN label='Teddy Bears'
+++++++++++++CELL label='50,000' cell_row_index=2 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='50,000'
+++++++++++++++++UNKNOWN label='50,000'
+++++++++++++CELL label='30,000' cell_row_index=2 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='30,000'
+++++++++++++++++UNKNOWN label='30,000'
+++++++++++++CELL label='100,000' cell_row_index=2 cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='100,000'
+++++++++++++++++UNKNOWN label='100,000'
+++++++++++++CELL label='80,000' cell_row_index=2 cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='80,000'
+++++++++++++++++UNKNOWN label='80,000'
+++++++++++TABLE_ROW row_index=3
+++++++++++++UNKNOWN label='Action Figures' cell_row_index=3 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Action Figures'
+++++++++++++++++UNKNOWN label='Action Figures'
+++++++++++++CELL label='25,000' cell_row_index=3 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='25,000'
+++++++++++++++++UNKNOWN label='25,000'
+++++++++++++CELL label='15,000' cell_row_index=3 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='15,000'
+++++++++++++++++UNKNOWN label='15,000'
+++++++++++++CELL label='50,000' cell_row_index=3 cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='50,000'
+++++++++++++++++UNKNOWN label='50,000'
+++++++++++++CELL label='40,000' cell_row_index=3 cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='40,000'
+++++++++++++++++UNKNOWN label='40,000'
+++++++++++TABLE_ROW row_index=4
+++++++++++++UNKNOWN label='For Teens' cell_row_index=4 cell_row_span=2 cell_column_span=1
+++++++++++++++STATIC_TEXT label='For Teens'
+++++++++++++++++UNKNOWN label='For Teens'
+++++++++++++UNKNOWN label='Board Games' cell_row_index=4 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Board Games'
+++++++++++++++++UNKNOWN label='Board Games'
+++++++++++++CELL label='5,000' cell_row_index=4 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='5,000'
+++++++++++++++++UNKNOWN label='5,000'
+++++++++++++CELL label='2,000' cell_row_index=4 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='2,000'
+++++++++++++++++UNKNOWN label='2,000'
+++++++++++++CELL label='6,000' cell_row_index=4 cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='6,000'
+++++++++++++++++UNKNOWN label='6,000'
+++++++++++++CELL label='4,000' cell_row_index=4 cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='4,000'
+++++++++++++++++UNKNOWN label='4,000'
+++++++++++TABLE_ROW row_index=5
+++++++++++++UNKNOWN label='Video Games' cell_row_index=5 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Video Games'
+++++++++++++++++UNKNOWN label='Video Games'
+++++++++++++CELL label='10,000' cell_row_index=5 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='10,000'
+++++++++++++++++UNKNOWN label='10,000'
+++++++++++++CELL label='5,000' cell_row_index=5 cell_column_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='5,000'
+++++++++++++++++UNKNOWN label='5,000'
+++++++++++++CELL label='12,000' cell_row_index=5 cell_column_index=4 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='12,000'
+++++++++++++++++UNKNOWN label='12,000'
+++++++++++++CELL label='9,000' cell_row_index=5 cell_column_index=5 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='9,000'
+++++++++++++++++UNKNOWN label='9,000'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/table-presentation-expected-fuchsia.txt b/content/test/data/accessibility/html/table-presentation-expected-fuchsia.txt
new file mode 100644
index 0000000..a39b1dfc
--- /dev/null
+++ b/content/test/data/accessibility/html/table-presentation-expected-fuchsia.txt
@@ -0,0 +1,19 @@
+UNKNOWN focusable label='Table with role=presentation'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN hidden
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN
+++++++++++++++STATIC_TEXT label='1'
+++++++++++++++++UNKNOWN label='1'
+++++++++++++UNKNOWN
+++++++++++++++STATIC_TEXT label='2'
+++++++++++++++++UNKNOWN label='2'
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN
+++++++++++++++STATIC_TEXT label='4'
+++++++++++++++++UNKNOWN label='4'
+++++++++++++UNKNOWN
+++++++++++++++STATIC_TEXT label='5'
+++++++++++++++++UNKNOWN label='5'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/table-row-add-expected-fuchsia.txt b/content/test/data/accessibility/html/table-row-add-expected-fuchsia.txt
new file mode 100644
index 0000000..845727a
--- /dev/null
+++ b/content/test/data/accessibility/html/table-row-add-expected-fuchsia.txt
@@ -0,0 +1,32 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TABLE number_of_rows=4 number_of_columns=2
+++++++++TABLE_ROW
+++++++++++CELL label='Row1' cell_row_span=1 cell_column_span=1
+++++++++++++STATIC_TEXT label='Row1'
+++++++++++++++UNKNOWN label='Row1'
+++++++++++CELL label='Foo1' cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++STATIC_TEXT label='Foo1'
+++++++++++++++UNKNOWN label='Foo1'
+++++++++TABLE_ROW row_index=1
+++++++++++CELL label='Row2' cell_row_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++STATIC_TEXT label='Row2'
+++++++++++++++UNKNOWN label='Row2'
+++++++++++CELL label='Foo2' cell_row_index=1 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++STATIC_TEXT label='Foo2'
+++++++++++++++UNKNOWN label='Foo2'
+++++++++TABLE_ROW row_index=2
+++++++++++CELL label='Row3' cell_row_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++STATIC_TEXT label='Row3'
+++++++++++++++UNKNOWN label='Row3'
+++++++++++CELL label='Foo3' cell_row_index=2 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++STATIC_TEXT label='Foo3'
+++++++++++++++UNKNOWN label='Foo3'
+++++++++TABLE_ROW row_index=3
+++++++++++CELL label='Row4' cell_row_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++STATIC_TEXT label='Row4'
+++++++++++++++UNKNOWN label='Row4'
+++++++++++CELL label='Foo4' cell_row_index=3 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++STATIC_TEXT label='Foo4'
+++++++++++++++UNKNOWN label='Foo4'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/table-simple-expected-fuchsia.txt b/content/test/data/accessibility/html/table-simple-expected-fuchsia.txt
new file mode 100644
index 0000000..0f56125
--- /dev/null
+++ b/content/test/data/accessibility/html/table-simple-expected-fuchsia.txt
@@ -0,0 +1,26 @@
+UNKNOWN focusable label='Table example'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TABLE number_of_rows=3 number_of_columns=2
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW
+++++++++++++COLUMN_HEADER label='Pair' cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Pair'
+++++++++++++++++UNKNOWN label='Pair'
+++++++++++++COLUMN_HEADER label='Single' cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Single'
+++++++++++++++++UNKNOWN label='Single'
+++++++++++TABLE_ROW row_index=1
+++++++++++++CELL label='AB' cell_row_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='AB'
+++++++++++++++++UNKNOWN label='AB'
+++++++++++++CELL label='B' cell_row_index=1 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='B'
+++++++++++++++++UNKNOWN label='B'
+++++++++++TABLE_ROW row_index=2
+++++++++++++CELL label='CD' cell_row_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='CD'
+++++++++++++++++UNKNOWN label='CD'
+++++++++++++CELL label='D' cell_row_index=2 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='D'
+++++++++++++++++UNKNOWN label='D'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/table-spans-expected-fuchsia.txt b/content/test/data/accessibility/html/table-spans-expected-fuchsia.txt
new file mode 100644
index 0000000..e6b43e0
--- /dev/null
+++ b/content/test/data/accessibility/html/table-spans-expected-fuchsia.txt
@@ -0,0 +1,32 @@
+UNKNOWN focusable label='Table example with rowspan and colspan'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TABLE number_of_rows=2 number_of_columns=2
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW
+++++++++++++CELL label='AD' cell_row_span=2 cell_column_span=1
+++++++++++++++STATIC_TEXT label='AD'
+++++++++++++++++UNKNOWN label='AD'
+++++++++++++CELL label='BC' cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='BC'
+++++++++++++++++UNKNOWN label='BC'
+++++++++++TABLE_ROW row_index=1
+++++++++++++CELL label='EF' cell_row_index=1 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='EF'
+++++++++++++++++UNKNOWN label='EF'
+++++++TABLE number_of_rows=2 number_of_columns=3
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW
+++++++++++++CELL label='AD' cell_row_span=2 cell_column_span=1
+++++++++++++++STATIC_TEXT label='AD'
+++++++++++++++++UNKNOWN label='AD'
+++++++++++++CELL label='BC' cell_column_index=1 cell_row_span=1 cell_column_span=2
+++++++++++++++STATIC_TEXT label='BC'
+++++++++++++++++UNKNOWN label='BC'
+++++++++++TABLE_ROW row_index=1
+++++++++++++CELL label='EF' cell_row_index=1 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='EF'
+++++++++++++++++UNKNOWN label='EF'
+++++++++++++CELL label='GH' cell_row_index=1 cell_column_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='GH'
+++++++++++++++++UNKNOWN label='GH'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/table-th-colheader-expected-fuchsia.txt b/content/test/data/accessibility/html/table-th-colheader-expected-fuchsia.txt
new file mode 100644
index 0000000..d2029260
--- /dev/null
+++ b/content/test/data/accessibility/html/table-th-colheader-expected-fuchsia.txt
@@ -0,0 +1,19 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TABLE number_of_rows=2 number_of_columns=2
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW
+++++++++++++COLUMN_HEADER label='Firstname' cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Firstname'
+++++++++++++++++UNKNOWN label='Firstname'
+++++++++++++COLUMN_HEADER label='Lastname' cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Lastname'
+++++++++++++++++UNKNOWN label='Lastname'
+++++++++++TABLE_ROW row_index=1
+++++++++++++CELL label='Jill' cell_row_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Jill'
+++++++++++++++++UNKNOWN label='Jill'
+++++++++++++CELL label='Smith' cell_row_index=1 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Smith'
+++++++++++++++++UNKNOWN label='Smith'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/table-th-rowheader-expected-fuchsia.txt b/content/test/data/accessibility/html/table-th-rowheader-expected-fuchsia.txt
new file mode 100644
index 0000000..c5b9662
--- /dev/null
+++ b/content/test/data/accessibility/html/table-th-rowheader-expected-fuchsia.txt
@@ -0,0 +1,19 @@
+UNKNOWN focusable label='Table example - th rowheader'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TABLE number_of_rows=2 number_of_columns=2
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW
+++++++++++++UNKNOWN label='Firstname' cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Firstname'
+++++++++++++++++UNKNOWN label='Firstname'
+++++++++++++CELL label='Jill' cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Jill'
+++++++++++++++++UNKNOWN label='Jill'
+++++++++++TABLE_ROW row_index=1
+++++++++++++UNKNOWN label='Lastname' cell_row_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Lastname'
+++++++++++++++++UNKNOWN label='Lastname'
+++++++++++++CELL label='Smith' cell_row_index=1 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Smith'
+++++++++++++++++UNKNOWN label='Smith'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/table-thead-tbody-tfoot-expected-fuchsia.txt b/content/test/data/accessibility/html/table-thead-tbody-tfoot-expected-fuchsia.txt
new file mode 100644
index 0000000..e2bc463
--- /dev/null
+++ b/content/test/data/accessibility/html/table-thead-tbody-tfoot-expected-fuchsia.txt
@@ -0,0 +1,35 @@
+UNKNOWN focusable label='Table example - thead, tbody, tfoot'
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TABLE number_of_rows=4 number_of_columns=2
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW
+++++++++++++COLUMN_HEADER label='Sum' cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Sum'
+++++++++++++++++UNKNOWN label='Sum'
+++++++++++++COLUMN_HEADER label='Subtraction' cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='Subtraction'
+++++++++++++++++UNKNOWN label='Subtraction'
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW row_index=1
+++++++++++++CELL label='10' cell_row_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='10'
+++++++++++++++++UNKNOWN label='10'
+++++++++++++CELL label='7' cell_row_index=1 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='7'
+++++++++++++++++UNKNOWN label='7'
+++++++++++TABLE_ROW row_index=2
+++++++++++++CELL label='2' cell_row_index=2 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='2'
+++++++++++++++++UNKNOWN label='2'
+++++++++++++CELL label='4' cell_row_index=2 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='4'
+++++++++++++++++UNKNOWN label='4'
+++++++++ROW_GROUP hidden
+++++++++++TABLE_ROW row_index=3
+++++++++++++CELL label='12' cell_row_index=3 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='12'
+++++++++++++++++UNKNOWN label='12'
+++++++++++++CELL label='3' cell_row_index=3 cell_column_index=1 cell_row_span=1 cell_column_span=1
+++++++++++++++STATIC_TEXT label='3'
+++++++++++++++++UNKNOWN label='3'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/text-align-expected-fuchsia.txt b/content/test/data/accessibility/html/text-align-expected-fuchsia.txt
new file mode 100644
index 0000000..6f91d148
--- /dev/null
+++ b/content/test/data/accessibility/html/text-align-expected-fuchsia.txt
@@ -0,0 +1,36 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Left-aligned text'
+++++++++++UNKNOWN label='Left-aligned text'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Right-aligned text'
+++++++++++UNKNOWN label='Right-aligned text'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Centered text'
+++++++++++UNKNOWN label='Centered text'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Justified text'
+++++++++++UNKNOWN label='Justified text'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Webkit left-aligned text'
+++++++++++UNKNOWN label='Webkit left-aligned text'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Webkit right-aligned text'
+++++++++++UNKNOWN label='Webkit right-aligned text'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Webkit centered text'
+++++++++++UNKNOWN label='Webkit centered text'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Start-aligned text'
+++++++++++UNKNOWN label='Start-aligned text'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='End-aligned text'
+++++++++++UNKNOWN label='End-aligned text'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='No text alignment specified'
+++++++++++UNKNOWN label='No text alignment specified'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Invalid text alignment'
+++++++++++UNKNOWN label='Invalid text alignment'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/text-decoration-styles-expected-fuchsia.txt b/content/test/data/accessibility/html/text-decoration-styles-expected-fuchsia.txt
new file mode 100644
index 0000000..76be630
--- /dev/null
+++ b/content/test/data/accessibility/html/text-decoration-styles-expected-fuchsia.txt
@@ -0,0 +1,57 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='overline style: none'
+++++++++++UNKNOWN label='overline style: none'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='overline style: dotted'
+++++++++++UNKNOWN label='overline style: dotted'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='overline style: dashed'
+++++++++++UNKNOWN label='overline style: dashed'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='overline style: solid'
+++++++++++UNKNOWN label='overline style: solid'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='overline style: double'
+++++++++++UNKNOWN label='overline style: double'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='overline style: wavy'
+++++++++++UNKNOWN label='overline style: wavy'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='underline style: none'
+++++++++++UNKNOWN label='underline style: none'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='underline style: dotted'
+++++++++++UNKNOWN label='underline style: dotted'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='underline style: dashed'
+++++++++++UNKNOWN label='underline style: dashed'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='underline style: solid'
+++++++++++UNKNOWN label='underline style: solid'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='underline style: double'
+++++++++++UNKNOWN label='underline style: double'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='underline style: wavy'
+++++++++++UNKNOWN label='underline style: wavy'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='line-through style: none'
+++++++++++UNKNOWN label='line-through style: none'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='line-through style: dotted'
+++++++++++UNKNOWN label='line-through style: dotted'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='line-through style: dashed'
+++++++++++UNKNOWN label='line-through style: dashed'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='line-through style: solid'
+++++++++++UNKNOWN label='line-through style: solid'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='line-through style: double'
+++++++++++UNKNOWN label='line-through style: double'
+++++++PARAGRAPH
+++++++++STATIC_TEXT label='line-through style: wavy'
+++++++++++UNKNOWN label='line-through style: wavy'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/text-indent-expected-fuchsia.txt b/content/test/data/accessibility/html/text-indent-expected-fuchsia.txt
new file mode 100644
index 0000000..bd2827b
--- /dev/null
+++ b/content/test/data/accessibility/html/text-indent-expected-fuchsia.txt
@@ -0,0 +1,24 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Text indent 50px'
+++++++++++UNKNOWN label='Text indent 50px'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Text indent -50px'
+++++++++++UNKNOWN label='Text indent -50px'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Text indent 0px'
+++++++++++UNKNOWN label='Text indent 0px'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Text indent initial'
+++++++++++UNKNOWN label='Text indent initial'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Text indent inherit'
+++++++++++UNKNOWN label='Text indent inherit'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='No text indent specified'
+++++++++++UNKNOWN label='No text indent specified'
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Invalid text indent'
+++++++++++UNKNOWN label='Invalid text indent'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/textarea-changes-expected-fuchsia.txt b/content/test/data/accessibility/html/textarea-changes-expected-fuchsia.txt
new file mode 100644
index 0000000..4eec29e
--- /dev/null
+++ b/content/test/data/accessibility/html/textarea-changes-expected-fuchsia.txt
@@ -0,0 +1,7 @@
+UNKNOWN focusable label='done'
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='xyz'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='xyz'
+++++++++++++UNKNOWN label='xyz'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/textarea-expected-fuchsia.txt b/content/test/data/accessibility/html/textarea-expected-fuchsia.txt
new file mode 100644
index 0000000..e7b6505
--- /dev/null
+++ b/content/test/data/accessibility/html/textarea-expected-fuchsia.txt
@@ -0,0 +1,14 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='The <newline>textarea tag  defines a multi-line text input control.<newline>'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='The <newline>textarea tag  defines a multi-line text input control.<newline>'
+++++++++++++UNKNOWN label='The '
+++++++++++++UNKNOWN label='<newline>'
+++++++++++++UNKNOWN label='textarea tag  defines a multi-line text input'
+++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN label='control.'
+++++++++++++UNKNOWN label='<newline>'
+++++++++++UNKNOWN label='<newline>'
+++++++++++++UNKNOWN label='<newline>'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/textarea-read-only-expected-fuchsia.txt b/content/test/data/accessibility/html/textarea-read-only-expected-fuchsia.txt
new file mode 100644
index 0000000..31d50ad
--- /dev/null
+++ b/content/test/data/accessibility/html/textarea-read-only-expected-fuchsia.txt
@@ -0,0 +1,12 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD focusable actions='{DEFAULT}' value='The textarea tag defines a multi-line text input control.<newline>'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='The textarea tag defines a multi-line text input control.<newline>'
+++++++++++++UNKNOWN label='The textarea tag defines a multi-line text input'
+++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN label='control.'
+++++++++++++UNKNOWN label='<newline>'
+++++++++++UNKNOWN label='<newline>'
+++++++++++++UNKNOWN label='<newline>'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/textarea-with-selection-expected-fuchsia.txt b/content/test/data/accessibility/html/textarea-with-selection-expected-fuchsia.txt
new file mode 100644
index 0000000..909cd30
--- /dev/null
+++ b/content/test/data/accessibility/html/textarea-with-selection-expected-fuchsia.txt
@@ -0,0 +1,12 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='The textarea tag defines a multi-line text input control.<newline>'
+++++++++UNKNOWN
+++++++++++STATIC_TEXT label='The textarea tag defines a multi-line text input control.<newline>'
+++++++++++++UNKNOWN label='The textarea tag defines a multi-line text input'
+++++++++++++UNKNOWN label=' '
+++++++++++++UNKNOWN label='control.'
+++++++++++++UNKNOWN label='<newline>'
+++++++++++UNKNOWN label='<newline>'
+++++++++++++UNKNOWN label='<newline>'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/time-expected-fuchsia.txt b/content/test/data/accessibility/html/time-expected-fuchsia.txt
new file mode 100644
index 0000000..616010a
--- /dev/null
+++ b/content/test/data/accessibility/html/time-expected-fuchsia.txt
@@ -0,0 +1,11 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN
+++++++++STATIC_TEXT label='10:00'
+++++++++++UNKNOWN label='10:00'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++UNKNOWN
+++++++++STATIC_TEXT label='Valentines day'
+++++++++++UNKNOWN label='Valentines day'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/title-expected-fuchsia.txt b/content/test/data/accessibility/html/title-expected-fuchsia.txt
new file mode 100644
index 0000000..8f75ff6
--- /dev/null
+++ b/content/test/data/accessibility/html/title-expected-fuchsia.txt
@@ -0,0 +1,3 @@
+UNKNOWN focusable label='Title of the document'
+++UNKNOWN hidden
+++++UNKNOWN hidden
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/transition-expected-fuchsia.txt b/content/test/data/accessibility/html/transition-expected-fuchsia.txt
new file mode 100644
index 0000000..1e8551e
--- /dev/null
+++ b/content/test/data/accessibility/html/transition-expected-fuchsia.txt
@@ -0,0 +1,8 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++BUTTON focusable label='GrowButton' actions='{DEFAULT}'
+++++++++STATIC_TEXT label='GrowButton'
+++++++++++UNKNOWN label='GrowButton'
+++++++STATIC_TEXT label='Done'
+++++++++UNKNOWN label='Done'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/ul-contenteditable-expected-fuchsia.txt b/content/test/data/accessibility/html/ul-contenteditable-expected-fuchsia.txt
new file mode 100644
index 0000000..ceb5d1b2
--- /dev/null
+++ b/content/test/data/accessibility/html/ul-contenteditable-expected-fuchsia.txt
@@ -0,0 +1,15 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++TEXT_FIELD focusable actions='{DEFAULT, SET_VALUE}' value='Hello<newline><newline>Bye'
+++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++UNKNOWN label='%E2%80%A2 ' actions='{DEFAULT}'
+++++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++++STATIC_TEXT label='Hello'
+++++++++++++++UNKNOWN label='Hello'
+++++++++++UNKNOWN actions='{DEFAULT}'
+++++++++++++UNKNOWN label='%E2%80%A2 ' actions='{DEFAULT}'
+++++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++++STATIC_TEXT label='Bye'
+++++++++++++++UNKNOWN label='Bye'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/ul-expected-fuchsia.txt b/content/test/data/accessibility/html/ul-expected-fuchsia.txt
new file mode 100644
index 0000000..27e70a78
--- /dev/null
+++ b/content/test/data/accessibility/html/ul-expected-fuchsia.txt
@@ -0,0 +1,19 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN hidden
+++++++UNKNOWN
+++++++++UNKNOWN
+++++++++++UNKNOWN label='%E2%80%A2 '
+++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++STATIC_TEXT label='Item 1'
+++++++++++++UNKNOWN label='Item 1'
+++++++++UNKNOWN
+++++++++++UNKNOWN label='%E2%80%A2 '
+++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++STATIC_TEXT label='Item 2'
+++++++++++++UNKNOWN label='Item 2'
+++++++++UNKNOWN
+++++++++++UNKNOWN label='%E2%80%A2 '
+++++++++++++STATIC_TEXT hidden label='%E2%80%A2 '
+++++++++++STATIC_TEXT label='Item 3'
+++++++++++++UNKNOWN label='Item 3'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/var-expected-fuchsia.txt b/content/test/data/accessibility/html/var-expected-fuchsia.txt
new file mode 100644
index 0000000..bf1834c
--- /dev/null
+++ b/content/test/data/accessibility/html/var-expected-fuchsia.txt
@@ -0,0 +1,5 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++STATIC_TEXT label='Variable'
+++++++++UNKNOWN label='Variable'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/video-text-only-expected-fuchsia.txt b/content/test/data/accessibility/html/video-text-only-expected-fuchsia.txt
new file mode 100644
index 0000000..e0fcd89
--- /dev/null
+++ b/content/test/data/accessibility/html/video-text-only-expected-fuchsia.txt
@@ -0,0 +1,141 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++UNKNOWN label='Unable to play media.'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++BUTTON hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++BUTTON hidden
+++++++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='0:00'
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='/ 0:00'
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++UNKNOWN hidden
+++++++++++++++++++SLIDER hidden
+++++++++++++++++++BUTTON hidden
+++++++++++++++++BUTTON hidden
+++++++++++++++++++UNKNOWN hidden
+++++++++++++++++BUTTON hidden
+++++++++++++++++++UNKNOWN hidden
+++++++++++++++SLIDER hidden
+++++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Play'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Fullscreen'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Download'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Mute'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Cast'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Captions'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Playback speed'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Picture in picture'
+++++++STATIC_TEXT label=' '
+++++++++UNKNOWN label=' '
+++++++UNKNOWN label='Unable to play media.'
+++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++BUTTON hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++BUTTON hidden
+++++++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='0:00'
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='/ 0:00'
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++UNKNOWN hidden
+++++++++++++++++++SLIDER hidden
+++++++++++++++++++BUTTON hidden
+++++++++++++++++BUTTON hidden
+++++++++++++++++++UNKNOWN hidden
+++++++++++++++++BUTTON hidden
+++++++++++++++++++UNKNOWN hidden
+++++++++++++++SLIDER hidden
+++++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++UNKNOWN hidden
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Play'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Fullscreen'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Download'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Mute'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Cast'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Captions'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Playback speed'
+++++++++++++UNKNOWN hidden
+++++++++++++++BUTTON hidden
+++++++++++++++UNKNOWN hidden
+++++++++++++++++UNKNOWN hidden
+++++++++++++++++++STATIC_TEXT hidden label='Picture in picture'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/wbr-expected-fuchsia.txt b/content/test/data/accessibility/html/wbr-expected-fuchsia.txt
new file mode 100644
index 0000000..19dba73
--- /dev/null
+++ b/content/test/data/accessibility/html/wbr-expected-fuchsia.txt
@@ -0,0 +1,11 @@
+UNKNOWN focusable
+++UNKNOWN hidden
+++++UNKNOWN
+++++++STATIC_TEXT label='Supercali'
+++++++++UNKNOWN label='Supercali'
+++++++UNKNOWN hidden
+++++++STATIC_TEXT label='fragilistic'
+++++++++UNKNOWN label='fragilistic'
+++++++UNKNOWN hidden
+++++++STATIC_TEXT label='expialidocious'
+++++++++UNKNOWN label='expialidocious'
\ No newline at end of file
diff --git a/content/test/data/gpu/pixel_webgpu_copy_externalImage_webgpu_canvas.html b/content/test/data/gpu/pixel_webgpu_copy_externalImage_webgpu_canvas.html
new file mode 100644
index 0000000..539adbb
--- /dev/null
+++ b/content/test/data/gpu/pixel_webgpu_copy_externalImage_webgpu_canvas.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>WebGPU copyExternalImageToTexture test</title>
+  <style type="text/css">
+  .nomargin {
+    margin: 0px auto;
+  }
+  </style>
+  <script type="text/javascript" src="pixel_webgpu_util.js"></script>
+  <script type="text/javascript">
+    var g_swapsBeforeAck = 15;
+
+    function getInitGPUCanvasData(width, height) {
+      const rectWidth = Math.floor(width / 2);
+      const rectHeight = Math.floor(height / 2);
+
+      const alphaValue = 255;
+      const colorValue = 255;
+
+      // BGRA8Unorm texture
+      const initialData = new Uint8ClampedArray(4 * width * height);
+      const maxRectHeightIndex = width * rectHeight;
+      for (let pixelIndex = 0; pixelIndex < initialData.length / 4; ++pixelIndex) {
+        const index = pixelIndex * 4;
+
+        // Top-half two rectangles
+        if (pixelIndex < maxRectHeightIndex) {
+          // top-left side rectangle
+          if (pixelIndex % width < rectWidth) {
+            // top-left side rectangle
+            initialData[index] = colorValue;
+            initialData[index + 1] = 0;
+            initialData[index + 2] = 0;
+            initialData[index + 3] = alphaValue;
+          } else {
+            // top-right side rectangle
+            initialData[index] = 0;
+            initialData[index + 1] = colorValue;
+            initialData[index + 2] = 0;
+            initialData[index + 3] = alphaValue;
+          }
+        } else { // Bottom-half two rectangles
+          // bottom-left side rectangle
+          if (pixelIndex % width < rectWidth) {
+            initialData[index] = 0;
+            initialData[index + 1] = 0;
+            initialData[index + 2] = colorValue;
+            initialData[index + 3] = alphaValue;
+          } else {
+            // bottom-right side rectangle
+            initialData[index] = 0;
+            initialData[index + 1] = 0;
+            initialData[index + 2] = 0;
+            initialData[index + 3] = alphaValue;
+          }
+        }
+      }
+      return initialData;
+    }
+
+    async function main() {
+
+      const gpuCanvas = document.getElementById('canvas_gpu');
+      const [gpuDevice, gpuContext] = await webGpuUtils.init(gpuCanvas);
+      if (!gpuDevice || !gpuContext) {
+        console.error("Failed to initialize WebGPU - skipping test");
+        domAutomationController.send("FAILURE");
+        return;
+      }
+
+      const gpuCanvasSource = document.getElementById('canvas_gpu_src');
+      const gpuContextSource = gpuCanvasSource.getContext('webgpu');
+      if (!gpuContext) {
+        console.error('getContext(webgpu) failed');
+        domAutomationController.send("FAILURE");
+        return;
+      }
+
+      gpuContextSource.configure({
+        device: gpuDevice,
+        format: "bgra8unorm",
+        usage: GPUTextureUsage.COPY_DST
+      });
+
+      const initialData = getInitGPUCanvasData(gpuCanvasSource.width, gpuCanvasSource.height);
+      let canvasTexture = gpuContextSource.getCurrentTexture();
+      gpuDevice.queue.writeTexture(
+        { texture: canvasTexture},
+        initialData,
+        {
+          bytesPerRow: gpuCanvasSource.width * 4,
+          rowsPerImage: gpuCanvasSource.height,
+        },
+        {
+          width: gpuCanvasSource.width, height: gpuCanvasSource.height, depthOrArrayLayers: 1,
+        }
+      );
+
+      const renderCallback = function() {
+        webGpuUtils.uploadToGPUTextureTest(gpuDevice, gpuContext, gpuCanvasSource,
+                                           {useImport: false, isWebGLCanvas: false});
+
+        waitForFinish();
+      };
+
+      window.requestAnimationFrame(renderCallback);
+    }
+
+    function waitForFinish() {
+      if (g_swapsBeforeAck == 0) {
+        domAutomationController.send("SUCCESS");
+      } else {
+        g_swapsBeforeAck--;
+        window.requestAnimationFrame(waitForFinish);
+      }
+    }
+  </script>
+</head>
+<body onload="main()">
+  <canvas id="canvas_gpu_src" width="200" height="200" class="nomargin"></canvas>
+  <canvas id="canvas_gpu" width="200" height="200" class="nomargin"></canvas>
+</body>
+</html>
diff --git a/content/test/data/gpu/webcodecs/copyTo.html b/content/test/data/gpu/webcodecs/copyTo.html
index d58dd4a5..2a19e40 100644
--- a/content/test/data/gpu/webcodecs/copyTo.html
+++ b/content/test/data/gpu/webcodecs/copyTo.html
@@ -84,7 +84,7 @@
     let source_type = arg.source_type;
     let source = await createFrameSource(source_type, 320, 240);
     if (!source) {
-      TEST.log('Skipping unsupported source: ' + source_type);
+      TEST.skip('Unsupported source: ' + source_type);
       return;
     }
 
diff --git a/content/test/data/gpu/webcodecs/draw-image.html b/content/test/data/gpu/webcodecs/draw-image.html
index ac272af..d1f4f4b 100644
--- a/content/test/data/gpu/webcodecs/draw-image.html
+++ b/content/test/data/gpu/webcodecs/draw-image.html
@@ -18,7 +18,7 @@
       let ctx = cnv.getContext('2d');
       let source = await createFrameSource(source_type, cnv.width, cnv.height);
       if (!source) {
-        TEST.log('Skipping unsupported source: ' + source_type);
+        TEST.skip('Unsupported source: ' + source_type);
         return;
       }
 
diff --git a/content/test/data/gpu/webcodecs/encode-color-space.html b/content/test/data/gpu/webcodecs/encode-color-space.html
index c5aa3fd..a2aa3f6e 100644
--- a/content/test/data/gpu/webcodecs/encode-color-space.html
+++ b/content/test/data/gpu/webcodecs/encode-color-space.html
@@ -85,7 +85,7 @@
 
       let support = await VideoEncoder.isConfigSupported(encoderConfig);
       if (!support.supported) {
-        TEST.log('Skipping unsupported codec: ' + arg.codec);
+        TEST.skip('Unsupported codec: ' + arg.codec);
         return;
       }
 
diff --git a/content/test/data/gpu/webcodecs/encode-decode.html b/content/test/data/gpu/webcodecs/encode-decode.html
index bb3f1861..5eb3354b 100644
--- a/content/test/data/gpu/webcodecs/encode-decode.html
+++ b/content/test/data/gpu/webcodecs/encode-decode.html
@@ -33,7 +33,7 @@
 
       let support = await VideoEncoder.isConfigSupported(encoder_config);
       if (!support.supported) {
-        TEST.log('Skipping unsupported codec: ' + arg.codec);
+        TEST.skip('Unsupported codec: ' + arg.codec);
         return;
       }
 
diff --git a/content/test/data/gpu/webcodecs/encode.html b/content/test/data/gpu/webcodecs/encode.html
index 98de7635..b4faf94 100644
--- a/content/test/data/gpu/webcodecs/encode.html
+++ b/content/test/data/gpu/webcodecs/encode.html
@@ -20,7 +20,7 @@
       const frames_in_one_pass = 15;
       let source = await createFrameSource(arg.source_type, width, height);
       if (!source) {
-        TEST.log('Skipping unsupported source: ' + arg.source_type);
+        TEST.skip('Unsupported source: ' + arg.source_type);
         return;
       }
 
@@ -39,7 +39,7 @@
 
       let support = await VideoEncoder.isConfigSupported(encoder_config);
       if (!support.supported) {
-        TEST.log('Skipping unsupported codec: ' + arg.codec);
+        TEST.skip('Unsupported codec: ' + arg.codec);
         return;
       }
 
diff --git a/content/test/data/gpu/webcodecs/encoding-modes.html b/content/test/data/gpu/webcodecs/encoding-modes.html
index c1ec666..f8deda84 100644
--- a/content/test/data/gpu/webcodecs/encoding-modes.html
+++ b/content/test/data/gpu/webcodecs/encoding-modes.html
@@ -15,7 +15,7 @@
       const frames_in_one_pass = 15;
       let source = await createFrameSource(arg.source_type, width, height);
       if (!source) {
-        TEST.log('Skipping unsupported source: ' + arg.source_type);
+        TEST.skip('Unsupported source: ' + arg.source_type);
         return;
       }
 
@@ -36,7 +36,7 @@
 
       let support = await VideoEncoder.isConfigSupported(encoder_config);
       if (!support.supported) {
-        TEST.log('Skipping unsupported codec: ' + arg.codec);
+        TEST.skip('Unsupported codec: ' + arg.codec);
         return;
       }
 
diff --git a/content/test/data/gpu/webcodecs/svc.html b/content/test/data/gpu/webcodecs/svc.html
index 69a5e91..65c398e0 100644
--- a/content/test/data/gpu/webcodecs/svc.html
+++ b/content/test/data/gpu/webcodecs/svc.html
@@ -32,7 +32,7 @@
 
       let support = await VideoEncoder.isConfigSupported(encoder_config);
       if (!support.supported) {
-        TEST.log('Skipping unsupported codec: ' + arg.codec);
+        TEST.skip('Unsupported codec: ' + arg.codec);
         return;
       }
 
diff --git a/content/test/data/gpu/webcodecs/tex-image-2d.html b/content/test/data/gpu/webcodecs/tex-image-2d.html
index bcc90dab..8315b4e 100644
--- a/content/test/data/gpu/webcodecs/tex-image-2d.html
+++ b/content/test/data/gpu/webcodecs/tex-image-2d.html
@@ -33,7 +33,7 @@
       let source =
         await createFrameSource(source_type, canvas.width, canvas.height);
       if (!source) {
-        TEST.log('Skipping unsupported source: ' + source_type);
+        TEST.skip('Unsupported source: ' + source_type);
         return;
       }
 
diff --git a/content/test/data/gpu/webcodecs/webcodecs_common.js b/content/test/data/gpu/webcodecs/webcodecs_common.js
index 7a2c5a73..bf38f00c 100644
--- a/content/test/data/gpu/webcodecs/webcodecs_common.js
+++ b/content/test/data/gpu/webcodecs/webcodecs_common.js
@@ -7,11 +7,18 @@
 class TestHarness {
   finished = false;
   success = false;
+  skipped = false;
   message = 'ok';
   logs = [];
 
   constructor() {}
 
+  skip(message) {
+    this.skipped = true;
+    this.finished = true;
+    this.message = message;
+  }
+
   reportSuccess() {
     this.finished = true;
     this.success = true;
diff --git a/content/test/gpu/gpu_tests/gpu_integration_test.py b/content/test/gpu/gpu_tests/gpu_integration_test.py
index bea0def..4abbb16 100644
--- a/content/test/gpu/gpu_tests/gpu_integration_test.py
+++ b/content/test/gpu/gpu_tests/gpu_integration_test.py
@@ -9,6 +9,7 @@
 import re
 import sys
 import time
+import unittest
 
 from telemetry.internal.results import artifact_compatibility_wrapper as acw
 from telemetry.testing import serially_executed_browser_test_case
@@ -343,6 +344,9 @@
       if os_name == 'android':
         self.browser.platform.android_action_runner.TurnScreenOn()
       self.RunActualGpuTest(url, *args)
+    except unittest.SkipTest:
+      self.programmaticSkipIsExpected = True  # pylint: disable=attribute-defined-outside-init
+      raise
     except Exception:
       if ResultType.Failure in expected_results or should_retry_on_failure:
         # We don't check the return value here since we'll be raising the
diff --git a/content/test/gpu/gpu_tests/pixel_test_pages.py b/content/test/gpu/gpu_tests/pixel_test_pages.py
index 0eef51b..ef5689f 100644
--- a/content/test/gpu/gpu_tests/pixel_test_pages.py
+++ b/content/test/gpu/gpu_tests/pixel_test_pages.py
@@ -400,6 +400,10 @@
                       base_name + '_WebGPUCopyExternalImageWebGLCanvas',
                       test_rect=[0, 0, 400, 200],
                       browser_args=webgpu_args),
+        PixelTestPage('pixel_webgpu_copy_externalImage_webgpu_canvas.html',
+                      base_name + '_WebGPUCopyExternalImageWebGPUCanvas',
+                      test_rect=[0, 0, 400, 200],
+                      browser_args=webgpu_args),
     ]
 
   # Pages that should be run with GPU rasterization enabled.
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index 7a113c2..a0d796fd 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -285,7 +285,6 @@
 crbug.com/852089 [ chromeos ] Pixel_WebGPU* [ Skip ]
 crbug.com/852089 [ fuchsia ] Pixel_WebGPU* [ Skip ]
 crbug.com/852089 [ win7 ] Pixel_WebGPU* [ Skip ]
-crbug.com/852089 [ win10 skia-renderer-gl ] Pixel_WebGPUCopyExternalImage* [ Skip ]
 
 ###############################
 # Temporary Skip Expectations #
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index a0cee5d..a2443284 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -221,7 +221,6 @@
 crbug.com/1131224 conformance2/rendering/framebuffer-mismatched-attachment-targets.html [ Failure ]
 crbug.com/1108086 [ no-passthrough ] conformance2/renderbuffers/framebuffer-object-attachment.html [ Failure ]
 crbug.com/angleproject/4807 [ win angle-d3d11 passthrough ] conformance2/glsl3/switch-case.html [ Failure ]
-crbug.com/1081973 conformance/buffers/buffer-data-and-buffer-sub-data.html [ Failure ]
 crbug.com/1082533 [ mac intel ] conformance/textures/misc/texture-copying-and-deletion.html [ Failure ]
 crbug.com/angleproject/5038 conformance/extensions/ext-color-buffer-half-float.html [ Failure ]
 crbug.com/1136231 [ win passthrough ] conformance/extensions/s3tc-and-rgtc.html [ Failure ]
@@ -406,28 +405,10 @@
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] conformance/ogles/GL/build/build_017_to_024.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] conformance2/rendering/fs-color-type-mismatch-color-buffer-type.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] conformance2/transform_feedback/too-small-buffers.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] conformance2/rendering/vertex-id.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/fboinvalidate/sub.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/fboinvalidate/whole.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/occlusionquery_conservative.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/occlusionquery_strict.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/basic_types_interleaved_lines.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/basic_types_interleaved_points.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/basic_types_interleaved_triangles.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/basic_types_separate_lines.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/basic_types_separate_points.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/basic_types_separate_triangles.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/interpolation_centroid.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/interpolation_flat.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/interpolation_smooth.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/point_size.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/position.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/random_interleaved_lines.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/random_interleaved_points.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/random_interleaved_triangles.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/random_separate_lines.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/random_separate_points.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/transformfeedback/random_separate_triangles.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/fbomultisample.2_samples.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/fbomultisample.4_samples.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/fbomultisample.8_samples.html [ Failure ]
@@ -455,7 +436,6 @@
 crbug.com/angleproject/6430 [ mac passthrough angle-metal amd ] deqp/functional/gles3/textureshadow/2d_nearest_mipmap_nearest_less.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal amd ] deqp/functional/gles3/texturespecification/teximage3d_depth.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal amd ] deqp/functional/gles3/texturespecification/texstorage2d_format_depth_stencil.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal amd ] deqp/functional/gles3/uniformbuffers/random.html [ Failure ]
 
 # Metal AMD ASAN
 crbug.com/1270755 [ mac amd-0x6821 angle-metal passthrough asan ] deqp/functional/gles3/fborender/shared_colorbuffer_01.html [ RetryOnFailure ]
@@ -1267,6 +1247,7 @@
 crbug.com/1285111 [ chromeos chromeos-board-kevin passthrough ] conformance/textures/misc/copy-tex-image-and-sub-image-2d.html [ Failure ]
 crbug.com/1285112 [ chromeos chromeos-board-kevin passthrough ] conformance2/rendering/blitframebuffer-multisampled-readbuffer.html [ Failure ]
 crbug.com/1285114 [ chromeos chromeos-board-kevin passthrough ] conformance2/transform_feedback/transform_feedback.html [ Failure ]
+crbug.com/1286099 [ chromeos chromeos-board-kevin passthrough ] conformance2/rendering/blitframebuffer-r11f-g11f-b10f.html [ Failure ]
 
 ##############################
 # Lacros-like Linux Failures #
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 79ee4c2..739c42d7 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
@@ -293,7 +293,6 @@
 # ========================
 # Fails on all platforms
 
-crbug.com/1081973 conformance/buffers/buffer-data-and-buffer-sub-data.html [ Failure ]
 crbug.com/1082533 [ mac intel ] conformance/textures/misc/texture-copying-and-deletion.html [ Failure ]
 # TODO(crbug.com/1136231): Uncomment suppressions for s3tc-and-rgtc.html below
 # under crbug.com/963205 and crbug.com/964321 once these two failures are fixed.
diff --git a/content/test/gpu/gpu_tests/webcodecs_integration_test.py b/content/test/gpu/gpu_tests/webcodecs_integration_test.py
index 2b4e9ed..b1de54a5 100644
--- a/content/test/gpu/gpu_tests/webcodecs_integration_test.py
+++ b/content/test/gpu/gpu_tests/webcodecs_integration_test.py
@@ -105,6 +105,8 @@
         'document.readyState == "complete"')
     tab.EvaluateJavaScript('TEST.run(' + json.dumps(arg_obj) + ')')
     tab.action_runner.WaitForJavaScriptCondition('TEST.finished', timeout=60)
+    if tab.EvaluateJavaScript('TEST.skipped'):
+      self.skipTest('Skipping test:' + tab.EvaluateJavaScript('TEST.summary()'))
     if not tab.EvaluateJavaScript('TEST.success'):
       self.fail('Test failure:' + tab.EvaluateJavaScript('TEST.summary()'))
 
diff --git a/content/test/test_background_sync_manager.cc b/content/test/test_background_sync_manager.cc
index 36a2af5..c5d6bc6c 100644
--- a/content/test/test_background_sync_manager.cc
+++ b/content/test/test_background_sync_manager.cc
@@ -95,7 +95,7 @@
 }
 
 void TestBackgroundSyncManager::HasMainFrameWindowClient(
-    const url::Origin& origin,
+    const blink::StorageKey& key,
     BoolCallback callback) {
   std::move(callback).Run(has_main_frame_window_client_);
 }
diff --git a/content/test/test_background_sync_manager.h b/content/test/test_background_sync_manager.h
index 3af8849..56262797 100644
--- a/content/test/test_background_sync_manager.h
+++ b/content/test/test_background_sync_manager.h
@@ -140,7 +140,7 @@
 
   // Override to avoid actual check for main frame, instead return the value set
   // by tests.
-  void HasMainFrameWindowClient(const url::Origin& origin,
+  void HasMainFrameWindowClient(const blink::StorageKey& key,
                                 BoolCallback callback) override;
 
  private:
diff --git a/dbus/values_util.cc b/dbus/values_util.cc
index 2cd7b75..b32d831 100644
--- a/dbus/values_util.cc
+++ b/dbus/values_util.cc
@@ -24,10 +24,11 @@
 }
 
 // Pops values from |reader| and appends them to |list_value|.
-bool PopListElements(MessageReader* reader, base::ListValue* list_value) {
+bool PopListElements(MessageReader* reader, base::Value* list_value) {
+  DCHECK(list_value->is_list());
   while (reader->HasMoreData()) {
-    std::unique_ptr<base::Value> element_value = PopDataAsValue(reader);
-    if (!element_value)
+    base::Value element_value = PopDataAsValue(reader);
+    if (element_value.is_none())
       return false;
     list_value->Append(std::move(element_value));
   }
@@ -36,7 +37,8 @@
 
 // Pops dict-entries from |reader| and sets them to |dictionary_value|
 bool PopDictionaryEntries(MessageReader* reader,
-                          base::DictionaryValue* dictionary_value) {
+                          base::Value* dictionary_value) {
+  DCHECK(dictionary_value->is_dict());
   while (reader->HasMoreData()) {
     DCHECK_EQ(Message::DICT_ENTRY, reader->GetDataType());
     MessageReader entry_reader(nullptr);
@@ -50,18 +52,17 @@
         return false;
     } else {
       // If the type of keys is not STRING, convert it to string.
-      std::unique_ptr<base::Value> key(PopDataAsValue(&entry_reader));
-      if (!key)
+      base::Value key = PopDataAsValue(&entry_reader);
+      if (key.is_none())
         return false;
       // Use JSONWriter to convert an arbitrary value to a string.
-      base::JSONWriter::Write(*key, &key_string);
+      base::JSONWriter::Write(key, &key_string);
     }
     // Get the value and set the key-value pair.
-    std::unique_ptr<base::Value> value = PopDataAsValue(&entry_reader);
-    if (!value)
+    base::Value value = PopDataAsValue(&entry_reader);
+    if (value.is_none())
       return false;
-    dictionary_value->SetKey(key_string,
-                             base::Value::FromUniquePtrValue(std::move(value)));
+    dictionary_value->SetKey(key_string, std::move(value));
   }
   return true;
 }
@@ -91,8 +92,8 @@
 
 }  // namespace
 
-std::unique_ptr<base::Value> PopDataAsValue(MessageReader* reader) {
-  std::unique_ptr<base::Value> result;
+base::Value PopDataAsValue(MessageReader* reader) {
+  base::Value result;
   switch (reader->GetDataType()) {
     case Message::INVALID_DATA:
       // Do nothing.
@@ -100,37 +101,37 @@
     case Message::BYTE: {
       uint8_t value = 0;
       if (reader->PopByte(&value))
-        result = std::make_unique<base::Value>(value);
+        result = base::Value(value);
       break;
     }
     case Message::BOOL: {
       bool value = false;
       if (reader->PopBool(&value))
-        result = std::make_unique<base::Value>(value);
+        result = base::Value(value);
       break;
     }
     case Message::INT16: {
       int16_t value = 0;
       if (reader->PopInt16(&value))
-        result = std::make_unique<base::Value>(value);
+        result = base::Value(value);
       break;
     }
     case Message::UINT16: {
       uint16_t value = 0;
       if (reader->PopUint16(&value))
-        result = std::make_unique<base::Value>(value);
+        result = base::Value(value);
       break;
     }
     case Message::INT32: {
       int32_t value = 0;
       if (reader->PopInt32(&value))
-        result = std::make_unique<base::Value>(value);
+        result = base::Value(value);
       break;
     }
     case Message::UINT32: {
       uint32_t value = 0;
       if (reader->PopUint32(&value)) {
-        result = std::make_unique<base::Value>(static_cast<double>(value));
+        result = base::Value(static_cast<double>(value));
       }
       break;
     }
@@ -139,7 +140,7 @@
       if (reader->PopInt64(&value)) {
         DLOG_IF(WARNING, !IsExactlyRepresentableByDouble(value))
             << value << " is not exactly representable by double";
-        result = std::make_unique<base::Value>(static_cast<double>(value));
+        result = base::Value(static_cast<double>(value));
       }
       break;
     }
@@ -148,26 +149,26 @@
       if (reader->PopUint64(&value)) {
         DLOG_IF(WARNING, !IsExactlyRepresentableByDouble(value))
             << value << " is not exactly representable by double";
-        result = std::make_unique<base::Value>(static_cast<double>(value));
+        result = base::Value(static_cast<double>(value));
       }
       break;
     }
     case Message::DOUBLE: {
       double value = 0;
       if (reader->PopDouble(&value))
-        result = std::make_unique<base::Value>(value);
+        result = base::Value(value);
       break;
     }
     case Message::STRING: {
       std::string value;
       if (reader->PopString(&value))
-        result = std::make_unique<base::Value>(value);
+        result = base::Value(value);
       break;
     }
     case Message::OBJECT_PATH: {
       ObjectPath value;
       if (reader->PopObjectPath(&value))
-        result = std::make_unique<base::Value>(value.value());
+        result = base::Value(value.value());
       break;
     }
     case Message::UNIX_FD: {
@@ -179,15 +180,15 @@
       MessageReader sub_reader(nullptr);
       if (reader->PopArray(&sub_reader)) {
         // If the type of the array's element is DICT_ENTRY, create a
-        // DictionaryValue, otherwise create a ListValue.
+        // Value with type base::Value::Type::DICTIONARY, otherwise create a
+        // Value with type base::Value::Type::LIST.
         if (sub_reader.GetDataType() == Message::DICT_ENTRY) {
-          std::unique_ptr<base::DictionaryValue> dictionary_value(
-              new base::DictionaryValue);
-          if (PopDictionaryEntries(&sub_reader, dictionary_value.get()))
+          auto dictionary_value = base::Value(base::Value::Type::DICTIONARY);
+          if (PopDictionaryEntries(&sub_reader, &dictionary_value))
             result = std::move(dictionary_value);
         } else {
-          std::unique_ptr<base::ListValue> list_value(new base::ListValue);
-          if (PopListElements(&sub_reader, list_value.get()))
+          auto list_value = base::Value(base::Value::Type::LIST);
+          if (PopListElements(&sub_reader, &list_value))
             result = std::move(list_value);
         }
       }
@@ -196,8 +197,8 @@
     case Message::STRUCT: {
       MessageReader sub_reader(nullptr);
       if (reader->PopStruct(&sub_reader)) {
-        std::unique_ptr<base::ListValue> list_value(new base::ListValue);
-        if (PopListElements(&sub_reader, list_value.get()))
+        auto list_value = base::Value(base::Value::Type::LIST);
+        if (PopListElements(&sub_reader, &list_value))
           result = std::move(list_value);
       }
       break;
@@ -251,16 +252,13 @@
 void AppendValueData(MessageWriter* writer, const base::Value& value) {
   switch (value.type()) {
     case base::Value::Type::DICTIONARY: {
-      const base::DictionaryValue* dictionary = nullptr;
-      value.GetAsDictionary(&dictionary);
       dbus::MessageWriter array_writer(nullptr);
       writer->OpenArray("{sv}", &array_writer);
-      for (base::DictionaryValue::Iterator iter(*dictionary); !iter.IsAtEnd();
-           iter.Advance()) {
+      for (auto item : value.DictItems()) {
         dbus::MessageWriter dict_entry_writer(nullptr);
         array_writer.OpenDictEntry(&dict_entry_writer);
-        dict_entry_writer.AppendString(iter.key());
-        AppendValueDataAsVariant(&dict_entry_writer, iter.value());
+        dict_entry_writer.AppendString(item.first);
+        AppendValueDataAsVariant(&dict_entry_writer, item.second);
         array_writer.CloseContainer(&dict_entry_writer);
       }
       writer->CloseContainer(&array_writer);
diff --git a/dbus/values_util.h b/dbus/values_util.h
index 81b839b..09432159 100644
--- a/dbus/values_util.h
+++ b/dbus/values_util.h
@@ -7,8 +7,6 @@
 
 #include <stdint.h>
 
-#include <memory>
-
 #include "dbus/dbus_export.h"
 
 namespace base {
@@ -21,11 +19,10 @@
 class MessageWriter;
 
 // Pops a value from |reader| as a base::Value.
-// Returns NULL if an error occurs.
+// Returns base::Value() if an error occurs.
 // Note: Integer values larger than int32_t (including uint32_t) are converted
 // to double.  Non-string dictionary keys are converted to strings.
-CHROME_DBUS_EXPORT std::unique_ptr<base::Value> PopDataAsValue(
-    MessageReader* reader);
+CHROME_DBUS_EXPORT base::Value PopDataAsValue(MessageReader* reader);
 
 // Appends a basic type value to |writer|. Basic types are BOOLEAN, INTEGER,
 // DOUBLE, and STRING. Use this function for values that are known to be basic
diff --git a/dbus/values_util_unittest.cc b/dbus/values_util_unittest.cc
index ab2f1ee6..0b16a7d 100644
--- a/dbus/values_util_unittest.cc
+++ b/dbus/values_util_unittest.cc
@@ -50,71 +50,55 @@
   writer.AppendObjectPath(kObjectPathValue);
 
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value;
-  std::unique_ptr<base::Value> expected_value;
+  base::Value value;
   // Pop a byte.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value = std::make_unique<base::Value>(kByteValue);
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(kByteValue));
   // Pop a bool.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value = std::make_unique<base::Value>(kBoolValue);
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(kBoolValue));
   // Pop an int16_t.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value = std::make_unique<base::Value>(kInt16Value);
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(kInt16Value));
   // Pop a uint16_t.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value = std::make_unique<base::Value>(kUint16Value);
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(kUint16Value));
   // Pop an int32_t.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value = std::make_unique<base::Value>(kInt32Value);
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(kInt32Value));
   // Pop a uint32_t.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value =
-      std::make_unique<base::Value>(static_cast<double>(kUint32Value));
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(static_cast<double>(kUint32Value)));
   // Pop an int64_t.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value =
-      std::make_unique<base::Value>(static_cast<double>(kInt64Value));
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(static_cast<double>(kInt64Value)));
   // Pop a uint64_t.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value =
-      std::make_unique<base::Value>(static_cast<double>(kUint64Value));
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(static_cast<double>(kUint64Value)));
   // Pop a double.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value = std::make_unique<base::Value>(kDoubleValue);
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(kDoubleValue));
   // Pop a string.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value = std::make_unique<base::Value>(kStringValue);
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(kStringValue));
   // Pop an empty string.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value = std::make_unique<base::Value>(kEmptyStringValue);
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(kEmptyStringValue));
   // Pop an object path.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value = std::make_unique<base::Value>(kObjectPathValue.value());
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(kObjectPathValue.value()));
 }
 
 TEST(ValuesUtilTest, PopVariant) {
@@ -131,28 +115,23 @@
   writer.AppendVariantOfString(kStringValue);
 
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value;
-  std::unique_ptr<base::Value> expected_value;
+  base::Value value;
   // Pop a bool.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value = std::make_unique<base::Value>(kBoolValue);
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(kBoolValue));
   // Pop an int32_t.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value = std::make_unique<base::Value>(kInt32Value);
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(kInt32Value));
   // Pop a double.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value = std::make_unique<base::Value>(kDoubleValue);
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(kDoubleValue));
   // Pop a string.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value = std::make_unique<base::Value>(kStringValue);
-  EXPECT_TRUE(value->Equals(expected_value.get()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(kStringValue));
 }
 
 // Pop extremely large integers which cannot be precisely represented in
@@ -167,24 +146,19 @@
   writer.AppendUint64(kUint64Value);
 
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value;
-  std::unique_ptr<base::Value> expected_value;
+  base::Value value;
   // Pop an int64_t.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value =
-      std::make_unique<base::Value>(static_cast<double>(kInt64Value));
-  EXPECT_TRUE(value->Equals(expected_value.get()));
-  ASSERT_TRUE(value->is_double());
-  EXPECT_NE(kInt64Value, static_cast<int64_t>(value->GetDouble()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(static_cast<double>(kInt64Value)));
+  ASSERT_TRUE(value.is_double());
+  EXPECT_NE(kInt64Value, static_cast<int64_t>(value.GetDouble()));
   // Pop a uint64_t.
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  expected_value =
-      std::make_unique<base::Value>(static_cast<double>(kUint64Value));
-  EXPECT_TRUE(value->Equals(expected_value.get()));
-  ASSERT_TRUE(value->is_double());
-  EXPECT_NE(kUint64Value, static_cast<uint64_t>(value->GetDouble()));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, base::Value(static_cast<double>(kUint64Value)));
+  ASSERT_TRUE(value.is_double());
+  EXPECT_NE(kUint64Value, static_cast<uint64_t>(value.GetDouble()));
 }
 
 TEST(ValuesUtilTest, PopIntArray) {
@@ -208,9 +182,9 @@
 
   // Pop an int32_t array.
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value(PopDataAsValue(&reader));
-  ASSERT_NE(nullptr, value);
-  EXPECT_EQ(*value, list_value);
+  base::Value value(PopDataAsValue(&reader));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, list_value);
 }
 
 TEST(ValuesUtilTest, PopStringArray) {
@@ -231,9 +205,9 @@
 
   // Pop a string array.
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value(PopDataAsValue(&reader));
-  ASSERT_NE(nullptr, value);
-  EXPECT_EQ(*value, list_value);
+  base::Value value(PopDataAsValue(&reader));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, list_value);
 }
 
 TEST(ValuesUtilTest, PopStruct) {
@@ -261,9 +235,9 @@
 
   // Pop a struct.
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value(PopDataAsValue(&reader));
-  ASSERT_NE(nullptr, value);
-  EXPECT_EQ(*value, list_value);
+  base::Value value(PopDataAsValue(&reader));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, list_value);
 }
 
 TEST(ValuesUtilTest, PopStringToVariantDictionary) {
@@ -300,17 +274,17 @@
   writer.CloseContainer(&sub_writer);
 
   // Create the expected value.
-  base::DictionaryValue dictionary_value;
-  dictionary_value.SetBoolean(kKey1, kBoolValue);
-  dictionary_value.SetInteger(kKey2, kInt32Value);
-  dictionary_value.SetDouble(kKey3, kDoubleValue);
-  dictionary_value.SetString(kKey4, kStringValue);
+  base::Value dictionary_value(base::Value::Type::DICTIONARY);
+  dictionary_value.SetBoolKey(kKey1, kBoolValue);
+  dictionary_value.SetIntKey(kKey2, kInt32Value);
+  dictionary_value.SetDoubleKey(kKey3, kDoubleValue);
+  dictionary_value.SetStringKey(kKey4, kStringValue);
 
   // Pop a dictinoary.
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value(PopDataAsValue(&reader));
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&dictionary_value));
+  base::Value value(PopDataAsValue(&reader));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, dictionary_value);
 }
 
 TEST(ValuesUtilTest, PopDictionaryWithDottedStringKey) {
@@ -341,16 +315,16 @@
   writer.CloseContainer(&sub_writer);
 
   // Create the expected value.
-  base::DictionaryValue dictionary_value;
+  base::Value dictionary_value(base::Value::Type::DICTIONARY);
   dictionary_value.SetKey(kKey1, base::Value(kBoolValue));
   dictionary_value.SetKey(kKey2, base::Value(kInt32Value));
   dictionary_value.SetKey(kKey3, base::Value(kDoubleValue));
 
   // Pop a dictinoary.
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value(PopDataAsValue(&reader));
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&dictionary_value));
+  base::Value value(PopDataAsValue(&reader));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, dictionary_value);
 }
 
 TEST(ValuesUtilTest, PopDoubleToIntDictionary) {
@@ -376,7 +350,7 @@
   writer.CloseContainer(&sub_writer);
 
   // Create the expected value.
-  base::DictionaryValue dictionary_value;
+  base::Value dictionary_value(base::Value::Type::DICTIONARY);
   for (size_t i = 0; i != values.size(); ++i) {
     std::string key_string;
     base::JSONWriter::Write(base::Value(keys[i]), &key_string);
@@ -385,9 +359,9 @@
 
   // Pop a dictionary.
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value(PopDataAsValue(&reader));
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&dictionary_value));
+  base::Value value(PopDataAsValue(&reader));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, dictionary_value);
 }
 
 TEST(ValuesUtilTest, AppendBasicTypes) {
@@ -404,19 +378,19 @@
   AppendBasicTypeValueData(&writer, kStringValue);
 
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value;
+  base::Value value;
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kBoolValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kBoolValue);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kIntegerValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kIntegerValue);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kDoubleValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kDoubleValue);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kStringValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kStringValue);
 }
 
 TEST(ValuesUtilTest, AppendBasicTypesAsVariant) {
@@ -433,19 +407,19 @@
   AppendBasicTypeValueDataAsVariant(&writer, kStringValue);
 
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value;
+  base::Value value;
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kBoolValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kBoolValue);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kIntegerValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kIntegerValue);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kDoubleValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kDoubleValue);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kStringValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kStringValue);
 }
 
 TEST(ValuesUtilTest, AppendValueDataBasicTypes) {
@@ -462,19 +436,19 @@
   AppendValueData(&writer, kStringValue);
 
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value;
+  base::Value value;
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kBoolValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kBoolValue);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kIntegerValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kIntegerValue);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kDoubleValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kDoubleValue);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kStringValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kStringValue);
 }
 
 TEST(ValuesUtilTest, AppendValueDataAsVariantBasicTypes) {
@@ -491,19 +465,19 @@
   AppendValueDataAsVariant(&writer, kStringValue);
 
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value;
+  base::Value value;
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kBoolValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kBoolValue);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kIntegerValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kIntegerValue);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kDoubleValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kDoubleValue);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&kStringValue));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, kStringValue);
 }
 
 TEST(ValuesUtilTest, AppendDictionary) {
@@ -524,18 +498,17 @@
   list_value.Append(kBoolValue);
   list_value.Append(kInt32Value);
 
-  auto dictionary_value = std::make_unique<base::DictionaryValue>();
-  dictionary_value->SetBoolean(kKey1, kBoolValue);
-  dictionary_value->SetInteger(kKey2, kDoubleValue);
+  base::Value dictionary_value(base::Value::Type::DICTIONARY);
+  dictionary_value.SetBoolKey(kKey1, kBoolValue);
+  dictionary_value.SetIntKey(kKey2, kDoubleValue);
 
-  base::DictionaryValue test_dictionary;
-  test_dictionary.SetBoolean(kKey1, kBoolValue);
-  test_dictionary.SetInteger(kKey2, kInt32Value);
-  test_dictionary.SetDouble(kKey3, kDoubleValue);
-  test_dictionary.SetString(kKey4, kStringValue);
-  test_dictionary.Set(kKey5,
-                      base::Value::ToUniquePtrValue(std::move(list_value)));
-  test_dictionary.Set(kKey6, std::move(dictionary_value));
+  base::Value test_dictionary(base::Value::Type::DICTIONARY);
+  test_dictionary.SetBoolKey(kKey1, kBoolValue);
+  test_dictionary.SetIntKey(kKey2, kInt32Value);
+  test_dictionary.SetDoubleKey(kKey3, kDoubleValue);
+  test_dictionary.SetStringKey(kKey4, kStringValue);
+  test_dictionary.SetKey(kKey5, std::move(list_value));
+  test_dictionary.SetKey(kKey6, std::move(dictionary_value));
 
   std::unique_ptr<Response> response(Response::CreateEmpty());
   MessageWriter writer(response.get());
@@ -545,13 +518,13 @@
 
   // Read the data.
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value;
+  base::Value value;
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&test_dictionary));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, test_dictionary);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&int_value));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, int_value);
 }
 
 TEST(ValuesUtilTest, AppendDictionaryAsVariant) {
@@ -572,18 +545,17 @@
   list_value.Append(kBoolValue);
   list_value.Append(kInt32Value);
 
-  auto dictionary_value = std::make_unique<base::DictionaryValue>();
-  dictionary_value->SetBoolean(kKey1, kBoolValue);
-  dictionary_value->SetInteger(kKey2, kDoubleValue);
+  base::Value dictionary_value(base::Value::Type::DICTIONARY);
+  dictionary_value.SetBoolKey(kKey1, kBoolValue);
+  dictionary_value.SetIntKey(kKey2, kDoubleValue);
 
-  base::DictionaryValue test_dictionary;
-  test_dictionary.SetBoolean(kKey1, kBoolValue);
-  test_dictionary.SetInteger(kKey2, kInt32Value);
-  test_dictionary.SetDouble(kKey3, kDoubleValue);
-  test_dictionary.SetString(kKey4, kStringValue);
-  test_dictionary.Set(kKey5,
-                      base::Value::ToUniquePtrValue(std::move(list_value)));
-  test_dictionary.Set(kKey6, std::move(dictionary_value));
+  base::Value test_dictionary(base::Value::Type::DICTIONARY);
+  test_dictionary.SetBoolKey(kKey1, kBoolValue);
+  test_dictionary.SetIntKey(kKey2, kInt32Value);
+  test_dictionary.SetDoubleKey(kKey3, kDoubleValue);
+  test_dictionary.SetStringKey(kKey4, kStringValue);
+  test_dictionary.SetKey(kKey5, std::move(list_value));
+  test_dictionary.SetKey(kKey6, std::move(dictionary_value));
 
   std::unique_ptr<Response> response(Response::CreateEmpty());
   MessageWriter writer(response.get());
@@ -593,13 +565,13 @@
 
   // Read the data.
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value;
+  base::Value value;
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&test_dictionary));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, test_dictionary);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&int_value));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, int_value);
 }
 
 TEST(ValuesUtilTest, AppendList) {
@@ -636,13 +608,13 @@
 
   // Read the data.
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value;
+  base::Value value;
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&test_list));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, test_list);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&int_value));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, int_value);
 }
 
 TEST(ValuesUtilTest, AppendListAsVariant) {
@@ -679,13 +651,13 @@
 
   // Read the data.
   MessageReader reader(response.get());
-  std::unique_ptr<base::Value> value;
+  base::Value value;
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&test_list));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, test_list);
   value = PopDataAsValue(&reader);
-  ASSERT_NE(nullptr, value);
-  EXPECT_TRUE(value->Equals(&int_value));
+  ASSERT_FALSE(value.is_none());
+  EXPECT_EQ(value, int_value);
 }
 
 }  // namespace dbus
diff --git a/device/bluetooth/bluetooth_strings.grd b/device/bluetooth/bluetooth_strings.grd
index 8b19b37e..36898ff7 100644
--- a/device/bluetooth/bluetooth_strings.grd
+++ b/device/bluetooth/bluetooth_strings.grd
@@ -41,7 +41,9 @@
       <output filename="bluetooth_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="bluetooth_strings_af.pak" type="data_package" lang="af" />
       <output filename="bluetooth_strings_is.pak" type="data_package" lang="is" />
+      <output filename="bluetooth_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="bluetooth_strings_am.pak" type="data_package" lang="am" />
     <output filename="bluetooth_strings_ar.pak" type="data_package" lang="ar" />
diff --git a/device/fido/fido_strings.grd b/device/fido/fido_strings.grd
index ea20486..0cbe3ca0 100644
--- a/device/fido/fido_strings.grd
+++ b/device/fido/fido_strings.grd
@@ -41,7 +41,9 @@
       <output filename="fido_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="fido_strings_af.pak" type="data_package" lang="af" />
       <output filename="fido_strings_is.pak" type="data_package" lang="is" />
+      <output filename="fido_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="fido_strings_am.pak" type="data_package" lang="am" />
     <output filename="fido_strings_ar.pak" type="data_package" lang="ar" />
diff --git a/docs/testing/web_platform_tests.md b/docs/testing/web_platform_tests.md
index a446c5ce..760ef86e 100644
--- a/docs/testing/web_platform_tests.md
+++ b/docs/testing/web_platform_tests.md
@@ -223,7 +223,7 @@
 
 When a test under `external/wpt/css/css-grid/` newly fails in a WPT import, the
 importer will automatically file a bug against the Blink>Layout>Grid component
-in [crbug.com][https://crbug.com], with details of which test failed and the
+in [crbug.com](https://crbug.com), with details of which test failed and the
 output.
 
 Note that we are considering making the notifications opt-out instead of
diff --git a/extensions/browser/api/app_window/app_window_apitest.cc b/extensions/browser/api/app_window/app_window_apitest.cc
index 6dd6d36..91b916b 100644
--- a/extensions/browser/api/app_window/app_window_apitest.cc
+++ b/extensions/browser/api/app_window/app_window_apitest.cc
@@ -23,7 +23,7 @@
 #include "ui/base/base_window.h"
 #include "ui/gfx/geometry/rect.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "ui/base/win/shell.h"
 #endif
 
@@ -56,7 +56,7 @@
 // TODO(crbug.com/794771): These fail on Linux with HEADLESS env var set.
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
 #define MAYBE_OnMinimizedEvent DISABLED_OnMinimizedEvent
 #define MAYBE_OnMaximizedEvent DISABLED_OnMaximizedEvent
 #define MAYBE_OnRestoredEvent DISABLED_OnRestoredEvent
@@ -64,7 +64,7 @@
 #define MAYBE_OnMinimizedEvent OnMinimizedEvent
 #define MAYBE_OnMaximizedEvent OnMaximizedEvent
 #define MAYBE_OnRestoredEvent OnRestoredEvent
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
 
 IN_PROC_BROWSER_TEST_F(AppWindowApiTest, MAYBE_OnMinimizedEvent) {
   EXPECT_TRUE(RunExtensionTest("platform_apps/windows_api_properties",
@@ -145,10 +145,10 @@
 
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#if defined(USE_AURA) && !(defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
+#if defined(USE_AURA) && !(BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
   test_dir = kHasAlphaDir;
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   if (!ui::win::IsAeroGlassEnabled()) {
     test_dir = kNoAlphaDir;
   }
diff --git a/extensions/browser/api/audio/audio_device_id_calculator.cc b/extensions/browser/api/audio/audio_device_id_calculator.cc
index 052f42e..a831c27 100644
--- a/extensions/browser/api/audio/audio_device_id_calculator.cc
+++ b/extensions/browser/api/audio/audio_device_id_calculator.cc
@@ -54,7 +54,7 @@
   DCHECK(stable_id_map_loaded_);
   DCHECK_EQ(0u, stable_id_map_.count(audio_service_stable_id));
 
-  ListPrefUpdate update(
+  ListPrefUpdateDeprecated update(
       ExtensionsBrowserClient::Get()->GetPrefServiceForContext(context_),
       kAudioApiStableDeviceIds);
 
diff --git a/extensions/browser/api/bluetooth/bluetooth_api_utils.cc b/extensions/browser/api/bluetooth/bluetooth_api_utils.cc
index 4aefbc61..385fa402 100644
--- a/extensions/browser/api/bluetooth/bluetooth_api_utils.cc
+++ b/extensions/browser/api/bluetooth/bluetooth_api_utils.cc
@@ -20,7 +20,7 @@
 using bluetooth::VendorIdSource;
 using device::BluetoothDevice;
 using device::BluetoothDeviceType;
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 using device::BluetoothTransport;
 #endif
 
@@ -94,7 +94,7 @@
   }
 }
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 bool ConvertTransportToApi(const BluetoothTransport& input,
                            bluetooth::Transport* output) {
   switch (input) {
@@ -176,7 +176,7 @@
     out->battery_percentage.reset();
 #endif
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   ConvertTransportToApi(device.GetType(), &(out->transport));
 #endif
 }
diff --git a/extensions/browser/api/bluetooth/bluetooth_appshell_test.cc b/extensions/browser/api/bluetooth/bluetooth_appshell_test.cc
index c2f2bfb7..3f702fdc 100644
--- a/extensions/browser/api/bluetooth/bluetooth_appshell_test.cc
+++ b/extensions/browser/api/bluetooth/bluetooth_appshell_test.cc
@@ -8,7 +8,7 @@
 using BluetoothShellApiTest = extensions::ShellApiTest;
 
 // TODO(crbug.com/1165955): this test flakes on Mac ASAN
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 #define MAYBE_ApiSanityCheck DISABLED_ApiSanityCheck
 #else
 #define MAYBE_ApiSanityCheck ApiSanityCheck
diff --git a/extensions/browser/api/bluetooth/bluetooth_private_apitest.cc b/extensions/browser/api/bluetooth/bluetooth_private_apitest.cc
index 8e62e445..529cd96 100644
--- a/extensions/browser/api/bluetooth/bluetooth_private_apitest.cc
+++ b/extensions/browser/api/bluetooth/bluetooth_private_apitest.cc
@@ -247,7 +247,7 @@
 }
 
 // Device::Forget not implemented on OSX.
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
 IN_PROC_BROWSER_TEST_F(BluetoothPrivateApiTest, ForgetDevice) {
   EXPECT_CALL(*mock_device_, Forget(_, _))
       .WillOnce(
diff --git a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.cc b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.cc
index 169712a..7d48f41 100644
--- a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.cc
+++ b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.cc
@@ -82,7 +82,7 @@
     "An advertisement is already advertising";
 const char kStatusAdvertisementDoesNotExist[] =
     "This advertisement does not exist";
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 const char kStatusInvalidAdvertisingInterval[] =
     "Invalid advertising interval specified.";
 #endif
@@ -1228,7 +1228,7 @@
 }
 
 void BluetoothLowEnergyResetAdvertisingFunction::DoWork() {
-#if defined(OS_CHROMEOS) || defined(OS_LINUX)
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
 
@@ -1282,7 +1282,7 @@
 }
 
 void BluetoothLowEnergySetAdvertisingIntervalFunction::DoWork() {
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   BluetoothLowEnergyEventRouter* event_router =
       GetEventRouter(browser_context());
   event_router->adapter()->SetAdvertisingInterval(
@@ -1303,7 +1303,7 @@
 
 void BluetoothLowEnergySetAdvertisingIntervalFunction::ErrorCallback(
     device::BluetoothAdvertisement::ErrorCode status) {
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   switch (status) {
     case device::BluetoothAdvertisement::ErrorCode::
         ERROR_INVALID_ADVERTISEMENT_INTERVAL:
@@ -1325,7 +1325,7 @@
 
 bool BluetoothLowEnergyCreateServiceFunction::ParseParams() {
 // Causes link error on Windows. API will never be on Windows, so #ifdefing.
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
   params_ = apibtle::CreateService::Params::Create(args());
   return params_.get() != nullptr;
 #else
@@ -1338,7 +1338,7 @@
 // TODO: Ideally this should be handled by our feature system, so that this
 // code doesn't even compile on OSes it isn't being used on, but currently this
 // is not possible.
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
   base::WeakPtr<device::BluetoothLocalGattService> service =
       device::BluetoothLocalGattService::Create(
           event_router_->adapter(),
diff --git a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.h b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.h
index aef1688b..506cf90d 100644
--- a/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.h
+++ b/extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_api.h
@@ -11,6 +11,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
+#include "build/build_config.h"
 #include "content/public/browser/browser_context.h"
 #include "device/bluetooth/bluetooth_advertisement.h"
 #include "extensions/browser/api/api_resource_manager.h"
@@ -614,7 +615,7 @@
   bool ParseParams() override;
 
   // Causes link error on Windows. API will never be on Windows, so #ifdefing.
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
   std::unique_ptr<bluetooth_low_energy::CreateService::Params> params_;
 #endif
 };
diff --git a/extensions/browser/api/bluetooth_socket/bluetooth_socket_api_unittest.cc b/extensions/browser/api/bluetooth_socket/bluetooth_socket_api_unittest.cc
index 8602897f..c101cdea 100644
--- a/extensions/browser/api/bluetooth_socket/bluetooth_socket_api_unittest.cc
+++ b/extensions/browser/api/bluetooth_socket/bluetooth_socket_api_unittest.cc
@@ -37,7 +37,7 @@
 // Regression test for https://crbug.com/831651.
 // TODO(https://crbug.com/1251347): Port //device/bluetooth to Fuchsia to enable
 // bluetooth extensions.
-#if defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_FUCHSIA)
 #define MAYBE_CreateThenClose DISABLED_CreateThenClose
 #else
 #define MAYBE_CreateThenClose CreateThenClose
diff --git a/extensions/browser/api/content_settings/content_settings_service.cc b/extensions/browser/api/content_settings/content_settings_service.cc
index e99b362..109fb0079 100644
--- a/extensions/browser/api/content_settings/content_settings_service.cc
+++ b/extensions/browser/api/content_settings/content_settings_service.cc
@@ -42,18 +42,17 @@
 void ContentSettingsService::OnExtensionPrefsLoaded(
     const std::string& extension_id,
     const ExtensionPrefs* prefs) {
-  const base::ListValue* content_settings = NULL;
+  const base::ListValue* content_settings = nullptr;
   if (prefs->ReadPrefAsList(
           extension_id, pref_names::kPrefContentSettings, &content_settings)) {
     content_settings_store_->SetExtensionContentSettingFromList(
-        extension_id, content_settings, kExtensionPrefsScopeRegular);
+        extension_id, content_settings->GetList(), kExtensionPrefsScopeRegular);
   }
   if (prefs->ReadPrefAsList(extension_id,
                             pref_names::kPrefIncognitoContentSettings,
                             &content_settings)) {
     content_settings_store_->SetExtensionContentSettingFromList(
-        extension_id,
-        content_settings,
+        extension_id, content_settings->GetList(),
         kExtensionPrefsScopeIncognitoPersistent);
   }
 }
diff --git a/extensions/browser/api/content_settings/content_settings_store.cc b/extensions/browser/api/content_settings/content_settings_store.cc
index 86a2648..e60481888 100644
--- a/extensions/browser/api/content_settings/content_settings_store.cc
+++ b/extensions/browser/api/content_settings/content_settings_store.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <memory>
 #include <set>
+#include <string>
 #include <utility>
 
 #include "base/check_op.h"
@@ -22,6 +23,7 @@
 #include "components/content_settings/core/browser/content_settings_rule.h"
 #include "components/content_settings/core/browser/content_settings_utils.h"
 #include "components/content_settings/core/browser/website_settings_info.h"
+#include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_utils.h"
 #include "components/permissions/features.h"
@@ -55,8 +57,7 @@
   DCHECK(OnCorrectThread());
 }
 
-ContentSettingsStore::~ContentSettingsStore() {
-}
+ContentSettingsStore::~ContentSettingsStore() = default;
 
 constexpr char ContentSettingsStore::kContentSettingKey[];
 constexpr char ContentSettingsStore::kContentSettingsTypeKey[];
@@ -264,15 +265,15 @@
     NotifyOfContentSettingChanged(ext_id, scope != kExtensionPrefsScopeRegular);
 }
 
-std::unique_ptr<base::ListValue> ContentSettingsStore::GetSettingsForExtension(
+std::vector<base::Value> ContentSettingsStore::GetSettingsForExtension(
     const std::string& extension_id,
     ExtensionPrefsScope scope) const {
   base::AutoLock lock(lock_);
   const OriginIdentifierValueMap* map = GetValueMap(extension_id, scope);
   if (!map)
-    return nullptr;
+    return {};
 
-  auto settings = std::make_unique<base::ListValue>();
+  std::vector<base::Value> settings;
   for (const auto& it : *map) {
     const auto& key = it.first;
     std::unique_ptr<RuleIterator> rule_iterator(
@@ -283,13 +284,12 @@
 
     while (rule_iterator->HasNext()) {
       const Rule& rule = rule_iterator->Next();
-      std::unique_ptr<base::DictionaryValue> setting_dict(
-          new base::DictionaryValue());
-      setting_dict->SetString(kPrimaryPatternKey,
-                              rule.primary_pattern.ToString());
-      setting_dict->SetString(kSecondaryPatternKey,
-                              rule.secondary_pattern.ToString());
-      setting_dict->SetString(
+      base::Value setting_dict(base::Value::Type::DICTIONARY);
+      setting_dict.SetStringKey(kPrimaryPatternKey,
+                                rule.primary_pattern.ToString());
+      setting_dict.SetStringKey(kSecondaryPatternKey,
+                                rule.secondary_pattern.ToString());
+      setting_dict.SetStringKey(
           kContentSettingsTypeKey,
           content_settings_helpers::ContentSettingsTypeToString(key));
       ContentSetting content_setting =
@@ -300,47 +300,69 @@
           content_settings::ContentSettingToString(content_setting);
       DCHECK(!setting_string.empty());
 
-      setting_dict->SetString(kContentSettingKey, setting_string);
-      settings->Append(std::move(setting_dict));
+      setting_dict.SetStringKey(kContentSettingKey, setting_string);
+      settings.push_back(std::move(setting_dict));
     }
   }
   return settings;
 }
 
+#define LOG_INVALID_EXTENSION_PREFERENCE_DETAILS         \
+  LOG(ERROR) << "Found invalid extension pref: " << dict \
+             << " extension id: " << extension_id
+
 void ContentSettingsStore::SetExtensionContentSettingFromList(
     const std::string& extension_id,
-    const base::ListValue* list,
+    base::Value::ConstListView list,
     ExtensionPrefsScope scope) {
-  for (const auto& value : list->GetList()) {
-    const base::DictionaryValue* dict = nullptr;
-    if (!value.GetAsDictionary(&dict)) {
-      NOTREACHED();
+  for (const base::Value& dict : list) {
+    if (!dict.is_dict()) {
+      LOG_INVALID_EXTENSION_PREFERENCE_DETAILS;
       continue;
     }
-    std::string primary_pattern_str;
-    dict->GetString(kPrimaryPatternKey, &primary_pattern_str);
+
+    const std::string* primary_pattern_str =
+        dict.FindStringKey(kPrimaryPatternKey);
+    if (!primary_pattern_str) {
+      LOG_INVALID_EXTENSION_PREFERENCE_DETAILS;
+      continue;
+    }
     ContentSettingsPattern primary_pattern =
-        ContentSettingsPattern::FromString(primary_pattern_str);
-    DCHECK(primary_pattern.IsValid());
+        ContentSettingsPattern::FromString(*primary_pattern_str);
+    if (!primary_pattern.IsValid()) {
+      LOG_INVALID_EXTENSION_PREFERENCE_DETAILS;
+      continue;
+    }
 
-    std::string secondary_pattern_str;
-    dict->GetString(kSecondaryPatternKey, &secondary_pattern_str);
+    const std::string* secondary_pattern_str =
+        dict.FindStringKey(kSecondaryPatternKey);
+    if (!secondary_pattern_str) {
+      LOG_INVALID_EXTENSION_PREFERENCE_DETAILS;
+      continue;
+    }
     ContentSettingsPattern secondary_pattern =
-        ContentSettingsPattern::FromString(secondary_pattern_str);
-    DCHECK(secondary_pattern.IsValid());
+        ContentSettingsPattern::FromString(*secondary_pattern_str);
+    if (!secondary_pattern.IsValid()) {
+      LOG_INVALID_EXTENSION_PREFERENCE_DETAILS;
+      continue;
+    }
 
-    std::string content_settings_type_str;
-    dict->GetString(kContentSettingsTypeKey, &content_settings_type_str);
+    auto* content_settings_type_str =
+        dict.FindStringKey(kContentSettingsTypeKey);
+    if (!content_settings_type_str) {
+      LOG_INVALID_EXTENSION_PREFERENCE_DETAILS;
+      continue;
+    }
     ContentSettingsType content_settings_type =
         content_settings_helpers::StringToContentSettingsType(
-            content_settings_type_str);
+            *content_settings_type_str);
     if (content_settings_type == ContentSettingsType::DEFAULT) {
       // We'll end up with DEFAULT here if the type string isn't recognised.
       // This could be if it's a string from an old settings type that has been
       // deleted. DCHECK to make sure this is the case (not some random string).
-      DCHECK(content_settings_type_str == "fullscreen" ||
-             content_settings_type_str == "mouselock" ||
-             content_settings_type_str == "plugins");
+      DCHECK(*content_settings_type_str == "fullscreen" ||
+             *content_settings_type_str == "mouselock" ||
+             *content_settings_type_str == "plugins");
 
       // In this case, we just skip over that setting, effectively deleting it
       // from the in-memory model. This will implicitly delete these old
@@ -353,8 +375,9 @@
             content_settings_type);
 
     if (secondary_pattern == primary_pattern &&
-        info->website_settings_info()->SupportsSecondaryPattern())
+        info->website_settings_info()->SupportsSecondaryPattern()) {
       secondary_pattern = ContentSettingsPattern::Wildcard();
+    }
 
     if (primary_pattern != secondary_pattern &&
         secondary_pattern != ContentSettingsPattern::Wildcard() &&
@@ -362,24 +385,34 @@
       // Some types may have had embedded exceptions written even though they
       // aren't supported. This will implicitly delete these old settings from
       // the pref store when it is written back.
+      LOG_INVALID_EXTENSION_PREFERENCE_DETAILS;
       continue;
     }
 
-    std::string content_setting_string;
-    dict->GetString(kContentSettingKey, &content_setting_string);
     ContentSetting setting;
+    const std::string* content_setting_str =
+        dict.FindStringKey(kContentSettingKey);
+    if (!content_setting_str) {
+      LOG_INVALID_EXTENSION_PREFERENCE_DETAILS;
+      continue;
+    }
+
     bool result = content_settings::ContentSettingFromString(
-        content_setting_string, &setting);
-    DCHECK(result);
+        *content_setting_str, &setting);
     // The content settings extensions API does not support setting any content
     // settings to |CONTENT_SETTING_DEFAULT|.
-    DCHECK_NE(CONTENT_SETTING_DEFAULT, setting);
+    if (!result != CONTENT_SETTING_DEFAULT) {
+      LOG_INVALID_EXTENSION_PREFERENCE_DETAILS;
+      continue;
+    }
 
     SetExtensionContentSetting(extension_id, primary_pattern, secondary_pattern,
                                content_settings_type, setting, scope);
   }
 }
 
+#undef LOG_INVALID_EXTENSION_PREFERENCE_DETAILS
+
 void ContentSettingsStore::AddObserver(Observer* observer) {
   DCHECK(OnCorrectThread());
   observers_.AddObserver(observer);
diff --git a/extensions/browser/api/content_settings/content_settings_store.h b/extensions/browser/api/content_settings/content_settings_store.h
index f9dea77..26c8bcb9 100644
--- a/extensions/browser/api/content_settings/content_settings_store.h
+++ b/extensions/browser/api/content_settings/content_settings_store.h
@@ -21,7 +21,7 @@
 #include "extensions/browser/extension_prefs_scope.h"
 
 namespace base {
-class ListValue;
+class Value;
 }
 
 namespace content_settings {
@@ -40,7 +40,7 @@
  public:
   class Observer {
    public:
-    virtual ~Observer() {}
+    virtual ~Observer() = default;
 
     // Called when a content setting changes in the
     // ContentSettingsStore.
@@ -90,17 +90,16 @@
       ContentSettingsType content_type);
 
   // Serializes all content settings set by the extension with ID |extension_id|
-  // and returns them as a ListValue. The caller takes ownership of the returned
-  // value.
-  std::unique_ptr<base::ListValue> GetSettingsForExtension(
+  // and returns them as a list of Values.
+  std::vector<base::Value> GetSettingsForExtension(
       const std::string& extension_id,
       ExtensionPrefsScope scope) const;
 
   // Deserializes content settings rules from |list| and applies them as set by
   // the extension with ID |extension_id|.
   void SetExtensionContentSettingFromList(const std::string& extension_id,
-                                           const base::ListValue* list,
-                                           ExtensionPrefsScope scope);
+                                          base::Value::ConstListView list,
+                                          ExtensionPrefsScope scope);
 
   // //////////////////////////////////////////////////////////////////////////
 
diff --git a/extensions/browser/api/content_settings/content_settings_store_unittest.cc b/extensions/browser/api/content_settings/content_settings_store_unittest.cc
index c82a85c..7b728a4 100644
--- a/extensions/browser/api/content_settings/content_settings_store_unittest.cc
+++ b/extensions/browser/api/content_settings/content_settings_store_unittest.cc
@@ -243,43 +243,43 @@
   EXPECT_CALL(observer, OnContentSettingChanged(ext_id, false));
 
   // Build a preference list in JSON format.
-  base::ListValue pref_list;
+  std::vector<base::Value> pref_list;
   // {"primaryPattern": pattern, "secondaryPattern": pattern, "type": "cookies",
   //  "setting": "allow"}
-  auto dict_value = std::make_unique<base::DictionaryValue>();
-  dict_value->SetString(ContentSettingsStore::kPrimaryPatternKey,
-                        pattern.ToString());
-  dict_value->SetString(ContentSettingsStore::kSecondaryPatternKey,
-                        pattern.ToString());
-  dict_value->SetString(ContentSettingsStore::kContentSettingsTypeKey,
-                        "cookies");
-  dict_value->SetString(ContentSettingsStore::kContentSettingKey, "allow");
-  pref_list.Append(std::move(dict_value));
+  base::Value dict_value(base::Value::Type::DICTIONARY);
+  dict_value.SetStringKey(ContentSettingsStore::kPrimaryPatternKey,
+                          pattern.ToString());
+  dict_value.SetStringKey(ContentSettingsStore::kSecondaryPatternKey,
+                          pattern.ToString());
+  dict_value.SetStringKey(ContentSettingsStore::kContentSettingsTypeKey,
+                          "cookies");
+  dict_value.SetStringKey(ContentSettingsStore::kContentSettingKey, "allow");
+  pref_list.push_back(std::move(dict_value));
   // Test content settings types that have been removed. Should be ignored.
   // {"primaryPattern": pattern, "secondaryPattern": pattern,
   //  "type": "fullscreen", "setting": "allow"}
-  dict_value = std::make_unique<base::DictionaryValue>();
-  dict_value->SetString(ContentSettingsStore::kPrimaryPatternKey,
-                        pattern.ToString());
-  dict_value->SetString(ContentSettingsStore::kSecondaryPatternKey,
-                        pattern.ToString());
-  dict_value->SetString(ContentSettingsStore::kContentSettingsTypeKey,
-                        "fullscreen");
-  dict_value->SetString(ContentSettingsStore::kContentSettingKey, "allow");
-  pref_list.Append(std::move(dict_value));
+  dict_value = base::Value(base::Value::Type::DICTIONARY);
+  dict_value.SetStringKey(ContentSettingsStore::kPrimaryPatternKey,
+                          pattern.ToString());
+  dict_value.SetStringKey(ContentSettingsStore::kSecondaryPatternKey,
+                          pattern.ToString());
+  dict_value.SetStringKey(ContentSettingsStore::kContentSettingsTypeKey,
+                          "fullscreen");
+  dict_value.SetStringKey(ContentSettingsStore::kContentSettingKey, "allow");
+  pref_list.push_back(std::move(dict_value));
   // {"primaryPattern": pattern, "secondaryPattern": pattern,
   //  "type": "mouselock", "setting": "allow"}
-  dict_value = std::make_unique<base::DictionaryValue>();
-  dict_value->SetString(ContentSettingsStore::kPrimaryPatternKey,
-                        pattern.ToString());
-  dict_value->SetString(ContentSettingsStore::kSecondaryPatternKey,
-                        pattern.ToString());
-  dict_value->SetString(ContentSettingsStore::kContentSettingsTypeKey,
-                        "mouselock");
-  dict_value->SetString(ContentSettingsStore::kContentSettingKey, "allow");
-  pref_list.Append(std::move(dict_value));
+  dict_value = base::Value(base::Value::Type::DICTIONARY);
+  dict_value.SetStringKey(ContentSettingsStore::kPrimaryPatternKey,
+                          pattern.ToString());
+  dict_value.SetStringKey(ContentSettingsStore::kSecondaryPatternKey,
+                          pattern.ToString());
+  dict_value.SetStringKey(ContentSettingsStore::kContentSettingsTypeKey,
+                          "mouselock");
+  dict_value.SetStringKey(ContentSettingsStore::kContentSettingKey, "allow");
+  pref_list.push_back(std::move(dict_value));
 
-  store()->SetExtensionContentSettingFromList(ext_id, &pref_list,
+  store()->SetExtensionContentSettingFromList(ext_id, pref_list,
                                               kExtensionPrefsScopeRegular);
   Mock::VerifyAndClear(&observer);
 
@@ -312,28 +312,28 @@
   EXPECT_CALL(observer, OnContentSettingChanged(ext_id, false)).Times(1);
 
   // Build a preference list in JSON format.
-  base::ListValue pref_list;
-  auto dict_value = std::make_unique<base::DictionaryValue>();
-  dict_value->SetString(ContentSettingsStore::kPrimaryPatternKey,
-                        primary_pattern.ToString());
-  dict_value->SetString(ContentSettingsStore::kSecondaryPatternKey,
-                        secondary_pattern.ToString());
-  dict_value->SetString(ContentSettingsStore::kContentSettingsTypeKey,
-                        "cookies");
-  dict_value->SetString(ContentSettingsStore::kContentSettingKey, "allow");
-  pref_list.Append(std::move(dict_value));
+  std::vector<base::Value> pref_list;
+  base::Value dict_value(base::Value::Type::DICTIONARY);
+  dict_value.SetStringKey(ContentSettingsStore::kPrimaryPatternKey,
+                          primary_pattern.ToString());
+  dict_value.SetStringKey(ContentSettingsStore::kSecondaryPatternKey,
+                          secondary_pattern.ToString());
+  dict_value.SetStringKey(ContentSettingsStore::kContentSettingsTypeKey,
+                          "cookies");
+  dict_value.SetStringKey(ContentSettingsStore::kContentSettingKey, "allow");
+  pref_list.push_back(std::move(dict_value));
 
-  dict_value = std::make_unique<base::DictionaryValue>();
-  dict_value->SetString(ContentSettingsStore::kPrimaryPatternKey,
-                        primary_pattern.ToString());
-  dict_value->SetString(ContentSettingsStore::kSecondaryPatternKey,
-                        secondary_pattern.ToString());
-  dict_value->SetString(ContentSettingsStore::kContentSettingsTypeKey,
-                        "geolocation");
-  dict_value->SetString(ContentSettingsStore::kContentSettingKey, "allow");
-  pref_list.Append(std::move(dict_value));
+  dict_value = base::Value(base::Value::Type::DICTIONARY);
+  dict_value.SetStringKey(ContentSettingsStore::kPrimaryPatternKey,
+                          primary_pattern.ToString());
+  dict_value.SetStringKey(ContentSettingsStore::kSecondaryPatternKey,
+                          secondary_pattern.ToString());
+  dict_value.SetStringKey(ContentSettingsStore::kContentSettingsTypeKey,
+                          "geolocation");
+  dict_value.SetStringKey(ContentSettingsStore::kContentSettingKey, "allow");
+  pref_list.push_back(std::move(dict_value));
 
-  store()->SetExtensionContentSettingFromList(ext_id, &pref_list,
+  store()->SetExtensionContentSettingFromList(ext_id, pref_list,
                                               kExtensionPrefsScopeRegular);
 
   // The embedded geolocation pattern should be removed but cookies kept.
@@ -349,4 +349,64 @@
   store()->RemoveObserver(&observer);
 }
 
+TEST_F(ContentSettingsStoreTest, SetExtensionContentSettingFromList) {
+  content_settings::ContentSettingsRegistry::GetInstance();
+
+  std::string extension = "extension_id";
+  RegisterExtension(extension);
+
+  base::Value valid_setting(base::Value::Type::DICTIONARY);
+  valid_setting.SetStringKey(ContentSettingsStore::kPrimaryPatternKey,
+                             "http://example1.com");
+  valid_setting.SetStringKey(ContentSettingsStore::kSecondaryPatternKey, "*");
+  valid_setting.SetStringKey(ContentSettingsStore::kContentSettingsTypeKey,
+                             "javascript");
+  valid_setting.SetStringKey(ContentSettingsStore::kContentSettingKey, "allow");
+
+  // Missing secondary key.
+  base::Value invalid_setting1(base::Value::Type::DICTIONARY);
+  invalid_setting1.SetStringKey(ContentSettingsStore::kPrimaryPatternKey,
+                                "http://example2.com");
+  invalid_setting1.SetStringKey(ContentSettingsStore::kContentSettingsTypeKey,
+                                "javascript");
+  invalid_setting1.SetStringKey(ContentSettingsStore::kContentSettingKey,
+                                "allow");
+
+  // Invalid secondary pattern.
+  base::Value invalid_setting2(base::Value::Type::DICTIONARY);
+  invalid_setting2.SetStringKey(ContentSettingsStore::kPrimaryPatternKey,
+                                "http://example3.com");
+  invalid_setting2.SetStringKey(ContentSettingsStore::kSecondaryPatternKey,
+                                "[*.].");
+  invalid_setting2.SetStringKey(ContentSettingsStore::kContentSettingsTypeKey,
+                                "javascript");
+  invalid_setting2.SetStringKey(ContentSettingsStore::kContentSettingKey,
+                                "allow");
+
+  // Invalid setting.
+  base::Value invalid_setting3(base::Value::Type::DICTIONARY);
+  invalid_setting3.SetStringKey(ContentSettingsStore::kPrimaryPatternKey,
+                                "http://example4.com");
+  invalid_setting3.SetStringKey(ContentSettingsStore::kSecondaryPatternKey,
+                                "*");
+  invalid_setting3.SetStringKey(ContentSettingsStore::kContentSettingsTypeKey,
+                                "javascript");
+  invalid_setting3.SetStringKey(ContentSettingsStore::kContentSettingKey,
+                                "notasetting");
+
+  std::vector<base::Value> list;
+  list.push_back(valid_setting.Clone());
+  list.push_back(invalid_setting1.Clone());
+  list.push_back(invalid_setting2.Clone());
+  list.push_back(invalid_setting3.Clone());
+  store()->SetExtensionContentSettingFromList(
+      extension, list, ExtensionPrefsScope::kExtensionPrefsScopeRegular);
+
+  std::vector<base::Value> expected;
+  expected.push_back(valid_setting.Clone());
+  EXPECT_EQ(expected,
+            store()->GetSettingsForExtension(
+                extension, ExtensionPrefsScope::kExtensionPrefsScopeRegular));
+}
+
 }  // namespace extensions
diff --git a/extensions/browser/api/declarative_net_request/filter_list_converter/main.cc b/extensions/browser/api/declarative_net_request/filter_list_converter/main.cc
index a5efc56..aaa99b1 100644
--- a/extensions/browser/api/declarative_net_request/filter_list_converter/main.cc
+++ b/extensions/browser/api/declarative_net_request/filter_list_converter/main.cc
@@ -11,6 +11,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "extensions/browser/api/declarative_net_request/filter_list_converter/converter.h"
 
 namespace {
@@ -59,7 +60,7 @@
   base::CommandLine::StringType comma_separated_paths =
       command_line.GetSwitchValueNative(kSwitchInputFilterlistFiles);
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   base::CommandLine::StringType separator = L",";
 #else
   base::CommandLine::StringType separator(",");
diff --git a/extensions/browser/api/device_permissions_prompt.cc b/extensions/browser/api/device_permissions_prompt.cc
index a191f1d..6e61b1a 100644
--- a/extensions/browser/api/device_permissions_prompt.cc
+++ b/extensions/browser/api/device_permissions_prompt.cc
@@ -29,9 +29,9 @@
 #include "services/device/public/mojom/usb_enumeration_options.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
 #include "chromeos/dbus/permission_broker/permission_broker_client.h"  // nogncheck
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 using device::HidDeviceFilter;
 using device::mojom::UsbDeviceFilterPtr;
@@ -149,7 +149,7 @@
       remaining_initial_devices_++;
 
     auto device_info = std::make_unique<UsbDeviceInfo>(device.Clone());
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
     auto* device_manager = UsbDeviceManager::Get(browser_context());
     DCHECK(device_manager);
     device_manager->CheckAccess(
@@ -159,7 +159,7 @@
 #else
     AddCheckedDevice(std::move(device_info), initial_enumeration,
                      /*allowed=*/true);
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
   }
 
   void AddCheckedDevice(std::unique_ptr<UsbDeviceInfo> device_info,
diff --git a/extensions/browser/api/extensions_api_client.cc b/extensions/browser/api/extensions_api_client.cc
index 20e4ee9..cf7b1ac 100644
--- a/extensions/browser/api/extensions_api_client.cc
+++ b/extensions/browser/api/extensions_api_client.cc
@@ -4,6 +4,7 @@
 
 #include "extensions/browser/api/extensions_api_client.h"
 
+#include "build/build_config.h"
 #include "extensions/browser/api/device_permissions_prompt.h"
 #include "extensions/browser/api/system_display/display_info_provider.h"
 #include "extensions/browser/api/virtual_keyboard_private/virtual_keyboard_delegate.h"
@@ -109,11 +110,11 @@
   return nullptr;
 }
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
 bool ExtensionsAPIClient::ShouldAllowDetachingUsb(int vid, int pid) const {
   return false;
 }
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 std::unique_ptr<VirtualKeyboardDelegate>
 ExtensionsAPIClient::CreateVirtualKeyboardDelegate(
@@ -164,14 +165,14 @@
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
 void ExtensionsAPIClient::SaveImageDataToClipboard(
     std::vector<uint8_t> image_data,
     api::clipboard::ImageType type,
     AdditionalDataItemList additional_items,
     base::OnceClosure success_callback,
     base::OnceCallback<void(const std::string&)> error_callback) {}
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 AutomationInternalApiDelegate*
 ExtensionsAPIClient::GetAutomationInternalApiDelegate() {
diff --git a/extensions/browser/api/extensions_api_client.h b/extensions/browser/api/extensions_api_client.h
index f4b2cd6..20ddb020 100644
--- a/extensions/browser/api/extensions_api_client.h
+++ b/extensions/browser/api/extensions_api_client.h
@@ -165,10 +165,10 @@
   virtual std::unique_ptr<DevicePermissionsPrompt>
   CreateDevicePermissionsPrompt(content::WebContents* web_contents) const;
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
   // Returns true if device policy allows detaching a given USB device.
   virtual bool ShouldAllowDetachingUsb(int vid, int pid) const;
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
   // Returns a delegate for some of VirtualKeyboardAPI's behavior.
   virtual std::unique_ptr<VirtualKeyboardDelegate>
@@ -210,7 +210,7 @@
   virtual MediaPerceptionAPIDelegate* GetMediaPerceptionAPIDelegate();
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
   // Saves image data on clipboard.
   virtual void SaveImageDataToClipboard(
       std::vector<uint8_t> image_data,
@@ -218,7 +218,7 @@
       AdditionalDataItemList additional_items,
       base::OnceClosure success_callback,
       base::OnceCallback<void(const std::string&)> error_callback);
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
   virtual AutomationInternalApiDelegate* GetAutomationInternalApiDelegate();
 
diff --git a/extensions/browser/api/feedback_private/feedback_private_api.cc b/extensions/browser/api/feedback_private/feedback_private_api.cc
index caa01559..341ab8b 100644
--- a/extensions/browser/api/feedback_private/feedback_private_api.cc
+++ b/extensions/browser/api/feedback_private/feedback_private_api.cc
@@ -245,7 +245,7 @@
     info->trace_id = std::make_unique<int>(manager->RequestTrace());
   }
   info->flow = flow;
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   const bool use_system_window_frame = true;
 #else
   const bool use_system_window_frame = false;
diff --git a/extensions/browser/api/file_system/file_system_api.cc b/extensions/browser/api/file_system/file_system_api.cc
index 45de131..be461da 100644
--- a/extensions/browser/api/file_system/file_system_api.cc
+++ b/extensions/browser/api/file_system/file_system_api.cc
@@ -61,7 +61,7 @@
 #include "ui/shell_dialogs/select_file_dialog.h"
 #include "url/origin.h"
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 #include <CoreFoundation/CoreFoundation.h>
 #include "base/mac/foundation_util.h"
 #endif
@@ -159,7 +159,7 @@
     for (std::vector<std::string>::const_iterator iter = list->begin();
          iter != list->end(); ++iter) {
       std::string extension = base::ToLowerASCII(*iter);
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
       extension_set.insert(base::UTF8ToWide(*iter));
 #else
       extension_set.insert(*iter);
@@ -185,8 +185,10 @@
 
 const int kGraylistedPaths[] = {
     base::DIR_HOME,
-#if defined(OS_WIN)
-    base::DIR_PROGRAM_FILES, base::DIR_PROGRAM_FILESX86, base::DIR_WINDOWS,
+#if BUILDFLAG(IS_WIN)
+    base::DIR_PROGRAM_FILES,
+    base::DIR_PROGRAM_FILESX86,
+    base::DIR_WINDOWS,
 #endif
 };
 
diff --git a/extensions/browser/api/lock_screen_data/lock_screen_item_storage.cc b/extensions/browser/api/lock_screen_data/lock_screen_item_storage.cc
index 16d6707..cca8a49 100644
--- a/extensions/browser/api/lock_screen_data/lock_screen_item_storage.cc
+++ b/extensions/browser/api/lock_screen_data/lock_screen_item_storage.cc
@@ -408,7 +408,7 @@
                                                     std::move(item));
 
   {
-    DictionaryPrefUpdate update(local_state_, kLockScreenDataPrefKey);
+    DictionaryPrefUpdateDeprecated update(local_state_, kLockScreenDataPrefKey);
     update->SetPath({user_id_, extension_id, kExtensionItemCountPrefKey},
                     base::Value(static_cast<int>(
                         data_item_cache_[extension_id].data_items.size())));
@@ -468,7 +468,7 @@
 
   data_item_cache_[extension_id].data_items.erase(item_id);
   {
-    DictionaryPrefUpdate update(local_state_, kLockScreenDataPrefKey);
+    DictionaryPrefUpdateDeprecated update(local_state_, kLockScreenDataPrefKey);
     update->SetPath({user_id_, extension_id, kExtensionItemCountPrefKey},
                     base::Value(static_cast<int>(
                         data_item_cache_[extension_id].data_items.size())));
@@ -562,7 +562,7 @@
   }
 
   {
-    DictionaryPrefUpdate update(local_state_, kLockScreenDataPrefKey);
+    DictionaryPrefUpdateDeprecated update(local_state_, kLockScreenDataPrefKey);
     base::Value info(base::Value::Type::DICTIONARY);
     info.SetKey(kExtensionItemCountPrefKey,
                 base::Value(static_cast<int>(data->second.data_items.size())));
@@ -670,7 +670,7 @@
 void LockScreenItemStorage::RemoveExtensionFromLocalState(
     const std::string& id) {
   {
-    DictionaryPrefUpdate update(local_state_, kLockScreenDataPrefKey);
+    DictionaryPrefUpdateDeprecated update(local_state_, kLockScreenDataPrefKey);
     update->RemovePath(base::StrCat({user_id_, ".", id}));
   }
 
diff --git a/extensions/browser/api/lock_screen_data/lock_screen_item_storage_unittest.cc b/extensions/browser/api/lock_screen_data/lock_screen_item_storage_unittest.cc
index 68d7f5d..25b20d6 100644
--- a/extensions/browser/api/lock_screen_data/lock_screen_item_storage_unittest.cc
+++ b/extensions/browser/api/lock_screen_data/lock_screen_item_storage_unittest.cc
@@ -606,7 +606,8 @@
       ASSERT_TRUE(state.storage_version == 1 || state.storage_version == 2)
           << "Failed to init local state " << state.extension_id;
 
-      DictionaryPrefUpdate update(&local_state_, "lockScreenDataItems");
+      DictionaryPrefUpdateDeprecated update(&local_state_,
+                                            "lockScreenDataItems");
       if (state.storage_version == 1) {
         update->SetPath({kTestUserIdHash, state.extension_id},
                         base::Value(state.item_count));
diff --git a/extensions/browser/api/management/management_api.cc b/extensions/browser/api/management/management_api.cc
index db7073d..3edb822 100644
--- a/extensions/browser/api/management/management_api.cc
+++ b/extensions/browser/api/management/management_api.cc
@@ -752,7 +752,7 @@
         ErrorUtils::FormatErrorMessage(keys::kNotAnAppError, params->id)));
   }
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   if (!extension->is_platform_app())
     return RespondNow(Error(keys::kCreateOnlyPackagedAppShortcutMac));
 #endif
diff --git a/extensions/browser/api/messaging/message_service.cc b/extensions/browser/api/messaging/message_service.cc
index 771595a..629c867 100644
--- a/extensions/browser/api/messaging/message_service.cc
+++ b/extensions/browser/api/messaging/message_service.cc
@@ -73,8 +73,8 @@
 
 const char kReceivingEndDoesntExistError[] =
     "Could not establish connection. Receiving end does not exist.";
-#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
-    defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
+    BUILDFLAG(IS_CHROMEOS)
 const char kMissingPermissionError[] =
     "Access to native messaging requires nativeMessaging permission.";
 const char kProhibitedByPoliciesError[] =
@@ -438,8 +438,8 @@
   if (!opener_port->IsValidPort())
     return;
 
-#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
-    defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
+    BUILDFLAG(IS_CHROMEOS)
   bool has_permission = extension->permissions_data()->HasAPIPermission(
       mojom::APIPermissionID::kNativeMessaging);
   if (!has_permission) {
@@ -492,13 +492,13 @@
   channel->opener->IncrementLazyKeepaliveCount();
 
   AddChannel(std::move(channel), receiver_port_id);
-#else   // !(defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) ||
-        // defined(OS_CHROMEOS))
+#else   // !(BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) ||
+        // BUILDFLAG(IS_CHROMEOS))
   const char kNativeMessagingNotSupportedError[] =
       "Native Messaging is not supported on this platform.";
   opener_port->DispatchOnDisconnect(kNativeMessagingNotSupportedError);
-#endif  // !(defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) ||
-        // defined(OS_CHROMEOS))
+#endif  // !(BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) ||
+        // BUILDFLAG(IS_CHROMEOS))
 }
 
 void MessageService::OpenChannelToTab(const ChannelEndpoint& source,
diff --git a/extensions/browser/api/networking_private/networking_private_delegate_factory.cc b/extensions/browser/api/networking_private/networking_private_delegate_factory.cc
index 0d8b62f2..fda660b8 100644
--- a/extensions/browser/api/networking_private/networking_private_delegate_factory.cc
+++ b/extensions/browser/api/networking_private/networking_private_delegate_factory.cc
@@ -12,9 +12,9 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "extensions/browser/api/networking_private/networking_private_chromeos.h"
-#elif defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "extensions/browser/api/networking_private/networking_private_linux.h"
-#elif defined(OS_WIN) || defined(OS_MAC)
+#elif BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
 #include "components/wifi/wifi_service.h"
 #include "extensions/browser/api/networking_private/networking_private_service_client.h"
 #endif
@@ -62,9 +62,9 @@
   NetworkingPrivateDelegate* delegate;
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   delegate = new NetworkingPrivateChromeOS(browser_context);
-#elif defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
   delegate = new NetworkingPrivateLinux();
-#elif defined(OS_WIN) || defined(OS_MAC)
+#elif BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
   std::unique_ptr<wifi::WiFiService> wifi_service(wifi::WiFiService::Create());
   delegate = new NetworkingPrivateServiceClient(std::move(wifi_service));
 #else
diff --git a/extensions/browser/api/serial/serial_api.cc b/extensions/browser/api/serial/serial_api.cc
index e19952e..a705a3880 100644
--- a/extensions/browser/api/serial/serial_api.cc
+++ b/extensions/browser/api/serial/serial_api.cc
@@ -93,7 +93,7 @@
       info.display_name = std::make_unique<std::string>(*device->display_name);
     results.push_back(std::move(info));
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
     if (device->alternate_path) {
       extensions::api::serial::DeviceInfo alternate_info;
       alternate_info.path = device->alternate_path->AsUTF8Unsafe();
@@ -107,7 +107,7 @@
       }
       results.push_back(std::move(alternate_info));
     }
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
   }
   Respond(ArgumentList(serial::GetDevices::Results::Create(results)));
 }
diff --git a/extensions/browser/api/serial/serial_connection.cc b/extensions/browser/api/serial/serial_connection.cc
index b6c450f3..cdbffd2a 100644
--- a/extensions/browser/api/serial/serial_connection.cc
+++ b/extensions/browser/api/serial/serial_connection.cc
@@ -7,12 +7,12 @@
 #include <algorithm>
 #include <memory>
 #include <string>
+#include <tuple>
 #include <utility>
 #include <vector>
 
 #include "base/bind.h"
 #include "base/files/file_path.h"
-#include "base/ignore_result.h"
 #include "base/lazy_instance.h"
 #include "base/location.h"
 #include "base/task/single_thread_task_runner.h"
@@ -530,7 +530,7 @@
 }
 
 void SerialConnection::InitSerialPortForTesting() {
-  ignore_result(serial_port_.BindNewPipeAndPassReceiver());
+  std::ignore = serial_port_.BindNewPipeAndPassReceiver();
 }
 
 void SerialConnection::SetTimeoutCallback() {
diff --git a/extensions/browser/api/serial/serial_port_manager.cc b/extensions/browser/api/serial/serial_port_manager.cc
index 9fb4999..8e6e200 100644
--- a/extensions/browser/api/serial/serial_port_manager.cc
+++ b/extensions/browser/api/serial/serial_port_manager.cc
@@ -198,7 +198,7 @@
       return;
     }
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
     if (device->alternate_path &&
         device->alternate_path->AsUTF8Unsafe() == path) {
       port_manager_->OpenPort(device->token, /*use_alternate_path=*/true,
@@ -207,7 +207,7 @@
                               std::move(callback));
       return;
     }
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
   }
 }
 
diff --git a/extensions/browser/api/sockets_udp/sockets_udp_apitest.cc b/extensions/browser/api/sockets_udp/sockets_udp_apitest.cc
index 20f1ef3..de72f66 100644
--- a/extensions/browser/api/sockets_udp/sockets_udp_apitest.cc
+++ b/extensions/browser/api/sockets_udp/sockets_udp_apitest.cc
@@ -52,7 +52,7 @@
 // Disable SocketsUdpExtension on Mac due to time out.
 // See https://crbug.com/844402.
 // Disable on Linux for flakiness. See https://crbug.com/875920.
-#if defined(OS_MAC) || defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #define MAYBE_SocketsUdpExtension DISABLED_SocketsUdpExtension
 #else
 #define MAYBE_SocketsUdpExtension SocketsUdpExtension
diff --git a/extensions/browser/api/system_cpu/cpu_info_provider.cc b/extensions/browser/api/system_cpu/cpu_info_provider.cc
index 14bc3583..60f2289 100644
--- a/extensions/browser/api/system_cpu/cpu_info_provider.cc
+++ b/extensions/browser/api/system_cpu/cpu_info_provider.cc
@@ -7,9 +7,9 @@
 #include "base/system/sys_info.h"
 #include "build/build_config.h"
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
 #include "chromeos/system/cpu_temperature_reader.h"
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 namespace extensions {
 
@@ -45,7 +45,7 @@
   if (!QueryCpuTimePerProcessor(&info_.processors))
     info_.processors.clear();
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
   using CPUTemperatureInfo =
       chromeos::system::CPUTemperatureReader::CPUTemperatureInfo;
   std::vector<CPUTemperatureInfo> cpu_temp_info =
@@ -55,7 +55,7 @@
   for (const CPUTemperatureInfo& info : cpu_temp_info) {
     info_.temperatures.push_back(info.temp_celsius);
   }
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
   return true;
 }
diff --git a/extensions/browser/api/system_network/system_network_api_unittest.cc b/extensions/browser/api/system_network/system_network_api_unittest.cc
index 976f07bd..5b75d6d7 100644
--- a/extensions/browser/api/system_network/system_network_api_unittest.cc
+++ b/extensions/browser/api/system_network/system_network_api_unittest.cc
@@ -21,11 +21,11 @@
 }  // namespace
 
 // TODO(crbug.com/1255187): Fails on Fuchsia running with run-test-component.
-#if defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_FUCHSIA)
 #define MAYBE_GetNetworkInterfaces DISABLED_GetNetworkInterfaces
 #else
 #define MAYBE_GetNetworkInterfaces GetNetworkInterfaces
-#endif  // defined(OS_FUCHSIA)
+#endif  // BUILDFLAG(IS_FUCHSIA)
 TEST_F(SystemNetworkApiUnitTest, MAYBE_GetNetworkInterfaces) {
   scoped_refptr<SystemNetworkGetNetworkInterfacesFunction> socket_function(
       new SystemNetworkGetNetworkInterfacesFunction());
diff --git a/extensions/browser/api/usb/usb_apitest.cc b/extensions/browser/api/usb/usb_apitest.cc
index b481b65..32266696 100644
--- a/extensions/browser/api/usb/usb_apitest.cc
+++ b/extensions/browser/api/usb/usb_apitest.cc
@@ -115,11 +115,11 @@
     return std::make_unique<TestDevicePermissionsPrompt>(web_contents);
   }
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
   bool ShouldAllowDetachingUsb(int vid, int pid) const override {
     return vid == 1 && pid == 2;
   }
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 };
 
 class UsbApiTest : public ShellApiTest {
@@ -366,7 +366,7 @@
   ASSERT_TRUE(result_listener.WaitUntilSatisfied());
 }
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(UsbApiTest, MassStorage) {
   ExtensionTestMessageListener ready_listener("ready", false);
   ready_listener.set_failure_message("failure");
@@ -398,6 +398,6 @@
 
   ASSERT_TRUE(result_listener.WaitUntilSatisfied());
 }
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace extensions
diff --git a/extensions/browser/api/usb/usb_device_manager.cc b/extensions/browser/api/usb/usb_device_manager.cc
index a8c6ac0..8a5c742 100644
--- a/extensions/browser/api/usb/usb_device_manager.cc
+++ b/extensions/browser/api/usb/usb_device_manager.cc
@@ -11,6 +11,7 @@
 #include "base/containers/contains.h"
 #include "base/lazy_instance.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/device_service.h"
 #include "extensions/browser/api/device_permissions_manager.h"
@@ -51,12 +52,12 @@
     }
   }
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
   if (ExtensionsAPIClient::Get()->ShouldAllowDetachingUsb(
           device_info.vendor_id, device_info.product_id)) {
     return true;
   }
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
   return false;
 }
@@ -211,14 +212,14 @@
   return true;
 }
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
 void UsbDeviceManager::CheckAccess(
     const std::string& guid,
     device::mojom::UsbDeviceManager::CheckAccessCallback callback) {
   EnsureConnectionWithDeviceManager();
   device_manager_->CheckAccess(guid, std::move(callback));
 }
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 void UsbDeviceManager::EnsureConnectionWithDeviceManager() {
   if (device_manager_)
diff --git a/extensions/browser/api/usb/usb_device_manager.h b/extensions/browser/api/usb/usb_device_manager.h
index 12d0b193..b40550e 100644
--- a/extensions/browser/api/usb/usb_device_manager.h
+++ b/extensions/browser/api/usb/usb_device_manager.h
@@ -73,11 +73,11 @@
   const device::mojom::UsbDeviceInfo* GetDeviceInfo(const std::string& guid);
   bool UpdateActiveConfig(const std::string& guid, uint8_t config_value);
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
   void CheckAccess(
       const std::string& guid,
       device::mojom::UsbDeviceManager::CheckAccessCallback callback);
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
   void EnsureConnectionWithDeviceManager();
 
diff --git a/extensions/browser/api/usb/usb_manual_apitest.cc b/extensions/browser/api/usb/usb_manual_apitest.cc
index b10a599..1f2df21 100644
--- a/extensions/browser/api/usb/usb_manual_apitest.cc
+++ b/extensions/browser/api/usb/usb_manual_apitest.cc
@@ -11,7 +11,7 @@
 
 // TODO(1020591): The win7 bots do not seem to recognize the MANUAL_ prefix,
 // so we explicitly disable this test.
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #define MAYBE_MANUAL_ListInterfaces DISABLED_MANUAL_ListInterfaces
 #else
 #define MAYBE_MANUAL_ListInterfaces MANUAL_ListInterfaces
diff --git a/extensions/browser/api/web_request/web_request_permissions.cc b/extensions/browser/api/web_request/web_request_permissions.cc
index 5673efa..c5a9bce 100644
--- a/extensions/browser/api/web_request/web_request_permissions.cc
+++ b/extensions/browser/api/web_request/web_request_permissions.cc
@@ -336,7 +336,7 @@
   // Safebrowsing and Chrome Webstore URLs are always protected, i.e. also
   // for requests from common renderers.
   if (extension_urls::IsWebstoreUpdateUrl(url) ||
-      extension_urls::IsBlacklistUpdateUrl(url) ||
+      extension_urls::IsBlocklistUpdateUrl(url) ||
       extension_urls::IsSafeBrowsingUrl(url::Origin::Create(url),
                                         url.path_piece()) ||
       (url.DomainIs("chrome.google.com") &&
diff --git a/extensions/browser/app_window/app_window.cc b/extensions/browser/app_window/app_window.cc
index 723f941..bb3107e 100644
--- a/extensions/browser/app_window/app_window.cc
+++ b/extensions/browser/app_window/app_window.cc
@@ -60,7 +60,7 @@
 #include "ui/display/screen.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
 #include "components/prefs/pref_service.h"
 #include "extensions/browser/pref_names.h"
 #endif
@@ -447,11 +447,11 @@
 }
 
 bool AppWindow::ShouldShowStaleContentOnEviction(content::WebContents* source) {
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
   return true;
 #else
   return false;
-#endif  // defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 }
 
 bool AppWindow::OnMessageReceived(const IPC::Message& message,
@@ -509,7 +509,7 @@
   if (!native_app_window_)
     return;
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   // On Mac the user can change the window's fullscreen state. If that has
   // happened, update AppWindow's internal state.
   if (native_app_window_->IsFullscreen()) {
@@ -524,7 +524,7 @@
 
   SaveWindowPosition();
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   if (cached_always_on_top_ && !IsFullscreen() &&
       !native_app_window_->IsMaximized() &&
       !native_app_window_->IsMinimized()) {
@@ -615,7 +615,7 @@
   DCHECK_NE(FULLSCREEN_TYPE_NONE, type);
 
   if (enable) {
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
     // Do not enter fullscreen mode if disallowed by pref.
     // TODO(bartfab): Add a test once it becomes possible to simulate a user
     // gesture. http://crbug.com/174178
@@ -837,7 +837,7 @@
 }
 
 bool AppWindow::IntersectsWithTaskbar() const {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   display::Screen* screen = display::Screen::GetScreen();
   gfx::Rect window_bounds = native_app_window_->GetRestoredBounds();
   std::vector<display::Display> displays = screen->GetAllDisplays();
diff --git a/extensions/browser/app_window/app_window_browsertest.cc b/extensions/browser/app_window/app_window_browsertest.cc
index b7c82dc..b435027 100644
--- a/extensions/browser/app_window/app_window_browsertest.cc
+++ b/extensions/browser/app_window/app_window_browsertest.cc
@@ -19,7 +19,7 @@
 // at all, so the test may fail on certain window managers.
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
 #define MAYBE_FrameInsetsForDefaultFrame DISABLED_FrameInsetsForDefaultFrame
 #else
 #define MAYBE_FrameInsetsForDefaultFrame FrameInsetsForDefaultFrame
diff --git a/extensions/browser/browser_context_keyed_service_factories.cc b/extensions/browser/browser_context_keyed_service_factories.cc
index cd319b2..9b94db9 100644
--- a/extensions/browser/browser_context_keyed_service_factories.cc
+++ b/extensions/browser/browser_context_keyed_service_factories.cc
@@ -45,7 +45,7 @@
 #include "extensions/browser/renderer_startup_helper.h"
 #include "extensions/browser/updater/update_service_factory.h"
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
 #include "extensions/browser/api/clipboard/clipboard_api.h"
 #endif
 
@@ -70,7 +70,7 @@
   AudioAPI::GetFactoryInstance();
   BluetoothAPI::GetFactoryInstance();
   BluetoothPrivateAPI::GetFactoryInstance();
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
   ClipboardAPI::GetFactoryInstance();
 #endif
   api::BluetoothSocketEventDispatcher::GetFactoryInstance();
@@ -90,8 +90,8 @@
   HidDeviceManager::GetFactoryInstance();
   IdleManagerFactory::GetInstance();
   ManagementAPI::GetFactoryInstance();
-#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_WIN) || \
-    defined(OS_MAC)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN) || \
+    BUILDFLAG(IS_MAC)
   NetworkingPrivateEventRouterFactory::GetInstance();
 #endif
   PowerAPI::GetFactoryInstance();
diff --git a/extensions/browser/content_script_matching_browsertest.cc b/extensions/browser/content_script_matching_browsertest.cc
index 8b73cdab..7ec3418 100644
--- a/extensions/browser/content_script_matching_browsertest.cc
+++ b/extensions/browser/content_script_matching_browsertest.cc
@@ -324,7 +324,7 @@
 }
 
 // Flaky on MacOS since r622662. See https://crbug.com/921883
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 #define MAYBE_ContentScriptMatching_NotAllFrames \
   DISABLED_ContentScriptMatching_NotAllFrames
 #else
diff --git a/extensions/browser/content_script_tracker.cc b/extensions/browser/content_script_tracker.cc
index 61b7759..1d208d7 100644
--- a/extensions/browser/content_script_tracker.cc
+++ b/extensions/browser/content_script_tracker.cc
@@ -343,7 +343,7 @@
       if (!script_ids.empty()) {
         TRACE_EVENT_INSTANT(
             "extensions",
-            "ContentScriptTracker/DoesContentScriptMatch=true(guest)",
+            "ContentScriptTracker/DoContentScriptsMatch=true(guest)",
             ChromeTrackEvent::kRenderProcessHost, process,
             ChromeTrackEvent::kChromeExtensionId,
             ExtensionIdForTracing(extension.id()));
@@ -360,7 +360,7 @@
     if (DoContentScriptsMatch(manifest_scripts, frame, url)) {
       TRACE_EVENT_INSTANT(
           "extensions",
-          "ContentScriptTracker/DoesContentScriptMatch=true(manifest)",
+          "ContentScriptTracker/DoContentScriptsMatch=true(manifest)",
           ChromeTrackEvent::kRenderProcessHost, process,
           ChromeTrackEvent::kChromeExtensionId,
           ExtensionIdForTracing(extension.id()));
@@ -379,7 +379,7 @@
       if (DoContentScriptsMatch(dynamic_scripts, frame, url)) {
         TRACE_EVENT_INSTANT(
             "extensions",
-            "ContentScriptTracker/DoesContentScriptMatch=true(dynamic)",
+            "ContentScriptTracker/DoContentScriptsMatch=true(dynamic)",
             ChromeTrackEvent::kRenderProcessHost, process,
             ChromeTrackEvent::kChromeExtensionId,
             ExtensionIdForTracing(extension.id()));
@@ -390,7 +390,7 @@
 
   // Otherwise, no content script from `extension` can run in `frame` at `url`.
   TRACE_EVENT_INSTANT("extensions",
-                      "ContentScriptTracker/DoesContentScriptMatch=false",
+                      "ContentScriptTracker/DoContentScriptsMatch=false",
                       ChromeTrackEvent::kRenderProcessHost, process,
                       ChromeTrackEvent::kChromeExtensionId,
                       ExtensionIdForTracing(extension.id()));
diff --git a/extensions/browser/content_verifier/content_verifier_utils.h b/extensions/browser/content_verifier/content_verifier_utils.h
index f4d8e58..80488d7c 100644
--- a/extensions/browser/content_verifier/content_verifier_utils.h
+++ b/extensions/browser/content_verifier/content_verifier_utils.h
@@ -30,7 +30,7 @@
 
 // Returns true if this system/OS's file access is case sensitive.
 constexpr bool IsFileAccessCaseSensitive() {
-#if defined(OS_WIN) || defined(OS_MAC)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
   return false;
 #else
   return true;
@@ -40,7 +40,7 @@
 // Returns true if this system/OS ignores (.| )+ suffix in a filepath while
 // accessing the file.
 constexpr bool IsDotSpaceFilenameSuffixIgnored() {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   static_assert(!IsFileAccessCaseSensitive(),
                 "DotSpace suffix should only be ignored in case-insensitive"
                 "systems");
diff --git a/extensions/browser/extension_creator_filter.cc b/extensions/browser/extension_creator_filter.cc
index 7c592879..c49c4a81 100644
--- a/extensions/browser/extension_creator_filter.cc
+++ b/extensions/browser/extension_creator_filter.cc
@@ -13,7 +13,7 @@
 #include "build/build_config.h"
 #include "extensions/common/constants.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <windows.h>
 #endif
 
@@ -68,7 +68,7 @@
     return false;
   }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // It's correct that we use file_path, not base_name, here, because we
   // are working with the actual file.
   DWORD file_attributes = ::GetFileAttributes(file_path.value().c_str());
diff --git a/extensions/browser/extension_creator_filter_unittest.cc b/extensions/browser/extension_creator_filter_unittest.cc
index bc1de01..775ba2d 100644
--- a/extensions/browser/extension_creator_filter_unittest.cc
+++ b/extensions/browser/extension_creator_filter_unittest.cc
@@ -15,7 +15,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include <windows.h>
 #endif
 
@@ -157,7 +157,7 @@
   }
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 struct StringBooleanWithBooleanTestData {
   const base::FilePath::CharType* input_char;
   bool input_bool;
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index 66524e8..efdde56 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -419,7 +419,7 @@
       parent.value().length());
   if (base::FilePath::IsSeparator(retval[0]))
     retval = retval.substr(1);
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   return base::WideToUTF8(retval);
 #else
   return retval;
@@ -1400,7 +1400,7 @@
   // true, which signifies that the registry key was deleted or the pref file
   // no longer lists the extension).
   if (!external_uninstall && Manifest::IsExternalLocation(location)) {
-    ListPrefUpdate update(prefs_, kExternalUninstalls);
+    ListPrefUpdateDeprecated update(prefs_, kExternalUninstalls);
     update->Append(extension_id);
   }
 
@@ -2257,7 +2257,7 @@
   registry->RegisterStringPref(pref_names::kLastChromeVersion, std::string());
   registry->RegisterDictionaryPref(kInstallSignature);
   registry->RegisterListPref(kExternalUninstalls);
-#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
   registry->RegisterBooleanPref(pref_names::kChromeAppsEnabled, false);
 #endif
   registry->RegisterBooleanPref(pref_names::kU2fSecurityKeyApiEnabled, false);
@@ -2270,7 +2270,7 @@
   // defined.
   registry->RegisterIntegerPref(kCorruptedDisableCount.name, 0);
 
-#if !defined(OS_MAC)
+#if !BUILDFLAG(IS_MAC)
   registry->RegisterBooleanPref(pref_names::kAppFullscreenAllowed, true);
 #endif
 
@@ -2303,7 +2303,7 @@
 void ExtensionPrefs::SetExtensionPrefFromContainer(
     const char* pref,
     const ExtensionIdContainer& strings) {
-  ListPrefUpdate update(prefs_, pref);
+  ListPrefUpdateDeprecated update(prefs_, pref);
   base::Value* list_of_values = update.Get();
   list_of_values->ClearList();
   for (auto iter = strings.cbegin(); iter != strings.cend(); ++iter) {
@@ -2650,7 +2650,7 @@
   if (uninstalled_ids.empty())
     return;
 
-  ListPrefUpdate update(prefs_, kExternalUninstalls);
+  ListPrefUpdateDeprecated update(prefs_, kExternalUninstalls);
   base::Value* current_ids = update.Get();
   for (const auto& id : uninstalled_ids) {
     base::Value::ListView list = current_ids->GetList();
@@ -2723,7 +2723,8 @@
 
 bool ExtensionPrefs::ShouldInstallObsoleteComponentExtension(
     const std::string& extension_id) {
-  ListPrefUpdate update(prefs_, pref_names::kDeletedComponentExtensions);
+  ListPrefUpdateDeprecated update(prefs_,
+                                  pref_names::kDeletedComponentExtensions);
   base::Value* current_ids = update.Get();
   base::Value::ListView list = current_ids->GetList();
   auto existing_entry = std::find_if(
@@ -2736,7 +2737,8 @@
 void ExtensionPrefs::MarkObsoleteComponentExtensionAsRemoved(
     const std::string& extension_id,
     const ManifestLocation location) {
-  ListPrefUpdate update(prefs_, pref_names::kDeletedComponentExtensions);
+  ListPrefUpdateDeprecated update(prefs_,
+                                  pref_names::kDeletedComponentExtensions);
   base::Value* current_ids = update.Get();
   base::Value::ListView list = current_ids->GetList();
   auto existing_entry = std::find_if(
@@ -2750,7 +2752,7 @@
 }
 
 void ExtensionPrefs::ClearExternalUninstallBit(const ExtensionId& id) {
-  ListPrefUpdate update(prefs_, kExternalUninstalls);
+  ListPrefUpdateDeprecated update(prefs_, kExternalUninstalls);
   base::Value* current_ids = update.Get();
   current_ids->EraseListValueIf([&id](const base::Value& value) {
     return value.is_string() && value.GetString() == id;
diff --git a/extensions/browser/extension_protocols.cc b/extensions/browser/extension_protocols.cc
index 014276c..3ba4b67 100644
--- a/extensions/browser/extension_protocols.cc
+++ b/extensions/browser/extension_protocols.cc
@@ -451,11 +451,11 @@
   // On Fuchsia, some resources are served from read-only filesystems which
   // don't manage creation timestamps. Cache-control headers should still
   // be generated for those resources.
-#if !defined(OS_FUCHSIA)
+#if !BUILDFLAG(IS_FUCHSIA)
   if (last_modified_time.is_null()) {
     return;
   }
-#endif  // !defined(OS_FUCHSIA)
+#endif  // !BUILDFLAG(IS_FUCHSIA)
 
   // Hash the time and make an etag to avoid exposing the exact
   // user installation time of the extension.
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_interactive_uitest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_interactive_uitest.cc
index a5eaf55..494d5453 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_interactive_uitest.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_interactive_uitest.cc
@@ -116,7 +116,7 @@
 };
 
 // Test is flaky on Linux.  https://crbug.com/877627
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #define MAYBE_Fullscreen DISABLED_Fullscreen
 #else
 #define MAYBE_Fullscreen Fullscreen
@@ -128,7 +128,7 @@
 namespace {
 
 void WaitForFullscreenAnimation() {
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   const int delay_in_ms = 1500;
 #else
   const int delay_in_ms = 100;
diff --git a/extensions/browser/guest_view/web_view/web_view_apitest.cc b/extensions/browser/guest_view/web_view/web_view_apitest.cc
index bd593ce2..d735791 100644
--- a/extensions/browser/guest_view/web_view/web_view_apitest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_apitest.cc
@@ -368,7 +368,7 @@
 // The test launches an app with guest and closes the window on loadcommit. It
 // then launches the app window again. The process is repeated 3 times.
 // http://crbug.com/291278
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #define MAYBE_CloseOnLoadcommit DISABLED_CloseOnLoadcommit
 #else
 #define MAYBE_CloseOnLoadcommit CloseOnLoadcommit
@@ -640,7 +640,7 @@
 }
 
 // Crashes on Win only.  http://crbug.com/805903
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #define MAYBE_TestLoadStartLoadRedirect DISABLED_TestLoadStartLoadRedirect
 #else
 #define MAYBE_TestLoadStartLoadRedirect TestLoadStartLoadRedirect
@@ -773,7 +773,7 @@
   RunTest("testRemoveWebviewAfterNavigation", "web_view/apitest");
 }
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #define MAYBE_TestResizeWebviewResizesContent \
   DISABLED_TestResizeWebviewResizesContent
 #else
@@ -796,7 +796,7 @@
 }
 
 // Crashes on Win only.  http://crbug.com/805903
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #define MAYBE_TestWebRequestAPIWithHeaders DISABLED_TestWebRequestAPIWithHeaders
 #else
 #define MAYBE_TestWebRequestAPIWithHeaders TestWebRequestAPIWithHeaders
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.cc b/extensions/browser/guest_view/web_view/web_view_guest.cc
index 2e1b055..589703f 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_guest.cc
@@ -141,7 +141,7 @@
     case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
     case base::TERMINATION_STATUS_STILL_RUNNING:
       return "abnormal";
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
     case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
       return "oom killed";
 #endif
@@ -153,7 +153,7 @@
       return "crashed";
     case base::TERMINATION_STATUS_LAUNCH_FAILED:
       return "failed to launch";
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     case base::TERMINATION_STATUS_INTEGRITY_FAILURE:
       return "integrity failure";
 #endif
@@ -1069,7 +1069,7 @@
         blink::mojom::PointerLockResult::kUserRejected);
   }
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   if (event.GetModifiers() != blink::WebInputEvent::kMetaKey)
     return false;
 
diff --git a/extensions/browser/image_sanitizer_unittest.cc b/extensions/browser/image_sanitizer_unittest.cc
index c355673..178d96b 100644
--- a/extensions/browser/image_sanitizer_unittest.cc
+++ b/extensions/browser/image_sanitizer_unittest.cc
@@ -162,7 +162,7 @@
 
 TEST_F(ImageSanitizerTest, InvalidPathAbsolute) {
   base::FilePath normal_path(FILE_PATH_LITERAL("hello.png"));
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   base::FilePath absolute_path(FILE_PATH_LITERAL("c:\\Windows\\win32"));
 #else
   base::FilePath absolute_path(FILE_PATH_LITERAL("/usr/bin/root"));
diff --git a/extensions/browser/path_util.cc b/extensions/browser/path_util.cc
index bc89f31..0b8883d 100644
--- a/extensions/browser/path_util.cc
+++ b/extensions/browser/path_util.cc
@@ -15,7 +15,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/text/bytes_formatting.h"
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 #include <CoreFoundation/CoreFoundation.h>
 #include "base/mac/foundation_util.h"
 #endif
@@ -24,7 +24,7 @@
 namespace path_util {
 
 namespace {
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 
 // Retrieves the localized display name for the base name of the given path.
 // If the path is not localized, this will just return the base name.
@@ -43,7 +43,7 @@
   return result;
 }
 
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
 
 const base::FilePath::CharType kHomeShortcut[] = FILE_PATH_LITERAL("~");
 
@@ -74,7 +74,7 @@
   if (source_path == home_path)
     return display_path;
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   DCHECK(source_path.IsAbsolute());
 
   // Break down the incoming path into components, and grab the display name
@@ -99,11 +99,11 @@
   }
   DCHECK_EQ(actual_path.value(), source_path.value());
   return display_path;
-#else   // defined(OS_MAC)
+#else   // BUILDFLAG(IS_MAC)
   if (home_path.AppendRelativePath(source_path, &display_path))
     return display_path;
   return source_path;
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
 }
 
 void CalculateAndFormatExtensionDirectorySize(
@@ -118,7 +118,7 @@
 }
 
 base::FilePath ResolveHomeDirectory(const base::FilePath& path) {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   return path;
 #else
   const auto& value = path.value();
diff --git a/extensions/browser/path_util_unittest.cc b/extensions/browser/path_util_unittest.cc
index bdac4ffe..093aeb7 100644
--- a/extensions/browser/path_util_unittest.cc
+++ b/extensions/browser/path_util_unittest.cc
@@ -55,7 +55,7 @@
   const FilePath rel_path_with_tilde_no_separator(FILE_PATH_LITERAL("~foobar"));
 
 // This function is a no-op on Windows.
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   EXPECT_EQ(rel_path_with_tilde,
             path_util::ResolveHomeDirectory(rel_path_with_tilde));
 #else
diff --git a/extensions/browser/pref_names.cc b/extensions/browser/pref_names.cc
index a87dcef..beecbb8 100644
--- a/extensions/browser/pref_names.cc
+++ b/extensions/browser/pref_names.cc
@@ -5,6 +5,7 @@
 #include "extensions/browser/pref_names.h"
 
 #include "base/notreached.h"
+#include "build/build_config.h"
 
 namespace extensions {
 namespace pref_names {
@@ -47,7 +48,7 @@
 const char kToolbar[] = "extensions.toolbar";
 const char kDeletedComponentExtensions[] =
     "extensions.deleted_component_extensions";
-#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 extern const char kChromeAppsEnabled[] = "extensions.chrome_apps_enabled";
 #endif
 const char kU2fSecurityKeyApiEnabled[] =
diff --git a/extensions/browser/pref_names.h b/extensions/browser/pref_names.h
index 18d9df3..77de19a 100644
--- a/extensions/browser/pref_names.h
+++ b/extensions/browser/pref_names.h
@@ -101,7 +101,7 @@
 // uninstalled/removed and should not be reloaded.
 extern const char kDeletedComponentExtensions[];
 
-#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 // A preference for whether Chrome Apps should be allowed. The default depends
 // on the ChromeAppsDeprecation feature flag, and this pref can extend support
 // for Chrome Apps by enterprise policy.
diff --git a/extensions/browser/sandboxed_unpacker_unittest.cc b/extensions/browser/sandboxed_unpacker_unittest.cc
index 8faca2d6..9448dbf 100644
--- a/extensions/browser/sandboxed_unpacker_unittest.cc
+++ b/extensions/browser/sandboxed_unpacker_unittest.cc
@@ -5,13 +5,13 @@
 #include "extensions/browser/sandboxed_unpacker.h"
 
 #include <memory>
+#include <tuple>
 
 #include "base/base64.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/files/file_util.h"
-#include "base/ignore_result.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/path_service.h"
@@ -501,7 +501,7 @@
   // receiver, effectively simulating a crashy service process.
   unzip::SetUnzipperLaunchOverrideForTesting(base::BindRepeating([]() -> auto {
     mojo::PendingRemote<unzip::mojom::Unzipper> remote;
-    ignore_result(remote.InitWithNewPipeAndPassReceiver());
+    std::ignore = remote.InitWithNewPipeAndPassReceiver();
     return remote;
   }));
 
diff --git a/extensions/common/api/declarative/declarative_manifest_data.cc b/extensions/common/api/declarative/declarative_manifest_data.cc
index 0d6d948..1078f85 100644
--- a/extensions/common/api/declarative/declarative_manifest_data.cc
+++ b/extensions/common/api/declarative/declarative_manifest_data.cc
@@ -125,14 +125,13 @@
   ErrorBuilder error_builder(error);
   std::unique_ptr<DeclarativeManifestData> result(
       new DeclarativeManifestData());
-  const base::ListValue* list = nullptr;
-  if (!value.GetAsList(&list)) {
+  if (!value.is_list()) {
     error_builder.Append("'event_rules' expected list, got %s",
                          base::Value::GetTypeName(value.type()));
     return nullptr;
   }
 
-  for (const auto& element : list->GetList()) {
+  for (const auto& element : value.GetList()) {
     const base::DictionaryValue* dict = nullptr;
     if (!element.GetAsDictionary(&dict)) {
       error_builder.Append("expected dictionary, got %s",
diff --git a/extensions/common/component_extension_url_pattern_unittest.cc b/extensions/common/component_extension_url_pattern_unittest.cc
index 6fcab7e..2dd7499 100644
--- a/extensions/common/component_extension_url_pattern_unittest.cc
+++ b/extensions/common/component_extension_url_pattern_unittest.cc
@@ -36,7 +36,7 @@
 
 TEST(ComponentExtensionUrlPattern, ChromeVoxExtension) {
   // The ChromeVox extension has access to "chrome" scheme URLs through the
-  // "<all_urls>" meta-pattern because it's whitelisted.
+  // "<all_urls>" meta-pattern because it's allowlisted.
   auto all_urls = ExtensionBuilder("all urls")
                       .AddPermission("<all_urls>")
                       .SetLocation(mojom::ManifestLocation::kComponent)
diff --git a/extensions/common/constants.cc b/extensions/common/constants.cc
index 6386c124..306ae76 100644
--- a/extensions/common/constants.cc
+++ b/extensions/common/constants.cc
@@ -6,6 +6,7 @@
 
 #include "base/cxx17_backports.h"
 #include "base/strings/string_piece.h"
+#include "build/build_config.h"
 #include "build/chromecast_buildflags.h"
 #include "build/chromeos_buildflags.h"
 
@@ -117,7 +118,7 @@
 
 namespace extension_misc {
 
-#if defined(OS_CHROMEOS) || BUILDFLAG(IS_CHROMECAST)
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_CHROMECAST)
 // The extension id for the built-in component extension.
 const char kChromeVoxExtensionId[] = "mndnfokpggljbaajbnioimlmbfngpief";
 #else
diff --git a/extensions/common/content_script_injection_url_getter.cc b/extensions/common/content_script_injection_url_getter.cc
index d95fd70..a197636f 100644
--- a/extensions/common/content_script_injection_url_getter.cc
+++ b/extensions/common/content_script_injection_url_getter.cc
@@ -7,6 +7,7 @@
 #include "base/containers/contains.h"
 #include "base/containers/flat_set.h"
 #include "base/notreached.h"
+#include "base/trace_event/typed_macros.h"
 #include "url/scheme_host_port.h"
 
 namespace extensions {
@@ -35,23 +36,53 @@
       url::kFileSystemScheme,
   };
 
+  // TODO(https://crbug.com/1212918): Consider reducing tracing instrumentation
+  // in the main function bodu and in the lambda below (once the bug is
+  // understood and fixed).
   auto should_consider_origin = [&document_url, match_origin_as_fallback]() {
+    bool result = false;
     switch (match_origin_as_fallback) {
-      case MatchOriginAsFallbackBehavior::kNever:
-        return false;
-      case MatchOriginAsFallbackBehavior::kMatchForAboutSchemeAndClimbTree:
-        return document_url.SchemeIs(url::kAboutScheme);
-      case MatchOriginAsFallbackBehavior::kAlways:
-        return base::Contains(kAllowedSchemesToMatchOriginAsFallback,
-                              document_url.scheme());
+      case MatchOriginAsFallbackBehavior::kNever: {
+        TRACE_EVENT_INSTANT("extensions",
+                            "ContentScriptInjectionUrlGetter::Get/"
+                            "should_consider_origin: origin-never");
+        result = false;
+        break;
+      }
+      case MatchOriginAsFallbackBehavior::kMatchForAboutSchemeAndClimbTree: {
+        TRACE_EVENT_INSTANT("extensions",
+                            "ContentScriptInjectionUrlGetter::Get/"
+                            "should_consider_origin: origin-climb");
+        result = document_url.SchemeIs(url::kAboutScheme);
+        break;
+      }
+      case MatchOriginAsFallbackBehavior::kAlways: {
+        TRACE_EVENT_INSTANT("extensions",
+                            "ContentScriptInjectionUrlGetter::Get/"
+                            "should_consider_origin: origin-always");
+        result = base::Contains(kAllowedSchemesToMatchOriginAsFallback,
+                                document_url.scheme());
+        break;
+      }
     }
-
-    NOTREACHED();
+    if (result) {
+      TRACE_EVENT_INSTANT("extensions",
+                          "ContentScriptInjectionUrlGetter::Get/"
+                          "should_consider_origin=true");
+    } else {
+      TRACE_EVENT_INSTANT("extensions",
+                          "ContentScriptInjectionUrlGetter::Get/"
+                          "should_consider_origin=false");
+    }
+    return result;
   };
 
   // If we don't need to consider the origin, we're done.
-  if (!should_consider_origin())
+  if (!should_consider_origin()) {
+    TRACE_EVENT_INSTANT(
+        "extensions", "ContentScriptInjectionUrlGetter::Get/!consider-origin");
     return document_url;
+  }
 
   // Get the security origin for the `frame`. For about: frames, this is the
   // origin of that of the controlling frame - e.g., an about:blank frame on
@@ -65,8 +96,11 @@
   // When there's no valid tuple (which can happen in the case of e.g. a
   // browser-initiated navigation to an opaque URL), there's no origin to
   // fallback to. Bail.
-  if (!tuple_or_precursor_tuple.IsValid())
+  if (!tuple_or_precursor_tuple.IsValid()) {
+    TRACE_EVENT_INSTANT("extensions",
+                        "ContentScriptInjectionUrlGetter::Get/invalid-tuple");
     return document_url;
+  }
 
   const url::Origin origin_or_precursor_origin =
       url::Origin::Create(tuple_or_precursor_tuple.GetURL());
@@ -74,6 +108,9 @@
   if (!allow_inaccessible_parents &&
       !frame.CanAccess(origin_or_precursor_origin)) {
     // The `frame` can't access its precursor. Bail.
+    TRACE_EVENT_INSTANT(
+        "extensions",
+        "ContentScriptInjectionUrlGetter::Get/no-precursor-access");
     return document_url;
   }
 
@@ -87,6 +124,9 @@
 
   if (match_origin_as_fallback == MatchOriginAsFallbackBehavior::kAlways) {
     // The easy case! We use the origin directly. We're done.
+    TRACE_EVENT_INSTANT(
+        "extensions",
+        "ContentScriptInjectionUrlGetter::Get/origin-or-precursor");
     return origin_or_precursor_origin.GetURL();
   }
 
@@ -114,13 +154,19 @@
     // We reached the end of the ancestral chain without finding a valid parent,
     // or found a remote web frame (in which case, it's a different origin).
     // Bail and use the original URL.
-    if (!parent)
+    if (!parent) {
+      TRACE_EVENT_INSTANT(
+          "extensions", "ContentScriptInjectionUrlGetter::Get/no-more-parents");
       return document_url;
+    }
 
     // Avoid an infinite loop - see https://crbug.com/568432 and
     // https://crbug.com/883526.
-    if (base::Contains(already_visited_frame_ids, parent->GetId()))
+    if (base::Contains(already_visited_frame_ids, parent->GetId())) {
+      TRACE_EVENT_INSTANT("extensions",
+                          "ContentScriptInjectionUrlGetter::Get/infinite-loop");
       return document_url;
+    }
 
     url::SchemeHostPort parent_tuple_or_precursor_tuple =
         url::Origin(parent->GetOrigin()).GetTupleOrPrecursorTupleIfOpaque();
@@ -145,6 +191,8 @@
       // example.com, but the parent tuple origin is a.com.
       // Note that usually, this would have bailed earlier with a remote frame,
       // but it may not if we're at the process limit.
+      TRACE_EVENT_INSTANT("extensions",
+                          "ContentScriptInjectionUrlGetter::Get/tuple-diff");
       return document_url;
     }
 
@@ -153,6 +201,9 @@
     // disallowdocumentaccess attribute on iframe.
     if (!allow_inaccessible_parents && !frame.CanAccess(*parent)) {
       // The frame can't access its precursor. Bail.
+      TRACE_EVENT_INSTANT(
+          "extensions",
+          "ContentScriptInjectionUrlGetter::Get/no-parent-access");
       return document_url;
     }
 
@@ -164,6 +215,8 @@
   // We should know that the frame can access the parent document (unless we
   // explicitly allow it not to), since it has the same tuple origin as the
   // frame, and we checked the frame access above.
+  TRACE_EVENT_INSTANT("extensions",
+                      "ContentScriptInjectionUrlGetter::Get/parent-url");
   DCHECK(allow_inaccessible_parents || frame.CanAccess(parent->GetOrigin()));
   return parent_url;
 }
diff --git a/extensions/common/csp_validator.cc b/extensions/common/csp_validator.cc
index c9c6511..11f6419 100644
--- a/extensions/common/csp_validator.cc
+++ b/extensions/common/csp_validator.cc
@@ -195,7 +195,7 @@
   if (!is_wildcard_subdomain || !should_check_rcd)
     return true;
 
-  // Allow *.googleapis.com to be whitelisted for backwards-compatibility.
+  // Allow *.googleapis.com to be allowlisted for backwards-compatibility.
   // (crbug.com/409952)
   if (host == "googleapis.com")
     return true;
@@ -240,7 +240,7 @@
     std::string source_lower = base::ToLowerASCII(source_literal);
     bool is_secure_csp_token = false;
 
-    // We might need to relax this whitelist over time.
+    // We might need to relax this allowlist over time.
     if (source_lower == kSelfSource || source_lower == kNoneSource ||
         source_lower == kWasmEvalSource || source_lower == "blob:" ||
         source_lower == "filesystem:" ||
diff --git a/extensions/common/extension_resource_unittest.cc b/extensions/common/extension_resource_unittest.cc
index 26745fa..bbc9be0 100644
--- a/extensions/common/extension_resource_unittest.cc
+++ b/extensions/common/extension_resource_unittest.cc
@@ -62,7 +62,7 @@
   ASSERT_EQ(1, base::WriteFile(inner_file, "X", 1));
   std::string extension_id = crx_file::id_util::GenerateId("test");
 
-#if defined(OS_POSIX)
+#if BUILDFLAG(IS_POSIX)
   base::FilePath symlink_file = inner_dir.AppendASCII("symlink");
   base::CreateSymbolicLink(
       base::FilePath().AppendASCII("..").AppendASCII("outer"),
@@ -103,7 +103,7 @@
   r4a.set_follow_symlinks_anywhere();
   EXPECT_TRUE(r4a.GetFilePath().empty());
 
-#if defined(OS_POSIX)
+#if BUILDFLAG(IS_POSIX)
   // The non-packing extension should also not be able to access a resource that
   // symlinks out of the directory.
   ExtensionResource r5(extension_id, inner_dir,
diff --git a/extensions/common/extension_set_unittest.cc b/extensions/common/extension_set_unittest.cc
index 95e45e7..ce0f0cc 100644
--- a/extensions/common/extension_set_unittest.cc
+++ b/extensions/common/extension_set_unittest.cc
@@ -22,7 +22,7 @@
 scoped_refptr<Extension> CreateTestExtension(const std::string& name,
                                              const std::string& launch_url,
                                              const std::string& extent) {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   base::FilePath path(FILE_PATH_LITERAL("c:\\"));
 #else
   base::FilePath path(FILE_PATH_LITERAL("/"));
diff --git a/extensions/common/extension_urls.cc b/extensions/common/extension_urls.cc
index 6491d8a..f38d169 100644
--- a/extensions/common/extension_urls.cc
+++ b/extensions/common/extension_urls.cc
@@ -78,10 +78,10 @@
           update_url.path_piece() == store_url.path_piece());
 }
 
-bool IsBlacklistUpdateUrl(const GURL& url) {
+bool IsBlocklistUpdateUrl(const GURL& url) {
   extensions::ExtensionsClient* client = extensions::ExtensionsClient::Get();
   if (client)
-    return client->IsBlacklistUpdateURL(url);
+    return client->IsBlocklistUpdateURL(url);
   return false;
 }
 
diff --git a/extensions/common/extension_urls.h b/extensions/common/extension_urls.h
index 556eb66b..58e1edc 100644
--- a/extensions/common/extension_urls.h
+++ b/extensions/common/extension_urls.h
@@ -67,8 +67,8 @@
 // and path, not scheme, query, etc.)
 bool IsWebstoreUpdateUrl(const GURL& update_url);
 
-// Returns true if the URL points to an extension blacklist.
-bool IsBlacklistUpdateUrl(const GURL& url);
+// Returns true if the URL points to an extension blocklist.
+bool IsBlocklistUpdateUrl(const GURL& url);
 
 // Returns true if the origin points to an URL used for safebrowsing.
 // TODO(devlin): Update other methods to also take an url::Origin?
diff --git a/extensions/common/extensions_client.h b/extensions/common/extensions_client.h
index 6a1dcb60..2ae3a098 100644
--- a/extensions/common/extensions_client.h
+++ b/extensions/common/extensions_client.h
@@ -118,8 +118,8 @@
   virtual const GURL& GetWebstoreUpdateURL() const = 0;
 
   // Returns a flag indicating whether or not a given URL is a valid
-  // extension blacklist URL.
-  virtual bool IsBlacklistUpdateURL(const GURL& url) const = 0;
+  // extension blocklist URL.
+  virtual bool IsBlocklistUpdateURL(const GURL& url) const = 0;
 
   // Returns the set of file paths corresponding to any images within an
   // extension's contents that may be displayed directly within the browser UI
diff --git a/extensions/common/feature_switch.cc b/extensions/common/feature_switch.cc
index 63e74ecf..efdc713 100644
--- a/extensions/common/feature_switch.cc
+++ b/extensions/common/feature_switch.cc
@@ -22,7 +22,7 @@
         // Intentionally no flag since turning this off outside of tests
         // is a security risk.
         prompt_for_external_extensions(nullptr,
-#if defined(OS_WIN) || defined(OS_MAC)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
                                        FeatureSwitch::DEFAULT_ENABLED),
 #else
                                        FeatureSwitch::DEFAULT_DISABLED),
diff --git a/extensions/common/features/feature.cc b/extensions/common/features/feature.cc
index 56b627b6..62a7ad8 100644
--- a/extensions/common/features/feature.cc
+++ b/extensions/common/features/feature.cc
@@ -20,19 +20,19 @@
 // static
 Feature::Platform Feature::GetCurrentPlatform() {
 // TODO(https://crbug.com/1052397): For readability, this should become
-// defined(OS_CHROMEOS) && BUILDFLAG(IS_CHROMEOS_LACROS). The second conditional
-// should be defined(OS_CHROMEOS) && BUILDFLAG(IS_CHROMEOS_ASH).
+// BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(IS_CHROMEOS_LACROS). The second
+// conditional should be BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(IS_CHROMEOS_ASH).
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
   return LACROS_PLATFORM;
 #elif BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
   return CHROMEOS_PLATFORM;
-#elif defined(OS_LINUX)
+#elif BUILDFLAG(IS_LINUX)
   return LINUX_PLATFORM;
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   return MACOSX_PLATFORM;
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   return WIN_PLATFORM;
-#elif defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_FUCHSIA)
   return FUCHSIA_PLATFORM;
 #else
   return UNSPECIFIED_PLATFORM;
diff --git a/extensions/common/features/feature.h b/extensions/common/features/feature.h
index 4b3a48f..e0af95b 100644
--- a/extensions/common/features/feature.h
+++ b/extensions/common/features/feature.h
@@ -57,7 +57,7 @@
   // why not.
   enum AvailabilityResult {
     IS_AVAILABLE,
-    NOT_FOUND_IN_WHITELIST,
+    NOT_FOUND_IN_ALLOWLIST,
     INVALID_URL,
     INVALID_TYPE,
     INVALID_CONTEXT,
@@ -68,7 +68,7 @@
     INVALID_SESSION_TYPE,
     NOT_PRESENT,
     UNSUPPORTED_CHANNEL,
-    FOUND_IN_BLACKLIST,
+    FOUND_IN_BLOCKLIST,
     MISSING_COMMAND_LINE_SWITCH,
     FEATURE_FLAG_DISABLED,
   };
diff --git a/extensions/common/features/feature_provider_unittest.cc b/extensions/common/features/feature_provider_unittest.cc
index 20995e3..10b5f77c 100644
--- a/extensions/common/features/feature_provider_unittest.cc
+++ b/extensions/common/features/feature_provider_unittest.cc
@@ -114,19 +114,19 @@
                                        GURL())
                 .result());
 
-  // A permission only available to whitelisted extensions returns availability
-  // NOT_FOUND_IN_WHITELIST.
+  // A permission only available to allowlisted extensions returns availability
+  // NOT_FOUND_IN_ALLOWLIST.
   // TODO(https://crbug.com/1251347): Port //device/bluetooth to Fuchsia to
   // enable bluetooth extensions.
-#if !defined(OS_FUCHSIA)
+#if !BUILDFLAG(IS_FUCHSIA)
   feature = provider->GetFeature("bluetoothPrivate");
   ASSERT_TRUE(feature);
-  EXPECT_EQ(Feature::NOT_FOUND_IN_WHITELIST,
+  EXPECT_EQ(Feature::NOT_FOUND_IN_ALLOWLIST,
             feature
                 ->IsAvailableToContext(app.get(), Feature::UNSPECIFIED_CONTEXT,
                                        GURL())
                 .result());
-#endif  // !defined(OS_FUCHSIA)
+#endif  // !BUILDFLAG(IS_FUCHSIA)
 
   // A permission that isn't part of the manifest returns NOT_PRESENT.
   feature = provider->GetFeature("serial");
diff --git a/extensions/common/features/simple_feature.cc b/extensions/common/features/simple_feature.cc
index e92eaf5..7e2c7b08 100644
--- a/extensions/common/features/simple_feature.cc
+++ b/extensions/common/features/simple_feature.cc
@@ -50,7 +50,7 @@
   }
   std::string hashed_id;
 };
-// A singleton copy of the --whitelisted-extension-id so that we don't need to
+// A singleton copy of the --allowlisted-extension-id so that we don't need to
 // copy it from the CommandLine each time.
 base::LazyInstance<AllowlistInfo>::Leaky g_allowlist_info =
     LAZY_INSTANCE_INITIALIZER;
@@ -309,8 +309,8 @@
   switch (result) {
     case IS_AVAILABLE:
       return std::string();
-    case NOT_FOUND_IN_WHITELIST:
-    case FOUND_IN_BLACKLIST:
+    case NOT_FOUND_IN_ALLOWLIST:
+    case FOUND_IN_BLOCKLIST:
       return base::StringPrintf(
           "'%s' is not allowed for specified extension ID.",
           name().c_str());
@@ -636,7 +636,7 @@
   }
 
   if (!blocklist_.empty() && IsIdInBlocklist(hashed_id))
-    return CreateAvailability(FOUND_IN_BLACKLIST);
+    return CreateAvailability(FOUND_IN_BLOCKLIST);
 
   // TODO(benwells): don't grant all component extensions.
   // See http://crbug.com/370375 for more details.
@@ -648,7 +648,7 @@
 
   if (!allowlist_.empty() && !IsIdInAllowlist(hashed_id) &&
       !IsAllowlistedForTest(hashed_id)) {
-    return CreateAvailability(NOT_FOUND_IN_WHITELIST);
+    return CreateAvailability(NOT_FOUND_IN_ALLOWLIST);
   }
 
   if (location_ && !MatchesManifestLocation(location) &&
diff --git a/extensions/common/features/simple_feature.h b/extensions/common/features/simple_feature.h
index fece67e..b8df3ea2 100644
--- a/extensions/common/features/simple_feature.h
+++ b/extensions/common/features/simple_feature.h
@@ -31,7 +31,7 @@
 
 class SimpleFeature : public Feature {
  public:
-  // Used by tests to override the cached --whitelisted-extension-id.
+  // Used by tests to override the cached --allowlisted-extension-id.
   // NOTE: Not thread-safe! This is because it sets extension id on global
   // singleton during its construction and destruction.
   class ScopedThreadUnsafeAllowlistForTest {
diff --git a/extensions/common/features/simple_feature_unittest.cc b/extensions/common/features/simple_feature_unittest.cc
index 23b99315..d48d35713 100644
--- a/extensions/common/features/simple_feature_unittest.cc
+++ b/extensions/common/features/simple_feature_unittest.cc
@@ -141,14 +141,14 @@
                                        Feature::UNSPECIFIED_PLATFORM)
                 .result());
 
-  EXPECT_EQ(Feature::NOT_FOUND_IN_WHITELIST,
+  EXPECT_EQ(Feature::NOT_FOUND_IN_ALLOWLIST,
             feature
                 .IsAvailableToManifest(kIdBaz, Manifest::TYPE_UNKNOWN,
                                        ManifestLocation::kInvalidLocation, -1,
                                        Feature::UNSPECIFIED_PLATFORM)
                 .result());
   EXPECT_EQ(
-      Feature::NOT_FOUND_IN_WHITELIST,
+      Feature::NOT_FOUND_IN_ALLOWLIST,
       feature
           .IsAvailableToManifest(HashedExtensionId(), Manifest::TYPE_UNKNOWN,
                                  ManifestLocation::kInvalidLocation, -1,
@@ -157,7 +157,7 @@
 
   feature.set_extension_types({Manifest::TYPE_LEGACY_PACKAGED_APP});
   EXPECT_EQ(
-      Feature::NOT_FOUND_IN_WHITELIST,
+      Feature::NOT_FOUND_IN_ALLOWLIST,
       feature
           .IsAvailableToManifest(kIdBaz, Manifest::TYPE_LEGACY_PACKAGED_APP,
                                  ManifestLocation::kInvalidLocation, -1,
@@ -188,14 +188,14 @@
                                        ManifestLocation::kInvalidLocation, -1,
                                        Feature::UNSPECIFIED_PLATFORM)
                 .result());
-  EXPECT_EQ(Feature::NOT_FOUND_IN_WHITELIST,
+  EXPECT_EQ(Feature::NOT_FOUND_IN_ALLOWLIST,
             feature
                 .IsAvailableToManifest(
                     HashedExtensionId("slightlytoooolongforanextensionid"),
                     Manifest::TYPE_UNKNOWN, ManifestLocation::kInvalidLocation,
                     -1, Feature::UNSPECIFIED_PLATFORM)
                 .result());
-  EXPECT_EQ(Feature::NOT_FOUND_IN_WHITELIST,
+  EXPECT_EQ(Feature::NOT_FOUND_IN_ALLOWLIST,
             feature
                 .IsAvailableToManifest(
                     HashedExtensionId("tooshortforanextensionid"),
@@ -211,13 +211,13 @@
   SimpleFeature feature;
   feature.set_blocklist({kIdFoo.value().c_str(), kIdBar.value().c_str()});
 
-  EXPECT_EQ(Feature::FOUND_IN_BLACKLIST,
+  EXPECT_EQ(Feature::FOUND_IN_BLOCKLIST,
             feature
                 .IsAvailableToManifest(kIdFoo, Manifest::TYPE_UNKNOWN,
                                        ManifestLocation::kInvalidLocation, -1,
                                        Feature::UNSPECIFIED_PLATFORM)
                 .result());
-  EXPECT_EQ(Feature::FOUND_IN_BLACKLIST,
+  EXPECT_EQ(Feature::FOUND_IN_BLOCKLIST,
             feature
                 .IsAvailableToManifest(kIdBar, Manifest::TYPE_UNKNOWN,
                                        ManifestLocation::kInvalidLocation, -1,
@@ -248,14 +248,14 @@
 
   feature.set_blocklist({kIdFooHashed.c_str()});
 
-  EXPECT_EQ(Feature::FOUND_IN_BLACKLIST,
+  EXPECT_EQ(Feature::FOUND_IN_BLOCKLIST,
             feature
                 .IsAvailableToManifest(HashedExtensionId(kIdFoo),
                                        Manifest::TYPE_UNKNOWN,
                                        ManifestLocation::kInvalidLocation, -1,
                                        Feature::UNSPECIFIED_PLATFORM)
                 .result());
-  EXPECT_NE(Feature::FOUND_IN_BLACKLIST,
+  EXPECT_NE(Feature::FOUND_IN_BLOCKLIST,
             feature
                 .IsAvailableToManifest(HashedExtensionId(kIdFooHashed),
                                        Manifest::TYPE_UNKNOWN,
@@ -337,9 +337,12 @@
   ASSERT_TRUE(extension.get());
 
   feature.set_allowlist({"monkey"});
-  EXPECT_EQ(Feature::NOT_FOUND_IN_WHITELIST, feature.IsAvailableToContext(
-      extension.get(), Feature::BLESSED_EXTENSION_CONTEXT,
-      Feature::CHROMEOS_PLATFORM).result());
+  EXPECT_EQ(Feature::NOT_FOUND_IN_ALLOWLIST,
+            feature
+                .IsAvailableToContext(extension.get(),
+                                      Feature::BLESSED_EXTENSION_CONTEXT,
+                                      Feature::CHROMEOS_PLATFORM)
+                .result());
   feature.set_allowlist({});
 
   feature.set_extension_types({Manifest::TYPE_THEME});
diff --git a/extensions/common/file_util_unittest.cc b/extensions/common/file_util_unittest.cc
index 7130547e..c59f6fce 100644
--- a/extensions/common/file_util_unittest.cc
+++ b/extensions/common/file_util_unittest.cc
@@ -285,7 +285,7 @@
 // These tests do not work on Windows, because it is illegal to create a
 // file/directory with a Windows reserved name. Because we cannot create a
 // file that will cause the test to fail, let's skip the test.
-#if !defined(OS_WIN)
+#if !BUILDFLAG(IS_WIN)
 TEST_F(FileUtilTest, CheckIllegalFilenamesDirectoryWindowsReserved) {
   base::ScopedTempDir temp;
   ASSERT_TRUE(temp.CreateUniqueTempDir());
@@ -619,7 +619,7 @@
     {URL_PREFIX "%C3%9Cber.html",
      "\xC3\x9C"
      "ber.html"},
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     {URL_PREFIX "C%3A/simple.html", ""},
 #endif
     {URL_PREFIX "////simple.html", "simple.html"},
diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc
index 2fd9a74..e0a17070 100644
--- a/extensions/common/manifest_constants.cc
+++ b/extensions/common/manifest_constants.cc
@@ -334,7 +334,7 @@
     "'*': Insecure CSP value \"*\" in directive '*'.";
 const char kInvalidCSPMissingSecureSrc[] =
     "'*': CSP directive '*' must be specified (either explicitly, or "
-    "implicitly via 'default-src') and must whitelist only secure resources.";
+    "implicitly via 'default-src') and must allowlist only secure resources.";
 const char kInvalidDefaultLocale[] =
     "Invalid value for default locale - locale name must be a string.";
 const char16_t kInvalidDefaultLocale16[] =
diff --git a/extensions/common/manifest_handlers/externally_connectable_unittest.cc b/extensions/common/manifest_handlers/externally_connectable_unittest.cc
index a24e1f0..d1640a4 100644
--- a/extensions/common/manifest_handlers/externally_connectable_unittest.cc
+++ b/extensions/common/manifest_handlers/externally_connectable_unittest.cc
@@ -248,24 +248,24 @@
   EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
 }
 
-TEST_F(ExternallyConnectableTest, AllURLsNotWhitelisted) {
+TEST_F(ExternallyConnectableTest, AllURLsNotAllowlisted) {
   scoped_refptr<Extension> extension = LoadAndExpectSuccess(
-      "externally_connectable_all_urls_not_whitelisted.json");
+      "externally_connectable_all_urls_not_allowlisted.json");
   ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
   EXPECT_FALSE(info->matches.MatchesAllURLs());
 }
 
-TEST_F(ExternallyConnectableTest, AllHttpsURLsNotWhitelisted) {
+TEST_F(ExternallyConnectableTest, AllHttpsURLsNotAllowlisted) {
   scoped_refptr<Extension> extension = LoadAndExpectSuccess(
-      "externally_connectable_all_https_urls_not_whitelisted.json");
+      "externally_connectable_all_https_urls_not_allowlisted.json");
   ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
   EXPECT_FALSE(info->matches.MatchesAllURLs());
   EXPECT_FALSE(info->matches.MatchesURL(GURL("https://example.com")));
 }
 
-TEST_F(ExternallyConnectableTest, AllURLsWhitelisted) {
+TEST_F(ExternallyConnectableTest, AllURLsAllowlisted) {
   scoped_refptr<Extension> extension =
-      LoadAndExpectSuccess("externally_connectable_all_urls_whitelisted.json");
+      LoadAndExpectSuccess("externally_connectable_all_urls_allowlisted.json");
   ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
   EXPECT_TRUE(info->matches.MatchesAllURLs());
   URLPattern pattern(URLPattern::SCHEME_ALL, "<all_urls>");
@@ -274,9 +274,9 @@
   EXPECT_TRUE(info->matches.MatchesURL(GURL("http://build.chromium.org")));
 }
 
-TEST_F(ExternallyConnectableTest, AllHttpsURLsWhitelisted) {
+TEST_F(ExternallyConnectableTest, AllHttpsURLsAllowlisted) {
   scoped_refptr<Extension> extension = LoadAndExpectSuccess(
-      "externally_connectable_all_https_urls_whitelisted.json");
+      "externally_connectable_all_https_urls_allowlisted.json");
   ExternallyConnectableInfo* info = GetExternallyConnectableInfo(extension);
 
   URLPattern all_urls_pattern(URLPattern::SCHEME_ALL, "<all_urls>");
diff --git a/extensions/common/manifest_handlers/oauth2_manifest_unittest.cc b/extensions/common/manifest_handlers/oauth2_manifest_unittest.cc
index 77e93b3..a29b440 100644
--- a/extensions/common/manifest_handlers/oauth2_manifest_unittest.cc
+++ b/extensions/common/manifest_handlers/oauth2_manifest_unittest.cc
@@ -57,7 +57,7 @@
   };
 
   base::Value CreateManifest(AutoApproveValue auto_approve,
-                             bool extension_id_whitelisted,
+                             bool extension_id_allowlisted,
                              ClientIdValue client_id) {
     base::Value manifest = base::test::ParseJson(R"({
           "name": "test",
@@ -95,7 +95,7 @@
         manifest.SetPath(GetOauth2KeyPath(OAuth2Info::kClientId),
                          base::Value(""));
     }
-    if (extension_id_whitelisted) {
+    if (extension_id_allowlisted) {
       manifest.SetPath(TokenizeDictionaryPath(keys::kKey),
                        base::Value(kExtensionKey));
     }
@@ -187,7 +187,7 @@
   }
 }
 
-TEST_F(OAuth2ManifestTest, AutoApproveNotSetExtensionNotOnWhitelist) {
+TEST_F(OAuth2ManifestTest, AutoApproveNotSetExtensionNotOnAllowlist) {
   base::Value ext_manifest =
       CreateManifest(AUTO_APPROVE_NOT_SET, false, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
@@ -197,7 +197,7 @@
   EXPECT_FALSE(OAuth2ManifestHandler::GetOAuth2Info(*extension).auto_approve);
 }
 
-TEST_F(OAuth2ManifestTest, AutoApproveFalseExtensionNotOnWhitelist) {
+TEST_F(OAuth2ManifestTest, AutoApproveFalseExtensionNotOnAllowlist) {
   base::Value ext_manifest =
       CreateManifest(AUTO_APPROVE_FALSE, false, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
@@ -210,7 +210,7 @@
   EXPECT_FALSE(OAuth2ManifestHandler::GetOAuth2Info(*extension).auto_approve);
 }
 
-TEST_F(OAuth2ManifestTest, AutoApproveTrueExtensionNotOnWhitelist) {
+TEST_F(OAuth2ManifestTest, AutoApproveTrueExtensionNotOnAllowlist) {
   base::Value ext_manifest =
       CreateManifest(AUTO_APPROVE_TRUE, false, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
@@ -223,7 +223,7 @@
   EXPECT_FALSE(OAuth2ManifestHandler::GetOAuth2Info(*extension).auto_approve);
 }
 
-TEST_F(OAuth2ManifestTest, AutoApproveInvalidExtensionNotOnWhitelist) {
+TEST_F(OAuth2ManifestTest, AutoApproveInvalidExtensionNotOnAllowlist) {
   base::Value ext_manifest =
       CreateManifest(AUTO_APPROVE_INVALID, false, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
@@ -236,7 +236,7 @@
   EXPECT_FALSE(OAuth2ManifestHandler::GetOAuth2Info(*extension).auto_approve);
 }
 
-TEST_F(OAuth2ManifestTest, AutoApproveNotSetExtensionOnWhitelist) {
+TEST_F(OAuth2ManifestTest, AutoApproveNotSetExtensionOnAllowlist) {
   base::Value ext_manifest =
       CreateManifest(AUTO_APPROVE_NOT_SET, true, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
@@ -246,7 +246,7 @@
   EXPECT_FALSE(OAuth2ManifestHandler::GetOAuth2Info(*extension).auto_approve);
 }
 
-TEST_F(OAuth2ManifestTest, AutoApproveFalseExtensionOnWhitelist) {
+TEST_F(OAuth2ManifestTest, AutoApproveFalseExtensionOnAllowlist) {
   base::Value ext_manifest =
       CreateManifest(AUTO_APPROVE_FALSE, true, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
@@ -257,7 +257,7 @@
   EXPECT_FALSE(*OAuth2ManifestHandler::GetOAuth2Info(*extension).auto_approve);
 }
 
-TEST_F(OAuth2ManifestTest, AutoApproveTrueExtensionOnWhitelist) {
+TEST_F(OAuth2ManifestTest, AutoApproveTrueExtensionOnAllowlist) {
   base::Value ext_manifest =
       CreateManifest(AUTO_APPROVE_TRUE, true, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
@@ -268,7 +268,7 @@
   EXPECT_TRUE(*OAuth2ManifestHandler::GetOAuth2Info(*extension).auto_approve);
 }
 
-TEST_F(OAuth2ManifestTest, AutoApproveInvalidExtensionOnWhitelist) {
+TEST_F(OAuth2ManifestTest, AutoApproveInvalidExtensionOnAllowlist) {
   base::Value ext_manifest =
       CreateManifest(AUTO_APPROVE_INVALID, true, CLIENT_ID_DEFAULT);
   ManifestData manifest(std::move(ext_manifest), "test");
diff --git a/extensions/common/manifest_handlers/options_page_info.cc b/extensions/common/manifest_handlers/options_page_info.cc
index 29c44ef..b013005 100644
--- a/extensions/common/manifest_handlers/options_page_info.cc
+++ b/extensions/common/manifest_handlers/options_page_info.cc
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include "base/files/file_util.h"
-#include "base/ignore_result.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "extensions/common/api/extensions_manifest_types.h"
diff --git a/extensions/common/manifest_handlers/permissions_parser.cc b/extensions/common/manifest_handlers/permissions_parser.cc
index 2f95503d..3738cda 100644
--- a/extensions/common/manifest_handlers/permissions_parser.cc
+++ b/extensions/common/manifest_handlers/permissions_parser.cc
@@ -246,7 +246,7 @@
     }
 
     // Sneaky check for "experimental", which we always allow for extensions
-    // installed from the Webstore. This way we can whitelist extensions to
+    // installed from the Webstore. This way we can allowlist extensions to
     // have access to experimental in just the store, and not have to push a
     // new version of the client. Otherwise, experimental goes through the
     // usual features check.
diff --git a/extensions/common/manifest_test.h b/extensions/common/manifest_test.h
index ed34be8..de95fee6 100644
--- a/extensions/common/manifest_test.h
+++ b/extensions/common/manifest_test.h
@@ -55,7 +55,7 @@
   };
 
   // Allows the test implementation to override a loaded test manifest's
-  // extension ID. Useful for testing features behind a whitelist.
+  // extension ID. Useful for testing features behind a allowlist.
   virtual std::string GetTestExtensionID() const;
 
   // Returns the path in which to find test manifest data files, for example
diff --git a/extensions/common/mojom/renderer.mojom b/extensions/common/mojom/renderer.mojom
index 093a45c4..d66c92b 100644
--- a/extensions/common/mojom/renderer.mojom
+++ b/extensions/common/mojom/renderer.mojom
@@ -128,7 +128,7 @@
   UpdateTabSpecificPermissions(string extension_id,
                                URLPatternSet new_hosts,
                                int32 tab_id,
-                               bool update_origin_whitelist);
+                               bool update_origin_allowlist);
 
   // Notifies the renderer that the user scripts have been updated. It has one
   // ReadOnlySharedMemoryRegion argument consisting of the pickled script data.
@@ -141,7 +141,7 @@
   // extensions.
   ClearTabSpecificPermissions(array<string> extension_ids,
                               int32 tab_id,
-                              bool update_origin_whitelist);
+                              bool update_origin_allowlist);
 
   // Notifies the renderer that an extension wants notifications when certain
   // searches match the active page. This method replaces the old set of
diff --git a/extensions/common/permissions/set_disjunction_permission.h b/extensions/common/permissions/set_disjunction_permission.h
index 53d1552..64c22c6 100644
--- a/extensions/common/permissions/set_disjunction_permission.h
+++ b/extensions/common/permissions/set_disjunction_permission.h
@@ -105,21 +105,19 @@
       std::string* error,
       std::vector<std::string>* unhandled_permissions) override {
     data_set_.clear();
-    const base::ListValue* list = NULL;
 
     if (!value) {
       // treat null as an empty list.
       return true;
     }
 
-    if (!value->GetAsList(&list)) {
+    if (!value->is_list()) {
       if (error)
         *error = "Cannot parse the permission list. It's not a list.";
       return false;
     }
 
-    for (size_t i = 0; i < list->GetList().size(); ++i) {
-      const base::Value& item_value = list->GetList()[i];
+    for (const base::Value& item_value : value->GetList()) {
       PermissionDataType data;
       if (data.FromValue(&item_value)) {
         data_set_.insert(data);
diff --git a/extensions/renderer/api/automation/automation_internal_custom_bindings.cc b/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
index f949996..c23b9d79 100644
--- a/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
+++ b/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
@@ -1837,12 +1837,6 @@
   if (tree_id == accessibility_focused_tree_id_)
     accessibility_focused_tree_id_ = ui::AXTreeIDUnknown();
 
-  auto iter = tree_id_to_tree_wrapper_map_.find(tree_id);
-  if (iter == tree_id_to_tree_wrapper_map_.end())
-    return;
-
-  AutomationAXTreeWrapper* tree_wrapper = iter->second.get();
-  axtree_to_tree_wrapper_map_.erase(tree_wrapper->tree());
   tree_id_to_tree_wrapper_map_.erase(tree_id);
 }
 
@@ -2421,8 +2415,6 @@
     tree_wrapper = new AutomationAXTreeWrapper(tree_id, this);
     tree_id_to_tree_wrapper_map_.insert(
         std::make_pair(tree_id, base::WrapUnique(tree_wrapper)));
-    axtree_to_tree_wrapper_map_.insert(
-        std::make_pair(tree_wrapper->tree(), tree_wrapper));
   } else {
     tree_wrapper = iter->second.get();
   }
@@ -2519,11 +2511,7 @@
   if (!has_filter)
     return false;
 
-  auto iter = axtree_to_tree_wrapper_map_.find(tree);
-  if (iter == axtree_to_tree_wrapper_map_.end())
-    return false;
-
-  ui::AXTreeID tree_id = iter->second->GetTreeID();
+  ui::AXTreeID tree_id = tree->GetAXTreeID();
   bool did_send_event = false;
   for (const auto& observer : tree_change_observers_) {
     switch (observer.filter) {
@@ -2771,12 +2759,7 @@
 void AutomationInternalCustomBindings::SendNodesRemovedEvent(
     ui::AXTree* tree,
     const std::vector<int>& ids) {
-  auto iter = axtree_to_tree_wrapper_map_.find(tree);
-  if (iter == axtree_to_tree_wrapper_map_.end())
-    return;
-
-  ui::AXTreeID tree_id = iter->second->GetTreeID();
-
+  ui::AXTreeID tree_id = tree->GetAXTreeID();
   base::Value args(base::Value::Type::LIST);
   args.Append(tree_id.ToString());
   {
@@ -2854,7 +2837,7 @@
   if (!head_pos->AtStartOfParagraph()) {
     ui::AXNodePosition::AXPositionInstance start_para_pos =
         head_pos->CreatePreviousParagraphStartPosition(
-            ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+            ui::AXBoundaryBehavior::StopAtLastAnchorBoundary);
     ui::AXRange<ui::AXPosition<ui::AXNodePosition, ui::AXNode>> pre_range(
         start_para_pos->Clone(), head_pos->Clone());
     pre_str = pre_range.GetText();
@@ -2864,7 +2847,7 @@
   // node to the end of the paragraph.
   ui::AXNodePosition::AXPositionInstance end_para_pos =
       head_pos->CreateNextParagraphEndPosition(
-          ui::AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+          ui::AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ui::AXRange<ui::AXPosition<ui::AXNodePosition, ui::AXNode>> post_range(
       head_pos->Clone(), end_para_pos->Clone());
   post_str = post_range.GetText();
diff --git a/extensions/renderer/api/automation/automation_internal_custom_bindings.h b/extensions/renderer/api/automation/automation_internal_custom_bindings.h
index 964ed23..91fb984 100644
--- a/extensions/renderer/api/automation/automation_internal_custom_bindings.h
+++ b/extensions/renderer/api/automation/automation_internal_custom_bindings.h
@@ -267,7 +267,6 @@
 
   std::map<ui::AXTreeID, std::unique_ptr<AutomationAXTreeWrapper>>
       tree_id_to_tree_wrapper_map_;
-  std::map<ui::AXTree*, AutomationAXTreeWrapper*> axtree_to_tree_wrapper_map_;
   scoped_refptr<AutomationMessageFilter> message_filter_;
   bool is_active_profile_;
   std::vector<TreeChangeObserver> tree_change_observers_;
diff --git a/extensions/renderer/api/automation/automation_position.cc b/extensions/renderer/api/automation/automation_position.cc
index 445862f..a704805b 100644
--- a/extensions/renderer/api/automation/automation_position.cc
+++ b/extensions/renderer/api/automation/automation_position.cc
@@ -273,118 +273,118 @@
 void AutomationPosition::MoveToNextCharacterPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreateNextCharacterPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToPreviousCharacterPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreatePreviousCharacterPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToNextWordStartPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreateNextWordStartPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToPreviousWordStartPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreatePreviousWordStartPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToNextWordEndPosition(gin::Arguments* arguments) {
   position_ = position_->CreateNextWordEndPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToPreviousWordEndPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreatePreviousWordEndPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToNextLineStartPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreateNextLineStartPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToPreviousLineStartPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreatePreviousLineStartPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToNextLineEndPosition(gin::Arguments* arguments) {
   position_ = position_->CreateNextLineEndPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToPreviousLineEndPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreatePreviousLineEndPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToPreviousFormatStartPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreatePreviousFormatStartPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToNextFormatEndPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreateNextFormatEndPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToNextParagraphStartPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreateNextParagraphStartPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToPreviousParagraphStartPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreatePreviousParagraphStartPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToNextParagraphEndPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreateNextParagraphEndPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToPreviousParagraphEndPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreatePreviousParagraphEndPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToNextPageStartPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreateNextPageStartPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToPreviousPageStartPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreatePreviousPageStartPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToNextPageEndPosition(gin::Arguments* arguments) {
   position_ = position_->CreateNextPageEndPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToPreviousPageEndPosition(
     gin::Arguments* arguments) {
   position_ = position_->CreatePreviousPageEndPosition(
-      ui::AXBoundaryBehavior::kCrossBoundary);
+      ui::AXBoundaryBehavior::CrossBoundary);
 }
 
 void AutomationPosition::MoveToNextAnchorPosition(gin::Arguments* arguments) {
diff --git a/extensions/renderer/bindings/api_binding_unittest.cc b/extensions/renderer/bindings/api_binding_unittest.cc
index 36747202..a7ebec84 100644
--- a/extensions/renderer/bindings/api_binding_unittest.cc
+++ b/extensions/renderer/bindings/api_binding_unittest.cc
@@ -4,11 +4,12 @@
 
 #include "extensions/renderer/bindings/api_binding.h"
 
+#include <tuple>
+
 #include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/cxx17_backports.h"
-#include "base/ignore_result.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "build/build_config.h"
@@ -736,7 +737,7 @@
             GetStringPropertyFromObject(binding_object, context, "linuxOnly"));
   EXPECT_EQ("undefined", GetStringPropertyFromObject(binding_object, context,
                                                      "notLinuxOrLacros"));
-#elif defined(OS_LINUX)
+#elif BUILDFLAG(IS_LINUX)
   EXPECT_EQ("\"linux\"",
             GetStringPropertyFromObject(binding_object, context, "linuxOnly"));
   EXPECT_EQ("undefined", GetStringPropertyFromObject(binding_object, context,
@@ -1660,8 +1661,8 @@
     v8::Local<v8::Value> args[] = {binding_object};
     v8::TryCatch try_catch(context->GetIsolate());
     // The throwException call will throw an exception; ignore it.
-    ignore_result(call->Call(context, v8::Undefined(context->GetIsolate()),
-                             base::size(args), args));
+    std::ignore = call->Call(context, v8::Undefined(context->GetIsolate()),
+                             base::size(args), args);
   };
 
   call_api_method("modifyArgs", "");
diff --git a/extensions/renderer/bindings/api_binding_util.cc b/extensions/renderer/bindings/api_binding_util.cc
index 2c0a080..175070b 100644
--- a/extensions/renderer/bindings/api_binding_util.cc
+++ b/extensions/renderer/bindings/api_binding_util.cc
@@ -126,19 +126,19 @@
 
 std::string GetPlatformString() {
 // TODO(https://crbug.com/1052397): For readability, this should become
-// defined(OS_CHROMEOS) && BUILDFLAG(IS_CHROMEOS_LACROS). The second conditional
-// should be defined(OS_CHROMEOS) && BUILDFLAG(IS_CHROMEOS_ASH).
+// BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(IS_CHROMEOS_LACROS). The second
+// conditional should be BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(IS_CHROMEOS_ASH).
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
   return "lacros";
 #elif BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS)
   return "chromeos";
-#elif defined(OS_LINUX)
+#elif BUILDFLAG(IS_LINUX)
   return "linux";
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   return "mac";
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   return "win";
-#elif defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_FUCHSIA)
   return "fuchsia";
 #else
   NOTREACHED();
diff --git a/extensions/renderer/bindings/api_last_error.cc b/extensions/renderer/bindings/api_last_error.cc
index 8e4b001..7496f7e6 100644
--- a/extensions/renderer/bindings/api_last_error.cc
+++ b/extensions/renderer/bindings/api_last_error.cc
@@ -4,7 +4,8 @@
 
 #include "extensions/renderer/bindings/api_last_error.h"
 
-#include "base/ignore_result.h"
+#include <tuple>
+
 #include "gin/converter.h"
 #include "gin/data_object_builder.h"
 #include "gin/handle.h"
@@ -187,9 +188,9 @@
   }
   // These Delete()s can fail, but there's nothing to do if it does (the
   // exception will be caught by the TryCatch above).
-  ignore_result(parent->Delete(context, key));
+  std::ignore = parent->Delete(context, key);
   if (!secondary_parent.IsEmpty())
-    ignore_result(secondary_parent->Delete(context, key));
+    std::ignore = secondary_parent->Delete(context, key);
 }
 
 bool APILastError::HasError(v8::Local<v8::Context> context) {
@@ -261,8 +262,8 @@
     DCHECK(!last_error.IsEmpty());
     // This SetAccessor() can fail, but there's nothing to do if it does (the
     // exception will be caught by the TryCatch in SetError()).
-    ignore_result(parent->SetAccessor(context, key, &LastErrorGetter,
-                                      &LastErrorSetter, last_error));
+    std::ignore = parent->SetAccessor(context, key, &LastErrorGetter,
+                                      &LastErrorSetter, last_error);
   }
 }
 
@@ -281,9 +282,9 @@
   v8::Local<v8::String> key = gin::StringToSymbol(isolate, kLastErrorProperty);
   // This CreateDataProperty() can fail, but there's nothing to do if it does
   // (the exception will be caught by the TryCatch in SetError()).
-  ignore_result(secondary_parent->CreateDataProperty(
+  std::ignore = secondary_parent->CreateDataProperty(
       context, key,
-      gin::DataObjectBuilder(isolate).Set("message", error).Build()));
+      gin::DataObjectBuilder(isolate).Set("message", error).Build());
 }
 
 }  // namespace extensions
diff --git a/extensions/renderer/bindings/exception_handler_unittest.cc b/extensions/renderer/bindings/exception_handler_unittest.cc
index 1dfb20a..a1e819fd 100644
--- a/extensions/renderer/bindings/exception_handler_unittest.cc
+++ b/extensions/renderer/bindings/exception_handler_unittest.cc
@@ -5,9 +5,9 @@
 #include "extensions/renderer/bindings/exception_handler.h"
 
 #include <string>
+#include <tuple>
 
 #include "base/bind.h"
-#include "base/ignore_result.h"
 #include "base/strings/stringprintf.h"
 #include "extensions/renderer/bindings/api_binding_test.h"
 #include "extensions/renderer/bindings/api_binding_test_util.h"
@@ -33,7 +33,7 @@
   v8::Local<v8::Function> function = FunctionFromString(
       context,
       base::StringPrintf("(function() { throw %s; })", to_throw.c_str()));
-  ignore_result(function->Call(context, v8::Undefined(isolate), 0, nullptr));
+  std::ignore = function->Call(context, v8::Undefined(isolate), 0, nullptr);
   ASSERT_TRUE(try_catch.HasCaught());
   handler->HandleException(context, "handled", &try_catch);
 }
@@ -169,8 +169,8 @@
     v8::TryCatch try_catch(isolate());
     v8::Local<v8::Function> throw_error_function = FunctionFromString(
         context, "(function() { throw new Error('function'); })");
-    ignore_result(throw_error_function->Call(context, v8::Undefined(isolate()),
-                                             0, nullptr));
+    std::ignore = throw_error_function->Call(context, v8::Undefined(isolate()),
+                                             0, nullptr);
     ASSERT_TRUE(try_catch.HasCaught());
     handler.HandleException(context, "handled", &try_catch);
     ASSERT_TRUE(logged_error);
diff --git a/extensions/renderer/script_context.cc b/extensions/renderer/script_context.cc
index 71259966..c4e4187 100644
--- a/extensions/renderer/script_context.cc
+++ b/extensions/renderer/script_context.cc
@@ -6,7 +6,6 @@
 
 #include "base/command_line.h"
 #include "base/containers/flat_set.h"
-#include "base/ignore_result.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
 #include "base/strings/string_util.h"
diff --git a/extensions/shell/app/shell_main.cc b/extensions/shell/app/shell_main.cc
index 0ee9877..a20a18d 100644
--- a/extensions/shell/app/shell_main.cc
+++ b/extensions/shell/app/shell_main.cc
@@ -6,12 +6,12 @@
 #include "content/public/app/content_main.h"
 #include "extensions/shell/app/shell_main_delegate.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "content/public/app/sandbox_helper_win.h"
 #include "sandbox/win/src/sandbox_types.h"
 #endif
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t*, int) {
   extensions::ShellMainDelegate delegate;
   content::ContentMainParams params(&delegate);
diff --git a/extensions/shell/app/shell_main_delegate.cc b/extensions/shell/app/shell_main_delegate.cc
index e78accf..1c5c11a 100644
--- a/extensions/shell/app/shell_main_delegate.cc
+++ b/extensions/shell/app/shell_main_delegate.cc
@@ -22,7 +22,7 @@
 #include "extensions/shell/renderer/shell_content_renderer_client.h"
 #include "ui/base/resource/resource_bundle.h"
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
 #include "chromeos/dbus/constants/dbus_paths.h"
 #endif
 
@@ -32,19 +32,19 @@
 
 #if BUILDFLAG(ENABLE_NACL)
 #include "components/nacl/common/nacl_switches.h"  // nogncheck
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include "components/nacl/common/nacl_paths.h"  // nogncheck
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
-#if defined(OS_POSIX) && !defined(OS_MAC) && !defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_ANDROID)
 #include "components/nacl/zygote/nacl_fork_delegate_linux.h"
-#endif  // OS_POSIX && !OS_MAC && !OS_ANDROID
+#endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_ANDROID)
 #endif  // BUILDFLAG(ENABLE_NACL)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "base/base_paths_win.h"
-#elif defined(OS_LINUX) || defined(OS_CHROMEOS)
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include "base/nix/xdg_util.h"
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
 #include "base/base_paths_mac.h"
 #endif
 
@@ -61,13 +61,13 @@
     return cmd_line->GetSwitchValuePath(switches::kContentShellDataPath);
 
   base::FilePath data_dir;
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   std::unique_ptr<base::Environment> env(base::Environment::Create());
   data_dir = base::nix::GetXDGDirectory(
       env.get(), base::nix::kXdgConfigHomeEnvVar, base::nix::kDotConfigDir);
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   CHECK(base::PathService::Get(base::DIR_LOCAL_APP_DATA, &data_dir));
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   CHECK(base::PathService::Get(base::DIR_APP_DATA, &data_dir));
 #else
   NOTIMPLEMENTED();
@@ -132,7 +132,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_ASH)
   chromeos::dbus_paths::RegisterPathProvider();
 #endif
-#if BUILDFLAG(ENABLE_NACL) && (defined(OS_LINUX) || defined(OS_CHROMEOS))
+#if BUILDFLAG(ENABLE_NACL) && (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
   nacl::RegisterPathProvider();
 #endif
   extensions::RegisterPathProvider();
@@ -169,7 +169,7 @@
   logging::CloseLogFile();
 }
 
-#if defined(OS_POSIX) && !defined(OS_MAC) && !defined(OS_ANDROID)
+#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_ANDROID)
 void ShellMainDelegate::ZygoteStarting(
     std::vector<std::unique_ptr<content::ZygoteForkDelegate>>* delegates) {
 #if BUILDFLAG(ENABLE_NACL)
@@ -194,7 +194,7 @@
 #if BUILDFLAG(ENABLE_NACL)
          process_type == switches::kNaClLoaderProcess ||
 #endif
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
          process_type == switches::kGpuProcess ||
 #endif
          process_type == switches::kUtilityProcess;
diff --git a/extensions/shell/app/shell_main_delegate.h b/extensions/shell/app/shell_main_delegate.h
index ab28bb9f..51bf192 100644
--- a/extensions/shell/app/shell_main_delegate.h
+++ b/extensions/shell/app/shell_main_delegate.h
@@ -37,11 +37,11 @@
   content::ContentBrowserClient* CreateContentBrowserClient() override;
   content::ContentRendererClient* CreateContentRendererClient() override;
   void ProcessExiting(const std::string& process_type) override;
-#if defined(OS_POSIX) && !defined(OS_MAC) && !defined(OS_ANDROID)
+#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_ANDROID)
   void ZygoteStarting(std::vector<std::unique_ptr<content::ZygoteForkDelegate>>*
                           delegates) override;
 #endif
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   void PreBrowserMain() override;
 #endif
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
diff --git a/extensions/shell/browser/api/runtime/shell_runtime_api_delegate.cc b/extensions/shell/browser/api/runtime/shell_runtime_api_delegate.cc
index b79aa0b9..292eace 100644
--- a/extensions/shell/browser/api/runtime/shell_runtime_api_delegate.cc
+++ b/extensions/shell/browser/api/runtime/shell_runtime_api_delegate.cc
@@ -45,7 +45,7 @@
 bool ShellRuntimeAPIDelegate::GetPlatformInfo(PlatformInfo* info) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   info->os = api::runtime::PLATFORM_OS_CROS;
-#elif defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
   info->os = api::runtime::PLATFORM_OS_LINUX;
 #endif
   return true;
diff --git a/extensions/shell/browser/default_shell_browser_main_delegate.cc b/extensions/shell/browser/default_shell_browser_main_delegate.cc
index 4bf708c..5cedd4dc 100644
--- a/extensions/shell/browser/default_shell_browser_main_delegate.cc
+++ b/extensions/shell/browser/default_shell_browser_main_delegate.cc
@@ -19,7 +19,7 @@
 #include "extensions/shell/browser/shell_desktop_controller_aura.h"
 #endif
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 #include "extensions/shell/browser/shell_desktop_controller_mac.h"
 #endif
 
@@ -106,7 +106,7 @@
     content::BrowserContext* context) {
 #if defined(USE_AURA)
   return new ShellDesktopControllerAura(context);
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   return new ShellDesktopControllerMac();
 #else
   return NULL;
diff --git a/extensions/shell/browser/shell_browser_main_parts.cc b/extensions/shell/browser/shell_browser_main_parts.cc
index e595ac3..766c48d 100644
--- a/extensions/shell/browser/shell_browser_main_parts.cc
+++ b/extensions/shell/browser/shell_browser_main_parts.cc
@@ -59,7 +59,7 @@
 #include "extensions/shell/browser/shell_network_controller_chromeos.h"
 #endif
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
 #endif
@@ -68,7 +68,7 @@
 #include "chromeos/dbus/audio/cras_audio_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power/power_manager_client.h"
-#elif defined(OS_LINUX) || defined(OS_CHROMEOS)
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include "device/bluetooth/dbus/bluez_dbus_thread_manager.h"
 #endif
 
@@ -142,7 +142,7 @@
       switches::kAppShellAllowRoaming)) {
     network_controller_->SetCellularAllowRoaming(true);
   }
-#elif defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
   // app_shell doesn't need GTK, so the fake input method context can work.
   // See crbug.com/381852 and revision fb69f142.
   // TODO(michaelpg): Verify this works for target environments.
@@ -151,7 +151,7 @@
   ui::InitializeInputMethodForTesting();
 #endif
 
-#if defined(OS_LINUX)
+#if BUILDFLAG(IS_LINUX)
   bluez::BluezDBusManager::Initialize(nullptr /* system_bus */);
 #endif
 }
@@ -291,7 +291,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
   device::BluetoothAdapterFactory::Shutdown();
   bluez::BluezDBusManager::Shutdown();
-#elif defined(OS_LINUX)
+#elif BUILDFLAG(IS_LINUX)
   device::BluetoothAdapterFactory::Shutdown();
   bluez::BluezDBusManager::Shutdown();
   bluez::BluezDBusThreadManager::Shutdown();
diff --git a/extensions/shell/browser/shell_extensions_api_client.cc b/extensions/shell/browser/shell_extensions_api_client.cc
index 7d08940..5e87322 100644
--- a/extensions/shell/browser/shell_extensions_api_client.cc
+++ b/extensions/shell/browser/shell_extensions_api_client.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "content/public/browser/browser_context.h"
 #include "extensions/browser/api/messaging/messaging_delegate.h"
@@ -19,7 +20,7 @@
 
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "extensions/shell/browser/api/file_system/shell_file_system_delegate.h"
 #endif
 
@@ -57,7 +58,7 @@
 
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
 FileSystemDelegate* ShellExtensionsAPIClient::GetFileSystemDelegate() {
   if (!file_system_delegate_)
     file_system_delegate_ = std::make_unique<ShellFileSystemDelegate>();
diff --git a/extensions/shell/browser/shell_extensions_api_client.h b/extensions/shell/browser/shell_extensions_api_client.h
index cd517d7a..ac7ba4f 100644
--- a/extensions/shell/browser/shell_extensions_api_client.h
+++ b/extensions/shell/browser/shell_extensions_api_client.h
@@ -36,7 +36,7 @@
       const override;
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
   FileSystemDelegate* GetFileSystemDelegate() override;
 #endif
   MessagingDelegate* GetMessagingDelegate() override;
@@ -45,7 +45,7 @@
  private:
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
-#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
   std::unique_ptr<FileSystemDelegate> file_system_delegate_;
 #endif
   std::unique_ptr<MessagingDelegate> messaging_delegate_;
diff --git a/extensions/shell/browser/shell_nacl_browser_delegate.cc b/extensions/shell/browser/shell_nacl_browser_delegate.cc
index 204741bc..12f99f2 100644
--- a/extensions/shell/browser/shell_nacl_browser_delegate.cc
+++ b/extensions/shell/browser/shell_nacl_browser_delegate.cc
@@ -52,9 +52,9 @@
 
 bool ShellNaClBrowserDelegate::GetCacheDirectory(base::FilePath* cache_dir) {
   // Just use the general cache directory, not a subdirectory like Chrome does.
-#if defined(OS_POSIX)
+#if BUILDFLAG(IS_POSIX)
   return base::PathService::Get(base::DIR_CACHE, cache_dir);
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   // TODO(yoz): Find an appropriate persistent directory to use here.
   return base::PathService::Get(base::DIR_TEMP, cache_dir);
 #endif
diff --git a/extensions/shell/common/shell_extensions_client.cc b/extensions/shell/common/shell_extensions_client.cc
index 75b2a912..f3725b3 100644
--- a/extensions/shell/common/shell_extensions_client.cc
+++ b/extensions/shell/common/shell_extensions_client.cc
@@ -126,9 +126,9 @@
   return webstore_update_url_;
 }
 
-bool ShellExtensionsClient::IsBlacklistUpdateURL(const GURL& url) const {
+bool ShellExtensionsClient::IsBlocklistUpdateURL(const GURL& url) const {
   // TODO(rockot): Maybe we want to do something else here. For now we accept
-  // any URL as a blacklist URL because we don't really care.
+  // any URL as a blocklist URL because we don't really care.
   return true;
 }
 
diff --git a/extensions/shell/common/shell_extensions_client.h b/extensions/shell/common/shell_extensions_client.h
index 3251b71..73f8a461 100644
--- a/extensions/shell/common/shell_extensions_client.h
+++ b/extensions/shell/common/shell_extensions_client.h
@@ -35,7 +35,7 @@
   bool IsScriptableURL(const GURL& url, std::string* error) const override;
   const GURL& GetWebstoreBaseURL() const override;
   const GURL& GetWebstoreUpdateURL() const override;
-  bool IsBlacklistUpdateURL(const GURL& url) const override;
+  bool IsBlocklistUpdateURL(const GURL& url) const override;
 
  private:
   ScriptingAllowlist scripting_allowlist_;
diff --git a/extensions/shell/test/shell_tests_main.cc b/extensions/shell/test/shell_tests_main.cc
index fceb6242..4c1a8a2 100644
--- a/extensions/shell/test/shell_tests_main.cc
+++ b/extensions/shell/test/shell_tests_main.cc
@@ -8,9 +8,9 @@
 #include "extensions/shell/test/shell_test_launcher_delegate.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "base/win/win_util.h"
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 int main(int argc, char** argv) {
   base::CommandLine::Init(argc, argv);
@@ -18,7 +18,7 @@
   if (parallel_jobs == 0U)
     return 1;
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // Load and pin user32.dll to avoid having to load it once tests start while
   // on the main thread loop where blocking calls are disallowed.
   base::win::PinUser32();
diff --git a/extensions/strings/extensions_strings.grd b/extensions/strings/extensions_strings.grd
index 2c17fec1..6898f5b9 100644
--- a/extensions/strings/extensions_strings.grd
+++ b/extensions/strings/extensions_strings.grd
@@ -43,7 +43,9 @@
     <output filename="extensions_strings_hu.pak" type="data_package" lang="hu" />
     <output filename="extensions_strings_id.pak" type="data_package" lang="id" />
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="extensions_strings_af.pak" type="data_package" lang="af" />
       <output filename="extensions_strings_is.pak" type="data_package" lang="is" />
+      <output filename="extensions_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="extensions_strings_it.pak" type="data_package" lang="it" />
     <output filename="extensions_strings_ja.pak" type="data_package" lang="ja" />
diff --git a/extensions/test/data/manifest_tests/externally_connectable_all_https_urls_whitelisted.json b/extensions/test/data/manifest_tests/externally_connectable_all_https_urls_allowlisted.json
similarity index 100%
rename from extensions/test/data/manifest_tests/externally_connectable_all_https_urls_whitelisted.json
rename to extensions/test/data/manifest_tests/externally_connectable_all_https_urls_allowlisted.json
diff --git a/extensions/test/data/manifest_tests/externally_connectable_all_https_urls_not_whitelisted.json b/extensions/test/data/manifest_tests/externally_connectable_all_https_urls_not_allowlisted.json
similarity index 100%
rename from extensions/test/data/manifest_tests/externally_connectable_all_https_urls_not_whitelisted.json
rename to extensions/test/data/manifest_tests/externally_connectable_all_https_urls_not_allowlisted.json
diff --git a/extensions/test/data/manifest_tests/externally_connectable_all_urls_whitelisted.json b/extensions/test/data/manifest_tests/externally_connectable_all_urls_allowlisted.json
similarity index 100%
rename from extensions/test/data/manifest_tests/externally_connectable_all_urls_whitelisted.json
rename to extensions/test/data/manifest_tests/externally_connectable_all_urls_allowlisted.json
diff --git a/extensions/test/data/manifest_tests/externally_connectable_all_urls_not_whitelisted.json b/extensions/test/data/manifest_tests/externally_connectable_all_urls_not_allowlisted.json
similarity index 100%
rename from extensions/test/data/manifest_tests/externally_connectable_all_urls_not_whitelisted.json
rename to extensions/test/data/manifest_tests/externally_connectable_all_urls_not_allowlisted.json
diff --git a/extensions/test/test_extension_dir.cc b/extensions/test/test_extension_dir.cc
index beed520..79261fc 100644
--- a/extensions/test/test_extension_dir.cc
+++ b/extensions/test/test_extension_dir.cc
@@ -4,8 +4,9 @@
 
 #include "extensions/test/test_extension_dir.h"
 
+#include <tuple>
+
 #include "base/files/file_util.h"
-#include "base/ignore_result.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_restrictions.h"
 #include "extensions/browser/extension_creator.h"
@@ -21,8 +22,8 @@
 
 TestExtensionDir::~TestExtensionDir() {
   base::ScopedAllowBlockingForTesting allow_blocking;
-  ignore_result(dir_.Delete());
-  ignore_result(crx_dir_.Delete());
+  std::ignore = dir_.Delete();
+  std::ignore = crx_dir_.Delete();
 }
 
 void TestExtensionDir::WriteManifest(base::StringPiece manifest) {
diff --git a/extensions/test/test_extensions_client.cc b/extensions/test/test_extensions_client.cc
index 9bb4c3f..63182ac9 100644
--- a/extensions/test/test_extensions_client.cc
+++ b/extensions/test/test_extensions_client.cc
@@ -96,7 +96,7 @@
   return webstore_update_url_;
 }
 
-bool TestExtensionsClient::IsBlacklistUpdateURL(const GURL& url) const {
+bool TestExtensionsClient::IsBlocklistUpdateURL(const GURL& url) const {
   return false;
 }
 
diff --git a/extensions/test/test_extensions_client.h b/extensions/test/test_extensions_client.h
index 8c7dae6..7279ac69 100644
--- a/extensions/test/test_extensions_client.h
+++ b/extensions/test/test_extensions_client.h
@@ -48,7 +48,7 @@
   bool IsScriptableURL(const GURL& url, std::string* error) const override;
   const GURL& GetWebstoreBaseURL() const override;
   const GURL& GetWebstoreUpdateURL() const override;
-  bool IsBlacklistUpdateURL(const GURL& url) const override;
+  bool IsBlocklistUpdateURL(const GURL& url) const override;
   std::set<base::FilePath> GetBrowserImagePaths(
       const Extension* extension) override;
 
diff --git a/fuchsia/engine/BUILD.gn b/fuchsia/engine/BUILD.gn
index 84c2f8c..134f292 100644
--- a/fuchsia/engine/BUILD.gn
+++ b/fuchsia/engine/BUILD.gn
@@ -572,9 +572,6 @@
     "//build/config/fuchsia/test/vulkan_capabilities.test-cmx",
   ]
   additional_manifests = [
-    # Required by ContextProvider to determine the services list.
-    "web_instance.cmx",
-
     # Required by ContextProvider unit-tests to launch the FakeContext process.
     "$target_gen_dir/web_engine_unittests_fake_instance.cmx",
   ]
@@ -620,9 +617,6 @@
     "//build/config/fuchsia/test/web_engine_required_capabilities.test-cmx",
     "//build/config/fuchsia/test/web_instance_host_capabilities.test-cmx",
   ]
-
-  # Required by web_instance_host.
-  additional_manifests = [ "//fuchsia/engine/web_instance.cmx" ]
 }
 
 cr_fuchsia_package("web_engine_shell_pkg") {
diff --git a/fuchsia/engine/web_engine_debug_integration_test.cc b/fuchsia/engine/web_engine_debug_integration_test.cc
index 71b11a7f..52769be 100644
--- a/fuchsia/engine/web_engine_debug_integration_test.cc
+++ b/fuchsia/engine/web_engine_debug_integration_test.cc
@@ -133,6 +133,7 @@
       return;
 
     fuchsia::web::CreateContextParams create_params;
+    create_params.set_features(fuchsia::web::ContextFeatureFlags::NETWORK);
     create_params.set_service_directory(std::move(directory));
     if (user_mode_debugging == UserModeDebugging::kEnabled)
       create_params.set_remote_debugging_port(0);
diff --git a/fuchsia/engine/web_engine_integration_logging_test.cc b/fuchsia/engine/web_engine_integration_logging_test.cc
index b93a496..04d6d15 100644
--- a/fuchsia/engine/web_engine_integration_logging_test.cc
+++ b/fuchsia/engine/web_engine_integration_logging_test.cc
@@ -119,9 +119,9 @@
   log_listener.ListenToLog(log(), nullptr);
 
   // Create the Context & Frame with all log severities enabled.
+  CreateContext(TestContextParams());
   fuchsia::web::CreateFrameParams frame_params;
   frame_params.set_debug_name(kFrameLogTag);
-  CreateContext(TestContextParams());
   CreateFrameWithParams(std::move(frame_params));
   frame_->SetJavaScriptLogLevel(fuchsia::web::ConsoleLogLevel::DEBUG);
 
diff --git a/fuchsia/engine/web_engine_integration_test.cc b/fuchsia/engine/web_engine_integration_test.cc
index 8a3933f..e96519a 100644
--- a/fuchsia/engine/web_engine_integration_test.cc
+++ b/fuchsia/engine/web_engine_integration_test.cc
@@ -78,7 +78,8 @@
   fuchsia::web::CreateContextParams ContextParamsWithAudioAndTestData() {
     fuchsia::web::CreateContextParams create_params =
         TestContextParamsWithTestData();
-    create_params.set_features(fuchsia::web::ContextFeatureFlags::AUDIO);
+    *create_params.mutable_features() |=
+        fuchsia::web::ContextFeatureFlags::AUDIO;
     return create_params;
   }
 
@@ -533,7 +534,8 @@
 TEST_F(MAYBE_VulkanWebEngineIntegrationTest,
        WebGLContextPresentWithVulkanFeature) {
   fuchsia::web::CreateContextParams create_params = TestContextParams();
-  create_params.set_features(fuchsia::web::ContextFeatureFlags::VULKAN);
+  *create_params.mutable_features() |=
+      fuchsia::web::ContextFeatureFlags::VULKAN;
   CreateContextAndFrame(std::move(create_params));
 
   ASSERT_NO_FATAL_FAILURE(LoadUrlAndExpectResponse(
@@ -609,10 +611,9 @@
   // The VULKAN flag is required for hardware video decoders to be available.
   fuchsia::web::CreateContextParams create_params =
       ContextParamsWithAudioAndTestData();
-  create_params.set_features(
+  *create_params.mutable_features() |=
       fuchsia::web::ContextFeatureFlags::VULKAN |
-      fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER |
-      fuchsia::web::ContextFeatureFlags::AUDIO);
+      fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER;
   CreateContextAndFrame(std::move(create_params));
 
   static const uint16_t kTestMediaSessionId = 1;
diff --git a/fuchsia/engine/web_engine_integration_test_base.cc b/fuchsia/engine/web_engine_integration_test_base.cc
index f830f33d..cba804c 100644
--- a/fuchsia/engine/web_engine_integration_test_base.cc
+++ b/fuchsia/engine/web_engine_integration_test_base.cc
@@ -73,6 +73,10 @@
 fuchsia::web::CreateContextParams
 WebEngineIntegrationTestBase::TestContextParams() {
   fuchsia::web::CreateContextParams create_params;
+
+  // Most integration tests require networking, to load test web content.
+  create_params.set_features(fuchsia::web::ContextFeatureFlags::NETWORK);
+
   zx_status_t status = filtered_service_directory_.ConnectClient(
       create_params.mutable_service_directory()->NewRequest());
   ZX_CHECK(status == ZX_OK, status)
diff --git a/fuchsia/engine/web_instance_host/web_instance_host.cc b/fuchsia/engine/web_instance_host/web_instance_host.cc
index 1fccbd7..808dc7ed 100644
--- a/fuchsia/engine/web_instance_host/web_instance_host.cc
+++ b/fuchsia/engine/web_instance_host/web_instance_host.cc
@@ -27,12 +27,9 @@
 #include "base/command_line.h"
 #include "base/containers/contains.h"
 #include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
 #include "base/fuchsia/file_utils.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/fuchsia/process_context.h"
-#include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "base/process/process.h"
 #include "base/strings/strcat.h"
@@ -71,9 +68,6 @@
 
 namespace {
 
-// Path to the definition file for web Component instances.
-constexpr char kWebInstanceComponentPath[] = "/pkg/meta/web_instance.cmx";
-
 // Test-only URL for web hosting Component instances with WebUI resources.
 const char kWebInstanceWithWebUiComponentUrl[] =
     "fuchsia-pkg://fuchsia.com/web_engine_with_webui#meta/web_instance.cmx";
@@ -443,15 +437,105 @@
 #endif
 }
 
-std::vector<std::string> LoadWebInstanceSandboxServices() {
-  std::string cmx;
-  CHECK(ReadFileToString(base::FilePath(kWebInstanceComponentPath), &cmx));
-  absl::optional<base::Value> json = base::JSONReader::Read(cmx);
-  const base::Value* services = json->FindListPath("sandbox.services");
-  std::vector<std::string> result;
-  for (auto& entry : services->GetList())
-    result.push_back(std::move(entry.GetString()));
-  return result;
+// Returns the names of all services required by a web_instance.cmx component
+// instance configured with the specified set of feature flags. The caller is
+// responsible for verifying that |params| specifies a valid combination of
+// settings, before calling this function.
+std::vector<std::string> GetRequiredServicesForConfig(
+    const fuchsia::web::CreateContextParams& params) {
+  // All web_instance.cmx instances require a common set of services, described
+  // at:
+  //   https://fuchsia.dev/reference/fidl/fuchsia.web#CreateContextParams.service_directory
+  std::vector<std::string> services{
+      "fuchsia.device.NameProvider",     "fuchsia.fonts.Provider",
+      "fuchsia.intl.PropertyProvider",   "fuchsia.logger.LogSink",
+      "fuchsia.memorypressure.Provider", "fuchsia.process.Launcher",
+      "fuchsia.settings.Display",  // Used if preferred theme is DEFAULT.
+      "fuchsia.sysmem.Allocator"};
+
+  // TODO(crbug.com/1209031): Provide these conditionally, once corresponding
+  // ContextFeatureFlags have been defined.
+  services.insert(services.end(), {"fuchsia.camera3.DeviceWatcher",
+                                   "fuchsia.media.ProfileProvider"});
+
+  // Additional services are required depending on particular configuration
+  // parameters.
+  if (params.has_playready_key_system()) {
+    services.emplace_back("fuchsia.media.drm.PlayReady");
+  }
+
+  // Additional services are required dependent on the set of features specified
+  // for the instance, as described at:
+  //   https://fuchsia.dev/reference/fidl/fuchsia.web#ContextFeatureFlags
+  // Features are listed here in order of their enum value.
+  fuchsia::web::ContextFeatureFlags features = {};
+  if (params.has_features())
+    features = params.features();
+
+  // TODO(crbug.com/1020273): Allow access to network services only if the
+  // NETWORK feature flag is set.
+  services.insert(services.end(), {
+                                      "fuchsia.net.interfaces.State",
+                                      "fuchsia.net.name.Lookup",
+                                      "fuchsia.posix.socket.Provider",
+                                  });
+
+  if ((features & fuchsia::web::ContextFeatureFlags::AUDIO) ==
+      fuchsia::web::ContextFeatureFlags::AUDIO) {
+    services.insert(services.end(),
+                    {
+                        "fuchsia.media.Audio",
+                        "fuchsia.media.AudioDeviceEnumerator",
+                        "fuchsia.media.SessionAudioConsumerFactory",
+                    });
+  }
+
+  if ((features & fuchsia::web::ContextFeatureFlags::VULKAN) ==
+      fuchsia::web::ContextFeatureFlags::VULKAN) {
+    services.emplace_back("fuchsia.vulkan.loader.Loader");
+  }
+
+  if ((features & fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER) ==
+      fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER) {
+    services.emplace_back("fuchsia.mediacodec.CodecFactory");
+  }
+
+  // HARDWARE_VIDEO_DECODER_ONLY does not require any additional services.
+
+  if ((features & fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM) ==
+      fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM) {
+    services.emplace_back("fuchsia.media.drm.Widevine");
+  }
+
+  // HEADLESS instances cannot create Views and therefore do not require access
+  // to any View-based services.
+  if ((features & fuchsia::web::ContextFeatureFlags::HEADLESS) !=
+      fuchsia::web::ContextFeatureFlags::HEADLESS) {
+    services.insert(services.end(),
+                    {
+                        "fuchsia.accessibility.semantics.SemanticsManager",
+                        "fuchsia.ui.composition.Allocator",
+                        "fuchsia.ui.composition.Flatland",
+                        "fuchsia.ui.scenic.Scenic",
+                    });
+  }
+
+  if ((features & fuchsia::web::ContextFeatureFlags::LEGACYMETRICS) ==
+      fuchsia::web::ContextFeatureFlags::LEGACYMETRICS) {
+    services.emplace_back("fuchsia.legacymetrics.MetricsRecorder");
+  }
+
+  if ((features & fuchsia::web::ContextFeatureFlags::KEYBOARD) ==
+      fuchsia::web::ContextFeatureFlags::KEYBOARD) {
+    services.emplace_back("fuchsia.ui.input3.Keyboard");
+  }
+
+  if ((features & fuchsia::web::ContextFeatureFlags::VIRTUAL_KEYBOARD) ==
+      fuchsia::web::ContextFeatureFlags::VIRTUAL_KEYBOARD) {
+    services.emplace_back("fuchsia.input.virtualkeyboard.ControllerCreator");
+  }
+
+  return services;
 }
 
 }  // namespace
@@ -513,16 +597,20 @@
     return ZX_ERR_INTERNAL;
   }
 
+  fuchsia::web::ContextFeatureFlags features = {};
+  if (params.has_features())
+    features = params.features();
+
   if (params.has_remote_debugging_port()) {
+    if ((features & fuchsia::web::ContextFeatureFlags::NETWORK) !=
+        fuchsia::web::ContextFeatureFlags::NETWORK) {
+      LOG(WARNING) << "Enabling remote debugging requires NETWORK feature.";
+    }
     launch_args.AppendSwitchNative(
         switches::kRemoteDebuggingPort,
         base::NumberToString(params.remote_debugging_port()));
   }
 
-  fuchsia::web::ContextFeatureFlags features = {};
-  if (params.has_features())
-    features = params.features();
-
   const bool is_headless =
       (features & fuchsia::web::ContextFeatureFlags::HEADLESS) ==
       fuchsia::web::ContextFeatureFlags::HEADLESS;
@@ -550,8 +638,8 @@
     // VULKAN is required for DRM-protected video playback. Allow DRM to also be
     // enabled for HEADLESS Contexts, since Vulkan is never required for audio.
     if (!enable_vulkan && !is_headless) {
-      DLOG(ERROR) << "WIDEVINE_CDM and PLAYREADY_CDM features require VULKAN "
-                     " or HEADLESS.";
+      LOG(ERROR) << "WIDEVINE_CDM and PLAYREADY_CDM features require VULKAN "
+                    " or HEADLESS.";
       return ZX_ERR_NOT_SUPPORTED;
     }
     if (!params.has_cdm_data_directory()) {
@@ -717,12 +805,12 @@
   // Pass on the caller's service-directory request.
   launch_info.directory_request = services_request.TakeChannel();
 
-  // Set |additional_services| to redirect requests for all services specified
-  // in the web instance component manifest to be satisfied by the caller-
-  // supplied service directory. This ensures that the instance cannot access
-  // any services outside those provided by the caller.
+  // Set |additional_services| to redirect requests for only those services
+  // required for the specified |params|, to be satisfied by the caller-
+  // supplied service directory. This reduces the risk of an instance being
+  // able to somehow exploit services other than those that it should be using.
   launch_info.additional_services = fuchsia::sys::ServiceList::New();
-  launch_info.additional_services->names = LoadWebInstanceSandboxServices();
+  launch_info.additional_services->names = GetRequiredServicesForConfig(params);
   launch_info.additional_services->host_directory =
       service_directory.TakeChannel();
 
diff --git a/fuchsia/engine/web_instance_host_integration_test.cc b/fuchsia/engine/web_instance_host_integration_test.cc
index 5eacb5ef..5f4c7a1d 100644
--- a/fuchsia/engine/web_instance_host_integration_test.cc
+++ b/fuchsia/engine/web_instance_host_integration_test.cc
@@ -63,6 +63,7 @@
  protected:
   fuchsia::web::CreateContextParams TestContextParams() {
     fuchsia::web::CreateContextParams create_params;
+    create_params.set_features(fuchsia::web::ContextFeatureFlags::NETWORK);
     zx_status_t status = filtered_service_directory_.ConnectClient(
         create_params.mutable_service_directory()->NewRequest());
     ZX_CHECK(status == ZX_OK, status)
diff --git a/fuchsia/runners/BUILD.gn b/fuchsia/runners/BUILD.gn
index a7be37d..b57b9823 100644
--- a/fuchsia/runners/BUILD.gn
+++ b/fuchsia/runners/BUILD.gn
@@ -159,8 +159,6 @@
   package_name = "cast_runner"
   manifest = "cast/cast_runner.cmx"
 
-  # Required by web_instance_host.
-  additional_manifests = [ "//fuchsia/engine/web_instance.cmx" ]
   excluded_files = _web_instance_host_deps_files_to_exclude
 }
 
@@ -292,8 +290,6 @@
   package_name = "web_runner"
   manifest = "web/web_runner.cmx"
 
-  # Required by web_instance_host.
-  additional_manifests = [ "//fuchsia/engine/web_instance.cmx" ]
   excluded_files = _web_instance_host_deps_files_to_exclude
 }
 
diff --git a/fuchsia/runners/cast/cast_runner.cc b/fuchsia/runners/cast/cast_runner.cc
index c90534f..eddd7458 100644
--- a/fuchsia/runners/cast/cast_runner.cc
+++ b/fuchsia/runners/cast/cast_runner.cc
@@ -603,6 +603,7 @@
     std::vector<fuchsia::web::ContentDirectoryProvider> content_directories) {
   fuchsia::web::CreateContextParams params = GetCommonContextParams();
   EnsureSoftwareVideoDecodersAreDisabled(params.mutable_features());
+  *params.mutable_features() |= fuchsia::web::ContextFeatureFlags::NETWORK;
   params.set_remote_debugging_port(kEphemeralRemoteDebuggingPort);
   params.set_content_directories(std::move(content_directories));
   zx_status_t status = isolated_services_->ConnectClient(
diff --git a/gpu/command_buffer/client/gl_helper.h b/gpu/command_buffer/client/gl_helper.h
index cb0b95a2..6be5cd50 100644
--- a/gpu/command_buffer/client/gl_helper.h
+++ b/gpu/command_buffer/client/gl_helper.h
@@ -34,7 +34,7 @@
                GenFunc gen_func,
                DeleteFunc delete_func)
       : gl_(gl), id_(0u), delete_func_(delete_func) {
-    (gl_->*gen_func)(1, &id_);
+    (gl_.get()->*gen_func)(1, &id_);
   }
 
   operator GLuint() const { return id_; }
@@ -46,7 +46,7 @@
 
   ~ScopedGLuint() {
     if (id_ != 0) {
-      (gl_->*delete_func_)(1, &id_);
+      (gl_.get()->*delete_func_)(1, &id_);
     }
   }
 
@@ -86,13 +86,13 @@
   typedef void (gles2::GLES2Interface::*BindFunc)(GLenum target, GLuint id);
   ScopedBinder(gles2::GLES2Interface* gl, GLuint id, BindFunc bind_func)
       : gl_(gl), bind_func_(bind_func) {
-    (gl_->*bind_func_)(Target, id);
+    (gl_.get()->*bind_func_)(Target, id);
   }
 
   ScopedBinder(const ScopedBinder&) = delete;
   ScopedBinder& operator=(const ScopedBinder&) = delete;
 
-  virtual ~ScopedBinder() { (gl_->*bind_func_)(Target, 0); }
+  virtual ~ScopedBinder() { (gl_.get()->*bind_func_)(Target, 0); }
 
  private:
   raw_ptr<gles2::GLES2Interface> gl_;
diff --git a/gpu/command_buffer/client/webgpu_implementation.h b/gpu/command_buffer/client/webgpu_implementation.h
index 0e803cc0..54a6c01 100644
--- a/gpu/command_buffer/client/webgpu_implementation.h
+++ b/gpu/command_buffer/client/webgpu_implementation.h
@@ -6,7 +6,6 @@
 #define GPU_COMMAND_BUFFER_CLIENT_WEBGPU_IMPLEMENTATION_H_
 
 #include <dawn/webgpu.h>
-#include <dawn_wire/WireClient.h>
 
 #include <memory>
 #include <utility>
@@ -22,10 +21,6 @@
 #include "gpu/command_buffer/client/webgpu_interface.h"
 #include "ui/gl/buildflags.h"
 
-namespace dawn_wire {
-class WireClient;
-}
-
 namespace gpu {
 namespace webgpu {
 
diff --git a/gpu/command_buffer/service/shared_image_backing_gl_image.cc b/gpu/command_buffer/service/shared_image_backing_gl_image.cc
index 1113d21..1ec037bd 100644
--- a/gpu/command_buffer/service/shared_image_backing_gl_image.cc
+++ b/gpu/command_buffer/service/shared_image_backing_gl_image.cc
@@ -253,7 +253,7 @@
 
 void SharedImageRepresentationSkiaImpl::CheckContext() {
 #if DCHECK_IS_ON()
-  if (context_)
+  if (!context_state_->context_lost() && context_)
     DCHECK(gl::GLContext::GetCurrent() == context_);
 #endif
 }
diff --git a/gpu/ipc/service/gpu_channel_test_common.cc b/gpu/ipc/service/gpu_channel_test_common.cc
index a0a6564..cb79b94 100644
--- a/gpu/ipc/service/gpu_channel_test_common.cc
+++ b/gpu/ipc/service/gpu_channel_test_common.cc
@@ -3,11 +3,11 @@
 // found in the LICENSE file.
 
 #include "gpu/ipc/service/gpu_channel_test_common.h"
-#include "base/memory/raw_ptr.h"
 
 #include <memory>
+#include <tuple>
 
-#include "base/ignore_result.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/unsafe_shared_memory_region.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
@@ -137,7 +137,7 @@
   auto quit = loop.QuitClosure();
   mojo::PendingAssociatedRemote<mojom::CommandBuffer> remote;
   mojo::PendingAssociatedRemote<mojom::CommandBufferClient> client;
-  ignore_result(client.InitWithNewEndpointAndPassReceiver());
+  std::ignore = client.InitWithNewEndpointAndPassReceiver();
   client.EnableUnassociatedUsage();
   channel.CreateCommandBuffer(
       std::move(init_params), routing_id, std::move(shared_state),
diff --git a/gpu/vulkan/vulkan_image_linux.cc b/gpu/vulkan/vulkan_image_linux.cc
index 0c5677a..f1e6c29 100644
--- a/gpu/vulkan/vulkan_image_linux.cc
+++ b/gpu/vulkan/vulkan_image_linux.cc
@@ -4,8 +4,9 @@
 
 #include "gpu/vulkan/vulkan_image.h"
 
+#include <tuple>
+
 #include "base/containers/cxx20_erase.h"
-#include "base/ignore_result.h"
 #include "base/logging.h"
 #include "gpu/vulkan/vulkan_device_queue.h"
 #include "gpu/vulkan/vulkan_function_pointers.h"
@@ -102,7 +103,7 @@
                            &import_memory_fd_info, requirements);
   // If Initialize successfully, the fd in scoped_fd should be owned by vulkan.
   if (result)
-    ignore_result(scoped_fd.release());
+    std::ignore = scoped_fd.release();
 
   return result;
 }
diff --git a/gpu/vulkan/vulkan_util_win32.cc b/gpu/vulkan/vulkan_util_win32.cc
index 2a9bd6b..b0d869b 100644
--- a/gpu/vulkan/vulkan_util_win32.cc
+++ b/gpu/vulkan/vulkan_util_win32.cc
@@ -4,7 +4,8 @@
 
 #include "gpu/vulkan/vulkan_util.h"
 
-#include "base/ignore_result.h"
+#include <tuple>
+
 #include "base/logging.h"
 #include "gpu/vulkan/vulkan_function_pointers.h"
 
@@ -39,7 +40,7 @@
   }
 
   // If import is successful, the VkSemaphore takes the ownership of the fd.
-  ignore_result(win32_handle.Take());
+  std::ignore = win32_handle.Take();
 
   return semaphore;
 }
diff --git a/infra/config/chops-weetbix-dev.cfg b/infra/config/chops-weetbix-dev.cfg
index cead963..2e0ce04f 100644
--- a/infra/config/chops-weetbix-dev.cfg
+++ b/infra/config/chops-weetbix-dev.cfg
@@ -7,6 +7,24 @@
   }
 }
 
+clustering {
+  test_name_rules {
+    name: "Blink Web Tests"
+    pattern: "^ninja://:blink_web_tests/(virtual/[^/]+/)?(?P<testname>([^/]+/)+[^/]+\\.[a-zA-Z]+).*$"
+    like_template: "ninja://:blink\\_web\\_tests/%${testname}%"
+  }
+  test_name_rules {
+    name: "Google Test (Value-parameterized)"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)\\.(?P<case>\\w+)/[\\w.]+$"
+    like_template: "ninja:${target}/%${suite}.${case}%"
+  }
+  test_name_rules {
+    name: "Google Test (Type-parameterized)"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)/\\w+\\.(?P<case>\\w+)$"
+    like_template: "ninja:${target}/%${suite}/%.${case}"
+  }
+}
+
 monorail {
   project: "chromium"
   default_field_values {
diff --git a/infra/config/chops-weetbix.cfg b/infra/config/chops-weetbix.cfg
index 154a32c..5365c824 100644
--- a/infra/config/chops-weetbix.cfg
+++ b/infra/config/chops-weetbix.cfg
@@ -7,6 +7,24 @@
   unexpected_failures_7d: 7000
 }
 
+clustering {
+  test_name_rules {
+    name: "Blink Web Tests"
+    pattern: "^ninja://:blink_web_tests/(virtual/[^/]+/)?(?P<testname>([^/]+/)+[^/]+\\.[a-zA-Z]+).*$"
+    like_template: "ninja://:blink\\_web\\_tests/%${testname}%"
+  }
+  test_name_rules {
+    name: "Google Test (Value-parameterized)"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)\\.(?P<case>\\w+)/[\\w.]+$"
+    like_template: "ninja:${target}/%${suite}.${case}%"
+  }
+  test_name_rules {
+    name: "Google Test (Type-parameterized)"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)/\\w+\\.(?P<case>\\w+)$"
+    like_template: "ninja:${target}/%${suite}/%.${case}"
+  }
+}
+
 monorail {
   project: "chromium"
   default_field_values {
diff --git "a/infra/config/generated/builders/ci/Android FYI Release \050Pixel 6\051/properties.textpb" "b/infra/config/generated/builders/ci/Android FYI Release \050Pixel 6\051/properties.textpb"
new file mode 100644
index 0000000..ee8f716
--- /dev/null
+++ "b/infra/config/generated/builders/ci/Android FYI Release \050Pixel 6\051/properties.textpb"
@@ -0,0 +1,15 @@
+{
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.gpu.fyi",
+  "perf_dashboard_machine_group": "ChromiumGPUFYI",
+  "recipe": "chromium",
+  "sheriff_rotations": [
+    "chromium.gpu"
+  ]
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-android-pixel-6-64/properties.textpb b/infra/config/generated/builders/try/gpu-fyi-try-android-pixel-6-64/properties.textpb
new file mode 100644
index 0000000..d90599c
--- /dev/null
+++ b/infra/config/generated/builders/try/gpu-fyi-try-android-pixel-6-64/properties.textpb
@@ -0,0 +1,17 @@
+{
+  "$build/goma": {
+    "enable_ats": true,
+    "rpc_extra_params": "?prod",
+    "server_host": "goma.chromium.org",
+    "use_luci_auth": true
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "tryserver.chromium.android",
+  "recipe": "chromium_trybot"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Win8 Tester/properties.textpb b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Win8 Tester/properties.textpb
deleted file mode 100644
index 7883bfd..0000000
--- a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Win8 Tester/properties.textpb
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "chromium.webrtc.fyi",
-  "recipe": "chromium"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/webrtc/WebRTC Chromium Win8 Tester/properties.textpb b/infra/config/generated/builders/webrtc/WebRTC Chromium Win8 Tester/properties.textpb
deleted file mode 100644
index 2cf90e9..0000000
--- a/infra/config/generated/builders/webrtc/WebRTC Chromium Win8 Tester/properties.textpb
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "chromium.webrtc",
-  "perf_dashboard_machine_group": "ChromiumWebRTC",
-  "recipe": "chromium"
-}
\ No newline at end of file
diff --git a/infra/config/generated/luci/chops-weetbix-dev.cfg b/infra/config/generated/luci/chops-weetbix-dev.cfg
index cead963..2e0ce04f 100644
--- a/infra/config/generated/luci/chops-weetbix-dev.cfg
+++ b/infra/config/generated/luci/chops-weetbix-dev.cfg
@@ -7,6 +7,24 @@
   }
 }
 
+clustering {
+  test_name_rules {
+    name: "Blink Web Tests"
+    pattern: "^ninja://:blink_web_tests/(virtual/[^/]+/)?(?P<testname>([^/]+/)+[^/]+\\.[a-zA-Z]+).*$"
+    like_template: "ninja://:blink\\_web\\_tests/%${testname}%"
+  }
+  test_name_rules {
+    name: "Google Test (Value-parameterized)"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)\\.(?P<case>\\w+)/[\\w.]+$"
+    like_template: "ninja:${target}/%${suite}.${case}%"
+  }
+  test_name_rules {
+    name: "Google Test (Type-parameterized)"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)/\\w+\\.(?P<case>\\w+)$"
+    like_template: "ninja:${target}/%${suite}/%.${case}"
+  }
+}
+
 monorail {
   project: "chromium"
   default_field_values {
diff --git a/infra/config/generated/luci/chops-weetbix.cfg b/infra/config/generated/luci/chops-weetbix.cfg
index 154a32c..5365c824 100644
--- a/infra/config/generated/luci/chops-weetbix.cfg
+++ b/infra/config/generated/luci/chops-weetbix.cfg
@@ -7,6 +7,24 @@
   unexpected_failures_7d: 7000
 }
 
+clustering {
+  test_name_rules {
+    name: "Blink Web Tests"
+    pattern: "^ninja://:blink_web_tests/(virtual/[^/]+/)?(?P<testname>([^/]+/)+[^/]+\\.[a-zA-Z]+).*$"
+    like_template: "ninja://:blink\\_web\\_tests/%${testname}%"
+  }
+  test_name_rules {
+    name: "Google Test (Value-parameterized)"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)\\.(?P<case>\\w+)/[\\w.]+$"
+    like_template: "ninja:${target}/%${suite}.${case}%"
+  }
+  test_name_rules {
+    name: "Google Test (Type-parameterized)"
+    pattern: "^ninja:(?P<target>[\\w/]+:\\w+)/(\\w+/)?(?P<suite>\\w+)/\\w+\\.(?P<case>\\w+)$"
+    like_template: "ninja:${target}/%${suite}/%.${case}"
+  }
+}
+
 monorail {
   project: "chromium"
   default_field_values {
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index ba3a6326..4fd89c13 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -892,6 +892,10 @@
         includable_only: true
       }
       builders {
+        name: "chromium/try/gpu-fyi-try-android-pixel-6-64"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/gpu-fyi-try-android-r-pixel-4-32"
         includable_only: true
       }
@@ -1785,6 +1789,10 @@
         location_regexp: ".+/[+]/tools/clang/scripts/update.py"
       }
       builders {
+        name: "chromium/try/requires-testing-checker"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/tricium-metrics-analysis"
         includable_only: true
       }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 700b381..a23fc00b 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -1432,6 +1432,92 @@
       }
     }
     builders {
+      name: "Android FYI Release (Pixel 6)"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:2"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-18.04"
+      dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/ci/Android FYI Release (Pixel 6)/properties.textpb",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.gpu.fyi",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium",'
+        '  "sheriff_rotations": ['
+        '    "chromium.gpu"'
+        '  ]'
+        '}'
+      execution_timeout_secs: 21600
+      build_numbers: YES
+      service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      experiments {
+        key: "luci.use_realms"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome/test:|content/test:fuchsia_)telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "Android FYI SkiaRenderer GL (Nexus 5X)"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -23860,6 +23946,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -23927,6 +24017,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -24000,6 +24094,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -24067,6 +24165,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -29574,6 +29676,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -30656,6 +30762,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -30723,6 +30833,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -32681,6 +32795,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -32754,6 +32872,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -32821,6 +32943,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -32888,6 +33014,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -32955,6 +33085,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -33022,6 +33156,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -36471,6 +36609,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -36546,6 +36688,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37378,6 +37524,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37448,6 +37598,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37515,6 +37669,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40647,6 +40805,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40720,6 +40882,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40793,6 +40959,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41944,6 +42114,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42019,6 +42193,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42094,6 +42272,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42169,6 +42351,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42657,6 +42843,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42724,6 +42914,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42791,6 +42985,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42858,6 +43056,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44011,6 +44213,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44078,6 +44284,10 @@
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -48531,6 +48741,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -48614,6 +48828,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57042,6 +57260,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57227,6 +57449,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -59011,6 +59237,96 @@
       }
     }
     builders {
+      name: "gpu-fyi-try-android-pixel-6-64"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-18.04"
+      dimensions: "pool:luci.chromium.gpu.android.pixel6.try"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/try/gpu-fyi-try-android-pixel-6-64/properties.textpb",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "tryserver.chromium.android",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium_trybot"'
+        '}'
+      execution_timeout_secs: 21600
+      expiration_secs: 7200
+      caches {
+        name: "win_toolchain"
+        path: "win_toolchain"
+      }
+      build_numbers: YES
+      service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      experiments {
+        key: "luci.use_realms"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome/test:|content/test:fuchsia_)telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "gpu-fyi-try-android-r-pixel-4-32"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -63271,6 +63587,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -65069,6 +65389,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -65245,6 +65569,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -69622,6 +69950,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -69702,6 +70034,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -73056,6 +73392,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -76221,6 +76561,84 @@
       }
     }
     builders {
+      name: "requires-testing-checker"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-18.04"
+      dimensions: "pool:luci.chromium.try"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/main"
+        cmd: "luciexe"
+      }
+      properties:
+        '{'
+        '  "$recipe_engine/resultdb/test_presentation": {'
+        '    "column_keys": [],'
+        '    "grouping_keys": ['
+        '      "status",'
+        '      "v.test_suite"'
+        '    ]'
+        '  },'
+        '  "recipe": "requires_testing_checker"'
+        '}'
+      priority: 25
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      grace_period {
+        seconds: 120
+      }
+      caches {
+        name: "win_toolchain"
+        path: "win_toolchain"
+      }
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      experiments {
+        key: "luci.use_realms"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome/test:|content/test:fuchsia_)telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: "prevents CLs that requires testing from landing on branches with no CQ"
+    }
+    builders {
       name: "tricium-metrics-analysis"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -76618,6 +77036,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -76701,6 +77123,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -76784,6 +77210,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -77502,6 +77932,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -77582,6 +78016,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -77662,6 +78100,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -77742,6 +78184,10 @@
         value: 5
       }
       experiments {
+        key: "luci.recipes.use_python3"
+        value: 5
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -80488,57 +80934,6 @@
         enable: true
       }
     }
-    builders {
-      name: "WebRTC Chromium Win8 Tester"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:WebRTC Chromium Win8 Tester"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Windows"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/webrtc/WebRTC Chromium Win8 Tester/properties.textpb",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "chromium.webrtc",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium"'
-        '}'
-      execution_timeout_secs: 7200
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
-      resultdb {
-        enable: true
-      }
-    }
   }
 }
 buckets {
@@ -81178,44 +81573,6 @@
       }
     }
     builders {
-      name: "WebRTC Chromium FYI Win8 Tester"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Windows-10"
-      dimensions: "pool:luci.chromium.webrtc.fyi"
-      exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/main"
-        cmd: "luciexe"
-      }
-      properties:
-        '{'
-        '  "$recipe_engine/resultdb/test_presentation": {'
-        '    "column_keys": [],'
-        '    "grouping_keys": ['
-        '      "status",'
-        '      "v.test_suite"'
-        '    ]'
-        '  },'
-        '  "builder_group": "chromium.webrtc.fyi",'
-        '  "recipe": "chromium"'
-        '}'
-      execution_timeout_secs: 7200
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
-      resultdb {
-        enable: true
-      }
-    }
-    builders {
       name: "WebRTC Chromium FYI ios-device"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "cpu:x86-64"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 3cebf77..a34addae 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -1637,6 +1637,9 @@
   builders {
     name: "buildbucket/luci.chromium.try/reclient-config-deployment-verifier"
   }
+  builders {
+    name: "buildbucket/luci.chromium.try/requires-testing-checker"
+  }
   builder_view_only: true
 }
 consoles {
@@ -2588,6 +2591,9 @@
     name: "buildbucket/luci.chromium.try/reclient-config-deployment-verifier"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/requires-testing-checker"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/win-libfuzzer-asan-rel"
   }
   builders {
@@ -8297,6 +8303,11 @@
     short_name: "P4"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Android FYI Release (Pixel 6)"
+    category: "Android|S64|ARM"
+    short_name: "P6"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/ChromeOS FYI Release (amd64-generic)"
     category: "ChromeOS|amd64|generic"
     short_name: "x64"
@@ -11907,11 +11918,6 @@
     category: "win"
     short_name: "7"
   }
-  builders {
-    name: "buildbucket/luci.chromium.webrtc/WebRTC Chromium Win8 Tester"
-    category: "win"
-    short_name: "8"
-  }
   header {
     oncalls {
       name: "Chromium"
@@ -12283,11 +12289,6 @@
     short_name: "7"
   }
   builders {
-    name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Win8 Tester"
-    category: "win|release|tester"
-    short_name: "8"
-  }
-  builders {
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI ios-device"
     category: "ios"
     short_name: "dev"
@@ -14272,6 +14273,9 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-p-pixel-2-skv-32"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-pixel-6-64"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-r-pixel-4-32"
   }
   builders {
@@ -14833,6 +14837,9 @@
     name: "buildbucket/luci.chromium.try/reclient-config-deployment-verifier"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/requires-testing-checker"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/tricium-metrics-analysis"
   }
   builders {
@@ -15254,6 +15261,9 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-p-pixel-2-skv-32"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-pixel-6-64"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-r-pixel-4-32"
   }
   builders {
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index d5a4f9f90..77d00ec6 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -230,6 +230,20 @@
   }
 }
 job {
+  id: "Android FYI Release (Pixel 6)"
+  realm: "ci"
+  acls {
+    role: TRIGGERER
+    granted_to: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+  }
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "Android FYI Release (Pixel 6)"
+  }
+}
+job {
   id: "Android FYI SkiaRenderer GL (Nexus 5X)"
   realm: "ci"
   acls {
@@ -3351,20 +3365,6 @@
   }
 }
 job {
-  id: "WebRTC Chromium FYI Win8 Tester"
-  realm: "webrtc.fyi"
-  acls {
-    role: TRIGGERER
-    granted_to: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-  }
-  acl_sets: "webrtc.fyi"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.webrtc.fyi"
-    builder: "WebRTC Chromium FYI Win8 Tester"
-  }
-}
-job {
   id: "WebRTC Chromium FYI ios-device"
   realm: "webrtc.fyi"
   acl_sets: "webrtc.fyi"
@@ -3471,20 +3471,6 @@
   }
 }
 job {
-  id: "WebRTC Chromium Win8 Tester"
-  realm: "webrtc"
-  acls {
-    role: TRIGGERER
-    granted_to: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-  }
-  acl_sets: "webrtc"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.webrtc"
-    builder: "WebRTC Chromium Win8 Tester"
-  }
-}
-job {
   id: "Win 10 Fast Ring"
   realm: "ci"
   acl_sets: "ci"
diff --git a/infra/config/recipes.star b/infra/config/recipes.star
index 53dbab08..f46e8fe 100644
--- a/infra/config/recipes.star
+++ b/infra/config/recipes.star
@@ -96,10 +96,16 @@
 
 build_recipe(
     name = "recipe:angle_chromium",
+    experiments = {
+        "luci.recipes.use_python3": 5,
+    },
 )
 
 build_recipe(
     name = "recipe:angle_chromium_trybot",
+    experiments = {
+        "luci.recipes.use_python3": 5,
+    },
 )
 
 build_recipe(
@@ -114,6 +120,7 @@
 
 build_recipe(
     name = "recipe:binary_size_fuchsia_trybot",
+    use_python3 = True,
 )
 
 build_recipe(
@@ -232,6 +239,10 @@
 )
 
 build_recipe(
+    name = "recipe:requires_testing_checker",
+)
+
+build_recipe(
     name = "recipe:swarming/deterministic_build",
     use_python3 = True,
 )
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
index a07efb0..a2b0463 100644
--- a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
@@ -110,6 +110,15 @@
 )
 
 ci.thin_tester(
+    name = "Android FYI Release (Pixel 6)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Android|S64|ARM",
+        short_name = "P6",
+    ),
+    triggered_by = ["GPU FYI Android arm64 Builder"],
+)
+
+ci.thin_tester(
     name = "Android FYI SkiaRenderer GL (Nexus 5X)",
     console_view_entry = consoles.console_view_entry(
         category = "Android|skgl|M64",
diff --git a/infra/config/subprojects/chromium/gpu.try.star b/infra/config/subprojects/chromium/gpu.try.star
index 8a1bcce..e9a3a95 100644
--- a/infra/config/subprojects/chromium/gpu.try.star
+++ b/infra/config/subprojects/chromium/gpu.try.star
@@ -93,6 +93,11 @@
 )
 
 gpu_android_builder(
+    name = "gpu-fyi-try-android-pixel-6-64",
+    pool = "luci.chromium.gpu.android.pixel6.try",
+)
+
+gpu_android_builder(
     name = "gpu-try-android-m-nexus-5x-64",
     pool = "luci.chromium.gpu.android.nexus5x.try",
 )
diff --git a/infra/config/subprojects/chromium/try/presubmit.star b/infra/config/subprojects/chromium/try/presubmit.star
index 82fa491..7779bc7 100644
--- a/infra/config/subprojects/chromium/try/presubmit.star
+++ b/infra/config/subprojects/chromium/try/presubmit.star
@@ -116,3 +116,10 @@
     },
     tryjob = try_.job(),
 )
+
+# TODO(crbug.com/1279594) Switch to presubmit_builder, add to fallback CQ
+try_.builder(
+    name = "requires-testing-checker",
+    description_html = "prevents CLs that requires testing from landing on branches with no CQ",
+    executable = "recipe:requires_testing_checker",
+)
diff --git a/infra/config/subprojects/webrtc/consoles/chromium.webrtc.fyi.star b/infra/config/subprojects/webrtc/consoles/chromium.webrtc.fyi.star
index 3db89a6..c4eb6a32 100644
--- a/infra/config/subprojects/webrtc/consoles/chromium.webrtc.fyi.star
+++ b/infra/config/subprojects/webrtc/consoles/chromium.webrtc.fyi.star
@@ -86,11 +86,6 @@
             short_name = "7",
         ),
         luci.console_view_entry(
-            builder = "webrtc.fyi/WebRTC Chromium FYI Win8 Tester",
-            category = "win|release|tester",
-            short_name = "8",
-        ),
-        luci.console_view_entry(
             builder = "webrtc.fyi/WebRTC Chromium FYI ios-device",
             category = "ios",
             short_name = "dev",
diff --git a/infra/config/subprojects/webrtc/consoles/chromium.webrtc.star b/infra/config/subprojects/webrtc/consoles/chromium.webrtc.star
index dddc534..892032ef4 100644
--- a/infra/config/subprojects/webrtc/consoles/chromium.webrtc.star
+++ b/infra/config/subprojects/webrtc/consoles/chromium.webrtc.star
@@ -54,10 +54,5 @@
             category = "win",
             short_name = "7",
         ),
-        luci.console_view_entry(
-            builder = "webrtc/WebRTC Chromium Win8 Tester",
-            category = "win",
-            short_name = "8",
-        ),
     ],
 )
diff --git a/infra/config/subprojects/webrtc/webrtc.fyi.star b/infra/config/subprojects/webrtc/webrtc.fyi.star
index 66b30e2a..57e97c3a 100644
--- a/infra/config/subprojects/webrtc/webrtc.fyi.star
+++ b/infra/config/subprojects/webrtc/webrtc.fyi.star
@@ -140,12 +140,6 @@
 )
 
 builder(
-    name = "WebRTC Chromium FYI Win8 Tester",
-    os = os.WINDOWS_DEFAULT,
-    triggered_by = ["WebRTC Chromium FYI Win Builder"],
-)
-
-builder(
     name = "WebRTC Chromium FYI ios-device",
     executable = "recipe:webrtc/chromium_ios",
     goma_backend = goma.backend.RBE_PROD,
diff --git a/infra/config/subprojects/webrtc/webrtc.star b/infra/config/subprojects/webrtc/webrtc.star
index c6ac67a..d264b34 100644
--- a/infra/config/subprojects/webrtc/webrtc.star
+++ b/infra/config/subprojects/webrtc/webrtc.star
@@ -93,9 +93,3 @@
     os = os.WINDOWS_ANY,
     triggered_by = ["WebRTC Chromium Win Builder"],
 )
-
-builder(
-    name = "WebRTC Chromium Win8 Tester",
-    os = os.WINDOWS_ANY,
-    triggered_by = ["WebRTC Chromium Win Builder"],
-)
diff --git a/ios/chrome/browser/browser_state/browser_state_info_cache.cc b/ios/chrome/browser/browser_state/browser_state_info_cache.cc
index 805406c8..8ddf96f6 100644
--- a/ios/chrome/browser/browser_state/browser_state_info_cache.cc
+++ b/ios/chrome/browser/browser_state/browser_state_info_cache.cc
@@ -32,7 +32,7 @@
     const base::FilePath& user_data_dir)
     : prefs_(prefs), user_data_dir_(user_data_dir) {
   // Populate the cache
-  DictionaryPrefUpdate update(prefs_, prefs::kBrowserStateInfoCache);
+  DictionaryPrefUpdateDeprecated update(prefs_, prefs::kBrowserStateInfoCache);
   base::DictionaryValue* cache = update.Get();
   for (base::DictionaryValue::Iterator it(*cache); !it.IsAtEnd();
        it.Advance()) {
@@ -49,7 +49,7 @@
     const std::string& gaia_id,
     const std::u16string& user_name) {
   std::string key = CacheKeyFromBrowserStatePath(browser_state_path);
-  DictionaryPrefUpdate update(prefs_, prefs::kBrowserStateInfoCache);
+  DictionaryPrefUpdateDeprecated update(prefs_, prefs::kBrowserStateInfoCache);
   base::DictionaryValue* cache = update.Get();
 
   std::unique_ptr<base::DictionaryValue> info(new base::DictionaryValue);
@@ -80,7 +80,7 @@
     NOTREACHED();
     return;
   }
-  DictionaryPrefUpdate update(prefs_, prefs::kBrowserStateInfoCache);
+  DictionaryPrefUpdateDeprecated update(prefs_, prefs::kBrowserStateInfoCache);
   base::DictionaryValue* cache = update.Get();
   std::string key = CacheKeyFromBrowserStatePath(browser_state_path);
   cache->RemoveKey(key);
@@ -185,7 +185,7 @@
 
 void BrowserStateInfoCache::SetInfoForBrowserStateAtIndex(size_t index,
                                                           base::Value info) {
-  DictionaryPrefUpdate update(prefs_, prefs::kBrowserStateInfoCache);
+  DictionaryPrefUpdateDeprecated update(prefs_, prefs::kBrowserStateInfoCache);
   base::DictionaryValue* cache = update.Get();
   cache->SetKey(sorted_keys_[index], std::move(info));
 }
diff --git a/ios/chrome/browser/complex_tasks/ios_task_tab_helper.h b/ios/chrome/browser/complex_tasks/ios_task_tab_helper.h
index 5077cd8..4299f236 100644
--- a/ios/chrome/browser/complex_tasks/ios_task_tab_helper.h
+++ b/ios/chrome/browser/complex_tasks/ios_task_tab_helper.h
@@ -5,7 +5,7 @@
 #ifndef IOS_CHROME_BROWSER_COMPLEX_TASKS_IOS_TASK_TAB_HELPER_H_
 #define IOS_CHROME_BROWSER_COMPLEX_TASKS_IOS_TASK_TAB_HELPER_H_
 
-#include <map>
+#include <unordered_map>
 
 #import "ios/chrome/browser/complex_tasks/ios_content_record_task_id.h"
 #include "ios/web/public/web_state_observer.h"
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 415846a..755f985d 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -923,6 +923,16 @@
     }];
   }
 
+  // If the New Tab Page URL is set (not empty) add the value to the list of
+  // test policies.
+  NSString* ntp_location = [defaults stringForKey:@"NTPLocation"];
+  if ([ntp_location length] > 0) {
+    NSString* ntp_location_key =
+        base::SysUTF8ToNSString(policy::key::kNewTabPageLocation);
+    [testing_policies
+        addEntriesFromDictionary:@{ntp_location_key : ntp_location}];
+  }
+
   // If any experimental policy was allowed, set the EnableExperimentalPolicies
   // policy.
   if ([allowed_experimental_policies count] > 0) {
diff --git a/ios/chrome/browser/net/cookie_util.mm b/ios/chrome/browser/net/cookie_util.mm
index 00004bb..52a5c625 100644
--- a/ios/chrome/browser/net/cookie_util.mm
+++ b/ios/chrome/browser/net/cookie_util.mm
@@ -59,7 +59,8 @@
     net::NetLog* net_log) {
   if (config.path.empty()) {
     // Empty path means in-memory store.
-    return std::make_unique<net::CookieMonster>(nullptr /* store */, net_log);
+    return std::make_unique<net::CookieMonster>(nullptr /* store */, net_log,
+                                                net::kFirstPartySetsEnabled);
   }
 
   const bool restore_old_session_cookies =
@@ -67,8 +68,8 @@
   scoped_refptr<net::SQLitePersistentCookieStore> persistent_store =
       CreatePersistentCookieStore(config.path, restore_old_session_cookies,
                                   config.crypto_delegate);
-  std::unique_ptr<net::CookieMonster> cookie_monster(
-      new net::CookieMonster(persistent_store.get(), net_log));
+  std::unique_ptr<net::CookieMonster> cookie_monster(new net::CookieMonster(
+      persistent_store.get(), net_log, net::kFirstPartySetsEnabled));
   if (restore_old_session_cookies)
     cookie_monster->SetPersistSessionCookies(true);
   return cookie_monster;
diff --git a/ios/chrome/browser/ntp/BUILD.gn b/ios/chrome/browser/ntp/BUILD.gn
index abde655..86e05f81 100644
--- a/ios/chrome/browser/ntp/BUILD.gn
+++ b/ios/chrome/browser/ntp/BUILD.gn
@@ -4,6 +4,8 @@
 
 source_set("ntp") {
   sources = [
+    "browser_policy_new_tab_page_rewriter.cc",
+    "browser_policy_new_tab_page_rewriter.h",
     "new_tab_page_tab_helper.h",
     "new_tab_page_tab_helper.mm",
     "new_tab_page_tab_helper_delegate.h",
@@ -12,9 +14,13 @@
   deps = [
     ":features",
     "//base:base",
+    "//components/prefs",
     "//components/strings:components_strings_grit",
+    "//components/url_formatter",
     "//ios/chrome/browser",
+    "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/ui:feature_flags",
+    "//ios/components/webui:url_constants",
     "//ios/web/common:features",
     "//ios/web/public",
     "//ui/base:base",
@@ -32,10 +38,15 @@
 source_set("unit_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
-  sources = [ "new_tab_page_tab_helper_unittest.mm" ]
+  sources = [
+    "browser_policy_new_tab_page_rewriter_unittest.mm",
+    "new_tab_page_tab_helper_unittest.mm",
+  ]
   deps = [
     "//base/test:test_support",
+    "//components/prefs",
     "//components/strings:components_strings_grit",
+    "//components/sync_preferences:test_support",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/favicon:favicon",
diff --git a/ios/chrome/browser/ntp/browser_policy_new_tab_page_rewriter.cc b/ios/chrome/browser/ntp/browser_policy_new_tab_page_rewriter.cc
new file mode 100644
index 0000000..3e05c14
--- /dev/null
+++ b/ios/chrome/browser/ntp/browser_policy_new_tab_page_rewriter.cc
@@ -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.
+
+#include "ios/chrome/browser/ntp/browser_policy_new_tab_page_rewriter.h"
+
+#include "components/prefs/pref_service.h"
+#include "components/url_formatter/url_fixer.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/chrome/browser/pref_names.h"
+#include "ios/chrome/browser/system_flags.h"
+#include "ios/components/webui/web_ui_url_constants.h"
+#include "ios/web/public/browser_state.h"
+
+bool WillHandleWebBrowserNewTabPageURLForPolicy(
+    GURL* url,
+    web::BrowserState* browser_state) {
+  // Don't change the URL when incognito mode.
+  if (browser_state->IsOffTheRecord()) {
+    return false;
+  }
+
+  // Extract value of kNewTabPageLocationOverride.
+  ChromeBrowserState* chrome_browser_state =
+      ChromeBrowserState::FromBrowserState(browser_state);
+  PrefService* prefs = chrome_browser_state->GetPrefs();
+  std::string new_tab_page_location_override =
+      prefs->GetString(prefs::kNewTabPageLocationOverride);
+
+  if (url->host() == kChromeUINewTabHost && url->SchemeIs(kChromeUIScheme) &&
+      !new_tab_page_location_override.empty()) {
+    GURL new_url = GURL(new_tab_page_location_override);
+    if (new_url.is_valid() && new_url != *url) {
+      // Overwrite the original URL with the new URL if it is valid.
+      *url = new_url;
+      return true;
+    } else {
+      return false;
+    }
+  }
+  return false;
+}
diff --git a/ios/chrome/browser/ntp/browser_policy_new_tab_page_rewriter.h b/ios/chrome/browser/ntp/browser_policy_new_tab_page_rewriter.h
new file mode 100644
index 0000000..378e549
--- /dev/null
+++ b/ios/chrome/browser/ntp/browser_policy_new_tab_page_rewriter.h
@@ -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.
+
+#ifndef IOS_CHROME_BROWSER_NTP_BROWSER_POLICY_NEW_TAB_PAGE_REWRITER_H_
+#define IOS_CHROME_BROWSER_NTP_BROWSER_POLICY_NEW_TAB_PAGE_REWRITER_H_
+
+class GURL;
+
+namespace web {
+class BrowserState;
+}
+
+// If the policy is using a custom New Tab Page URL, replace the default one by
+// this  custom one. Returns true if the browser policy new tab page handler
+// will handle the URL.
+bool WillHandleWebBrowserNewTabPageURLForPolicy(
+    GURL* url,
+    web::BrowserState* browser_state);
+
+#endif  // IOS_CHROME_BROWSER_NTP_BROWSER_POLICY_NEW_TAB_PAGE_REWRITER_H_
diff --git a/ios/chrome/browser/ntp/browser_policy_new_tab_page_rewriter_unittest.mm b/ios/chrome/browser/ntp/browser_policy_new_tab_page_rewriter_unittest.mm
new file mode 100644
index 0000000..a7d06fd
--- /dev/null
+++ b/ios/chrome/browser/ntp/browser_policy_new_tab_page_rewriter_unittest.mm
@@ -0,0 +1,89 @@
+// Copyright 2022 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 "ios/chrome/browser/ntp/browser_policy_new_tab_page_rewriter.h"
+
+#include "base/test/gtest_util.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/chrome/browser/pref_names.h"
+#include "ios/web/public/test/fakes/fake_browser_state.h"
+#import "ios/web/public/test/web_task_environment.h"
+#include "testing/platform_test.h"
+#include "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+class BrowserPolicyNewTabPageRewriterTest : public PlatformTest {
+ public:
+  void SetUp() override {
+    TestChromeBrowserState::Builder builder;
+    browser_state_ = builder.Build();
+  }
+
+ protected:
+  web::WebTaskEnvironment task_environment_;
+  std::unique_ptr<TestChromeBrowserState> browser_state_;
+};
+
+// Test that chrome://newtab is re-written to the custom NTP Location URL when
+// it is set by the policy.
+TEST_F(BrowserPolicyNewTabPageRewriterTest, CustomNtpUrl) {
+  std::string custom_url = "https://store.google.com";
+  browser_state_->GetPrefs()->SetString(prefs::kNewTabPageLocationOverride,
+                                        custom_url);
+  GURL url = GURL(kChromeUINewTabURL);
+
+  EXPECT_TRUE(
+      WillHandleWebBrowserNewTabPageURLForPolicy(&url, browser_state_.get()));
+  EXPECT_EQ(url, GURL(custom_url));
+}
+
+// Test that chrome://newtab is not re-written if the custom NTP Location URL is
+// the same.
+TEST_F(BrowserPolicyNewTabPageRewriterTest, SameNtpUrl) {
+  std::string custom_url = kChromeUINewTabURL;
+  browser_state_->GetPrefs()->SetString(prefs::kNewTabPageLocationOverride,
+                                        custom_url);
+
+  GURL url = GURL(kChromeUINewTabURL);
+
+  EXPECT_FALSE(
+      WillHandleWebBrowserNewTabPageURLForPolicy(&url, browser_state_.get()));
+  EXPECT_EQ(url, GURL(kChromeUINewTabURL));
+}
+
+// Test that chrome://newtab is not re-written if the custom NTP Location URL is
+// not valid.
+TEST_F(BrowserPolicyNewTabPageRewriterTest, InvalidCustomNtpUrl) {
+  std::string custom_url = "blabla";
+  browser_state_->GetPrefs()->SetString(prefs::kNewTabPageLocationOverride,
+                                        custom_url);
+  GURL url = GURL(kChromeUINewTabURL);
+
+  EXPECT_FALSE(
+      WillHandleWebBrowserNewTabPageURLForPolicy(&url, browser_state_.get()));
+  EXPECT_EQ(url, GURL(kChromeUINewTabURL));
+}
+
+// Test that chrome://newtab is not re-written when there is no custom NTP
+// Location URL.
+TEST_F(BrowserPolicyNewTabPageRewriterTest, NoCustomNtpUrl) {
+  GURL url = GURL(kChromeUINewTabURL);
+  EXPECT_FALSE(
+      WillHandleWebBrowserNewTabPageURLForPolicy(&url, browser_state_.get()));
+  EXPECT_EQ(url, GURL(kChromeUINewTabURL));
+}
+
+// Test that chrome://newtab is not re-written when it is in incognito mode.
+TEST_F(BrowserPolicyNewTabPageRewriterTest, IncognitoMode) {
+  web::FakeBrowserState fake_browser_state;
+  fake_browser_state.SetOffTheRecord(true);
+  GURL url = GURL(kChromeUINewTabURL);
+  EXPECT_FALSE(
+      WillHandleWebBrowserNewTabPageURLForPolicy(&url, &fake_browser_state));
+}
diff --git a/ios/chrome/browser/overlays/overlay_presenter_observer.cc b/ios/chrome/browser/overlays/overlay_presenter_observer.cc
index 201f9a6..513eb17 100644
--- a/ios/chrome/browser/overlays/overlay_presenter_observer.cc
+++ b/ios/chrome/browser/overlays/overlay_presenter_observer.cc
@@ -4,10 +4,17 @@
 
 #include "ios/chrome/browser/overlays/public/overlay_presenter_observer.h"
 
+#include "base/check.h"
 #include "ios/chrome/browser/overlays/public/overlay_request_support.h"
 
 OverlayPresenterObserver::OverlayPresenterObserver() = default;
 
+OverlayPresenterObserver::~OverlayPresenterObserver() {
+  CHECK(!IsInObserverList())
+      << "OverlayPresenterObserver needs to be removed from OverlayPresenter "
+         "observer list before their destruction.";
+}
+
 const OverlayRequestSupport* OverlayPresenterObserver::GetRequestSupport(
     OverlayPresenter* presenter) const {
   return OverlayRequestSupport::All();
diff --git a/ios/chrome/browser/overlays/public/overlay_presenter_observer.h b/ios/chrome/browser/overlays/public/overlay_presenter_observer.h
index ca6ba2b..2dfad26 100644
--- a/ios/chrome/browser/overlays/public/overlay_presenter_observer.h
+++ b/ios/chrome/browser/overlays/public/overlay_presenter_observer.h
@@ -16,6 +16,7 @@
 class OverlayPresenterObserver : public base::CheckedObserver {
  public:
   OverlayPresenterObserver();
+  ~OverlayPresenterObserver() override;
 
   // The request support for this observer.  Request-specific observer callbacks
   // will not be executed for unsupported requests.  By default, all requests
diff --git a/ios/chrome/browser/policy/configuration_policy_handler_list_factory.mm b/ios/chrome/browser/policy/configuration_policy_handler_list_factory.mm
index 2d5985dc..f3781c2 100644
--- a/ios/chrome/browser/policy/configuration_policy_handler_list_factory.mm
+++ b/ios/chrome/browser/policy/configuration_policy_handler_list_factory.mm
@@ -103,6 +103,9 @@
   { policy::key::kUrlKeyedAnonymizedDataCollectionEnabled,
     unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
     base::Value::Type::BOOLEAN },
+  { policy::key::kNewTabPageLocation,
+    prefs::kNewTabPageLocationOverride,
+    base::Value::Type::STRING },
 };
 // clang-format on
 
diff --git a/ios/chrome/browser/pref_names.cc b/ios/chrome/browser/pref_names.cc
index 8f91ae2..b279b65 100644
--- a/ios/chrome/browser/pref_names.cc
+++ b/ios/chrome/browser/pref_names.cc
@@ -155,4 +155,8 @@
 // in ios/chrome/browser/policy/policy_util.h.
 const char kBrowserSigninPolicy[] = "signin.browser_signin_policy";
 
+// Preference that holds the string value indicating the NTP URL to use for the
+// NTP Location policy.
+const char kNewTabPageLocationOverride[] = "ios.ntp.location_override";
+
 }  // namespace prefs
diff --git a/ios/chrome/browser/pref_names.h b/ios/chrome/browser/pref_names.h
index 9ad430f..19510ab4 100644
--- a/ios/chrome/browser/pref_names.h
+++ b/ios/chrome/browser/pref_names.h
@@ -51,6 +51,8 @@
 
 extern const char kBrowserSigninPolicy[];
 
+extern const char kNewTabPageLocationOverride[];
+
 }  // namespace prefs
 
 #endif  // IOS_CHROME_BROWSER_PREF_NAMES_H_
diff --git a/ios/chrome/browser/prefs/browser_prefs.mm b/ios/chrome/browser/prefs/browser_prefs.mm
index 9a0f24a5..80661db0 100644
--- a/ios/chrome/browser/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/prefs/browser_prefs.mm
@@ -281,6 +281,8 @@
   registry->RegisterStringPref(kLastPromptedGoogleURL, std::string());
   registry->RegisterStringPref(kGoogleServicesUsername, std::string());
   registry->RegisterStringPref(kGoogleServicesUserAccountId, std::string());
+  registry->RegisterStringPref(prefs::kNewTabPageLocationOverride,
+                               std::string());
 
   registry->RegisterBooleanPref(kGCMChannelStatus, true);
   registry->RegisterIntegerPref(kGCMChannelPollIntervalSeconds, 0);
diff --git a/ios/chrome/browser/reading_list/BUILD.gn b/ios/chrome/browser/reading_list/BUILD.gn
index d0f6e69..523c99b 100644
--- a/ios/chrome/browser/reading_list/BUILD.gn
+++ b/ios/chrome/browser/reading_list/BUILD.gn
@@ -49,7 +49,6 @@
     "//ios/chrome/common",
     "//ios/components/webui:url_constants",
     "//ios/web/public",
-    "//ios/web/public/deprecated",
     "//ios/web/public/js_messaging:js_messaging",
     "//ios/web/public/security",
     "//net",
diff --git a/ios/chrome/browser/reading_list/reading_list_distiller_page.mm b/ios/chrome/browser/reading_list/reading_list_distiller_page.mm
index 9aacd68..34557b5 100644
--- a/ios/chrome/browser/reading_list/reading_list_distiller_page.mm
+++ b/ios/chrome/browser/reading_list/reading_list_distiller_page.mm
@@ -15,7 +15,6 @@
 #include "components/google/core/common/google_util.h"
 #include "ios/chrome/browser/reading_list/favicon_web_state_dispatcher_impl.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
-#import "ios/web/public/deprecated/crw_js_injection_receiver.h"
 #import "ios/web/public/js_messaging/web_frame.h"
 #import "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/navigation/navigation_item.h"
diff --git a/ios/chrome/browser/resources/Settings.bundle/Experimental.plist b/ios/chrome/browser/resources/Settings.bundle/Experimental.plist
index 0afed09..d4325a9 100644
--- a/ios/chrome/browser/resources/Settings.bundle/Experimental.plist
+++ b/ios/chrome/browser/resources/Settings.bundle/Experimental.plist
@@ -232,7 +232,7 @@
 			</array>
 			<key>Titles</key>
 			<array>
-			    <string>Default</string>
+				<string>Default</string>
 				<string>Disable browser sign-in</string>
 				<string>Enable browser sign-in</string>
 				<string>Force browser sign-in</string>
@@ -264,6 +264,18 @@
 		</dict>
 		<dict>
 			<key>Type</key>
+			<string>PSTextFieldSpecifier</string>
+			<key>Title</key>
+			<string>NTP Location</string>
+			<key>Key</key>
+			<string>NTPLocation</string>
+			<key>DefaultValue</key>
+			<string></string>
+			<key>AutocorrectionType</key>
+			<string>No</string>
+		</dict>
+		<dict>
+			<key>Type</key>
 			<string>PSGroupSpecifier</string>
 			<key>Title</key>
 			<string>Google App Ecosystem</string>
diff --git a/ios/chrome/browser/ui/fullscreen/BUILD.gn b/ios/chrome/browser/ui/fullscreen/BUILD.gn
index 1a001d4..a0449b4 100644
--- a/ios/chrome/browser/ui/fullscreen/BUILD.gn
+++ b/ios/chrome/browser/ui/fullscreen/BUILD.gn
@@ -39,10 +39,8 @@
 
   configs += [ "//build/config/compiler:enable_arc" ]
 
-  deps = [
-    "//base",
-    "//components/flags_ui",
-  ]
+  deps = [ "//components/flags_ui" ]
+  public_deps = [ "//base" ]
 }
 
 source_set("coordinators") {
diff --git a/ios/chrome/browser/ui/settings/safety_check/BUILD.gn b/ios/chrome/browser/ui/settings/safety_check/BUILD.gn
index 6a76277..5859db8c 100644
--- a/ios/chrome/browser/ui/settings/safety_check/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/safety_check/BUILD.gn
@@ -2,19 +2,23 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/ios/swift_source_set.gni")
+
 source_set("safety_check_ui") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
+    "safety_check_bridge.h",
     "safety_check_consumer.h",
     "safety_check_navigation_commands.h",
     "safety_check_service_delegate.h",
-    "safety_check_table_view_controller.h",
-    "safety_check_table_view_controller.mm",
   ]
-  deps = [
+  public_deps = [
     "//components/strings",
     "//ios/chrome/app/strings",
+  ]
+  deps = [
     "//ios/chrome/browser/ui:feature_flags",
+    "//ios/chrome/browser/ui/list_model:list_model",
     "//ios/chrome/browser/ui/settings:settings_root",
     "//ios/chrome/browser/ui/settings/cells",
     "//ios/chrome/browser/ui/settings/cells:public",
@@ -24,6 +28,12 @@
   ]
 }
 
+swift_source_set("safety_check_ui_swift") {
+  bridge_header = "safety_check_bridge.h"
+  sources = [ "safety_check_table_view_controller.swift" ]
+  deps = [ ":safety_check_ui" ]
+}
+
 source_set("safety_check") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
@@ -38,6 +48,7 @@
   ]
   deps = [
     ":safety_check_ui",
+    ":safety_check_ui_swift",
     "//components/password_manager/core/browser",
     "//components/password_manager/core/common",
     "//components/prefs",
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_bridge.h b/ios/chrome/browser/ui/settings/safety_check/safety_check_bridge.h
new file mode 100644
index 0000000..ae53153
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_bridge.h
@@ -0,0 +1,23 @@
+// 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 IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_BRIDGE_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_BRIDGE_H_
+
+// Bridging header between Swift and Obj-C. These types/includes need to be pure
+// Obj-C and have no C++ in them.
+
+#import "ios/chrome/browser/ui/settings/cells/settings_check_cell.h"
+#import "ios/chrome/browser/ui/settings/safety_check/safety_check_consumer.h"
+#import "ios/chrome/browser/ui/settings/safety_check/safety_check_service_delegate.h"
+#import "ios/chrome/browser/ui/settings/settings_root_table_view_controller.h"
+
+// These are all just #defines so they can be included w/out any problem.
+#include "components/strings/grit/components_strings.h"
+#include "ios/chrome/grit/ios_chromium_strings.h"
+#include "ios/chrome/grit/ios_strings.h"
+
+#include "ui/base/l10n/l10n_util_mac_bridge.h"
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_SAFETY_CHECK_SAFETY_CHECK_BRIDGE_H_
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm b/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm
index c97ce92..84fae41 100644
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_coordinator.mm
@@ -29,7 +29,7 @@
 #import "ios/chrome/browser/ui/settings/safety_check/safety_check_constants.h"
 #import "ios/chrome/browser/ui/settings/safety_check/safety_check_mediator.h"
 #import "ios/chrome/browser/ui/settings/safety_check/safety_check_navigation_commands.h"
-#import "ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.h"
+#import "ios/chrome/browser/ui/settings/safety_check/safety_check_ui_swift.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 #import "ios/chrome/browser/ui/table_view/table_view_utils.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.mm b/ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.mm
deleted file mode 100644
index cb539f5..0000000
--- a/ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.mm
+++ /dev/null
@@ -1,165 +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.
-
-#import "ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.h"
-
-#import "base/mac/foundation_util.h"
-#include "components/strings/grit/components_strings.h"
-#import "ios/chrome/browser/ui/settings/cells/settings_check_cell.h"
-#import "ios/chrome/browser/ui/settings/safety_check/safety_check_navigation_commands.h"
-#import "ios/chrome/browser/ui/settings/safety_check/safety_check_service_delegate.h"
-#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
-#import "ios/chrome/browser/ui/table_view/cells/table_view_link_header_footer_item.h"
-#include "ios/chrome/browser/ui/ui_feature_flags.h"
-#include "ios/chrome/grit/ios_chromium_strings.h"
-#include "ios/chrome/grit/ios_strings.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-NSString* const kSafetyCheckTableViewId = @"kSafetyCheckTableViewId";
-
-namespace {
-
-typedef NS_ENUM(NSInteger, SectionIdentifier) {
-  SectionIdentifierCheckTypes = kSectionIdentifierEnumZero,
-  SectionIdentifierCheckStart,
-};
-
-}  // namespace
-
-@interface SafetyCheckTableViewController ()
-
-// Current state of array of items that form the safety check.
-@property(nonatomic, strong) NSArray<TableViewItem*>* checkTypesItems;
-
-// Header for the safety check page.
-@property(nonatomic, strong)
-    TableViewLinkHeaderFooterItem* safetyCheckHeaderItem;
-
-// Current display state of the check start item.
-@property(nonatomic, strong) TableViewItem* checkStartItem;
-
-// Footer with timestamp for the safety check page.
-@property(nonatomic, strong)
-    TableViewLinkHeaderFooterItem* safetyCheckFooterItem;
-
-@end
-
-@implementation SafetyCheckTableViewController
-
-- (void)viewDidLoad {
-  [super viewDidLoad];
-  self.tableView.accessibilityIdentifier = kSafetyCheckTableViewId;
-  self.title =
-      l10n_util::GetNSString(IDS_OPTIONS_ADVANCED_SECTION_TITLE_SAFETY_CHECK);
-}
-
-#pragma mark - SafetyCheckConsumer
-
-- (void)setCheckItems:(NSArray<TableViewItem*>*)items {
-  _checkTypesItems = items;
-  [self reloadData];
-}
-
-- (void)setSafetyCheckHeaderItem:(TableViewLinkHeaderFooterItem*)item {
-  _safetyCheckHeaderItem = item;
-  [self reloadData];
-}
-
-- (void)setCheckStartItem:(TableViewItem*)item {
-  _checkStartItem = item;
-  [self reloadData];
-}
-
-- (void)setTimestampFooterItem:(TableViewLinkHeaderFooterItem*)footer {
-  _safetyCheckFooterItem = footer;
-  [self reloadData];
-}
-
-#pragma mark - ChromeTableViewController
-
-- (void)loadModel {
-  [super loadModel];
-
-  if (self.checkTypesItems.count) {
-    [self.tableViewModel addSectionWithIdentifier:SectionIdentifierCheckTypes];
-    for (TableViewItem* item in self.checkTypesItems) {
-      [self.tableViewModel addItem:item
-           toSectionWithIdentifier:SectionIdentifierCheckTypes];
-    }
-
-    if (self.safetyCheckHeaderItem) {
-      [self.tableViewModel setHeader:self.safetyCheckHeaderItem
-            forSectionWithIdentifier:SectionIdentifierCheckTypes];
-    }
-  }
-
-  if (self.checkStartItem) {
-    [self.tableViewModel addSectionWithIdentifier:SectionIdentifierCheckStart];
-    [self.tableViewModel addItem:self.checkStartItem
-         toSectionWithIdentifier:SectionIdentifierCheckStart];
-
-    if (self.safetyCheckFooterItem) {
-      [self.tableViewModel setFooter:self.safetyCheckFooterItem
-            forSectionWithIdentifier:SectionIdentifierCheckStart];
-    }
-  }
-}
-
-#pragma mark - UIViewController
-
-- (void)didMoveToParentViewController:(UIViewController*)parent {
-  [super didMoveToParentViewController:parent];
-  if (!parent) {
-    [self.presentationDelegate safetyCheckTableViewControllerDidRemove:self];
-  }
-}
-
-#pragma mark - UITableViewDelegate
-
-- (void)tableView:(UITableView*)tableView
-    didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
-  [super tableView:tableView didSelectRowAtIndexPath:indexPath];
-  TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
-  [self.serviceDelegate didSelectItem:item];
-  [tableView deselectRowAtIndexPath:indexPath animated:YES];
-}
-
-- (BOOL)tableView:(UITableView*)tableView
-    shouldHighlightRowAtIndexPath:(NSIndexPath*)indexPath {
-  TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
-  return [self.serviceDelegate isItemClickable:item];
-}
-
-#pragma mark - UITableViewDataSource
-
-- (UITableViewCell*)tableView:(UITableView*)tableView
-        cellForRowAtIndexPath:(NSIndexPath*)indexPath {
-  UITableViewCell* cell = [super tableView:tableView
-                     cellForRowAtIndexPath:indexPath];
-  TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
-  if ([self.serviceDelegate isItemWithErrorInfo:item]) {
-    SettingsCheckCell* settingsCheckCell =
-        base::mac::ObjCCastStrict<SettingsCheckCell>(cell);
-    settingsCheckCell.infoButton.tag = item.type;
-    [settingsCheckCell.infoButton addTarget:self
-                                     action:@selector(didTapErrorInfoButton:)
-                           forControlEvents:UIControlEventTouchUpInside];
-  }
-  return cell;
-}
-
-#pragma mark - Private
-
-// Called when user tapped on the information button of an
-// item. Shows popover with detailed description of an error if needed.
-- (void)didTapErrorInfoButton:(UIButton*)buttonView {
-  [self.serviceDelegate infoButtonWasTapped:buttonView
-                              usingItemType:buttonView.tag];
-}
-
-@end
diff --git a/ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.swift b/ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.swift
new file mode 100644
index 0000000..5219820
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/safety_check/safety_check_table_view_controller.swift
@@ -0,0 +1,184 @@
+// 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.
+
+import UIKit
+
+// Delegate for presentation events related to SafetyCheckTableViewController.
+@objc
+protocol SafetyCheckTableViewControllerPresentationDelegate: AnyObject {
+
+  // Called when the view controller is removed from its parent.
+  func safetyCheckTableViewControllerDidRemove(_ controller: SafetyCheckTableViewController)
+
+}
+
+// Workaround to start the section identifier enum with the base value that's in
+// shared ObjC code. Swift doesn't allow named constants in this context.
+// Need to remember to use |settingsRawValue| instead of |rawValue|.
+protocol SettingsEnum: RawRepresentable where RawValue == Int {}
+extension SettingsEnum {
+  var settingsRawValue: Int {
+    // TODO(crbug.com/1285900): This fails on offical builders when trying
+    // to use the ObjC constant. Hard-code it as a workaround.
+    return self.rawValue + 10  // kSectionIdentifierEnumZero
+  }
+}
+
+@objc
+class SafetyCheckTableViewController: SettingsRootTableViewController, SafetyCheckConsumer {
+  enum Constants {
+    // The accessibility identifier of the privacy settings collection view.
+    static let safetyCheckTableViewId = "kSafetyCheckTableViewId"
+  }
+
+  @objc weak var presentationDelegate: SafetyCheckTableViewControllerPresentationDelegate?
+  // Handler for taps on items on the safety check page.
+  @objc weak var serviceDelegate: SafetyCheckServiceDelegate?
+
+  // MARK: Private enums
+
+  enum SettingsIdentifier: Int, SettingsEnum {
+    case checkTypes
+    case checkStart
+  }
+
+  // MARK: Private members
+
+  // Current state of array of items that form the safety check.
+  private var checkTypesItems: [TableViewItem]? {
+    didSet {
+      reloadData()
+    }
+  }
+
+  // Header for the safety check page.
+  private var safetyCheckHeaderItem: TableViewLinkHeaderFooterItem? {
+    didSet {
+      reloadData()
+    }
+  }
+
+  // Current display state of the check start item.
+  private var checkStartItem: TableViewItem? {
+    didSet {
+      reloadData()
+    }
+  }
+
+  // Footer with timestamp for the safety check page.
+  private var safetyCheckFooterItem: TableViewLinkHeaderFooterItem? {
+    didSet {
+      reloadData()
+    }
+  }
+
+  override func viewDidLoad() {
+    super.viewDidLoad()
+    self.tableView.accessibilityIdentifier = Constants.safetyCheckTableViewId
+    self.title = L10NUtils.string(forMessageId: IDS_OPTIONS_ADVANCED_SECTION_TITLE_SAFETY_CHECK)
+  }
+
+  // MARK: SafetyCheckConsumer
+
+  func setCheck(_ items: [TableViewItem]) {
+    checkTypesItems = items
+  }
+
+  func setSafetyCheckHeaderItem(_ item: TableViewLinkHeaderFooterItem!) {
+    safetyCheckHeaderItem = item
+  }
+
+  func setCheckStart(_ item: TableViewItem!) {
+    checkStartItem = item
+  }
+
+  func setTimestampFooterItem(_ item: TableViewLinkHeaderFooterItem!) {
+    safetyCheckFooterItem = item
+  }
+
+  // MARK: ChromeTableViewController
+
+  override func loadModel() {
+    super.loadModel()
+
+    if let items = self.checkTypesItems {
+      let checkTypes = SettingsIdentifier.checkTypes.settingsRawValue
+      if items.count != 0 {
+        self.tableViewModel.addSection(withIdentifier: checkTypes)
+        for item in items {
+          self.tableViewModel.add(item, toSectionWithIdentifier: checkTypes)
+        }
+      }
+
+      if let item = self.safetyCheckHeaderItem {
+        self.tableViewModel.setHeader(item, forSectionWithIdentifier: checkTypes)
+      }
+    }
+
+    if let item = self.checkStartItem {
+      let checkStart = SettingsIdentifier.checkStart.settingsRawValue
+      self.tableViewModel.addSection(withIdentifier: checkStart)
+      self.tableViewModel.add(item, toSectionWithIdentifier: checkStart)
+      if let footerItem = self.safetyCheckFooterItem {
+        self.tableViewModel.setFooter(footerItem, forSectionWithIdentifier: checkStart)
+      }
+    }
+  }
+
+  // MARK: UIViewController
+
+  override func didMove(toParent parent: UIViewController?) {
+    super.didMove(toParent: parent)
+    if parent == nil {
+      self.presentationDelegate?.safetyCheckTableViewControllerDidRemove(self)
+    }
+  }
+
+  // MARK: UITableViewDelegate
+
+  override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+    super.tableView(tableView, didSelectRowAt: indexPath)
+    let item = self.tableViewModel.item(at: indexPath)
+    self.serviceDelegate?.didSelect(item)
+    tableView.deselectRow(at: indexPath, animated: false)
+  }
+
+  override func tableView(
+    _ tableView: UITableView,
+    shouldHighlightRowAt indexPath: IndexPath
+  ) -> Bool {
+    let item = self.tableViewModel.item(at: indexPath)
+    return self.serviceDelegate?.isItemClickable(item) ?? false
+  }
+
+  // MARK: UITabelViewDataSource
+
+  override func tableView(
+    _ tableView: UITableView,
+    cellForRowAt indexPath: IndexPath
+  ) -> UITableViewCell {
+    let cell = super.tableView(tableView, cellForRowAt: indexPath)
+    let tableItem = self.tableViewModel.item(at: indexPath)
+    if let delegate = self.serviceDelegate,
+      let item = tableItem,
+      delegate.isItemWithErrorInfo(item),
+      let settingsCheckCell = cell as? SettingsCheckCell
+    {
+      settingsCheckCell.infoButton.tag = item.type
+      settingsCheckCell.infoButton.addTarget(
+        self,
+        action: #selector(didTapErrorInfoButton),
+        for: .touchUpInside)
+    }
+    return cell
+  }
+
+  // MARK: Private
+
+  // Called when user tapped on the information button of an item. Shows popover with detailed
+  // description of an error if needed.
+  @objc func didTapErrorInfoButton(sender: UIButton) {
+    self.serviceDelegate?.infoButtonWasTapped(sender, usingItemType: sender.tag)
+  }
+}
diff --git a/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm
index 5ee25b8..e5e3514 100644
--- a/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm
@@ -107,7 +107,7 @@
                            target:nil
                            action:nil];
 
-  UIBarButtonItem* toolbarLeftButton = nil;
+  UIBarButtonItem* toolbarLeftButton = flexibleSpace;
   if (self.tableView.editing && self.shouldShowDeleteButtonInToolbar) {
     toolbarLeftButton = self.deleteButton;
   } else if (self.shouldShowAddButtonInToolbar) {
@@ -118,14 +118,8 @@
       self.tableView.editing ? [self createEditModeDoneButtonForToolbar:YES]
                              : [self createEditButtonForToolbar:YES];
 
-  if (toolbarLeftButton) {
-    [self
-        setToolbarItems:@[ toolbarLeftButton, flexibleSpace, editOrDoneButton ]
+  [self setToolbarItems:@[ toolbarLeftButton, flexibleSpace, editOrDoneButton ]
                animated:YES];
-  } else {
-    [self setToolbarItems:@[ flexibleSpace, editOrDoneButton, flexibleSpace ]
-                 animated:YES];
-  }
 
   if (self.tableView.editing) {
     self.deleteButton.enabled = NO;
diff --git a/ios/chrome/browser/ui/text_fragments/BUILD.gn b/ios/chrome/browser/ui/text_fragments/BUILD.gn
index 4d3360d..de4a81b 100644
--- a/ios/chrome/browser/ui/text_fragments/BUILD.gn
+++ b/ios/chrome/browser/ui/text_fragments/BUILD.gn
@@ -10,10 +10,40 @@
     "text_fragments_mediator.mm",
   ]
   deps = [
+    "//components/shared_highlighting/core/common",
+    "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/browser/main:public",
+    "//ios/chrome/browser/ui/alert_coordinator",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/web_state_list",
     "//ios/web/public/text_fragments",
+    "//ui/base:base",
   ]
   configs += [ "//build/config/compiler:enable_arc" ]
 }
+
+source_set("eg2_tests") {
+  configs += [
+    "//build/config/compiler:enable_arc",
+    "//build/config/ios:xctest_config",
+  ]
+  testonly = true
+  sources = [ "text_fragments_egtest.mm" ]
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//components/shared_highlighting/core/common",
+    "//ios/chrome/app/strings:ios_strings_grit",
+    "//ios/chrome/browser/ui:feature_flags",
+    "//ios/chrome/test:eg_test_support+eg2",
+    "//ios/chrome/test/earl_grey:eg_test_support+eg2",
+    "//ios/testing/earl_grey:eg_test_support+eg2",
+    "//ios/third_party/earl_grey2:test_lib",
+    "//ios/web/common:features",
+    "//ios/web/public/test:element_selector",
+    "//ios/web/public/test/http_server",
+    "//net:test_support",
+    "//ui/base:base",
+  ]
+  frameworks = [ "UIKit.framework" ]
+}
diff --git a/ios/chrome/browser/ui/text_fragments/text_fragments_coordinator.mm b/ios/chrome/browser/ui/text_fragments/text_fragments_coordinator.mm
index c81ea937..be472c3 100644
--- a/ios/chrome/browser/ui/text_fragments/text_fragments_coordinator.mm
+++ b/ios/chrome/browser/ui/text_fragments/text_fragments_coordinator.mm
@@ -7,15 +7,20 @@
 #import <memory>
 
 #import "ios/chrome/browser/main/browser.h"
+#import "ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator.h"
 #import "ios/chrome/browser/ui/text_fragments/text_fragments_mediator.h"
 #import "ios/chrome/browser/web_state_list/web_state_dependency_installer_bridge.h"
+#import "ios/chrome/grit/ios_strings.h"
+#import "ios/web/public/text_fragments/text_fragments_manager.h"
 #import "ios/web/public/web_state.h"
+#import "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-@interface TextFragmentsCoordinator () <DependencyInstalling>
+@interface TextFragmentsCoordinator () <DependencyInstalling,
+                                        TextFragmentsDelegate>
 
 @property(nonatomic, strong, readonly) TextFragmentsMediator* mediator;
 
@@ -31,7 +36,7 @@
                                    browser:(Browser*)browser {
   if (self = [super initWithBaseViewController:baseViewController
                                        browser:browser]) {
-    _mediator = [[TextFragmentsMediator alloc] init];
+    _mediator = [[TextFragmentsMediator alloc] initWithConsumer:self];
     _dependencyInstallerBridge =
         std::make_unique<WebStateDependencyInstallerBridge>(
             self, browser->GetWebStateList());
@@ -39,6 +44,43 @@
   return self;
 }
 
+#pragma mark - TextFragmentsDelegate methods
+
+- (void)userTappedTextFragmentInWebState:(web::WebState*)webState {
+  // TODO(crbug.com/1267933): This works for phones, but for tablets the
+  //     alignment of the bubble is wrong. The values used for the rect need
+  //     to be piped through from the web layer, rather than the arbitrary
+  //     numbers currently used.
+  ActionSheetCoordinator* actionSheet = [[ActionSheetCoordinator alloc]
+      initWithBaseViewController:[self baseViewController]
+                         browser:[self browser]
+                           title:l10n_util::GetNSString(
+                                     IDS_IOS_SHARED_HIGHLIGHT_MENU_TITLE)
+                         message:nil
+                            rect:CGRectMake(0, 0, 100, 100)
+                            view:[self.baseViewController view]];
+
+  // TODO(crbug.com/1281931): The Learn More and Reshare options are currently
+  //     no-ops. This functionality will be implemented in a follow-up patch.
+  [actionSheet addItemWithTitle:l10n_util::GetNSString(
+                                    IDS_IOS_SHARED_HIGHLIGHT_LEARN_MORE)
+                         action:^{
+                         }
+                          style:UIAlertActionStyleDefault];
+  [actionSheet
+      addItemWithTitle:l10n_util::GetNSString(IDS_IOS_SHARED_HIGHLIGHT_RESHARE)
+                action:^{
+                }
+                 style:UIAlertActionStyleDefault];
+  [actionSheet
+      addItemWithTitle:l10n_util::GetNSString(IDS_IOS_SHARED_HIGHLIGHT_REMOVE)
+                action:^{
+                  [self.mediator removeTextFragmentsInWebState:webState];
+                }
+                 style:UIAlertActionStyleDestructive];
+  [actionSheet start];
+}
+
 #pragma mark - DependencyInstalling methods
 
 - (void)installDependencyForWebState:(web::WebState*)webState {
diff --git a/ios/chrome/browser/ui/text_fragments/text_fragments_egtest.mm b/ios/chrome/browser/ui/text_fragments/text_fragments_egtest.mm
new file mode 100644
index 0000000..12cc9495
--- /dev/null
+++ b/ios/chrome/browser/ui/text_fragments/text_fragments_egtest.mm
@@ -0,0 +1,83 @@
+// 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 "base/test/ios/wait_util.h"
+#import "components/shared_highlighting/core/common/shared_highlighting_features.h"
+#import "ios/chrome/grit/ios_strings.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
+#import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#import "ios/testing/earl_grey/earl_grey_test.h"
+#import "ios/web/public/test/element_selector.h"
+#import "net/test/embedded_test_server/default_handlers.h"
+#import "net/test/embedded_test_server/http_request.h"
+#import "net/test/embedded_test_server/http_response.h"
+#import "net/test/embedded_test_server/request_handler_util.h"
+#import "ui/base/l10n/l10n_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+const char kTestURL[] = "/testPage";
+const char kURLWithFragment[] = "/testPage/#:~:text=Lorem%20ipsum";
+const char kHTMLOfTestPage[] =
+    "<html><body><p>"
+    "<span id='target'>Lorem ipsum<span> dolor sit amet, consectetur "
+    "adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore "
+    "magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco "
+    "laboris nisi ut aliquip ex ea commodo consequat."
+    "</p></body></html>";
+const char kTestPageTextSample[] = "Lorem ipsum";
+
+std::unique_ptr<net::test_server::HttpResponse> LoadHtml(
+    const std::string& html,
+    const net::test_server::HttpRequest& request) {
+  std::unique_ptr<net::test_server::BasicHttpResponse> http_response(
+      new net::test_server::BasicHttpResponse);
+  http_response->set_content_type("text/html");
+  http_response->set_content(html);
+  return std::move(http_response);
+}
+
+}  // namespace
+
+// Test class verifying behavior of interactions with text fragments in web
+// pages.
+@interface TextFragmentsTestCase : ChromeTestCase
+@end
+
+@implementation TextFragmentsTestCase
+
+- (AppLaunchConfiguration)appConfigurationForTestCase {
+  AppLaunchConfiguration config;
+  config.features_enabled.push_back(
+      shared_highlighting::kIOSSharedHighlightingV2);
+  return config;
+}
+
+- (void)setUp {
+  [super setUp];
+
+  RegisterDefaultHandlers(self.testServer);
+  self.testServer->RegisterRequestHandler(
+      base::BindRepeating(&net::test_server::HandlePrefixedRequest, kTestURL,
+                          base::BindRepeating(&LoadHtml, kHTMLOfTestPage)));
+
+  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
+}
+
+- (void)testOpenMenu {
+  [ChromeEarlGrey loadURL:self.testServer->GetURL(kURLWithFragment)];
+  [ChromeEarlGrey waitForWebStateContainingText:kTestPageTextSample];
+
+  [ChromeEarlGrey tapWebStateElementWithID:@"target"];
+
+  [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher:
+                      grey_text(l10n_util::GetNSString(
+                          IDS_IOS_SHARED_HIGHLIGHT_MENU_TITLE))];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/text_fragments/text_fragments_mediator.h b/ios/chrome/browser/ui/text_fragments/text_fragments_mediator.h
index ebc37426..4508069b 100644
--- a/ios/chrome/browser/ui/text_fragments/text_fragments_mediator.h
+++ b/ios/chrome/browser/ui/text_fragments/text_fragments_mediator.h
@@ -14,9 +14,20 @@
 // through to Chrome-specific behaviors.
 @interface TextFragmentsMediator : NSObject <TextFragmentsDelegate>
 
+// Initializes a new TextFragmentsMediator which will forward messages received
+// in the web layer to a consumer, so the consumer can trigger UI changes in
+// response.
+- (instancetype)initWithConsumer:(id<TextFragmentsDelegate>)consumer
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
+
 // Attaches this mediator as the recipient for delegated events.
 - (void)registerWithWebState:(web::WebState*)webState;
 
+// Indicates to the web layer that JavaScript should be invoked to remove all
+// text fragments (i.e., highlights) from the given WebState.
+- (void)removeTextFragmentsInWebState:(web::WebState*)webState;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_TEXT_FRAGMENTS_TEXT_FRAGMENTS_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/text_fragments/text_fragments_mediator.mm b/ios/chrome/browser/ui/text_fragments/text_fragments_mediator.mm
index ca199c3..ca6bd083 100644
--- a/ios/chrome/browser/ui/text_fragments/text_fragments_mediator.mm
+++ b/ios/chrome/browser/ui/text_fragments/text_fragments_mediator.mm
@@ -6,19 +6,41 @@
 
 #import <memory>
 
+#import "base/feature_list.h"
+#import "components/shared_highlighting/core/common/shared_highlighting_features.h"
+#import "ios/web/public/text_fragments/text_fragments_manager.h"
 #import "ios/web/public/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+@interface TextFragmentsMediator ()
+
+@property(nonatomic, weak, readonly) id<TextFragmentsDelegate> consumer;
+
+@end
+
 @implementation TextFragmentsMediator
 
+#pragma mark - TextFragmentsDelegate methods
+
 - (void)userTappedTextFragmentInWebState:(web::WebState*)webState {
-  // TODO(crbug.com/1259227): Right now, this treats every tap as if the user
-  // wants to remove the highlight. Instead, the user should be provided with a
-  // menu to select an action to take.
-  web::TextFragmentsManager::FromWebState(webState)->RemoveHighlights();
+  if (base::FeatureList::IsEnabled(
+          shared_highlighting::kIOSSharedHighlightingV2)) {
+    [self.consumer userTappedTextFragmentInWebState:webState];
+  } else {
+    [self removeTextFragmentsInWebState:webState];
+  }
+}
+
+#pragma mark - public methods
+
+- (instancetype)initWithConsumer:(id<TextFragmentsDelegate>)consumer {
+  if (self = [super init]) {
+    _consumer = consumer;
+  }
+  return self;
 }
 
 - (void)registerWithWebState:(web::WebState*)webState {
@@ -30,4 +52,8 @@
   web::TextFragmentsManager::FromWebState(webState)->RegisterDelegate(self);
 }
 
+- (void)removeTextFragmentsInWebState:(web::WebState*)webState {
+  web::TextFragmentsManager::FromWebState(webState)->RemoveHighlights();
+}
+
 @end
diff --git a/ios/chrome/browser/voice/BUILD.gn b/ios/chrome/browser/voice/BUILD.gn
index b9edd4a..6ab8fb23 100644
--- a/ios/chrome/browser/voice/BUILD.gn
+++ b/ios/chrome/browser/voice/BUILD.gn
@@ -71,7 +71,6 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/web",
-    "//ios/web/public/deprecated",
     "//ios/web/public/js_messaging:js_messaging",
     "//net",
     "//url",
diff --git a/ios/chrome/browser/voice/text_to_speech_parser.mm b/ios/chrome/browser/voice/text_to_speech_parser.mm
index a3c9970..bbe45b3 100644
--- a/ios/chrome/browser/voice/text_to_speech_parser.mm
+++ b/ios/chrome/browser/voice/text_to_speech_parser.mm
@@ -8,7 +8,6 @@
 #include "base/logging.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/values.h"
-#import "ios/web/public/deprecated/crw_js_injection_receiver.h"
 #import "ios/web/public/js_messaging/web_frame.h"
 #import "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/web_state.h"
diff --git a/ios/chrome/browser/web/chrome_web_client.mm b/ios/chrome/browser/web/chrome_web_client.mm
index c68d57b..213d50d 100644
--- a/ios/chrome/browser/web/chrome_web_client.mm
+++ b/ios/chrome/browser/web/chrome_web_client.mm
@@ -32,6 +32,7 @@
 #include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "ios/chrome/browser/ios_chrome_main_parts.h"
 #import "ios/chrome/browser/link_to_text/link_to_text_java_script_feature.h"
+#include "ios/chrome/browser/ntp/browser_policy_new_tab_page_rewriter.h"
 #import "ios/chrome/browser/ntp/new_tab_page_tab_helper.h"
 #import "ios/chrome/browser/reading_list/offline_page_tab_helper.h"
 #include "ios/chrome/browser/reading_list/offline_url_utils.h"
@@ -270,6 +271,7 @@
 
 void ChromeWebClient::PostBrowserURLRewriterCreation(
     web::BrowserURLRewriter* rewriter) {
+  rewriter->AddURLRewriter(&WillHandleWebBrowserNewTabPageURLForPolicy);
   rewriter->AddURLRewriter(&WillHandleWebBrowserAboutURL);
   ios::provider::AddURLRewriters(rewriter);
 }
diff --git a/ios/chrome/browser/web/font_size/font_size_tab_helper.mm b/ios/chrome/browser/web/font_size/font_size_tab_helper.mm
index eb5cc41..b366d86 100644
--- a/ios/chrome/browser/web/font_size/font_size_tab_helper.mm
+++ b/ios/chrome/browser/web/font_size/font_size_tab_helper.mm
@@ -323,7 +323,8 @@
 }
 
 void FontSizeTabHelper::StoreCurrentUserZoomMultiplier(double multiplier) {
-  DictionaryPrefUpdate update(GetPrefService(), prefs::kIosUserZoomMultipliers);
+  DictionaryPrefUpdateDeprecated update(GetPrefService(),
+                                        prefs::kIosUserZoomMultipliers);
 
   // Don't bother to store all the ones. This helps keep the pref dict clean.
   if (multiplier == 1) {
diff --git a/ios/chrome/browser/web_state_list/BUILD.gn b/ios/chrome/browser/web_state_list/BUILD.gn
index 03d5005d..cc2256ae 100644
--- a/ios/chrome/browser/web_state_list/BUILD.gn
+++ b/ios/chrome/browser/web_state_list/BUILD.gn
@@ -83,7 +83,6 @@
     "//ios/chrome/browser/web:tab_helper_delegates",
     "//ios/components/security_interstitials:security_interstitials",
     "//ios/web/public",
-    "//ios/web/public/deprecated",
     "//ios/web/public/js_messaging:js_messaging",
   ]
   frameworks = [ "Foundation.framework" ]
diff --git a/ios/chrome/browser/web_state_list/view_source_browser_agent.mm b/ios/chrome/browser/web_state_list/view_source_browser_agent.mm
index c85cb85..1c82828 100644
--- a/ios/chrome/browser/web_state_list/view_source_browser_agent.mm
+++ b/ios/chrome/browser/web_state_list/view_source_browser_agent.mm
@@ -8,7 +8,6 @@
 #import "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/web_state_list/tab_insertion_browser_agent.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
-#import "ios/web/public/deprecated/crw_js_injection_receiver.h"
 #import "ios/web/public/js_messaging/web_frame.h"
 #import "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/navigation/navigation_manager.h"
diff --git a/ios/chrome/test/earl_grey2/BUILD.gn b/ios/chrome/test/earl_grey2/BUILD.gn
index 806cb45..52b1497 100644
--- a/ios/chrome/test/earl_grey2/BUILD.gn
+++ b/ios/chrome/test/earl_grey2/BUILD.gn
@@ -180,6 +180,7 @@
     "//ios/chrome/browser/ui/start_surface:eg2_tests",
     "//ios/chrome/browser/ui/tab_switcher/tab_grid:eg2_tests",
     "//ios/chrome/browser/ui/tabs:eg2_tests",
+    "//ios/chrome/browser/ui/text_fragments:eg2_tests",
     "//ios/chrome/browser/ui/thumb_strip:eg2_tests",
     "//ios/chrome/browser/ui/toolbar:eg2_tests",
     "//ios/chrome/browser/ui/webui:eg2_tests",
diff --git a/ios/components/DEPS b/ios/components/DEPS
index 4ebdb4ea..53cffd8 100644
--- a/ios/components/DEPS
+++ b/ios/components/DEPS
@@ -2,6 +2,7 @@
   # Do not add ios/chrome/ or ios/web_view as an allowed include. Please see
   # ios/components/README.
   "-ios",
+  "+ios/net",
   "+ios/testing",
   "+ios/web/public",
 
diff --git a/ios/components/io_thread/BUILD.gn b/ios/components/io_thread/BUILD.gn
index 2e47fa4d..d28d961 100644
--- a/ios/components/io_thread/BUILD.gn
+++ b/ios/components/io_thread/BUILD.gn
@@ -14,6 +14,7 @@
     "//components/proxy_config/ios",
     "//components/variations",
     "//components/version_info",
+    "//ios/net",
     "//ios/web",
     "//ios/web/common:user_agent",
     "//ios/web/public",
@@ -40,6 +41,7 @@
     "//components/prefs",
     "//components/prefs:test_support",
     "//components/proxy_config",
+    "//ios/net",
     "//ios/web",
     "//ios/web/public/test",
     "//net",
diff --git a/ios/components/io_thread/ios_io_thread.mm b/ios/components/io_thread/ios_io_thread.mm
index 00bf64c..fbf8009 100644
--- a/ios/components/io_thread/ios_io_thread.mm
+++ b/ios/components/io_thread/ios_io_thread.mm
@@ -31,6 +31,7 @@
 #include "components/variations/variations_associated_data.h"
 #include "components/version_info/version_info.h"
 #include "ios/components/io_thread/leak_tracker.h"
+#include "ios/net/cookies/cookie_store_ios.h"
 #include "ios/web/common/user_agent.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "ios/web/public/thread/web_thread.h"
@@ -253,8 +254,8 @@
       std::make_unique<net::HttpServerProperties>();
   // In-memory cookie store.
   // TODO(crbug.com/801910): Hook up logging by passing in a non-null netlog.
-  globals_->system_cookie_store.reset(
-      new net::CookieMonster(nullptr /* store */, nullptr /* netlog */));
+  globals_->system_cookie_store.reset(new net::CookieMonster(
+      nullptr /* store */, nullptr /* netlog */, net::kFirstPartySetsEnabled));
   globals_->http_user_agent_settings.reset(new net::StaticHttpUserAgentSettings(
       std::string(),
       web::GetWebClient()->GetUserAgent(web::UserAgentType::MOBILE)));
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 9ad266aa..84990aa 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-74966c1ab6efbf097512550b4b2d621ae959f32c
\ No newline at end of file
+89ba9b634f50f974c54d114a7548d8c043870144
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index bed8965..4ea7110 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-5d408fc9cd9ae49edbc9ddd061a42b5ad0f2d1f1
\ No newline at end of file
+4a413dd6d650cde748ee73abc77d52bb6034e9cc
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 6227fb0..161ef21 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-253e41a63a446c9163e594a3829664af6ac4abc9
\ No newline at end of file
+6d6a744a9c94fe19a936202d6a74d48b0ffc7962
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index c1cb298..b4caaf94 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-73852ab58dc552e2e14336a55ddbf730fd2e341a
\ No newline at end of file
+ac80c785736ba77853ded9a80e60f270f167f22d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index d66b416..7e3b2c4 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-917b611c65c2c78949dfecd26de370ebcec99f21
\ No newline at end of file
+53fd39d3fb9b5f9e166485ae14a54882e2af9881
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index 66e5928..911788d 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-f0afc413cb1d3a3ae025b0130fcd7de7292a5eff
\ No newline at end of file
+c74acccb77b44199b91d20b9fc194698d6455600
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index b0c8f28..9720c84 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-61d62fb570839b97f5fb2e10bf0a7865781ba6d3
\ No newline at end of file
+c2b1248388325b67feed59e65a225a879baa075c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index ee5ce7d..ee831f6 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-b435937650e6db8c97939f5b58ab14cd9392506e
\ No newline at end of file
+ccb3852b2f5719e96b86b7c5774f5c8b8ec45249
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index c75ceec..a47ccec 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-5d5431b73b3d66ef30ab495cfce0571f9b8c05ec
\ No newline at end of file
+906a4084db29f7aa629d585e8f8db721cc33af8b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 94608c1a..996c547 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-68e765d20dff181b22e1a9f69e225f5ac4fcc170
\ No newline at end of file
+2da0638d73f0f36a6a2e4ee3d17d8da7d6517e74
\ No newline at end of file
diff --git a/ios/net/cookies/cookie_store_ios.h b/ios/net/cookies/cookie_store_ios.h
index 0698e94..dd80d383 100644
--- a/ios/net/cookies/cookie_store_ios.h
+++ b/ios/net/cookies/cookie_store_ios.h
@@ -31,6 +31,8 @@
 
 namespace net {
 
+extern bool const kFirstPartySetsEnabled;
+
 class NetLog;
 
 // Observer for changes on |NSHTTPCookieStorge sharedHTTPCookieStorage|.
diff --git a/ios/net/cookies/cookie_store_ios.mm b/ios/net/cookies/cookie_store_ios.mm
index c7e14519..266adc1 100644
--- a/ios/net/cookies/cookie_store_ios.mm
+++ b/ios/net/cookies/cookie_store_ios.mm
@@ -44,6 +44,8 @@
 
 using CookieDeletionInfo = CookieDeletionInfo;
 
+bool const kFirstPartySetsEnabled = false;
+
 namespace {
 
 #pragma mark NotificationTrampoline
@@ -408,7 +410,8 @@
     std::unique_ptr<SystemCookieStore> system_store,
     NetLog* net_log)
     : cookie_monster_(new net::CookieMonster(persistent_store,
-                                             net_log)),
+                                             net_log,
+                                             net::kFirstPartySetsEnabled)),
       system_store_(std::move(system_store)),
       metrics_enabled_(false),
       cookie_cache_(new CookieCache()),
diff --git a/ios/net/crn_http_protocol_handler.mm b/ios/net/crn_http_protocol_handler.mm
index d5695d3a..b32a2eb 100644
--- a/ios/net/crn_http_protocol_handler.mm
+++ b/ios/net/crn_http_protocol_handler.mm
@@ -545,7 +545,7 @@
   switch ([request_ cachePolicy]) {
     case NSURLRequestReloadIgnoringLocalAndRemoteCacheData:
       load_flags |= LOAD_BYPASS_CACHE;
-      FALLTHROUGH;
+      [[fallthrough]];
     case NSURLRequestReloadIgnoringLocalCacheData:
       load_flags |= LOAD_DISABLE_CACHE;
       break;
diff --git a/ios/public/provider/chrome/browser/BUILD.gn b/ios/public/provider/chrome/browser/BUILD.gn
index 9c6deab2..414042f18 100644
--- a/ios/public/provider/chrome/browser/BUILD.gn
+++ b/ios/public/provider/chrome/browser/BUILD.gn
@@ -22,12 +22,6 @@
   frameworks = [ "CoreLocation.framework" ]
 }
 
-source_set("font_size_java_script_feature") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  sources = [ "font_size_java_script_feature.h" ]
-  public_deps = [ "//ios/chrome/browser/web/font_size" ]
-}
-
 group("provider_api") {
   deps = [
     # The individual APIs.
diff --git a/ios/public/provider/chrome/browser/DEPS b/ios/public/provider/chrome/browser/DEPS
index 65f0b72..4099d13 100644
--- a/ios/public/provider/chrome/browser/DEPS
+++ b/ios/public/provider/chrome/browser/DEPS
@@ -12,9 +12,3 @@
   # provider is not allowed to depends on //ios/chrome.
   "-ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h",
 ]
-
-specific_include_rules = {
-  "font_size_java_script_feature.h": [
-    "+ios/chrome/browser/web/font_size/font_size_java_script_feature.h"
-  ]
-}
diff --git a/ios/public/provider/chrome/browser/font_size_java_script_feature.h b/ios/public/provider/chrome/browser/font_size_java_script_feature.h
deleted file mode 100644
index 75dc7f24..0000000
--- a/ios/public/provider/chrome/browser/font_size_java_script_feature.h
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2022 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 IOS_PUBLIC_PROVIDER_CHROME_BROWSER_FONT_SIZE_JAVA_SCRIPT_FEATURE_H_
-#define IOS_PUBLIC_PROVIDER_CHROME_BROWSER_FONT_SIZE_JAVA_SCRIPT_FEATURE_H_
-
-// This is a forwarding header to allow renaming this file without breaking
-// the internal build. It will be removed once the internal repository has
-// been converted to use the new path.
-#import "ios/chrome/browser/web/font_size/font_size_java_script_feature.h"
-
-#endif  // IOS_PUBLIC_PROVIDER_CHROME_BROWSER_FONT_SIZE_JAVA_SCRIPT_FEATURE_H_
diff --git a/ipc/ipc_message_macros.h b/ipc/ipc_message_macros.h
index 02d7e9f..514097a8 100644
--- a/ipc/ipc_message_macros.h
+++ b/ipc/ipc_message_macros.h
@@ -327,20 +327,20 @@
       base::MD5Hash32Constexpr(IPC_TASK_ANNOTATOR_STRINGIFY(msg_class)); \
   base::TaskAnnotator::ScopedSetIpcHash scoped_ipc_hash(kMessageHash);
 
-#define IPC_BEGIN_MESSAGE_MAP(class_name, msg) \
-  { \
-    typedef class_name _IpcMessageHandlerClass ALLOW_UNUSED_TYPE; \
-    void* param__ = NULL; \
-    (void)param__; \
-    const IPC::Message& ipc_message__ = msg; \
+#define IPC_BEGIN_MESSAGE_MAP(class_name, msg)                   \
+  {                                                              \
+    using _IpcMessageHandlerClass [[maybe_unused]] = class_name; \
+    [[maybe_unused]] void* param__ = nullptr;                    \
+    const IPC::Message& ipc_message__ = msg;                     \
     switch (ipc_message__.type()) {
 
-#define IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(class_name, msg, param)  \
-  {                                                               \
-    typedef class_name _IpcMessageHandlerClass ALLOW_UNUSED_TYPE; \
-    decltype(param) param__ = param;                              \
-    const IPC::Message& ipc_message__ = msg;                      \
+#define IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(class_name, msg, param) \
+  {                                                              \
+    using _IpcMessageHandlerClass [[maybe_unused]] = class_name; \
+    decltype(param) param__ = param;                             \
+    const IPC::Message& ipc_message__ = msg;                     \
     switch (ipc_message__.type()) {
+
 #define IPC_MESSAGE_FORWARD(msg_class, obj, member_func)         \
   case msg_class::ID: {                                          \
     IPC_TASK_ANNOTATOR_CONTEXT(msg_class)                        \
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h
index 1d7d99d0..e7b7634 100644
--- a/ipc/ipc_message_utils.h
+++ b/ipc/ipc_message_utils.h
@@ -10,7 +10,6 @@
 #include <stdint.h>
 
 #include <algorithm>
-#include <bitset>
 #include <map>
 #include <memory>
 #include <set>
@@ -21,7 +20,6 @@
 
 #include "base/component_export.h"
 #include "base/containers/flat_map.h"
-#include "base/containers/small_map.h"
 #include "base/containers/stack_container.h"
 #include "base/files/file.h"
 #include "base/format_macros.h"
@@ -421,42 +419,6 @@
   }
 };
 
-template <std::size_t N>
-struct ParamTraits<std::bitset<N>> {
-  typedef std::bitset<N> param_type;
-  static void Write(base::Pickle* m, const param_type& p) {
-    WriteParam(m, base::checked_cast<int>(p.size()));
-    for (size_t i = 0; i < p.size(); i++)
-      WriteParam(m, p.test(i));
-  }
-
-  static bool Read(const base::Pickle* m,
-                   base::PickleIterator* iter,
-                   param_type* r) {
-    int size;
-    // ReadLength() checks for < 0 itself.
-    if (!iter->ReadLength(&size))
-      return false;
-    if (static_cast<size_t>(size) != r->size())
-      return false;
-    for (size_t i = 0; i < r->size(); i++) {
-      bool value;
-      if (!ReadParam(m, iter, &value))
-        return false;
-      (*r)[i] = value;
-    }
-    return true;
-  }
-
-  static void Log(const param_type& p, std::string* l) {
-    for (size_t i = 0; i < p.size(); ++i) {
-      if (i != 0)
-        l->push_back(' ');
-      LogParam(p.test(i), l);
-    }
-  }
-};
-
 template <class P>
 struct ParamTraits<std::set<P> > {
   typedef std::set<P> param_type;
@@ -918,43 +880,6 @@
   }
 };
 
-template <typename NormalMap,
-          int kArraySize,
-          typename EqualKey,
-          typename MapInit>
-struct ParamTraits<base::small_map<NormalMap, kArraySize, EqualKey, MapInit>> {
-  using param_type = base::small_map<NormalMap, kArraySize, EqualKey, MapInit>;
-  using K = typename param_type::key_type;
-  using V = typename param_type::data_type;
-  static void Write(base::Pickle* m, const param_type& p) {
-    WriteParam(m, base::checked_cast<int>(p.size()));
-    typename param_type::const_iterator iter;
-    for (iter = p.begin(); iter != p.end(); ++iter) {
-      WriteParam(m, iter->first);
-      WriteParam(m, iter->second);
-    }
-  }
-  static bool Read(const base::Pickle* m,
-                   base::PickleIterator* iter,
-                   param_type* r) {
-    int size;
-    if (!iter->ReadLength(&size))
-      return false;
-    for (int i = 0; i < size; ++i) {
-      K key;
-      if (!ReadParam(m, iter, &key))
-        return false;
-      V& value = (*r)[key];
-      if (!ReadParam(m, iter, &value))
-        return false;
-    }
-    return true;
-  }
-  static void Log(const param_type& p, std::string* l) {
-    l->append("<base::small_map>");
-  }
-};
-
 template <class Key, class Mapped, class Compare>
 struct ParamTraits<base::flat_map<Key, Mapped, Compare>> {
   using param_type = base::flat_map<Key, Mapped, Compare>;
diff --git a/media/base/status.cc b/media/base/status.cc
index 75bbde2..884f541 100644
--- a/media/base/status.cc
+++ b/media/base/status.cc
@@ -54,10 +54,16 @@
   frames.push_back(MediaSerialize(location));
 }
 
+std::ostream& operator<<(std::ostream& stream,
+                         const OkStatusImplicitConstructionHelper&) {
+  stream << "kOk";
+  return stream;
+}
+
 }  // namespace internal
 
-Status OkStatus() {
-  return Status(StatusCode::kOk);
+internal::OkStatusImplicitConstructionHelper OkStatus() {
+  return {};
 }
 
 }  // namespace media
diff --git a/media/base/status.h b/media/base/status.h
index 8b27fc0..965001b 100644
--- a/media/base/status.h
+++ b/media/base/status.h
@@ -121,6 +121,22 @@
 template <typename T>
 constexpr bool DoesHaveOkCode = OkStatusDetectorHelper<T, T>::has_ok;
 
+// Implicitly converts to `kOk` TypedStatus, for any traits.  Also converts to
+// the enum code 'kOk', for any enum that has a 'kOk'.
+struct OkStatusImplicitConstructionHelper {
+  template <typename T>
+  operator T() const {
+    return T::kOk;
+  }
+};
+
+// For gtest, so it can print this.  Otherwise, it tries to convert to an
+// integer for printing.  That'd be okay, except our implicit cast matches the
+// attempt to convert to long long, and tries to get `T::kOk` for `long long`.
+MEDIA_EXPORT std::ostream& operator<<(
+    std::ostream& stream,
+    const OkStatusImplicitConstructionHelper&);
+
 }  // namespace internal
 
 // See media/base/status.md for details and instructions for using TypedStatus.
@@ -156,6 +172,10 @@
   // default constructor to please the Mojo Gods.
   TypedStatus() = default;
 
+  // For TypedStatus(OkStatus())
+  TypedStatus(const internal::OkStatusImplicitConstructionHelper&)
+      : TypedStatus(Codes::kOk) {}
+
   // Constructor to create a new TypedStatus from a numeric code & message.
   // These are immutable; if you'd like to change them, then you likely should
   // create a new TypedStatus.
@@ -455,7 +475,7 @@
 // Convenience function to return |kOk|.
 // OK won't have a message, trace, or data associated with them, and DCHECK
 // if they are added.
-MEDIA_EXPORT Status OkStatus();
+MEDIA_EXPORT internal::OkStatusImplicitConstructionHelper OkStatus();
 
 }  // namespace media
 
diff --git a/media/base/status_unittest.cc b/media/base/status_unittest.cc
index 77ba3b1..192f500 100644
--- a/media/base/status_unittest.cc
+++ b/media/base/status_unittest.cc
@@ -55,6 +55,16 @@
   }
 };
 
+enum class NonZeroOkType : StatusCodeType {
+  kOk = 100,
+  kFoo = 101,
+};
+
+struct NonZeroOkTypeTraits {
+  using Codes = NonZeroOkType;
+  static constexpr StatusGroupType Group() { return "GroupWithNonZeroOkType"; }
+};
+
 struct MapValueCodeTraits {
   enum class Codes { kBadStartCode, kBadPtr, kLTZ, kNotSquare };
   static constexpr StatusGroupType Group() {
@@ -391,6 +401,29 @@
   EXPECT_TRUE(TypedStatus<NoDefaultHasOkTypeTraits>(
                   NoDefaultHasOkTypeTraits::Codes::kOk)
                   .is_ok());
+
+  EXPECT_FALSE(
+      TypedStatus<NonZeroOkTypeTraits>(NonZeroOkTypeTraits::Codes::kFoo)
+          .is_ok());
+  EXPECT_TRUE(TypedStatus<NonZeroOkTypeTraits>(NonZeroOkTypeTraits::Codes::kOk)
+                  .is_ok());
+}
+
+TEST_F(StatusTest, CanConvertOkToCode) {
+  // OkStatus() should also be convertible to the enum directly.
+  NoDefaultHasOkTypeTraits::Codes code = OkStatus();
+  EXPECT_EQ(code, NoDefaultHasOkTypeTraits::Codes::kOk);
+}
+
+TEST_F(StatusTest, OkStatusInitializesToOk) {
+  // Construction from the return value of OkStatus() should be `kOk`, for any
+  // status traits that has `kOk`.  We only test explicit construction, though
+  // this is probably used as an implicit construction in practice when it's
+  // a return value.
+  EXPECT_EQ(TypedStatus<NoDefaultHasOkTypeTraits>(OkStatus()).code(),
+            NoDefaultHasOkTypeTraits::Codes::kOk);
+  EXPECT_EQ(TypedStatus<NonZeroOkTypeTraits>(OkStatus()).code(),
+            NonZeroOkTypeTraits::Codes::kOk);
 }
 
 TEST_F(StatusTest, StatusOrEqOp) {
diff --git a/media/base/test_helpers.h b/media/base/test_helpers.h
index 8e1b4f21..04da5a9b 100644
--- a/media/base/test_helpers.h
+++ b/media/base/test_helpers.h
@@ -234,9 +234,19 @@
 std::unique_ptr<::testing::StrictMock<MockDemuxerStream>>
 CreateMockDemuxerStream(DemuxerStream::Type type, bool encrypted);
 
-// Compares two media::Status by StatusCode only.
+// Compares two media::Status by StatusCode only.  Also allows the ok helper to
+// match kOk.  It's a special case because we don't know the TypedStatus traits
+// we'll be comparing against until now.
 MATCHER_P(SameStatusCode, status, "") {
-  return arg.code() == status.code();
+  if constexpr (std::is_convertible<
+                    decltype(status),
+                    const internal::OkStatusImplicitConstructionHelper&>::
+                    value) {
+    // Cast to the correct enum type to match whatever we're compared against.
+    return arg.code() == static_cast<decltype(arg.code())>(status);
+  } else {
+    return arg.code() == status.code();
+  }
 }
 
 // Compares an `arg` Status.code() to a test-supplied StatusCode.
diff --git a/media/filters/decoder_selector_unittest.cc b/media/filters/decoder_selector_unittest.cc
index 4a6a59e..8bfbf8b1 100644
--- a/media/filters/decoder_selector_unittest.cc
+++ b/media/filters/decoder_selector_unittest.cc
@@ -73,9 +73,9 @@
     case kAlwaysFail:
       return StatusCode::kCodeOnlyForTesting;
     case kClearOnly:
-      return is_encrypted ? StatusCode::kCodeOnlyForTesting : OkStatus();
+      return is_encrypted ? StatusCode::kCodeOnlyForTesting : StatusCode::kOk;
     case kEncryptedOnly:
-      return is_encrypted ? OkStatus() : StatusCode::kCodeOnlyForTesting;
+      return is_encrypted ? StatusCode::kOk : StatusCode::kCodeOnlyForTesting;
     case kAlwaysSucceed:
       return OkStatus();
   }
diff --git a/media/gpu/h264_decoder.cc b/media/gpu/h264_decoder.cc
index 8875782..58be2c7f 100644
--- a/media/gpu/h264_decoder.cc
+++ b/media/gpu/h264_decoder.cc
@@ -1058,9 +1058,8 @@
     if (!(*output_candidate)->ref) {
       // Current picture hasn't been inserted into DPB yet, so don't remove it
       // if we managed to output it immediately.
-      int outputted_poc = (*output_candidate)->pic_order_cnt;
-      if (outputted_poc != pic->pic_order_cnt)
-        dpb_.DeleteByPOC(outputted_poc);
+      if (*output_candidate != pic)
+        dpb_.Delete(*output_candidate);
     }
 
     ++output_candidate;
diff --git a/media/gpu/h264_dpb.cc b/media/gpu/h264_dpb.cc
index 2e6f852..6a677547 100644
--- a/media/gpu/h264_dpb.cc
+++ b/media/gpu/h264_dpb.cc
@@ -82,15 +82,15 @@
   }
 }
 
-void H264DPB::DeleteByPOC(int poc) {
+void H264DPB::Delete(scoped_refptr<H264Picture> pic) {
   for (auto it = pics_.begin(); it != pics_.end(); ++it) {
-    if ((*it)->pic_order_cnt == poc) {
+    if ((*it) == pic) {
       pics_.erase(it);
       UpdatePicPositions();
       return;
     }
   }
-  NOTREACHED() << "Missing POC: " << poc;
+  NOTREACHED() << "Missing pic with POC: " << pic->pic_order_cnt;
 }
 
 void H264DPB::DeleteUnused() {
diff --git a/media/gpu/h264_dpb.h b/media/gpu/h264_dpb.h
index 6c43ac5..a68b96b 100644
--- a/media/gpu/h264_dpb.h
+++ b/media/gpu/h264_dpb.h
@@ -119,8 +119,8 @@
   // and free it.
   void DeleteUnused();
 
-  // Remove a picture by its pic_order_cnt and free it.
-  void DeleteByPOC(int poc);
+  // Remove a picture from DPB and free it.
+  void Delete(scoped_refptr<H264Picture> pic);
 
   // Clear DPB.
   void Clear();
diff --git a/media/mojo/mojom/speech_recognition_service.mojom b/media/mojo/mojom/speech_recognition_service.mojom
index d55af032..c7046d30 100644
--- a/media/mojo/mojom/speech_recognition_service.mojom
+++ b/media/mojo/mojom/speech_recognition_service.mojom
@@ -80,6 +80,12 @@
   // to the originating media.
   SendAudioToSpeechRecognitionService(AudioDataS16 buffer);
 
+  // Mark audio stream done. This informs the speech recognition client to stop
+  // speech recognition after it finishes processing the audio it has received
+  // already. This will eventually trigger the
+  // SpeechRecognitionRecognizerClient::OnSpeechRecognitionStopped callback.
+  MarkDone();
+
   // Notify the speech recognition recognizer that the language changed. Takes
   // in the locale string (e.g. "en-US").
   OnLanguageChanged(string language);
@@ -96,6 +102,9 @@
   OnSpeechRecognitionRecognitionEvent(SpeechRecognitionResult result)
       => (bool success);
 
+  // Called when speech recognition stops.
+  OnSpeechRecognitionStopped();
+
   // Triggered by an error within the speech recognition service.
   OnSpeechRecognitionError();
 
diff --git a/media/renderers/audio_renderer_impl_unittest.cc b/media/renderers/audio_renderer_impl_unittest.cc
index 1b211db..ca882f7 100644
--- a/media/renderers/audio_renderer_impl_unittest.cc
+++ b/media/renderers/audio_renderer_impl_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/format_macros.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/single_thread_task_runner.h"
@@ -19,13 +20,16 @@
 #include "base/test/simple_test_tick_clock.h"
 #include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
 #include "media/base/audio_buffer_converter.h"
+#include "media/base/audio_bus.h"
 #include "media/base/fake_audio_renderer_sink.h"
 #include "media/base/media_client.h"
 #include "media/base/media_switches.h"
 #include "media/base/media_util.h"
 #include "media/base/mock_audio_renderer_sink.h"
 #include "media/base/mock_filters.h"
+#include "media/base/speech_recognition_client.h"
 #include "media/base/test_helpers.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -84,7 +88,9 @@
   ASSERT_FALSE(test->ended());
 }
 
-class AudioRendererImplTest : public ::testing::Test, public RendererClient {
+class AudioRendererImplTest : public ::testing::Test,
+                              public RendererClient,
+                              public SpeechRecognitionClient {
  public:
   std::vector<std::unique_ptr<AudioDecoder>> CreateAudioDecoderForTest() {
     auto decoder = std::make_unique<MockAudioDecoder>();
@@ -133,7 +139,7 @@
         main_thread_task_runner_, sink_.get(),
         base::BindRepeating(&AudioRendererImplTest::CreateAudioDecoderForTest,
                             base::Unretained(this)),
-        &media_log_, nullptr);
+        &media_log_, this);
     renderer_->tick_clock_ = &tick_clock_;
     tick_clock_.Advance(base::Seconds(1));
   }
@@ -208,6 +214,8 @@
     ConfigureDemuxerStream(true);
   }
 
+  void EnableSpeechRecognition() { renderer_->EnableSpeechRecognition(); }
+
   // RendererClient implementation.
   MOCK_METHOD1(OnError, void(PipelineStatus));
   void OnEnded() override {
@@ -227,7 +235,13 @@
   MOCK_METHOD1(OnVideoFrameRateChange, void(absl::optional<int>));
   MOCK_METHOD1(OnDurationChange, void(base::TimeDelta));
   MOCK_METHOD1(OnRemotePlayStateChange, void(MediaStatus::State state));
-  MOCK_METHOD1(TranscribeAudioCallback, void(scoped_refptr<AudioBuffer>));
+  MOCK_METHOD1(TranscribeAudio, void(scoped_refptr<AudioBuffer>));
+
+  // SpeechRecognitionClient implementation.
+  MOCK_METHOD1(AddAudio, void(scoped_refptr<AudioBuffer>));
+  MOCK_METHOD3(AddAudio, void(std::unique_ptr<AudioBus>, int, ChannelLayout));
+  MOCK_METHOD0(IsSpeechRecognitionAvailable, bool());
+  MOCK_METHOD1(SetOnReadyCallback, void(OnReadyCallback));
 
   void InitializeRenderer(DemuxerStream* demuxer_stream,
                           PipelineStatusCallback pipeline_status_cb) {
@@ -265,7 +279,7 @@
         main_thread_task_runner_, sink_.get(),
         base::BindRepeating(&AudioRendererImplTest::CreateAudioDecoderForTest,
                             base::Unretained(this)),
-        &media_log_, nullptr);
+        &media_log_, this);
 
     Initialize();
   }
@@ -682,14 +696,6 @@
   WaitForPendingRead();
 }
 
-TEST_F(AudioRendererImplTest, TranscribeAudioCallback) {
-  Initialize();
-  EXPECT_CALL(*this, TranscribeAudioCallback(_)).Times(0);
-
-  Preroll();
-  StartTicking();
-}
-
 TEST_F(AudioRendererImplTest, EndOfStream) {
   Initialize();
   Preroll();
@@ -1744,4 +1750,66 @@
   renderer_->decoded_audio_ready_for_testing();
 }
 
+#if !defined(OS_ANDROID)
+TEST_F(AudioRendererImplTest,
+       TranscribeAudioCallback_SpeechRecognitionDisabled) {
+  EXPECT_CALL(*this, SetOnReadyCallback(_));
+  Initialize();
+
+  EXPECT_CALL(*this, AddAudio(_)).Times(0);
+  Preroll();
+
+  StartTicking();
+}
+
+TEST_F(AudioRendererImplTest,
+       TranscribeAudioCallback_Muted_WithoutUserActivation) {
+  EnableSpeechRecognition();
+  EXPECT_CALL(*this, SetOnReadyCallback(_));
+  Initialize();
+
+  EXPECT_CALL(*this, AddAudio(_)).Times(0);
+  Preroll();
+
+  StartTicking();
+}
+
+TEST_F(AudioRendererImplTest,
+       TranscribeAudioCallback_Unmuted_WithoutUserActivation) {
+  EnableSpeechRecognition();
+
+  EXPECT_CALL(*this, SetOnReadyCallback(_));
+  Initialize();
+
+  EXPECT_CALL(*this, AddAudio(_)).Times(3);
+  next_timestamp_->SetBaseTimestamp(base::TimeDelta());
+  renderer_->SetMediaTime(base::TimeDelta());
+  renderer_->StartPlaying();
+  renderer_->SetVolume(1);
+  WaitForPendingRead();
+
+  EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH,
+                                            BUFFERING_CHANGE_REASON_UNKNOWN));
+  DeliverRemainingAudio();
+  StartTicking();
+
+  EXPECT_EQ(renderer_->was_unmuted_for_testing(), 1);
+}
+
+TEST_F(AudioRendererImplTest,
+       TranscribeAudioCallback_Muted_WithUserActivation) {
+  EnableSpeechRecognition();
+  renderer_->SetWasPlayedWithUserActivation(true);
+
+  EXPECT_CALL(*this, SetOnReadyCallback(_));
+  Initialize();
+
+  EXPECT_CALL(*this, AddAudio(_)).Times(3);
+  Preroll();
+
+  StartTicking();
+  EXPECT_EQ(renderer_->was_unmuted_for_testing(), 1);
+}
+#endif
+
 }  // namespace media
diff --git a/net/base/address_list.h b/net/base/address_list.h
index 6542413..03aa6f70 100644
--- a/net/base/address_list.h
+++ b/net/base/address_list.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/compiler_specific.h"
@@ -51,6 +52,12 @@
   // Returns a copy of `list` with port on each element set to |port|.
   static AddressList CopyWithPort(const AddressList& list, uint16_t port);
 
+  bool operator==(const AddressList& other) const {
+    return std::tie(endpoints_, dns_aliases_) ==
+           std::tie(other.endpoints_, other.dns_aliases_);
+  }
+  bool operator!=(const AddressList& other) const { return !(*this == other); }
+
   // TODO(crbug.com/126134): Remove all references to canonical name
   // in net::AddressList.
   // Here and below, by "canonical name", we mean the value of the name for
diff --git a/net/base/connection_endpoint_metadata.cc b/net/base/connection_endpoint_metadata.cc
index 6c2917b..32d513b 100644
--- a/net/base/connection_endpoint_metadata.cc
+++ b/net/base/connection_endpoint_metadata.cc
@@ -4,8 +4,20 @@
 
 #include "net/base/connection_endpoint_metadata.h"
 
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/values.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
 namespace net {
 
+namespace {
+const char kSupportedProtocolAlpnsKey[] = "supported_protocol_alpns";
+const char kEchConfigListKey[] = "ech_config_list";
+}  // namespace
+
 ConnectionEndpointMetadata::ConnectionEndpointMetadata() = default;
 ConnectionEndpointMetadata::~ConnectionEndpointMetadata() = default;
 ConnectionEndpointMetadata::ConnectionEndpointMetadata(
@@ -13,4 +25,46 @@
 ConnectionEndpointMetadata::ConnectionEndpointMetadata(
     ConnectionEndpointMetadata&&) = default;
 
+base::Value ConnectionEndpointMetadata::ToValue() const {
+  base::Value::DictStorage dict;
+
+  base::Value::ListStorage alpns_list;
+  for (const std::string& alpn : supported_protocol_alpns) {
+    alpns_list.emplace_back(alpn);
+  }
+  dict.emplace(kSupportedProtocolAlpnsKey, std::move(alpns_list));
+
+  dict.emplace(kEchConfigListKey, ech_config_list);
+
+  return base::Value(std::move(dict));
+}
+
+// static
+absl::optional<ConnectionEndpointMetadata>
+ConnectionEndpointMetadata::FromValue(const base::Value& value) {
+  if (!value.is_dict())
+    return absl::nullopt;
+
+  const base::Value* alpns_value =
+      value.FindListKey(kSupportedProtocolAlpnsKey);
+  const std::vector<uint8_t>* ech_config_list_value =
+      value.FindBlobKey(kEchConfigListKey);
+
+  if (!alpns_value || !ech_config_list_value)
+    return absl::nullopt;
+
+  ConnectionEndpointMetadata metadata;
+
+  std::vector<std::string> alpns;
+  for (const base::Value& value : alpns_value->GetList()) {
+    if (!value.is_string())
+      return absl::nullopt;
+    metadata.supported_protocol_alpns.push_back(value.GetString());
+  }
+
+  metadata.ech_config_list = *ech_config_list_value;
+
+  return metadata;
+}
+
 }  // namespace net
diff --git a/net/base/connection_endpoint_metadata.h b/net/base/connection_endpoint_metadata.h
index fb450166..0465537 100644
--- a/net/base/connection_endpoint_metadata.h
+++ b/net/base/connection_endpoint_metadata.h
@@ -8,13 +8,18 @@
 #include <stdint.h>
 
 #include <string>
+#include <tuple>
 #include <vector>
 
+#include "base/values.h"
+#include "net/base/net_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
 namespace net {
 
 // Metadata used to create UDP/TCP/TLS/etc connections or information about such
 // a connection.
-struct ConnectionEndpointMetadata {
+struct NET_EXPORT_PRIVATE ConnectionEndpointMetadata {
   // Expected to be parsed/consumed only by BoringSSL code and thus passed
   // around here only as a raw byte array.
   using EchConfigList = std::vector<uint8_t>;
@@ -28,6 +33,15 @@
   ConnectionEndpointMetadata(ConnectionEndpointMetadata&&);
   ConnectionEndpointMetadata& operator=(ConnectionEndpointMetadata&&) = default;
 
+  bool operator==(const ConnectionEndpointMetadata& other) const {
+    return std::tie(supported_protocol_alpns, ech_config_list) ==
+           std::tie(other.supported_protocol_alpns, other.ech_config_list);
+  }
+
+  base::Value ToValue() const;
+  static absl::optional<ConnectionEndpointMetadata> FromValue(
+      const base::Value& value);
+
   // ALPN strings for protocols supported by the endpoint. Empty for default
   // non-protocol endpoint.
   std::vector<std::string> supported_protocol_alpns;
diff --git a/net/base/features.cc b/net/base/features.cc
index 6b5c2fd..781610c 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -228,12 +228,6 @@
     "TimeoutTcpConnectAttemptMax",
     base::Seconds(30));
 
-constexpr base::Feature kFirstPartySets{"FirstPartySets",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
-
-const base::FeatureParam<bool> kFirstPartySetsIsDogfooder{
-    &kFirstPartySets, "FirstPartySetsIsDogfooder", false};
-
 #if BUILDFLAG(ENABLE_REPORTING)
 const base::Feature kDocumentReporting{"DocumentReporting",
                                        base::FEATURE_ENABLED_BY_DEFAULT};
@@ -257,9 +251,6 @@
 const base::Feature kExtraCookieValidityChecks{
     "ExtraCookieValidityChecks", base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kFirstPartySetsV2ComponentFormat{
-    "FirstPartySetsV2ComponentFormat", base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kRecordRadioWakeupTrigger{
     "RecordRadioWakeupTrigger", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/net/base/features.h b/net/base/features.h
index 6906360e..de3606b 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -335,13 +335,6 @@
 NET_EXPORT extern const base::FeatureParam<base::TimeDelta>
     kTimeoutTcpConnectAttemptMax;
 
-// Enables usage of First Party Sets to determine cookie availability.
-NET_EXPORT extern const base::Feature kFirstPartySets;
-
-// Controls whether the client is considered a dogfooder for the FirstPartySets
-// feature.
-NET_EXPORT extern const base::FeatureParam<bool> kFirstPartySetsIsDogfooder;
-
 #if BUILDFLAG(ENABLE_REPORTING)
 // When enabled this feature will allow a new Reporting-Endpoints header to
 // configure reporting endpoints for report delivery. This is used to support
@@ -385,10 +378,6 @@
 // feature flag, assuming no breakage occurs with it enabled.
 NET_EXPORT extern const base::Feature kExtraCookieValidityChecks;
 
-// When enabled, the client will opt in to the V2 component format for the
-// First-Party Sets component.
-NET_EXPORT extern const base::Feature kFirstPartySetsV2ComponentFormat;
-
 // Enable recording UMAs for network activities which can wake-up radio on
 // Android.
 NET_EXPORT extern const base::Feature kRecordRadioWakeupTrigger;
diff --git a/net/cert/cert_verify_proc.cc b/net/cert/cert_verify_proc.cc
index c02bb5af..8f89b392 100644
--- a/net/cert/cert_verify_proc.cc
+++ b/net/cert/cert_verify_proc.cc
@@ -861,9 +861,9 @@
       // Not a real certificate - just for testing.
       // net/data/ssl/certificates/name_constraint_*.pem
       {
-          {{0x46, 0xef, 0xf4, 0xf1, 0x1b, 0xb6, 0xef, 0x96, 0x65, 0x7f, 0x8d,
-            0xac, 0x6c, 0x8e, 0xa5, 0xaa, 0x2d, 0x8e, 0x52, 0xe9, 0xf7, 0xaf,
-            0x86, 0x20, 0xae, 0xb2, 0xbf, 0xbc, 0x9d, 0xfe, 0x63, 0x39}},
+          {{0xa2, 0x2a, 0x88, 0x82, 0xba, 0x0c, 0xae, 0x9d, 0xf2, 0xc4, 0x5b,
+            0x15, 0xa6, 0x1e, 0xfd, 0xfd, 0x19, 0x6b, 0xb1, 0x09, 0x19, 0xfd,
+            0xac, 0x77, 0x9b, 0xd6, 0x08, 0x66, 0xda, 0xa8, 0xd2, 0x88}},
           kDomainsTest,
       },
   };
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index 8e3e26a2..d40384c 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -351,17 +351,20 @@
 }  // namespace
 
 CookieMonster::CookieMonster(scoped_refptr<PersistentCookieStore> store,
-                             NetLog* net_log)
+                             NetLog* net_log,
+                             bool first_party_sets_enabled)
     : CookieMonster(std::move(store),
                     base::Seconds(kDefaultAccessUpdateThresholdSeconds),
-                    net_log) {}
+                    net_log,
+                    first_party_sets_enabled) {}
 
 CookieMonster::CookieMonster(scoped_refptr<PersistentCookieStore> store,
                              base::TimeDelta last_access_threshold,
-                             NetLog* net_log)
+                             NetLog* net_log,
+                             bool first_party_sets_enabled)
     : num_keys_(0u),
       num_partitioned_cookies_(0u),
-      change_dispatcher_(this),
+      change_dispatcher_(this, first_party_sets_enabled),
       initialized_(false),
       started_fetching_all_cookies_(false),
       finished_fetching_all_cookies_(false),
@@ -370,12 +373,13 @@
       store_(std::move(store)),
       last_access_threshold_(last_access_threshold),
       last_statistic_record_time_(base::Time::Now()),
-      persist_session_cookies_(false) {
+      persist_session_cookies_(false),
+      first_party_sets_enabled_(first_party_sets_enabled) {
   cookieable_schemes_.insert(
       cookieable_schemes_.begin(), kDefaultCookieableSchemes,
       kDefaultCookieableSchemes + kDefaultCookieableSchemesCount);
   net_log_.BeginEvent(NetLogEventType::COOKIE_STORE_ALIVE, [&] {
-    return NetLogCookieMonsterConstructorParams(store != nullptr);
+    return NetLogCookieMonsterConstructorParams(store_ != nullptr);
   });
 }
 
@@ -1186,7 +1190,8 @@
         CookieAccessParams{
             GetAccessSemanticsForCookie(*cookie_ptr),
             delegate_treats_url_as_trustworthy,
-            cookie_util::GetSamePartyStatus(*cookie_ptr, options)});
+            cookie_util::GetSamePartyStatus(*cookie_ptr, options,
+                                            first_party_sets_enabled_)});
 
     if (!access_result.status.IsInclude()) {
       UMA_HISTOGRAM_BOOLEAN(
@@ -1478,7 +1483,8 @@
       source_url, options,
       CookieAccessParams(GetAccessSemanticsForCookie(*cc),
                          delegate_treats_url_as_trustworthy,
-                         cookie_util::GetSamePartyStatus(*cc, options)),
+                         cookie_util::GetSamePartyStatus(
+                             *cc, options, first_party_sets_enabled_)),
       cookieable_schemes_);
 
   const std::string key(GetKey(cc->Domain()));
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index 98ff48a..10c69ec 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -153,13 +153,15 @@
   // monster's existence. If |store| is NULL, then no backing store will be
   // updated. |net_log| must outlive the CookieMonster and can be null.
   CookieMonster(scoped_refptr<PersistentCookieStore> store,
-                NetLog* net_log);
+                NetLog* net_log,
+                bool first_party_sets_enabled);
 
   // Only used during unit testing.
   // |net_log| must outlive the CookieMonster.
   CookieMonster(scoped_refptr<PersistentCookieStore> store,
                 base::TimeDelta last_access_threshold,
-                NetLog* net_log);
+                NetLog* net_log,
+                bool first_party_sets_enabled);
 
   CookieMonster(const CookieMonster&) = delete;
   CookieMonster& operator=(const CookieMonster&) = delete;
@@ -751,6 +753,8 @@
 
   bool persist_session_cookies_;
 
+  bool first_party_sets_enabled_;
+
   base::ThreadChecker thread_checker_;
 
   base::WeakPtrFactory<CookieMonster> weak_ptr_factory_{this};
diff --git a/net/cookies/cookie_monster_change_dispatcher.cc b/net/cookies/cookie_monster_change_dispatcher.cc
index f724199..10384e3 100644
--- a/net/cookies/cookie_monster_change_dispatcher.cc
+++ b/net/cookies/cookie_monster_change_dispatcher.cc
@@ -36,6 +36,7 @@
     std::string name_key,
     GURL url,
     absl::optional<CookiePartitionKey> cookie_partition_key,
+    const bool first_party_sets_enabled,
     net::CookieChangeCallback callback)
     : change_dispatcher_(std::move(change_dispatcher)),
       domain_key_(std::move(domain_key)),
@@ -43,6 +44,7 @@
       url_(std::move(url)),
       cookie_partition_key_(std::move(cookie_partition_key)),
       callback_(std::move(callback)),
+      first_party_sets_enabled_(first_party_sets_enabled),
       task_runner_(base::ThreadTaskRunnerHandle::Get()) {
   DCHECK(url_.is_valid() || url_.is_empty());
   DCHECK_EQ(url_.is_empty(), domain_key_ == kGlobalDomainKey);
@@ -71,8 +73,8 @@
         cookie_access_delegate &&
         cookie_access_delegate->ShouldTreatUrlAsTrustworthy(url_);
     CookieOptions options = CookieOptions::MakeAllInclusive();
-    CookieSamePartyStatus same_party_status =
-        cookie_util::GetSamePartyStatus(cookie, options);
+    CookieSamePartyStatus same_party_status = cookie_util::GetSamePartyStatus(
+        cookie, options, first_party_sets_enabled_);
     if (!cookie
              .IncludeForRequestURL(
                  url_, options,
@@ -103,8 +105,10 @@
 }
 
 CookieMonsterChangeDispatcher::CookieMonsterChangeDispatcher(
-    const CookieMonster* cookie_monster)
-    : cookie_monster_(cookie_monster) {}
+    const CookieMonster* cookie_monster,
+    const bool first_party_sets_enabled)
+    : cookie_monster_(cookie_monster),
+      first_party_sets_enabled_(first_party_sets_enabled) {}
 
 CookieMonsterChangeDispatcher::~CookieMonsterChangeDispatcher() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -145,7 +149,7 @@
 
   std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>(
       weak_ptr_factory_.GetWeakPtr(), DomainKey(url), NameKey(name), url,
-      cookie_partition_key, std::move(callback));
+      cookie_partition_key, first_party_sets_enabled_, std::move(callback));
 
   LinkSubscription(subscription.get());
   return subscription;
@@ -161,7 +165,7 @@
   std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>(
       weak_ptr_factory_.GetWeakPtr(), DomainKey(url),
       std::string(kGlobalNameKey), url, cookie_partition_key,
-      std::move(callback));
+      first_party_sets_enabled_, std::move(callback));
 
   LinkSubscription(subscription.get());
   return subscription;
@@ -175,7 +179,7 @@
   std::unique_ptr<Subscription> subscription = std::make_unique<Subscription>(
       weak_ptr_factory_.GetWeakPtr(), std::string(kGlobalDomainKey),
       std::string(kGlobalNameKey), GURL(""), CookiePartitionKey::Todo(),
-      std::move(callback));
+      first_party_sets_enabled_, std::move(callback));
 
   LinkSubscription(subscription.get());
   return subscription;
diff --git a/net/cookies/cookie_monster_change_dispatcher.h b/net/cookies/cookie_monster_change_dispatcher.h
index bcb0e0ac..24d7320 100644
--- a/net/cookies/cookie_monster_change_dispatcher.h
+++ b/net/cookies/cookie_monster_change_dispatcher.h
@@ -33,7 +33,8 @@
       base::RepeatingCallbackList<void(const CookieChangeInfo&)>;
 
   // Expects |cookie_monster| to outlive this.
-  explicit CookieMonsterChangeDispatcher(const CookieMonster* cookie_monster);
+  CookieMonsterChangeDispatcher(const CookieMonster* cookie_monster,
+                                bool first_party_sets_enabled);
 
   CookieMonsterChangeDispatcher(const CookieMonsterChangeDispatcher&) = delete;
   CookieMonsterChangeDispatcher& operator=(
@@ -78,6 +79,7 @@
                  std::string name_key,
                  GURL url,
                  absl::optional<CookiePartitionKey> cookie_partition_key,
+                 const bool first_party_sets_enabled,
                  net::CookieChangeCallback callback);
 
     Subscription(const Subscription&) = delete;
@@ -106,6 +108,7 @@
     // nullopt means all Partitioned cookies will be ignored.
     const absl::optional<CookiePartitionKey> cookie_partition_key_;
     const net::CookieChangeCallback callback_;
+    const bool first_party_sets_enabled_;
 
     void DoDispatchChange(const CookieChangeInfo& change) const;
 
@@ -155,6 +158,8 @@
 
   CookieDomainMap cookie_domain_map_;
 
+  const bool first_party_sets_enabled_;
+
   THREAD_CHECKER(thread_checker_);
 
   // Vends weak pointers to subscriptions.
diff --git a/net/cookies/cookie_monster_perftest.cc b/net/cookies/cookie_monster_perftest.cc
index d6851a13..8555811 100644
--- a/net/cookies/cookie_monster_perftest.cc
+++ b/net/cookies/cookie_monster_perftest.cc
@@ -29,6 +29,7 @@
 
 const int kNumCookies = 20000;
 const char kCookieLine[] = "A  = \"b=;\\\"\"  ;secure;;; samesite=none";
+const bool kFirstPartySetsEnabled = false;
 
 static constexpr char kMetricPrefixParsedCookie[] = "ParsedCookie.";
 static constexpr char kMetricPrefixCookieMonster[] = "CookieMonster.";
@@ -176,7 +177,8 @@
 }
 
 TEST_F(CookieMonsterTest, TestAddCookiesOnSingleHost) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, nullptr);
+  auto cm =
+      std::make_unique<CookieMonster>(nullptr, nullptr, kFirstPartySetsEnabled);
   std::vector<std::string> cookies;
   for (int i = 0; i < kNumCookies; i++) {
     cookies.push_back(base::StringPrintf("a%03d=b; SameSite=None; Secure", i));
@@ -213,7 +215,8 @@
 }
 
 TEST_F(CookieMonsterTest, TestAddCookieOnManyHosts) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, nullptr);
+  auto cm =
+      std::make_unique<CookieMonster>(nullptr, nullptr, kFirstPartySetsEnabled);
   std::string cookie(kCookieLine);
   std::vector<GURL> gurls;  // just wanna have ffffuunnn
   for (int i = 0; i < kNumCookies; ++i) {
@@ -249,7 +252,8 @@
 }
 
 TEST_F(CookieMonsterTest, TestDomainTree) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, nullptr);
+  auto cm =
+      std::make_unique<CookieMonster>(nullptr, nullptr, kFirstPartySetsEnabled);
   GetCookieListCallback getCookieListCallback;
   SetCookieCallback setCookieCallback;
   const char domain_cookie_format_tree[] =
@@ -308,7 +312,8 @@
 }
 
 TEST_F(CookieMonsterTest, TestDomainLine) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, nullptr);
+  auto cm =
+      std::make_unique<CookieMonster>(nullptr, nullptr, kFirstPartySetsEnabled);
   SetCookieCallback setCookieCallback;
   GetCookieListCallback getCookieListCallback;
   std::vector<std::string> domain_list;
@@ -372,7 +377,8 @@
 
   store->SetLoadExpectation(true, std::move(initial_cookies));
 
-  std::unique_ptr<CookieMonster> cm(new CookieMonster(store.get(), nullptr));
+  std::unique_ptr<CookieMonster> cm(
+      new CookieMonster(store.get(), nullptr, kFirstPartySetsEnabled));
 
   // Import will happen on first access.
   GURL gurl("www.foo.com");
@@ -388,7 +394,8 @@
 }
 
 TEST_F(CookieMonsterTest, TestGetKey) {
-  std::unique_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr));
+  std::unique_ptr<CookieMonster> cm(
+      new CookieMonster(nullptr, nullptr, kFirstPartySetsEnabled));
   auto reporter = SetUpCookieMonsterReporter("baseline_story");
   base::ElapsedTimer get_key_timer;
   for (int i = 0; i < kNumCookies; i++)
diff --git a/net/cookies/cookie_monster_store_test.cc b/net/cookies/cookie_monster_store_test.cc
index 49230acb..4170ea3a 100644
--- a/net/cookies/cookie_monster_store_test.cc
+++ b/net/cookies/cookie_monster_store_test.cc
@@ -240,7 +240,8 @@
     store->AddCookie(*cc);
   }
 
-  return std::make_unique<CookieMonster>(store.get(), nullptr);
+  return std::make_unique<CookieMonster>(store.get(), /*net_log=*/nullptr,
+                                         /*first_party_sets_enabled=*/false);
 }
 
 MockSimplePersistentCookieStore::~MockSimplePersistentCookieStore() = default;
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index 73295df2..5ef9e35 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -31,11 +31,9 @@
 #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/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
-#include "net/base/features.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/canonical_cookie_test_helpers.h"
 #include "net/cookies/cookie_change_dispatcher.h"
@@ -87,11 +85,12 @@
 const char kTopLevelDomainPlus2Secure[] = "https://www.math.harvard.edu";
 const char kTopLevelDomainPlus3[] = "http://www.bourbaki.math.harvard.edu";
 const char kOtherDomain[] = "http://www.mit.edu";
+const bool kFirstPartySetsDefault = false;
 
 struct CookieMonsterTestTraits {
   static std::unique_ptr<CookieStore> Create() {
-    return std::make_unique<CookieMonster>(nullptr /* store */,
-                                           nullptr /* netlog */);
+    return std::make_unique<CookieMonster>(
+        nullptr /* store */, nullptr /* netlog */, kFirstPartySetsDefault);
   }
 
   static void DeliverChangeNotifications() { base::RunLoop().RunUntilIdle(); }
@@ -371,7 +370,8 @@
     const int more_than_enough_cookies = domain_max_cookies + 10;
     // Add a bunch of cookies on a single host, should purge them.
     {
-      auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+      auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                                kFirstPartySetsDefault);
       for (int i = 0; i < more_than_enough_cookies; ++i) {
         std::string cookie = base::StringPrintf("a%03d=b", i);
         EXPECT_TRUE(SetCookie(cm.get(), http_www_foo_.url(), cookie));
@@ -392,7 +392,8 @@
     // between them.  We shouldn't go above kDomainMaxCookies for both together.
     GURL url_google_specific(http_www_foo_.Format("http://www.gmail.%D"));
     {
-      auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+      auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                                kFirstPartySetsDefault);
       for (int i = 0; i < more_than_enough_cookies; ++i) {
         std::string cookie_general = base::StringPrintf("a%03d=b", i);
         EXPECT_TRUE(SetCookie(cm.get(), http_www_foo_.url(), cookie_general));
@@ -428,7 +429,8 @@
     // Test histogram for the number of registrable domains affected by domain
     // purge.
     {
-      auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+      auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                                kFirstPartySetsDefault);
       GURL url;
       for (int domain_num = 0; domain_num < 3; ++domain_num) {
         url = GURL(base::StringPrintf("http://domain%d.test", domain_num));
@@ -614,7 +616,8 @@
     std::unique_ptr<CookieMonster> cm;
 
     if (alt_host_entries == nullptr) {
-      cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+      cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                           kFirstPartySetsDefault);
     } else {
       // When generating all of these cookies on alternate hosts, they need to
       // be all older than the max "safe" date for GC, which is currently 30
@@ -659,7 +662,8 @@
     DCHECK_EQ(150U, CookieMonster::kDomainMaxCookies -
                         CookieMonster::kDomainPurgeCookies);
 
-    auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+    auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                              kFirstPartySetsDefault);
     // Key:
     // Round 1 => LN; round 2 => LS; round 3 => MN.
     // Round 4 => HN; round 5 => MS; round 6 => HS
@@ -725,7 +729,8 @@
     DCHECK_EQ(150U, CookieMonster::kDomainMaxCookies -
                         CookieMonster::kDomainPurgeCookies);
 
-    auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+    auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                              kFirstPartySetsDefault);
     // Key:
     // Round 1 => LN; round 2 => LS; round 3 => MN.
     // Round 4 => HN; round 5 => MS; round 6 => HS
@@ -785,7 +790,8 @@
     DCHECK_EQ(150U, CookieMonster::kDomainMaxCookies -
                         CookieMonster::kDomainPurgeCookies);
 
-    auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+    auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                              kFirstPartySetsDefault);
     // Key:
     // Round 1 => LN; round 2 => LS; round 3 => MN.
     // Round 4 => HN; round 5 => MS; round 6 => HS
@@ -907,7 +913,8 @@
   void TestPartitionedCookiesGarbageCollectionHelper() {
     DCHECK_EQ(10u, CookieMonster::kPerPartitionDomainMaxCookies);
     int max_cookies = CookieMonster::kPerPartitionDomainMaxCookies;
-    auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+    auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                              kFirstPartySetsDefault);
 
     auto cookie_partition_key =
         CookiePartitionKey::FromURLForTesting(GURL("https://toplevelsite.com"));
@@ -929,7 +936,8 @@
   // Function for creating a CM with a number of cookies in it,
   // no store (and hence no ability to affect access time).
   CookieMonster* CreateMonsterForGC(int num_cookies) {
-    CookieMonster* cm(new CookieMonster(nullptr, net::NetLog::Get()));
+    CookieMonster* cm(
+        new CookieMonster(nullptr, net::NetLog::Get(), kFirstPartySetsDefault));
     base::Time creation_time = base::Time::Now();
     for (int i = 0; i < num_cookies; i++) {
       std::unique_ptr<CanonicalCookie> cc(
@@ -996,8 +1004,8 @@
   DeferredCookieTaskTest() {
     persistent_store_ = base::MakeRefCounted<MockPersistentCookieStore>();
     persistent_store_->set_store_load_commands(true);
-    cookie_monster_ = std::make_unique<CookieMonster>(persistent_store_.get(),
-                                                      net::NetLog::Get());
+    cookie_monster_ = std::make_unique<CookieMonster>(
+        persistent_store_.get(), net::NetLog::Get(), kFirstPartySetsDefault);
   }
 
   // Defines a cookie to be returned from PersistentCookieStore::Load
@@ -1412,8 +1420,8 @@
 
 TEST_F(CookieMonsterTest, TestCookieDeleteAll) {
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
   CookieOptions options = CookieOptions::MakeAllInclusive();
 
   EXPECT_TRUE(SetCookie(cm.get(), http_www_foo_.url(), kValidCookieLine));
@@ -1456,7 +1464,8 @@
 }
 
 TEST_F(CookieMonsterTest, TestCookieDeleteAllCreatedInTimeRangeTimestamps) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   Time now = Time::Now();
 
@@ -1544,7 +1553,8 @@
 
 TEST_F(CookieMonsterTest,
        TestCookieDeleteAllCreatedInTimeRangeTimestampsWithInfo) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   Time now = Time::Now();
 
@@ -1632,7 +1642,8 @@
 }
 
 TEST_F(CookieMonsterTest, TestCookieDeleteMatchingCookies) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
   Time now = Time::Now();
 
   // Nothing has been added so nothing should be deleted.
@@ -1707,7 +1718,8 @@
 
 TEST_F(CookieMonsterTest, TestLastAccess) {
   std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(nullptr, kLastAccessThreshold, net::NetLog::Get()));
+      new CookieMonster(nullptr, kLastAccessThreshold, net::NetLog::Get(),
+                        kFirstPartySetsDefault));
 
   EXPECT_TRUE(SetCookie(cm.get(), http_www_foo_.url(), "A=B"));
   const Time last_access_date(GetFirstCookieAccessDate(cm.get()));
@@ -1765,10 +1777,11 @@
 }
 
 TEST_F(CookieMonsterTest, SetCookieableSchemes) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   std::unique_ptr<CookieMonster> cm_foo(
-      new CookieMonster(nullptr, net::NetLog::Get()));
+      new CookieMonster(nullptr, net::NetLog::Get(), kFirstPartySetsDefault));
 
   // Only cm_foo should allow foo:// cookies.
   std::vector<std::string> schemes;
@@ -1830,7 +1843,8 @@
 
 TEST_F(CookieMonsterTest, GetAllCookiesForURL) {
   std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(nullptr, kLastAccessThreshold, net::NetLog::Get()));
+      new CookieMonster(nullptr, kLastAccessThreshold, net::NetLog::Get(),
+                        kFirstPartySetsDefault));
 
   // Create an httponly cookie.
   CookieOptions options = CookieOptions::MakeAllInclusive();
@@ -1947,7 +1961,8 @@
 
 TEST_F(CookieMonsterTest, GetExcludedCookiesForURL) {
   std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(nullptr, kLastAccessThreshold, net::NetLog::Get()));
+      new CookieMonster(nullptr, kLastAccessThreshold, net::NetLog::Get(),
+                        kFirstPartySetsDefault));
 
   // Create an httponly cookie.
   CookieOptions options = CookieOptions::MakeAllInclusive();
@@ -2022,7 +2037,8 @@
 }
 
 TEST_F(CookieMonsterTest, GetAllCookiesForURLPathMatching) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   CookieOptions options = CookieOptions::MakeAllInclusive();
 
@@ -2061,7 +2077,8 @@
 }
 
 TEST_F(CookieMonsterTest, GetExcludedCookiesForURLPathMatching) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   CookieOptions options = CookieOptions::MakeAllInclusive();
 
@@ -2098,7 +2115,8 @@
 }
 
 TEST_F(CookieMonsterTest, CookieSorting) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   base::Time system_time = base::Time::Now();
   for (const char* cookie_line :
@@ -2125,7 +2143,8 @@
 }
 
 TEST_F(CookieMonsterTest, InheritCreationDate) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   base::Time the_not_so_distant_past(base::Time::Now() - base::Seconds(1000));
   EXPECT_TRUE(SetCookieWithCreationTime(cm.get(), http_www_foo_.url(),
@@ -2156,7 +2175,8 @@
 // Check that GetAllCookiesForURL() does not return expired cookies and deletes
 // them.
 TEST_F(CookieMonsterTest, DeleteExpiredCookiesOnGet) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   EXPECT_TRUE(SetCookie(cm.get(), http_www_foo_.url(), "A=B;"));
 
@@ -2255,8 +2275,8 @@
   // Inject our initial cookies into the mock PersistentCookieStore.
   store->SetLoadExpectation(true, std::move(initial_cookies));
 
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   // Verify that duplicates were not imported for path "/".
   // (If this had failed, GetCookies() would have also returned X=1, X=2, X=4).
@@ -2301,8 +2321,8 @@
   initial_cookies.push_back(std::move(cc));
 
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   store->SetLoadExpectation(true, std::move(initial_cookies));
 
@@ -2354,8 +2374,8 @@
   // Inject our initial cookies into the mock PersistentCookieStore.
   store->SetLoadExpectation(true, std::move(initial_cookies));
 
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   CookieList list(GetAllCookies(cm.get()));
   EXPECT_EQ(2U, list.size());
@@ -2412,8 +2432,8 @@
   // Inject our initial cookies into the mock PersistentCookieStore.
   store->SetLoadExpectation(true, std::move(initial_cookies));
 
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   CookieList list(GetAllCookies(cm.get()));
   EXPECT_EQ(2U, list.size());
@@ -2426,7 +2446,8 @@
 }
 
 TEST_F(CookieMonsterTest, PredicateSeesAllCookies) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   const base::Time now = PopulateCmForPredicateCheck(cm.get());
   // We test that we can see all cookies with |delete_info|. This includes
@@ -2454,7 +2475,8 @@
 // Mainly a test of GetEffectiveDomain, or more specifically, of the
 // expected behavior of GetEffectiveDomain within the CookieMonster.
 TEST_F(CookieMonsterTest, GetKey) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   // This test is really only interesting if GetKey() actually does something.
   EXPECT_EQ("foo.com", cm->GetKey("www.foo.com"));
@@ -2500,8 +2522,8 @@
 
   // Create new cookies and flush them to the store.
   {
-    std::unique_ptr<CookieMonster> cmout(
-        new CookieMonster(store.get(), net::NetLog::Get()));
+    std::unique_ptr<CookieMonster> cmout(new CookieMonster(
+        store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
     for (const auto& cookie : input_info) {
       EXPECT_TRUE(SetCanonicalCookie(
           cmout.get(),
@@ -2520,8 +2542,8 @@
 
   // Create a new cookie monster and make sure that everything is correct
   {
-    std::unique_ptr<CookieMonster> cmin(
-        new CookieMonster(store.get(), net::NetLog::Get()));
+    std::unique_ptr<CookieMonster> cmin(new CookieMonster(
+        store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
     CookieList cookies(GetAllCookies(cmin.get()));
     ASSERT_EQ(2u, cookies.size());
     // Ordering is path length, then creation time.  So second cookie
@@ -2555,7 +2577,8 @@
       base::MakeRefCounted<MockPersistentCookieStore>();
 
   {
-    CookieMonster cmout(store.get(), net::NetLog::Get());
+    CookieMonster cmout(store.get(), net::NetLog::Get(),
+                        kFirstPartySetsDefault);
     GURL url("http://www.example.com/");
     EXPECT_TRUE(
         SetCookieWithCreationTime(&cmout, url, "A=1; max-age=600", current));
@@ -2577,7 +2600,8 @@
 
   // Now read them in. Should get two cookies, not one.
   {
-    CookieMonster cmin(store2.get(), net::NetLog::Get());
+    CookieMonster cmin(store2.get(), net::NetLog::Get(),
+                       kFirstPartySetsDefault);
     CookieList cookies(GetAllCookies(&cmin));
     ASSERT_EQ(2u, cookies.size());
   }
@@ -2586,7 +2610,8 @@
 TEST_F(CookieMonsterTest, CookieListOrdering) {
   // Put a random set of cookies into a monster and make sure
   // they're returned in the right order.
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   EXPECT_TRUE(
       SetCookie(cm.get(), GURL("http://d.c.b.a.foo.com/aa/x.html"), "c=1"));
@@ -2725,8 +2750,8 @@
 
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
   store->set_store_load_commands(true);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   auto cookie = CanonicalCookie::Create(
       kUrl, "a=b", base::Time::Now(), absl::nullopt /* server_time */,
@@ -2775,8 +2800,8 @@
 
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
   store->set_store_load_commands(true);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   ResultSavingCookieCallback<uint32_t> delete_callback;
   cm->DeleteAllAsync(delete_callback.MakeCallback());
@@ -2815,8 +2840,8 @@
 
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
   store->set_store_load_commands(true);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   GetAllCookiesCallback get_cookies_callback1;
   cm->GetAllCookiesAsync(get_cookies_callback1.MakeCallback());
@@ -2866,8 +2891,8 @@
 
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
   store->set_store_load_commands(true);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   // Get all cookies task that queues a task to set a cookie when executed.
   auto cookie = CanonicalCookie::Create(
@@ -2911,7 +2936,8 @@
 TEST_F(CookieMonsterTest, FlushStore) {
   auto counter = base::MakeRefCounted<CallbackCounter>();
   auto store = base::MakeRefCounted<FlushablePersistentStore>();
-  auto cm = std::make_unique<CookieMonster>(store, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(store, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   ASSERT_EQ(0, store->flush_count());
   ASSERT_EQ(0, counter->callback_count());
@@ -2946,7 +2972,8 @@
   ASSERT_EQ(2, counter->callback_count());
 
   // If there's no backing store, FlushStore() is always a safe no-op.
-  cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                       kFirstPartySetsDefault);
   GetAllCookies(cm.get());  // Force init.
   cm->FlushStore(base::DoNothing());
   base::RunLoop().RunUntilIdle();
@@ -2961,7 +2988,8 @@
 
 TEST_F(CookieMonsterTest, SetAllCookies) {
   scoped_refptr<FlushablePersistentStore> store(new FlushablePersistentStore());
-  auto cm = std::make_unique<CookieMonster>(store.get(), net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(store.get(), net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
   cm->SetPersistSessionCookies(true);
 
   EXPECT_TRUE(SetCookie(cm.get(), http_www_foo_.url(), "U=V; path=/"));
@@ -3031,7 +3059,8 @@
 // Check that DeleteAll does flush (as a quick check that flush_count() works).
 TEST_F(CookieMonsterTest, DeleteAll) {
   scoped_refptr<FlushablePersistentStore> store(new FlushablePersistentStore());
-  auto cm = std::make_unique<CookieMonster>(store.get(), net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(store.get(), net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
   cm->SetPersistSessionCookies(true);
 
   EXPECT_TRUE(SetCookie(cm.get(), http_www_foo_.url(), "X=Y; path=/"));
@@ -3058,7 +3087,8 @@
 }
 
 TEST_F(CookieMonsterTest, HistogramCheck) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   // Should match call in InitializeHistograms, but doesn't really matter
   // since the histogram should have been initialized by the CM construction
@@ -3103,8 +3133,8 @@
 // CookieStore if the "persist session cookies" option is on.
 TEST_F(CookieMonsterTest, PersistSessionCookies) {
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
   cm->SetPersistSessionCookies(true);
 
   // All cookies set with SetCookie are session cookies.
@@ -3141,8 +3171,8 @@
 // Test the commands sent to the persistent cookie store.
 TEST_F(CookieMonsterTest, PersisentCookieStorageTest) {
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   // Add a cookie.
   EXPECT_TRUE(SetCookie(cm.get(), http_www_foo_.url(),
@@ -3237,8 +3267,8 @@
   // Inject our initial cookies into the mock PersistentCookieStore.
   store->SetLoadExpectation(true, std::move(initial_cookies));
 
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   EXPECT_EQ("foo=bar; hello=world",
             GetCookies(cm.get(), url,
@@ -3251,8 +3281,8 @@
   const std::string cookie_source_histogram = "Cookie.CookieSourceScheme";
 
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   histograms.ExpectTotalCount(cookie_source_histogram, 0);
 
@@ -3338,7 +3368,8 @@
       absl::nullopt /* cookie_partition_key */));
   store->SetLoadExpectation(true /* return_value */,
                             std::move(initial_cookies));
-  auto cm = std::make_unique<CookieMonster>(store.get(), net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(store.get(), net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
   {
     base::HistogramTester histogram_tester;
     // Access the cookies to trigger loading from the persistent store.
@@ -3409,7 +3440,8 @@
 // Test that localhost URLs can set and get secure cookies, even if
 // non-cryptographic.
 TEST_F(CookieMonsterTest, SecureCookieLocalhost) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, nullptr);
+  auto cm =
+      std::make_unique<CookieMonster>(nullptr, nullptr, kFirstPartySetsDefault);
 
   GURL insecure_localhost("http://localhost");
   GURL secure_localhost("https://localhost");
@@ -3483,8 +3515,8 @@
 
 TEST_F(CookieMonsterTest, MaybeDeleteEquivalentCookieAndUpdateStatus) {
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   // Set a secure, httponly cookie from a secure origin
   auto preexisting_cookie = CanonicalCookie::Create(
@@ -3590,8 +3622,8 @@
 TEST_F(CookieMonsterTest,
        MaybeDeleteEquivalentCookieAndUpdateStatus_PartitionedCookies) {
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   // Test adding two cookies with the same name, domain, and path but different
   // partition keys.
@@ -3634,8 +3666,8 @@
 // multiple reasons (Secure and HttpOnly).
 TEST_F(CookieMonsterTest, SkipDontOverwriteForMultipleReasons) {
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   // Set a secure, httponly cookie from a secure origin
   auto preexisting_cookie = CanonicalCookie::Create(
@@ -3673,8 +3705,8 @@
 // cookie should not be set.
 TEST_F(CookieMonsterTest, DontDeleteEquivalentCookieIfSetIsRejected) {
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   auto preexisting_cookie = CanonicalCookie::Create(
       http_www_foo_.url(), "cookie=foo", base::Time::Now(),
@@ -3700,7 +3732,8 @@
 }
 
 TEST_F(CookieMonsterTest, SetSecureCookies) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   GURL http_url("http://www.foo.com");
   GURL http_superdomain_url("http://foo.com");
@@ -3867,7 +3900,7 @@
 }
 
 TEST_F(CookieMonsterTest, SetSamePartyCookies) {
-  CookieMonster cm(nullptr, net::NetLog::Get());
+  CookieMonster cm(nullptr, net::NetLog::Get(), kFirstPartySetsDefault);
   GURL http_url("http://www.foo.com");
   GURL http_superdomain_url("http://foo.com");
   GURL https_url("https://www.foo.com");
@@ -3924,7 +3957,8 @@
 // Check domain-match criterion: If either cookie domain matches the other,
 // don't set the insecure cookie.
 TEST_F(CookieMonsterTest, LeaveSecureCookiesAlone_DomainMatch) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   // These domains will domain-match each other.
   const char* kRegistrableDomain = "foo.com";
@@ -4095,7 +4129,8 @@
 // Check path-match criterion: If the new cookie is for the same path or a
 // subdirectory of the preexisting cookie's path, don't set the new cookie.
 TEST_F(CookieMonsterTest, LeaveSecureCookiesAlone_PathMatch) {
-  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(nullptr, net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
 
   // A path that is later in this list will path-match all the paths before it.
   const char* kPaths[] = {"/", "/1", "/1/2", "/1/2/3"};
@@ -4319,7 +4354,8 @@
 // Tests that strict secure cookies doesn't trip equivalent cookie checks
 // accidentally. Regression test for https://crbug.com/569943.
 TEST_F(CookieMonsterTest, EquivalentCookies) {
-  std::unique_ptr<CookieMonster> cm(new CookieMonster(nullptr, nullptr));
+  std::unique_ptr<CookieMonster> cm(
+      new CookieMonster(nullptr, nullptr, kFirstPartySetsDefault));
   GURL http_url("http://www.foo.com");
   GURL http_superdomain_url("http://foo.com");
   GURL https_url("https://www.foo.com");
@@ -4348,7 +4384,7 @@
       base::MakeRefCounted<MockPersistentCookieStore>();
   // Collect load commands so we have control over their execution.
   persistent_store->set_store_load_commands(true);
-  CookieMonster cm(persistent_store.get(), nullptr);
+  CookieMonster cm(persistent_store.get(), nullptr, kFirstPartySetsDefault);
 
   // Start of a canonical cookie set.
   ResultSavingCookieCallback<CookieAccessResult> callback_set;
@@ -4395,7 +4431,7 @@
   // that the implementation doesn't just happen to pick the right one because
   // of implementation details.
   for (size_t run = 0; run < base::size(kNames); ++run) {
-    CookieMonster cm(nullptr, nullptr);
+    CookieMonster cm(nullptr, nullptr, kFirstPartySetsDefault);
     Time now = Time::Now();
     GURL url("http://www.example.com");
 
@@ -4435,7 +4471,7 @@
   CookieOptions options = CookieOptions::MakeAllInclusive();
   absl::optional<base::Time> server_time = absl::nullopt;
   absl::optional<CookiePartitionKey> partition_key = absl::nullopt;
-  CookieMonster cm(nullptr, nullptr);
+  CookieMonster cm(nullptr, nullptr, kFirstPartySetsDefault);
 
   // Write a cookie created at |t1|.
   auto cookie =
@@ -4467,7 +4503,7 @@
   GURL url("http://www.example.com");
   std::string cookie_line = "foo=bar; SameSite=Lax";
 
-  CookieMonster cm(nullptr, nullptr);
+  CookieMonster cm(nullptr, nullptr, kFirstPartySetsDefault);
   CookieOptions env_cross_site;
   env_cross_site.set_same_site_cookie_context(
       CookieOptions::SameSiteCookieContext(
@@ -4494,7 +4530,7 @@
   GURL http_url("http://www.example.com");
   std::string cookie_line = "foo=bar; Secure";
 
-  CookieMonster cm(nullptr, nullptr);
+  CookieMonster cm(nullptr, nullptr, kFirstPartySetsDefault);
   CookieInclusionStatus status;
   // Cookie can be created successfully from an any url. Secure is not checked
   // on Create.
@@ -4519,7 +4555,7 @@
   GURL url("http://www.example.com");
   std::string cookie_line = "foo=bar; HttpOnly";
 
-  CookieMonster cm(nullptr, nullptr);
+  CookieMonster cm(nullptr, nullptr, kFirstPartySetsDefault);
   CookieInclusionStatus status;
   // Cookie can be created successfully; HttpOnly is not checked on Create.
   auto cookie = CanonicalCookie::Create(
@@ -4595,14 +4631,14 @@
        CookieInclusionStatus(), CookieEffectiveSameSite::LAX_MODE, kLongAge},
   };
 
-  auto cm = std::make_unique<CookieMonster>(nullptr, nullptr);
+  auto cm =
+      std::make_unique<CookieMonster>(nullptr, nullptr, kFirstPartySetsDefault);
   GURL secure_url("https://www.example1.test");
   GURL insecure_url("http://www.example2.test");
 
   int length = sizeof(test_cases) / sizeof(test_cases[0]);
   for (int i = 0; i < length; ++i) {
     TestCase test = test_cases[i];
-    base::test::ScopedFeatureList feature_list;
 
     GURL url = test.is_url_secure ? secure_url : insecure_url;
     base::Time creation_time = base::Time::Now() - test.creation_time_delta;
@@ -4631,7 +4667,8 @@
   CookieMonsterNotificationTest()
       : test_url_("http://www.foo.com/foo"),
         store_(new MockPersistentCookieStore),
-        monster_(new CookieMonster(store_.get(), nullptr)) {}
+        monster_(
+            new CookieMonster(store_.get(), nullptr, kFirstPartySetsDefault)) {}
 
   ~CookieMonsterNotificationTest() override = default;
 
@@ -4663,7 +4700,8 @@
   store->set_store_load_commands(true);
 
   // Bind it to a CookieMonster
-  auto monster = std::make_unique<CookieMonster>(store.get(), nullptr);
+  auto monster = std::make_unique<CookieMonster>(store.get(), nullptr,
+                                                 kFirstPartySetsDefault);
 
   // Trigger load dispatch and confirm it.
   monster->GetAllCookiesAsync(CookieStore::GetAllCookiesCallback());
@@ -4727,7 +4765,8 @@
  public:
   CookieMonsterLegacyCookieAccessTest()
       : cm_(std::make_unique<CookieMonster>(nullptr /* store */,
-                                            nullptr /* netlog */)) {
+                                            nullptr /* netlog */,
+                                            kFirstPartySetsDefault)) {
     // Need to reset first because there cannot be two TaskEnvironments at the
     // same time.
     task_environment_.reset();
@@ -4912,8 +4951,8 @@
   const char kHistogramName[] = "Cookie.DomainSet";
 
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   histograms.ExpectTotalCount(kHistogramName, 0);
 
@@ -4941,8 +4980,8 @@
   const char kHistogramNameLocal[] = "Cookie.Port.Read.Localhost";
 
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   histograms.ExpectTotalCount(kHistogramName, 0);
 
@@ -4995,8 +5034,8 @@
   const char kHistogramNameLocal[] = "Cookie.Port.Set.Localhost";
 
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   histograms.ExpectTotalCount(kHistogramName, 0);
 
@@ -5050,8 +5089,8 @@
       "Cookie.Port.ReadDiffersFromSet.DomainSet";
 
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   histograms.ExpectTotalCount(kHistogramName, 0);
 
@@ -5150,8 +5189,8 @@
   const char kHistogramName[] = "Cookie.CookieSourceSchemeName";
 
   scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
-  std::unique_ptr<CookieMonster> cm(
-      new CookieMonster(store.get(), net::NetLog::Get()));
+  std::unique_ptr<CookieMonster> cm(new CookieMonster(
+      store.get(), net::NetLog::Get(), kFirstPartySetsDefault));
 
   histograms.ExpectTotalCount(kHistogramName, 0);
 
@@ -5211,13 +5250,13 @@
 class FirstPartySetEnabledCookieMonsterTest : public CookieMonsterTest {
  public:
   FirstPartySetEnabledCookieMonsterTest()
-      : cm_(nullptr /* store */, nullptr /* netlog */) {
+      : cm_(nullptr /* store */,
+            nullptr /* netlog */,
+            true /* first_party_sets_enabled */) {
     std::unique_ptr<TestCookieAccessDelegate> access_delegate =
         std::make_unique<TestCookieAccessDelegate>();
     access_delegate_ = access_delegate.get();
     cm_.SetCookieAccessDelegate(std::move(access_delegate));
-
-    feature_list_.InitAndEnableFeature(features::kFirstPartySets);
   }
 
   ~FirstPartySetEnabledCookieMonsterTest() override = default;
@@ -5225,10 +5264,6 @@
   CookieMonster* cm() { return &cm_; }
 
  protected:
-  // The FeatureList must be before the CookieMonster because the CookieMonster
-  // destructor expects the state of the features to be the same as when it's in
-  // use.
-  base::test::ScopedFeatureList feature_list_;
   CookieMonster cm_;
   raw_ptr<TestCookieAccessDelegate> access_delegate_;
 };
@@ -5275,7 +5310,8 @@
 
 TEST_F(CookieMonsterTest, GetAllCookiesForURLNonce) {
   auto store = base::MakeRefCounted<MockPersistentCookieStore>();
-  auto cm = std::make_unique<CookieMonster>(store.get(), net::NetLog::Get());
+  auto cm = std::make_unique<CookieMonster>(store.get(), net::NetLog::Get(),
+                                            kFirstPartySetsDefault);
   CookieOptions options = CookieOptions::MakeAllInclusive();
 
   auto anonymous_iframe_key = CookiePartitionKey::FromURLForTesting(
diff --git a/net/cookies/cookie_store_test_helpers.cc b/net/cookies/cookie_store_test_helpers.cc
index 6daa675..6bd7395 100644
--- a/net/cookies/cookie_store_test_helpers.cc
+++ b/net/cookies/cookie_store_test_helpers.cc
@@ -69,8 +69,9 @@
 }
 
 DelayedCookieMonster::DelayedCookieMonster()
-    : cookie_monster_(
-          new CookieMonster(nullptr /* store */, nullptr /* netlog */)),
+    : cookie_monster_(new CookieMonster(nullptr /* store */,
+                                        nullptr /* netlog */,
+                                        false /* first_party_sets_enabled */)),
       did_run_(false),
       result_(CookieAccessResult(CookieInclusionStatus(
           CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE))) {}
diff --git a/net/cookies/cookie_util.cc b/net/cookies/cookie_util.cc
index b04f3d7..c7bd72ae 100644
--- a/net/cookies/cookie_util.cc
+++ b/net/cookies/cookie_util.cc
@@ -738,10 +738,6 @@
   return base::FeatureList::IsEnabled(features::kSchemefulSameSite);
 }
 
-bool IsFirstPartySetsEnabled() {
-  return base::FeatureList::IsEnabled(features::kFirstPartySets);
-}
-
 // Returns First-Party Set metadata for the given context. Returns empty/default
 // metadata if `isolation_info` is not fully populated, or
 // `isolation_info.party_context` is nullopt.
@@ -765,8 +761,9 @@
 }
 
 CookieSamePartyStatus GetSamePartyStatus(const CanonicalCookie& cookie,
-                                         const CookieOptions& options) {
-  if (!IsFirstPartySetsEnabled() || !cookie.IsSameParty() ||
+                                         const CookieOptions& options,
+                                         const bool first_party_sets_enabled) {
+  if (!first_party_sets_enabled || !cookie.IsSameParty() ||
       !options.is_in_nontrivial_first_party_set()) {
     return CookieSamePartyStatus::kNoSamePartyEnforcement;
   }
diff --git a/net/cookies/cookie_util.h b/net/cookies/cookie_util.h
index a149a64f..f4baa07d 100644
--- a/net/cookies/cookie_util.h
+++ b/net/cookies/cookie_util.h
@@ -238,7 +238,6 @@
 
 // Returns whether the respective feature is enabled.
 NET_EXPORT bool IsSchemefulSameSiteEnabled();
-NET_EXPORT bool IsFirstPartySetsEnabled();
 
 // Computes the First-Party Sets metadata, determining which of the cookies for
 // `request_site` can be accessed. `isolation_info` must be fully populated.  If
@@ -255,7 +254,9 @@
 // kNoSamePartyEnforcement; if the cookie is SameParty but does not have a
 // valid context, returns kEnforceSamePartyExclude.
 NET_EXPORT CookieSamePartyStatus
-GetSamePartyStatus(const CanonicalCookie& cookie, const CookieOptions& options);
+GetSamePartyStatus(const CanonicalCookie& cookie,
+                   const CookieOptions& options,
+                   bool first_party_sets_enabled);
 
 // Takes a callback accepting a CookieAccessResult and returns a callback
 // that accepts a bool, setting the bool to true if the CookieInclusionStatus
diff --git a/net/cookies/cookie_util_unittest.cc b/net/cookies/cookie_util_unittest.cc
index a8a411e..8120dc27 100644
--- a/net/cookies/cookie_util_unittest.cc
+++ b/net/cookies/cookie_util_unittest.cc
@@ -1477,8 +1477,7 @@
 }
 
 TEST(CookieUtilTest, GetSamePartyStatus_NotInSet) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(features::kFirstPartySets);
+  const bool first_party_sets_enabled = true;
   CookieOptions options;
   options.set_is_in_nontrivial_first_party_set(false);
 
@@ -1505,7 +1504,8 @@
             options.set_same_party_context(
                 SamePartyContext(party_context_type));
             EXPECT_EQ(CookieSamePartyStatus::kNoSamePartyEnforcement,
-                      cookie_util::GetSamePartyStatus(*cookie, options));
+                      cookie_util::GetSamePartyStatus(
+                          *cookie, options, first_party_sets_enabled));
           }
         }
       }
@@ -1514,8 +1514,7 @@
 }
 
 TEST(CookieUtilTest, GetSamePartyStatus_FeatureDisabled) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(features::kFirstPartySets);
+  const bool first_party_sets_enabled = false;
   CookieOptions options;
   options.set_is_in_nontrivial_first_party_set(true);
 
@@ -1542,7 +1541,8 @@
             options.set_same_party_context(
                 SamePartyContext(party_context_type));
             EXPECT_EQ(CookieSamePartyStatus::kNoSamePartyEnforcement,
-                      cookie_util::GetSamePartyStatus(*cookie, options));
+                      cookie_util::GetSamePartyStatus(
+                          *cookie, options, first_party_sets_enabled));
           }
         }
       }
@@ -1551,8 +1551,7 @@
 }
 
 TEST(CookieUtilTest, GetSamePartyStatus_NotSameParty) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(features::kFirstPartySets);
+  const bool first_party_sets_enabled = true;
   CookieOptions options;
   options.set_is_in_nontrivial_first_party_set(true);
 
@@ -1577,7 +1576,8 @@
 
           options.set_same_party_context(SamePartyContext(party_context_type));
           EXPECT_EQ(CookieSamePartyStatus::kNoSamePartyEnforcement,
-                    cookie_util::GetSamePartyStatus(*cookie, options));
+                    cookie_util::GetSamePartyStatus(*cookie, options,
+                                                    first_party_sets_enabled));
         }
       }
     }
@@ -1585,8 +1585,7 @@
 }
 
 TEST(CookieUtilTest, GetSamePartyStatus_SamePartySemantics) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(features::kFirstPartySets);
+  const bool first_party_sets_enabled = true;
   CookieOptions options;
   options.set_is_in_nontrivial_first_party_set(true);
 
@@ -1635,12 +1634,14 @@
           options.set_same_party_context(
               SamePartyContext(SamePartyContext::Type::kCrossParty));
           EXPECT_EQ(CookieSamePartyStatus::kEnforceSamePartyExclude,
-                    cookie_util::GetSamePartyStatus(*cookie, options));
+                    cookie_util::GetSamePartyStatus(*cookie, options,
+                                                    first_party_sets_enabled));
 
           options.set_same_party_context(
               SamePartyContext(SamePartyContext::Type::kSameParty));
           EXPECT_EQ(CookieSamePartyStatus::kEnforceSamePartyInclude,
-                    cookie_util::GetSamePartyStatus(*cookie, options));
+                    cookie_util::GetSamePartyStatus(*cookie, options,
+                                                    first_party_sets_enabled));
         }
       }
     }
diff --git a/net/data/ssl/certificates/name_constraint_bad.pem b/net/data/ssl/certificates/name_constraint_bad.pem
index 0381d880..0863be68 100644
--- a/net/data/ssl/certificates/name_constraint_bad.pem
+++ b/net/data/ssl/certificates/name_constraint_bad.pem
@@ -1,70 +1,70 @@
 -----BEGIN PRIVATE KEY-----
-MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDgU/TzmMEUMwLI
-pG3+qir3lD2mbwDfO95Mn6PqB9Ss5VsN0azg7fnFmB01LeWzSZcUhUQP3EzSZwiI
-AaXYp+uT0Wqh91HnhH5SKn28bw7Y27amPt7c9aRolkQRhQLtRxLfuGBxlXtih2h6
-RFYJ1bTI8fbJRpKLaOiD1dWGcSPDgB6/bAHH0qS8QG3g48AuMHi9rd0lZtP1BwdW
-187icsUlfQzhp28AqNqrS1RDCWSktlI4L7fMAd0cAycDR7/f5jew7RjcUQvUdSLf
-UHs86zc5HJtvCHunBayMQ/fx2lEGs4JFPsiBc56wpc92lq+BLKwBKkpYSx2+/x+F
-wife8XgLAgMBAAECggEAO0gUmHdKtvLQDoPdiYogtrKXJC97dILWuTsKzyLoohQu
-XtWFMR/SfNQ5C7+oTxvocATTurlGF+ggigidckbV64dQ/aJlI6CQ3VfbSHu02bwe
-ZYqBzLShkP382QBkiJ3asAKCgiG1rJEKHB2I+ypdjyjaRdB/k5XStFxDBDdL8zKe
-kL1hytf1ALxYBQJ3TXcoIqlpzz6v+JnZTAuu4vrySfffyFTrytIwf/KGlBACBqXe
-oV+DK586PiyC0m1Hhy1rLTi2/IT4t4j3KO+c/OnpWkMEg1G/Ojy1zPMsMHr1VO9t
-UWxhEIYOLQVUOT+2ltO4bRJcgEPHSxlIY23qalJgqQKBgQD9oZwHPhLZg439Zr9O
-Zgl5fFkPe4SxqLY+ggE5O81+3RgL8GyAuy9oDtUGcw8L9tpKcBflqH4jEvUvT1/m
-8BGKIQteqcKbY0C+KfPvq77R4k2KqmxpKF5f4BS/9O7cgzJZwhSFKDLtcKtDIPUK
-3eoERuPB4/cmhQyb+/TKofZpfwKBgQDibEm1+nYj3tIXDxK3hZHjPz0s9vYV84py
-zg3HjuEmdacnhNPQQQpNG83RMjEnnBvHWH2vxRWyD+57r/FWeA60aUJo5WIP0rj8
-z6GqFT9IWaBrJtBU1YoxuyTH64yih6ljvm2skIEGlQYS+GBL9K/Tx4mh4lAKVzD5
-GXVqtyM/dQKBgFD0Ik8VewK+QLXe87TcUK3cCLkuXZ4vEWxGJonUErUpcKFu7dLw
-7CK0iT3zv5u8ANS9joMZEpmzVVryZNPbUF3cSjq+yIS8W0/XKCsZkGCBcOqPlubB
-oc3MQhM65HqxzYJkthQCTq8GxUM547zCNA2FavDaCGrdELdA5lM++t2VAoGAMLem
-AH68bqlhwM5gc5ZMtn2D1ynn9v8oudz2AAsRDKph5dHhlTx5T+/8j9dh1ijznSfA
-G1KngWGGKZzIq5c3ar//Jvy75bWsUdEG8saRkCqgpo16Y9ZyXpLqrg1TfCD+ZFSz
-2l5ZNKZZ4TkJ1y31qvaS+X7tQ9xQ0DgXGHgBIIUCgYABBBddUXpOyG54HvFHIgxS
-esBBkpM0uy3vOkyfMown2GCjKZaXS5kEmRwU9WQQKLPf2ieALgYEf87YGWmPNZNF
-tG5DoNhTMsrYORB11tuuBR1e6lrG/aoOkP/4udWC8d4Yp9wnkEu0l6X/kMfq2lb1
-67bBVmHqKJ0t8rytHzkP0Q==
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCwOdQrS3Jk6jJb
+Na2O4BadbxtUobCnikBNFFHn+Lof7YXQmy0DMiHL0iOhzt9ZcuRTLFzoYUvJk3Tm
+S8h7Vdj4sIvF1k8E0MCWwY2rsFbgZilV276UQr+hzMncbzU5cfgMcOlHfpycBg8U
+i4ygVUgttEWzilGA6NH9Vi7e4cb9DJyAI4AZXJI+B7d2n9HYxAuF1V417dpADz5g
+CznwFQI+i9uzITlAwWaIsZJ6GvvpSfnOFFFppbRLCLr8MTRLYv8efWYh9HcG0LFe
+vxuhtzYIIi40tzXTEYYnFhrx+HpuAR/pza/ewtvX2sVkXhr4n1B2m4+XfWHoVE4Y
+D725dY55AgMBAAECggEAVcLu5FsFQuNOumC3JC8eEmP98wP1SrPXcyuOaMv9GIip
+dMnv7/w3wk90E8zvmUJ2p5uRY23mSiU+4MzEtnEi9HRGsXMIZZmKAFQVtBZPUUmm
+mCgm6VRKml1lZ6efSWOTicpxXN/bK3svX5pCR8z5IXT37tZDr+6eMyH8EW/jPUZU
+wA/LVpmfRkcLg8VF6J70yj60peU3234b5Z9QLkR80g9U9Sj1QQhuDRAO97zireru
+QwFixeazgXzWPTk3iol6GiDbus9T0WGeUAWYZ8XyJ1voNHRNg9dfvpLAc0UBQ5Ho
+7Au6fYWQ8cEmYFIC5X46k/fZhZNgludUlQLfMsI1AQKBgQDin873bebHXpoFGXR3
+Sn8obsieSWwHCDU2gp+K8JwPtip1PrVfA1KW/wBo938zqpBZDAOOFGGl4x08wLmm
+WnlrPOsrcehDbiGTPheBPvOS11/v6IxoOVLLUnDaQIX9j8eOGG/WM0pan8wNG9Vz
+WHQduZcBQlKoyXjjJ84avorh8QKBgQDHEaAJab8sK//lCYrCpTfbS2tYCfc9b7Nx
+m/6GCJnxqVYfal9ocGd7U7RO7okDQAMyvwSpI9ejTDF8tO4BNHCgvi6rKK3Fj/CJ
+bgw8rCP1BOKLvDK/7A7/v1KqvYHtODnCH0iTVK/R/Oq2Jws0m88uSkANm1w2t+SG
+JVk/ymBtCQKBgC0L/RTbyKrKmCz5UVhA+6Oq2b/08j83l3Q9ZL82cp8A49GoZF79
+hxYym/9Bawx3E/hPVgmQ7ZQO4AnqeTyi8U2qr0hUfQmiQ5REHGH5hGsk2pISlI5H
+DrkRqxMHDltHkDAjlV9rlJUM/H+Cj9w8seASuvxqFYotehUVHXfddjfRAoGAYUXj
+hbX+jH8Tk7+N5n8FREseMO7tuT+T17f6L1SUpNmyE7fO1yHV7xV/zfIRUV0+MtXU
+WTICdPEOXXmrszsErgdAlrJR92/WgdEcealECL5SVSWpRs76pU2//16K1nfbAVh4
+BkYjg+CqcEez2gkou93cXsnDzZkeOc6WRe2GIMECgYEApyMBng9jXOD1Qw2CpWeC
+kO/GL98UXAvQAnQB1YepWDmbW2OxpXZ5TI5wQkJxE7z7EJkIm6Vq0fcHtDNwGWBq
+2zLyUW+QdR53eGEkhHNzq0hFWVMCaDbO6UNa1OFZqN/WSBFB3z4F0XAZ1ZX+PaFm
+NqcJRJ7sL5XqgAdRqcTTFcA=
 -----END PRIVATE KEY-----
 Certificate:
     Data:
         Version: 3 (0x2)
         Serial Number:
-            12:f6:d9:f1:80:1b:50:9f:64:09:c8:ea:2f:16:33:a4
+            bb:38:42:57:48:dd:2b:24:78:13:e0:59:b9:a3:d7:77
         Signature Algorithm: sha256WithRSAEncryption
         Issuer: C=US, ST=California, L=Mountain View, O=Test CA, CN=Test Root CA
         Validity
-            Not Before: Dec  1 15:42:07 2021 GMT
-            Not After : Dec  1 15:42:07 2023 GMT
+            Not Before: Jan 11 02:36:21 2022 GMT
+            Not After : Jan 11 02:36:21 2024 GMT
         Subject: CN=Leaf certificate
         Subject Public Key Info:
             Public Key Algorithm: rsaEncryption
                 RSA Public-Key: (2048 bit)
                 Modulus:
-                    00:e0:53:f4:f3:98:c1:14:33:02:c8:a4:6d:fe:aa:
-                    2a:f7:94:3d:a6:6f:00:df:3b:de:4c:9f:a3:ea:07:
-                    d4:ac:e5:5b:0d:d1:ac:e0:ed:f9:c5:98:1d:35:2d:
-                    e5:b3:49:97:14:85:44:0f:dc:4c:d2:67:08:88:01:
-                    a5:d8:a7:eb:93:d1:6a:a1:f7:51:e7:84:7e:52:2a:
-                    7d:bc:6f:0e:d8:db:b6:a6:3e:de:dc:f5:a4:68:96:
-                    44:11:85:02:ed:47:12:df:b8:60:71:95:7b:62:87:
-                    68:7a:44:56:09:d5:b4:c8:f1:f6:c9:46:92:8b:68:
-                    e8:83:d5:d5:86:71:23:c3:80:1e:bf:6c:01:c7:d2:
-                    a4:bc:40:6d:e0:e3:c0:2e:30:78:bd:ad:dd:25:66:
-                    d3:f5:07:07:56:d7:ce:e2:72:c5:25:7d:0c:e1:a7:
-                    6f:00:a8:da:ab:4b:54:43:09:64:a4:b6:52:38:2f:
-                    b7:cc:01:dd:1c:03:27:03:47:bf:df:e6:37:b0:ed:
-                    18:dc:51:0b:d4:75:22:df:50:7b:3c:eb:37:39:1c:
-                    9b:6f:08:7b:a7:05:ac:8c:43:f7:f1:da:51:06:b3:
-                    82:45:3e:c8:81:73:9e:b0:a5:cf:76:96:af:81:2c:
-                    ac:01:2a:4a:58:4b:1d:be:ff:1f:85:c2:27:de:f1:
-                    78:0b
+                    00:b0:39:d4:2b:4b:72:64:ea:32:5b:35:ad:8e:e0:
+                    16:9d:6f:1b:54:a1:b0:a7:8a:40:4d:14:51:e7:f8:
+                    ba:1f:ed:85:d0:9b:2d:03:32:21:cb:d2:23:a1:ce:
+                    df:59:72:e4:53:2c:5c:e8:61:4b:c9:93:74:e6:4b:
+                    c8:7b:55:d8:f8:b0:8b:c5:d6:4f:04:d0:c0:96:c1:
+                    8d:ab:b0:56:e0:66:29:55:db:be:94:42:bf:a1:cc:
+                    c9:dc:6f:35:39:71:f8:0c:70:e9:47:7e:9c:9c:06:
+                    0f:14:8b:8c:a0:55:48:2d:b4:45:b3:8a:51:80:e8:
+                    d1:fd:56:2e:de:e1:c6:fd:0c:9c:80:23:80:19:5c:
+                    92:3e:07:b7:76:9f:d1:d8:c4:0b:85:d5:5e:35:ed:
+                    da:40:0f:3e:60:0b:39:f0:15:02:3e:8b:db:b3:21:
+                    39:40:c1:66:88:b1:92:7a:1a:fb:e9:49:f9:ce:14:
+                    51:69:a5:b4:4b:08:ba:fc:31:34:4b:62:ff:1e:7d:
+                    66:21:f4:77:06:d0:b1:5e:bf:1b:a1:b7:36:08:22:
+                    2e:34:b7:35:d3:11:86:27:16:1a:f1:f8:7a:6e:01:
+                    1f:e9:cd:af:de:c2:db:d7:da:c5:64:5e:1a:f8:9f:
+                    50:76:9b:8f:97:7d:61:e8:54:4e:18:0f:bd:b9:75:
+                    8e:79
                 Exponent: 65537 (0x10001)
         X509v3 extensions:
             X509v3 Basic Constraints: critical
                 CA:FALSE
             X509v3 Subject Key Identifier: 
-                E2:E0:A4:73:95:9B:E9:6E:FD:CE:29:C4:6F:07:81:0B:96:BD:47:BA
+                E4:C5:B8:15:7B:F4:26:F0:4D:EB:27:D8:97:34:01:10:F5:96:D2:BE
             X509v3 Authority Key Identifier: 
                 keyid:9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
 
@@ -73,40 +73,40 @@
             X509v3 Subject Alternative Name: 
                 DNS:test.ExAmPlE.CoM, DNS:test.ExAmPlE.OrG
     Signature Algorithm: sha256WithRSAEncryption
-         0d:a9:30:6f:07:4e:6c:48:7a:27:e3:37:e4:aa:83:88:cc:f0:
-         35:ad:6b:6d:fc:56:a7:a5:dd:50:cc:cf:17:a8:70:77:7b:f8:
-         78:e7:a4:2f:45:6e:29:7d:e9:ff:b0:ee:5a:b9:6b:c2:fd:13:
-         32:f0:59:f1:10:9c:61:ba:94:96:23:12:19:2f:95:eb:4e:2b:
-         14:92:60:cd:16:1b:b3:ab:a4:8b:e8:03:c2:d4:40:6b:db:e7:
-         7d:33:59:43:a2:89:9b:aa:e5:d4:98:5e:c3:4d:72:9d:8b:9f:
-         18:81:d5:67:28:c8:05:3f:e3:c5:d6:24:12:7e:32:d0:be:20:
-         bd:20:37:1e:47:4b:61:fc:28:be:ef:66:45:a6:bd:23:0f:2d:
-         b3:f9:ec:54:79:39:35:98:e1:01:56:32:ee:8e:7b:f1:4b:77:
-         b2:1a:e7:22:77:21:11:79:c1:4f:84:f5:58:8e:9b:b9:8d:39:
-         87:78:7c:7b:ac:37:da:eb:4e:bd:06:6b:6f:ea:20:02:38:d5:
-         d9:7b:ff:95:c2:01:35:c6:cd:22:dc:0c:35:3b:71:31:06:a3:
-         78:a2:e4:03:31:1a:7a:e5:aa:b5:ae:33:1e:a4:1a:5b:f2:29:
-         ef:0f:ed:7e:82:f0:fc:2d:2e:82:e4:6d:1e:93:02:9e:12:5f:
-         ac:6c:46:c2
+         b6:d8:92:56:dd:62:58:be:cf:7f:a6:e5:b0:5e:a5:0f:15:cf:
+         44:55:be:b8:3e:43:ef:1b:0d:e4:11:1a:f6:38:5c:0d:14:a4:
+         23:0e:d6:e4:a2:f4:ef:44:7f:0b:b2:4f:f9:5f:b4:d8:a9:58:
+         20:e8:de:7a:8d:1b:74:5c:73:9b:0e:e5:fa:1c:d2:5f:99:b3:
+         e9:52:23:d3:cf:ae:49:53:d2:53:19:2f:8c:02:e8:98:4e:41:
+         f2:c4:c7:fb:c5:57:76:72:8e:db:fc:41:fe:2f:84:73:3a:da:
+         d3:83:c3:35:8a:00:a3:dd:b7:58:60:82:25:0d:fb:01:ee:e2:
+         7d:fd:e5:6a:74:31:77:24:77:9e:97:7d:ba:44:bf:6c:b4:06:
+         f7:c7:9e:d0:e1:cc:1f:5d:5a:e9:38:57:f3:a9:e3:07:b2:54:
+         9b:cd:68:eb:5c:b0:af:1a:09:25:72:da:9a:1d:03:0e:8e:c7:
+         3f:fa:c7:fe:45:55:cc:cd:5b:0c:7b:5c:7a:48:07:44:bb:e1:
+         d8:bd:6c:7a:d0:3c:af:53:0d:9d:6d:af:26:ef:3b:17:61:af:
+         2c:ff:c2:2a:95:1e:4b:5f:a1:85:7e:cc:2e:24:42:90:ad:d5:
+         a0:34:1a:cd:4f:06:09:e3:94:9c:6b:98:04:1a:54:79:c8:cf:
+         72:62:34:af
 -----BEGIN CERTIFICATE-----
-MIIDqDCCApCgAwIBAgIQEvbZ8YAbUJ9kCcjqLxYzpDANBgkqhkiG9w0BAQsFADBj
-MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
-bnRhaW4gVmlldzEQMA4GA1UECgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290
-IENBMB4XDTIxMTIwMTE1NDIwN1oXDTIzMTIwMTE1NDIwN1owGzEZMBcGA1UEAwwQ
-TGVhZiBjZXJ0aWZpY2F0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
-AOBT9POYwRQzAsikbf6qKveUPaZvAN873kyfo+oH1KzlWw3RrODt+cWYHTUt5bNJ
-lxSFRA/cTNJnCIgBpdin65PRaqH3UeeEflIqfbxvDtjbtqY+3tz1pGiWRBGFAu1H
-Et+4YHGVe2KHaHpEVgnVtMjx9slGkoto6IPV1YZxI8OAHr9sAcfSpLxAbeDjwC4w
-eL2t3SVm0/UHB1bXzuJyxSV9DOGnbwCo2qtLVEMJZKS2Ujgvt8wB3RwDJwNHv9/m
-N7DtGNxRC9R1It9QezzrNzkcm28Ie6cFrIxD9/HaUQazgkU+yIFznrClz3aWr4Es
-rAEqSlhLHb7/H4XCJ97xeAsCAwEAAaOBnzCBnDAMBgNVHRMBAf8EAjAAMB0GA1Ud
-DgQWBBTi4KRzlZvpbv3OKcRvB4ELlr1HujAfBgNVHSMEGDAWgBSbJguKmKm7Hbkf
-HOMaQDPtjheIqzAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwLQYDVR0R
-BCYwJIIQdGVzdC5FeEFtUGxFLkNvTYIQdGVzdC5FeEFtUGxFLk9yRzANBgkqhkiG
-9w0BAQsFAAOCAQEADakwbwdObEh6J+M35KqDiMzwNa1rbfxWp6XdUMzPF6hwd3v4
-eOekL0VuKX3p/7DuWrlrwv0TMvBZ8RCcYbqUliMSGS+V604rFJJgzRYbs6uki+gD
-wtRAa9vnfTNZQ6KJm6rl1Jhew01ynYufGIHVZyjIBT/jxdYkEn4y0L4gvSA3HkdL
-Yfwovu9mRaa9Iw8ts/nsVHk5NZjhAVYy7o578Ut3shrnInchEXnBT4T1WI6buY05
-h3h8e6w32utOvQZrb+ogAjjV2Xv/lcIBNcbNItwMNTtxMQajeKLkAzEaeuWqta4z
-HqQaW/Ip7w/tfoLw/C0uguRtHpMCnhJfrGxGwg==
+MIIDqTCCApGgAwIBAgIRALs4QldI3SskeBPgWbmj13cwDQYJKoZIhvcNAQELBQAw
+YzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1v
+dW50YWluIFZpZXcxEDAOBgNVBAoMB1Rlc3QgQ0ExFTATBgNVBAMMDFRlc3QgUm9v
+dCBDQTAeFw0yMjAxMTEwMjM2MjFaFw0yNDAxMTEwMjM2MjFaMBsxGTAXBgNVBAMM
+EExlYWYgY2VydGlmaWNhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQCwOdQrS3Jk6jJbNa2O4BadbxtUobCnikBNFFHn+Lof7YXQmy0DMiHL0iOhzt9Z
+cuRTLFzoYUvJk3TmS8h7Vdj4sIvF1k8E0MCWwY2rsFbgZilV276UQr+hzMncbzU5
+cfgMcOlHfpycBg8Ui4ygVUgttEWzilGA6NH9Vi7e4cb9DJyAI4AZXJI+B7d2n9HY
+xAuF1V417dpADz5gCznwFQI+i9uzITlAwWaIsZJ6GvvpSfnOFFFppbRLCLr8MTRL
+Yv8efWYh9HcG0LFevxuhtzYIIi40tzXTEYYnFhrx+HpuAR/pza/ewtvX2sVkXhr4
+n1B2m4+XfWHoVE4YD725dY55AgMBAAGjgZ8wgZwwDAYDVR0TAQH/BAIwADAdBgNV
+HQ4EFgQU5MW4FXv0JvBN6yfYlzQBEPWW0r4wHwYDVR0jBBgwFoAUmyYLipipux25
+HxzjGkAz7Y4XiKswHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMC0GA1Ud
+EQQmMCSCEHRlc3QuRXhBbVBsRS5Db02CEHRlc3QuRXhBbVBsRS5PckcwDQYJKoZI
+hvcNAQELBQADggEBALbYklbdYli+z3+m5bBepQ8Vz0RVvrg+Q+8bDeQRGvY4XA0U
+pCMO1uSi9O9EfwuyT/lftNipWCDo3nqNG3Rcc5sO5foc0l+Zs+lSI9PPrklT0lMZ
+L4wC6JhOQfLEx/vFV3Zyjtv8Qf4vhHM62tODwzWKAKPdt1hggiUN+wHu4n395Wp0
+MXckd56XfbpEv2y0BvfHntDhzB9dWuk4V/Op4weyVJvNaOtcsK8aCSVy2podAw6O
+xz/6x/5FVczNWwx7XHpIB0S74di9bHrQPK9TDZ1trybvOxdhryz/wiqVHktfoYV+
+zC4kQpCt1aA0Gs1PBgnjlJxrmAQaVHnIz3JiNK8=
 -----END CERTIFICATE-----
diff --git a/net/data/ssl/certificates/name_constraint_good.pem b/net/data/ssl/certificates/name_constraint_good.pem
index 7e4bb3b..9f963cb7 100644
--- a/net/data/ssl/certificates/name_constraint_good.pem
+++ b/net/data/ssl/certificates/name_constraint_good.pem
@@ -1,70 +1,70 @@
 -----BEGIN PRIVATE KEY-----
-MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDgU/TzmMEUMwLI
-pG3+qir3lD2mbwDfO95Mn6PqB9Ss5VsN0azg7fnFmB01LeWzSZcUhUQP3EzSZwiI
-AaXYp+uT0Wqh91HnhH5SKn28bw7Y27amPt7c9aRolkQRhQLtRxLfuGBxlXtih2h6
-RFYJ1bTI8fbJRpKLaOiD1dWGcSPDgB6/bAHH0qS8QG3g48AuMHi9rd0lZtP1BwdW
-187icsUlfQzhp28AqNqrS1RDCWSktlI4L7fMAd0cAycDR7/f5jew7RjcUQvUdSLf
-UHs86zc5HJtvCHunBayMQ/fx2lEGs4JFPsiBc56wpc92lq+BLKwBKkpYSx2+/x+F
-wife8XgLAgMBAAECggEAO0gUmHdKtvLQDoPdiYogtrKXJC97dILWuTsKzyLoohQu
-XtWFMR/SfNQ5C7+oTxvocATTurlGF+ggigidckbV64dQ/aJlI6CQ3VfbSHu02bwe
-ZYqBzLShkP382QBkiJ3asAKCgiG1rJEKHB2I+ypdjyjaRdB/k5XStFxDBDdL8zKe
-kL1hytf1ALxYBQJ3TXcoIqlpzz6v+JnZTAuu4vrySfffyFTrytIwf/KGlBACBqXe
-oV+DK586PiyC0m1Hhy1rLTi2/IT4t4j3KO+c/OnpWkMEg1G/Ojy1zPMsMHr1VO9t
-UWxhEIYOLQVUOT+2ltO4bRJcgEPHSxlIY23qalJgqQKBgQD9oZwHPhLZg439Zr9O
-Zgl5fFkPe4SxqLY+ggE5O81+3RgL8GyAuy9oDtUGcw8L9tpKcBflqH4jEvUvT1/m
-8BGKIQteqcKbY0C+KfPvq77R4k2KqmxpKF5f4BS/9O7cgzJZwhSFKDLtcKtDIPUK
-3eoERuPB4/cmhQyb+/TKofZpfwKBgQDibEm1+nYj3tIXDxK3hZHjPz0s9vYV84py
-zg3HjuEmdacnhNPQQQpNG83RMjEnnBvHWH2vxRWyD+57r/FWeA60aUJo5WIP0rj8
-z6GqFT9IWaBrJtBU1YoxuyTH64yih6ljvm2skIEGlQYS+GBL9K/Tx4mh4lAKVzD5
-GXVqtyM/dQKBgFD0Ik8VewK+QLXe87TcUK3cCLkuXZ4vEWxGJonUErUpcKFu7dLw
-7CK0iT3zv5u8ANS9joMZEpmzVVryZNPbUF3cSjq+yIS8W0/XKCsZkGCBcOqPlubB
-oc3MQhM65HqxzYJkthQCTq8GxUM547zCNA2FavDaCGrdELdA5lM++t2VAoGAMLem
-AH68bqlhwM5gc5ZMtn2D1ynn9v8oudz2AAsRDKph5dHhlTx5T+/8j9dh1ijznSfA
-G1KngWGGKZzIq5c3ar//Jvy75bWsUdEG8saRkCqgpo16Y9ZyXpLqrg1TfCD+ZFSz
-2l5ZNKZZ4TkJ1y31qvaS+X7tQ9xQ0DgXGHgBIIUCgYABBBddUXpOyG54HvFHIgxS
-esBBkpM0uy3vOkyfMown2GCjKZaXS5kEmRwU9WQQKLPf2ieALgYEf87YGWmPNZNF
-tG5DoNhTMsrYORB11tuuBR1e6lrG/aoOkP/4udWC8d4Yp9wnkEu0l6X/kMfq2lb1
-67bBVmHqKJ0t8rytHzkP0Q==
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCwOdQrS3Jk6jJb
+Na2O4BadbxtUobCnikBNFFHn+Lof7YXQmy0DMiHL0iOhzt9ZcuRTLFzoYUvJk3Tm
+S8h7Vdj4sIvF1k8E0MCWwY2rsFbgZilV276UQr+hzMncbzU5cfgMcOlHfpycBg8U
+i4ygVUgttEWzilGA6NH9Vi7e4cb9DJyAI4AZXJI+B7d2n9HYxAuF1V417dpADz5g
+CznwFQI+i9uzITlAwWaIsZJ6GvvpSfnOFFFppbRLCLr8MTRLYv8efWYh9HcG0LFe
+vxuhtzYIIi40tzXTEYYnFhrx+HpuAR/pza/ewtvX2sVkXhr4n1B2m4+XfWHoVE4Y
+D725dY55AgMBAAECggEAVcLu5FsFQuNOumC3JC8eEmP98wP1SrPXcyuOaMv9GIip
+dMnv7/w3wk90E8zvmUJ2p5uRY23mSiU+4MzEtnEi9HRGsXMIZZmKAFQVtBZPUUmm
+mCgm6VRKml1lZ6efSWOTicpxXN/bK3svX5pCR8z5IXT37tZDr+6eMyH8EW/jPUZU
+wA/LVpmfRkcLg8VF6J70yj60peU3234b5Z9QLkR80g9U9Sj1QQhuDRAO97zireru
+QwFixeazgXzWPTk3iol6GiDbus9T0WGeUAWYZ8XyJ1voNHRNg9dfvpLAc0UBQ5Ho
+7Au6fYWQ8cEmYFIC5X46k/fZhZNgludUlQLfMsI1AQKBgQDin873bebHXpoFGXR3
+Sn8obsieSWwHCDU2gp+K8JwPtip1PrVfA1KW/wBo938zqpBZDAOOFGGl4x08wLmm
+WnlrPOsrcehDbiGTPheBPvOS11/v6IxoOVLLUnDaQIX9j8eOGG/WM0pan8wNG9Vz
+WHQduZcBQlKoyXjjJ84avorh8QKBgQDHEaAJab8sK//lCYrCpTfbS2tYCfc9b7Nx
+m/6GCJnxqVYfal9ocGd7U7RO7okDQAMyvwSpI9ejTDF8tO4BNHCgvi6rKK3Fj/CJ
+bgw8rCP1BOKLvDK/7A7/v1KqvYHtODnCH0iTVK/R/Oq2Jws0m88uSkANm1w2t+SG
+JVk/ymBtCQKBgC0L/RTbyKrKmCz5UVhA+6Oq2b/08j83l3Q9ZL82cp8A49GoZF79
+hxYym/9Bawx3E/hPVgmQ7ZQO4AnqeTyi8U2qr0hUfQmiQ5REHGH5hGsk2pISlI5H
+DrkRqxMHDltHkDAjlV9rlJUM/H+Cj9w8seASuvxqFYotehUVHXfddjfRAoGAYUXj
+hbX+jH8Tk7+N5n8FREseMO7tuT+T17f6L1SUpNmyE7fO1yHV7xV/zfIRUV0+MtXU
+WTICdPEOXXmrszsErgdAlrJR92/WgdEcealECL5SVSWpRs76pU2//16K1nfbAVh4
+BkYjg+CqcEez2gkou93cXsnDzZkeOc6WRe2GIMECgYEApyMBng9jXOD1Qw2CpWeC
+kO/GL98UXAvQAnQB1YepWDmbW2OxpXZ5TI5wQkJxE7z7EJkIm6Vq0fcHtDNwGWBq
+2zLyUW+QdR53eGEkhHNzq0hFWVMCaDbO6UNa1OFZqN/WSBFB3z4F0XAZ1ZX+PaFm
+NqcJRJ7sL5XqgAdRqcTTFcA=
 -----END PRIVATE KEY-----
 Certificate:
     Data:
         Version: 3 (0x2)
         Serial Number:
-            12:f6:d9:f1:80:1b:50:9f:64:09:c8:ea:2f:16:33:a5
+            bb:38:42:57:48:dd:2b:24:78:13:e0:59:b9:a3:d7:78
         Signature Algorithm: sha256WithRSAEncryption
         Issuer: C=US, ST=California, L=Mountain View, O=Test CA, CN=Test Root CA
         Validity
-            Not Before: Dec  1 15:42:07 2021 GMT
-            Not After : Dec  1 15:42:07 2023 GMT
+            Not Before: Jan 11 02:36:21 2022 GMT
+            Not After : Jan 11 02:36:21 2024 GMT
         Subject: CN=Leaf Certificate
         Subject Public Key Info:
             Public Key Algorithm: rsaEncryption
                 RSA Public-Key: (2048 bit)
                 Modulus:
-                    00:e0:53:f4:f3:98:c1:14:33:02:c8:a4:6d:fe:aa:
-                    2a:f7:94:3d:a6:6f:00:df:3b:de:4c:9f:a3:ea:07:
-                    d4:ac:e5:5b:0d:d1:ac:e0:ed:f9:c5:98:1d:35:2d:
-                    e5:b3:49:97:14:85:44:0f:dc:4c:d2:67:08:88:01:
-                    a5:d8:a7:eb:93:d1:6a:a1:f7:51:e7:84:7e:52:2a:
-                    7d:bc:6f:0e:d8:db:b6:a6:3e:de:dc:f5:a4:68:96:
-                    44:11:85:02:ed:47:12:df:b8:60:71:95:7b:62:87:
-                    68:7a:44:56:09:d5:b4:c8:f1:f6:c9:46:92:8b:68:
-                    e8:83:d5:d5:86:71:23:c3:80:1e:bf:6c:01:c7:d2:
-                    a4:bc:40:6d:e0:e3:c0:2e:30:78:bd:ad:dd:25:66:
-                    d3:f5:07:07:56:d7:ce:e2:72:c5:25:7d:0c:e1:a7:
-                    6f:00:a8:da:ab:4b:54:43:09:64:a4:b6:52:38:2f:
-                    b7:cc:01:dd:1c:03:27:03:47:bf:df:e6:37:b0:ed:
-                    18:dc:51:0b:d4:75:22:df:50:7b:3c:eb:37:39:1c:
-                    9b:6f:08:7b:a7:05:ac:8c:43:f7:f1:da:51:06:b3:
-                    82:45:3e:c8:81:73:9e:b0:a5:cf:76:96:af:81:2c:
-                    ac:01:2a:4a:58:4b:1d:be:ff:1f:85:c2:27:de:f1:
-                    78:0b
+                    00:b0:39:d4:2b:4b:72:64:ea:32:5b:35:ad:8e:e0:
+                    16:9d:6f:1b:54:a1:b0:a7:8a:40:4d:14:51:e7:f8:
+                    ba:1f:ed:85:d0:9b:2d:03:32:21:cb:d2:23:a1:ce:
+                    df:59:72:e4:53:2c:5c:e8:61:4b:c9:93:74:e6:4b:
+                    c8:7b:55:d8:f8:b0:8b:c5:d6:4f:04:d0:c0:96:c1:
+                    8d:ab:b0:56:e0:66:29:55:db:be:94:42:bf:a1:cc:
+                    c9:dc:6f:35:39:71:f8:0c:70:e9:47:7e:9c:9c:06:
+                    0f:14:8b:8c:a0:55:48:2d:b4:45:b3:8a:51:80:e8:
+                    d1:fd:56:2e:de:e1:c6:fd:0c:9c:80:23:80:19:5c:
+                    92:3e:07:b7:76:9f:d1:d8:c4:0b:85:d5:5e:35:ed:
+                    da:40:0f:3e:60:0b:39:f0:15:02:3e:8b:db:b3:21:
+                    39:40:c1:66:88:b1:92:7a:1a:fb:e9:49:f9:ce:14:
+                    51:69:a5:b4:4b:08:ba:fc:31:34:4b:62:ff:1e:7d:
+                    66:21:f4:77:06:d0:b1:5e:bf:1b:a1:b7:36:08:22:
+                    2e:34:b7:35:d3:11:86:27:16:1a:f1:f8:7a:6e:01:
+                    1f:e9:cd:af:de:c2:db:d7:da:c5:64:5e:1a:f8:9f:
+                    50:76:9b:8f:97:7d:61:e8:54:4e:18:0f:bd:b9:75:
+                    8e:79
                 Exponent: 65537 (0x10001)
         X509v3 extensions:
             X509v3 Basic Constraints: critical
                 CA:FALSE
             X509v3 Subject Key Identifier: 
-                E2:E0:A4:73:95:9B:E9:6E:FD:CE:29:C4:6F:07:81:0B:96:BD:47:BA
+                E4:C5:B8:15:7B:F4:26:F0:4D:EB:27:D8:97:34:01:10:F5:96:D2:BE
             X509v3 Authority Key Identifier: 
                 keyid:9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
 
@@ -73,41 +73,41 @@
             X509v3 Subject Alternative Name: 
                 DNS:test.ExAmPlE.CoM, DNS:example.notarealtld, DNS:*.test2.ExAmPlE.CoM, DNS:*.example2.notarealtld
     Signature Algorithm: sha256WithRSAEncryption
-         59:dd:8d:b5:5e:17:6b:2d:0a:80:17:55:11:75:8e:54:ae:65:
-         40:1f:7d:a0:78:cf:d2:7d:d7:e4:1c:63:f2:1a:c3:24:20:2a:
-         67:dd:e4:d3:02:e5:c7:d8:ae:a0:cb:5a:4c:96:3a:a7:2a:1d:
-         33:c0:8d:1b:f9:ce:50:ec:db:16:ff:08:39:03:0f:35:d6:52:
-         8e:62:a5:4d:28:80:60:c4:62:ad:4d:be:cb:66:34:b0:98:c1:
-         7b:b8:6d:dd:10:1f:11:06:fc:3e:4d:2e:97:4d:4a:0b:28:86:
-         ae:93:9a:bf:ca:85:6c:b2:e7:2e:c9:23:a9:9a:c3:c9:32:95:
-         03:dd:61:8c:d2:04:d8:ea:61:01:b0:7f:d0:14:c9:e4:c7:cf:
-         a3:c7:0c:02:b3:ae:6f:52:ce:52:66:d0:3e:94:bd:c2:c0:ea:
-         38:29:42:ee:dd:5b:4b:54:97:93:51:81:2e:b0:28:39:92:4b:
-         95:91:c5:92:9e:d7:bf:4e:8a:b5:5f:4c:7d:bd:28:64:71:94:
-         f6:67:9b:80:3e:e5:e2:33:13:ad:ef:10:89:23:84:d4:a9:45:
-         95:a5:ce:10:61:f6:be:1d:19:dd:3c:80:14:3d:bc:3f:f5:39:
-         86:96:48:36:32:37:cf:91:65:d9:5a:d5:4d:6a:9a:69:4b:dc:
-         2b:a7:c1:41
+         c1:2d:db:8c:4d:f6:94:83:a3:e8:a8:93:df:20:52:95:3b:0b:
+         92:7a:f7:fd:c3:5c:ff:5e:27:3b:80:a8:cb:04:b3:40:46:3a:
+         94:24:1c:40:83:2e:ba:22:02:e8:d0:8f:46:51:e1:75:14:2a:
+         5b:82:70:f0:09:03:87:34:57:6f:14:60:df:f7:78:6c:d9:a2:
+         e2:42:d7:4d:b2:03:c7:ce:ad:b2:b2:d9:da:80:13:b1:66:35:
+         11:67:11:cc:ef:da:64:50:7b:ea:8c:88:a9:9d:c4:7d:09:63:
+         25:0f:d7:1c:b6:85:70:58:70:6c:38:62:b9:63:1f:a9:10:98:
+         bc:83:a1:63:98:79:c0:51:8e:bb:fe:97:1e:3e:51:af:27:9f:
+         73:40:9c:2e:a9:3d:2f:58:10:ca:3c:0b:ce:64:b1:0e:69:5e:
+         f8:21:3e:88:cb:00:fa:89:ef:ed:64:3b:82:4e:bd:56:56:cc:
+         a2:fa:98:b1:0e:ac:c7:61:84:32:a2:c8:98:a3:a4:c4:07:60:
+         b9:54:d4:04:3c:08:a4:27:53:6e:12:c4:67:cd:2f:ef:3a:59:
+         30:d6:e1:60:43:f8:7b:d4:3c:f1:85:f2:2e:c1:ac:a5:86:69:
+         16:70:60:92:fb:d7:5d:de:84:fe:e6:51:84:86:23:4c:d0:0f:
+         13:12:5b:e9
 -----BEGIN CERTIFICATE-----
-MIID2DCCAsCgAwIBAgIQEvbZ8YAbUJ9kCcjqLxYzpTANBgkqhkiG9w0BAQsFADBj
-MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
-bnRhaW4gVmlldzEQMA4GA1UECgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290
-IENBMB4XDTIxMTIwMTE1NDIwN1oXDTIzMTIwMTE1NDIwN1owGzEZMBcGA1UEAwwQ
-TGVhZiBDZXJ0aWZpY2F0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
-AOBT9POYwRQzAsikbf6qKveUPaZvAN873kyfo+oH1KzlWw3RrODt+cWYHTUt5bNJ
-lxSFRA/cTNJnCIgBpdin65PRaqH3UeeEflIqfbxvDtjbtqY+3tz1pGiWRBGFAu1H
-Et+4YHGVe2KHaHpEVgnVtMjx9slGkoto6IPV1YZxI8OAHr9sAcfSpLxAbeDjwC4w
-eL2t3SVm0/UHB1bXzuJyxSV9DOGnbwCo2qtLVEMJZKS2Ujgvt8wB3RwDJwNHv9/m
-N7DtGNxRC9R1It9QezzrNzkcm28Ie6cFrIxD9/HaUQazgkU+yIFznrClz3aWr4Es
-rAEqSlhLHb7/H4XCJ97xeAsCAwEAAaOBzzCBzDAMBgNVHRMBAf8EAjAAMB0GA1Ud
-DgQWBBTi4KRzlZvpbv3OKcRvB4ELlr1HujAfBgNVHSMEGDAWgBSbJguKmKm7Hbkf
-HOMaQDPtjheIqzAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwXQYDVR0R
-BFYwVIIQdGVzdC5FeEFtUGxFLkNvTYITZXhhbXBsZS5ub3RhcmVhbHRsZIITKi50
-ZXN0Mi5FeEFtUGxFLkNvTYIWKi5leGFtcGxlMi5ub3RhcmVhbHRsZDANBgkqhkiG
-9w0BAQsFAAOCAQEAWd2NtV4Xay0KgBdVEXWOVK5lQB99oHjP0n3X5Bxj8hrDJCAq
-Z93k0wLlx9iuoMtaTJY6pyodM8CNG/nOUOzbFv8IOQMPNdZSjmKlTSiAYMRirU2+
-y2Y0sJjBe7ht3RAfEQb8Pk0ul01KCyiGrpOav8qFbLLnLskjqZrDyTKVA91hjNIE
-2OphAbB/0BTJ5MfPo8cMArOub1LOUmbQPpS9wsDqOClC7t1bS1SXk1GBLrAoOZJL
-lZHFkp7Xv06KtV9Mfb0oZHGU9mebgD7l4jMTre8QiSOE1KlFlaXOEGH2vh0Z3TyA
-FD28P/U5hpZINjI3z5Fl2VrVTWqaaUvcK6fBQQ==
+MIID2TCCAsGgAwIBAgIRALs4QldI3SskeBPgWbmj13gwDQYJKoZIhvcNAQELBQAw
+YzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1v
+dW50YWluIFZpZXcxEDAOBgNVBAoMB1Rlc3QgQ0ExFTATBgNVBAMMDFRlc3QgUm9v
+dCBDQTAeFw0yMjAxMTEwMjM2MjFaFw0yNDAxMTEwMjM2MjFaMBsxGTAXBgNVBAMM
+EExlYWYgQ2VydGlmaWNhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQCwOdQrS3Jk6jJbNa2O4BadbxtUobCnikBNFFHn+Lof7YXQmy0DMiHL0iOhzt9Z
+cuRTLFzoYUvJk3TmS8h7Vdj4sIvF1k8E0MCWwY2rsFbgZilV276UQr+hzMncbzU5
+cfgMcOlHfpycBg8Ui4ygVUgttEWzilGA6NH9Vi7e4cb9DJyAI4AZXJI+B7d2n9HY
+xAuF1V417dpADz5gCznwFQI+i9uzITlAwWaIsZJ6GvvpSfnOFFFppbRLCLr8MTRL
+Yv8efWYh9HcG0LFevxuhtzYIIi40tzXTEYYnFhrx+HpuAR/pza/ewtvX2sVkXhr4
+n1B2m4+XfWHoVE4YD725dY55AgMBAAGjgc8wgcwwDAYDVR0TAQH/BAIwADAdBgNV
+HQ4EFgQU5MW4FXv0JvBN6yfYlzQBEPWW0r4wHwYDVR0jBBgwFoAUmyYLipipux25
+HxzjGkAz7Y4XiKswHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMF0GA1Ud
+EQRWMFSCEHRlc3QuRXhBbVBsRS5Db02CE2V4YW1wbGUubm90YXJlYWx0bGSCEyou
+dGVzdDIuRXhBbVBsRS5Db02CFiouZXhhbXBsZTIubm90YXJlYWx0bGQwDQYJKoZI
+hvcNAQELBQADggEBAMEt24xN9pSDo+iok98gUpU7C5J69/3DXP9eJzuAqMsEs0BG
+OpQkHECDLroiAujQj0ZR4XUUKluCcPAJA4c0V28UYN/3eGzZouJC102yA8fOrbKy
+2dqAE7FmNRFnEczv2mRQe+qMiKmdxH0JYyUP1xy2hXBYcGw4YrljH6kQmLyDoWOY
+ecBRjrv+lx4+Ua8nn3NAnC6pPS9YEMo8C85ksQ5pXvghPojLAPqJ7+1kO4JOvVZW
+zKL6mLEOrMdhhDKiyJijpMQHYLlU1AQ8CKQnU24SxGfNL+86WTDW4WBD+HvUPPGF
+8i7BrKWGaRZwYJL7113ehP7mUYSGI0zQDxMSW+k=
 -----END CERTIFICATE-----
diff --git a/net/data/ssl/scripts/generate-test-certs.sh b/net/data/ssl/scripts/generate-test-certs.sh
index 8a44dcf..4d17934e 100755
--- a/net/data/ssl/scripts/generate-test-certs.sh
+++ b/net/data/ssl/scripts/generate-test-certs.sh
@@ -102,6 +102,14 @@
   -out out/ok_cert.req \
   -config ee.cnf
 
+copy_or_generate_key ../certificates/name_constraint_good.pem \
+  out/name_constrained.key
+openssl req \
+  -new \
+  -key out/name_constrained.key \
+  -out out/name_constrained.req \
+  -config ee.cnf
+
 copy_or_generate_key ../certificates/wildcard.pem out/wildcard.key
 openssl req \
   -new \
@@ -181,7 +189,7 @@
     -extensions name_constraint_bad \
     -subj "/CN=Leaf certificate/" \
     -days ${CERT_LIFETIME} \
-    -in out/ok_cert.req \
+    -in out/name_constrained.req \
     -out out/name_constraint_bad.pem \
     -config ca.cnf
 
@@ -191,7 +199,7 @@
     -extensions name_constraint_good \
     -subj "/CN=Leaf Certificate/" \
     -days ${CERT_LIFETIME} \
-    -in out/ok_cert.req \
+    -in out/name_constrained.req \
     -out out/name_constraint_good.pem \
     -config ca.cnf
 
@@ -244,9 +252,9 @@
     > ../certificates/expired_cert.pem"
 /bin/sh -c "cat out/2048-sha256-root.key out/2048-sha256-root.pem \
     > ../certificates/root_ca_cert.pem"
-/bin/sh -c "cat out/ok_cert.key out/name_constraint_bad.pem \
+/bin/sh -c "cat out/name_constrained.key out/name_constraint_bad.pem \
     > ../certificates/name_constraint_bad.pem"
-/bin/sh -c "cat out/ok_cert.key out/name_constraint_good.pem \
+/bin/sh -c "cat out/name_constrained.key out/name_constraint_good.pem \
     > ../certificates/name_constraint_good.pem"
 /bin/sh -c "cat out/ok_cert.key out/bad_validity.pem \
     > ../certificates/bad_validity.pem"
diff --git a/net/dns/dns_response_result_extractor_unittest.cc b/net/dns/dns_response_result_extractor_unittest.cc
index dcf38fd4..266f6436 100644
--- a/net/dns/dns_response_result_extractor_unittest.cc
+++ b/net/dns/dns_response_result_extractor_unittest.cc
@@ -40,11 +40,11 @@
 
   EXPECT_THAT(results.error(), test::IsOk());
   IPEndPoint expected_endpoint(kExpected, 0 /* port */);
-  ASSERT_TRUE(results.addresses());
-  EXPECT_THAT(results.addresses().value().endpoints(),
+  ASSERT_TRUE(results.legacy_addresses());
+  EXPECT_THAT(results.legacy_addresses().value().endpoints(),
               testing::ElementsAre(expected_endpoint));
-  EXPECT_EQ(results.addresses().value().GetCanonicalName(), kName);
-  EXPECT_THAT(results.addresses().value().dns_aliases(),
+  EXPECT_EQ(results.legacy_addresses().value().GetCanonicalName(), kName);
+  EXPECT_THAT(results.legacy_addresses().value().dns_aliases(),
               testing::ElementsAre(kName));
   EXPECT_TRUE(results.has_ttl());
 }
@@ -64,11 +64,11 @@
 
   EXPECT_THAT(results.error(), test::IsOk());
   IPEndPoint expected_endpoint(expected, 0 /* port */);
-  ASSERT_TRUE(results.addresses());
-  EXPECT_THAT(results.addresses().value().endpoints(),
+  ASSERT_TRUE(results.legacy_addresses());
+  EXPECT_THAT(results.legacy_addresses().value().endpoints(),
               testing::ElementsAre(expected_endpoint));
-  EXPECT_EQ(results.addresses().value().GetCanonicalName(), kName);
-  EXPECT_THAT(results.addresses().value().dns_aliases(),
+  EXPECT_EQ(results.legacy_addresses().value().GetCanonicalName(), kName);
+  EXPECT_THAT(results.legacy_addresses().value().dns_aliases(),
               testing::ElementsAre(kName));
 }
 
@@ -86,11 +86,12 @@
 
   EXPECT_THAT(results.error(), test::IsOk());
   IPEndPoint expected_endpoint(kExpected, 0 /* port */);
-  ASSERT_TRUE(results.addresses());
-  EXPECT_THAT(results.addresses().value().endpoints(),
+  ASSERT_TRUE(results.legacy_addresses());
+  EXPECT_THAT(results.legacy_addresses().value().endpoints(),
               testing::ElementsAre(expected_endpoint));
-  EXPECT_EQ(results.addresses().value().GetCanonicalName(), kCanonicalName);
-  EXPECT_THAT(results.addresses().value().dns_aliases(),
+  EXPECT_EQ(results.legacy_addresses().value().GetCanonicalName(),
+            kCanonicalName);
+  EXPECT_THAT(results.legacy_addresses().value().dns_aliases(),
               testing::ElementsAre(kCanonicalName, "address.test"));
 }
 
@@ -112,16 +113,17 @@
             DnsResponseResultExtractor::ExtractionError::kOk);
 
   EXPECT_THAT(results.error(), test::IsOk());
-  ASSERT_TRUE(results.addresses());
-  EXPECT_THAT(results.addresses().value().endpoints(),
+  ASSERT_TRUE(results.legacy_addresses());
+  EXPECT_THAT(results.legacy_addresses().value().endpoints(),
               testing::UnorderedElementsAre(
                   IPEndPoint(IPAddress(74, 125, 226, 179), 0 /* port */),
                   IPEndPoint(IPAddress(74, 125, 226, 178), 0 /* port */),
                   IPEndPoint(IPAddress(74, 125, 226, 180), 0 /* port */),
                   IPEndPoint(IPAddress(74, 125, 226, 176), 0 /* port */),
                   IPEndPoint(IPAddress(74, 125, 226, 177), 0 /* port */)));
-  EXPECT_EQ(results.addresses().value().GetCanonicalName(), "alias.test");
-  EXPECT_THAT(results.addresses().value().dns_aliases(),
+  EXPECT_EQ(results.legacy_addresses().value().GetCanonicalName(),
+            "alias.test");
+  EXPECT_THAT(results.legacy_addresses().value().dns_aliases(),
               testing::ElementsAre("alias.test", "addresses.test"));
 }
 
@@ -141,10 +143,10 @@
             DnsResponseResultExtractor::ExtractionError::kOk);
 
   EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
-  ASSERT_TRUE(results.addresses());
-  EXPECT_TRUE(results.addresses().value().empty());
-  EXPECT_EQ(results.addresses().value().GetCanonicalName(), "");
-  EXPECT_TRUE(results.addresses().value().dns_aliases().empty());
+  ASSERT_TRUE(results.legacy_addresses());
+  EXPECT_TRUE(results.legacy_addresses().value().empty());
+  EXPECT_EQ(results.legacy_addresses().value().GetCanonicalName(), "");
+  EXPECT_TRUE(results.legacy_addresses().value().dns_aliases().empty());
 
   ASSERT_TRUE(results.has_ttl());
   EXPECT_EQ(results.ttl(), kTtl);
@@ -165,10 +167,10 @@
             DnsResponseResultExtractor::ExtractionError::kOk);
 
   EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
-  ASSERT_TRUE(results.addresses());
-  EXPECT_TRUE(results.addresses().value().empty());
-  EXPECT_EQ(results.addresses().value().GetCanonicalName(), "");
-  EXPECT_TRUE(results.addresses().value().dns_aliases().empty());
+  ASSERT_TRUE(results.legacy_addresses());
+  EXPECT_TRUE(results.legacy_addresses().value().empty());
+  EXPECT_EQ(results.legacy_addresses().value().GetCanonicalName(), "");
+  EXPECT_TRUE(results.legacy_addresses().value().dns_aliases().empty());
 
   ASSERT_TRUE(results.has_ttl());
   EXPECT_EQ(results.ttl(), kTtl);
@@ -213,10 +215,10 @@
             DnsResponseResultExtractor::ExtractionError::kOk);
 
   EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
-  ASSERT_TRUE(results.addresses());
-  EXPECT_TRUE(results.addresses().value().empty());
-  EXPECT_EQ(results.addresses().value().GetCanonicalName(), "");
-  EXPECT_TRUE(results.addresses().value().dns_aliases().empty());
+  ASSERT_TRUE(results.legacy_addresses());
+  EXPECT_TRUE(results.legacy_addresses().value().empty());
+  EXPECT_EQ(results.legacy_addresses().value().GetCanonicalName(), "");
+  EXPECT_TRUE(results.legacy_addresses().value().dns_aliases().empty());
   EXPECT_FALSE(results.has_ttl());
 }
 
@@ -236,12 +238,12 @@
             DnsResponseResultExtractor::ExtractionError::kOk);
 
   EXPECT_THAT(results.error(), test::IsOk());
-  ASSERT_TRUE(results.addresses());
+  ASSERT_TRUE(results.legacy_addresses());
   IPEndPoint expected_endpoint(kExpected, 0 /* port */);
-  EXPECT_THAT(results.addresses().value().endpoints(),
+  EXPECT_THAT(results.legacy_addresses().value().endpoints(),
               testing::ElementsAre(expected_endpoint));
-  EXPECT_EQ(results.addresses().value().GetCanonicalName(), kName);
-  EXPECT_THAT(results.addresses().value().dns_aliases(),
+  EXPECT_EQ(results.legacy_addresses().value().GetCanonicalName(), kName);
+  EXPECT_THAT(results.legacy_addresses().value().dns_aliases(),
               testing::ElementsAre(kName));
 
   ASSERT_TRUE(results.has_ttl());
@@ -993,11 +995,11 @@
             DnsResponseResultExtractor::ExtractionError::kOk);
 
   EXPECT_THAT(results.error(), test::IsOk());
-  ASSERT_TRUE(results.addresses());
-  EXPECT_THAT(results.addresses().value().endpoints(),
+  ASSERT_TRUE(results.legacy_addresses());
+  EXPECT_THAT(results.legacy_addresses().value().endpoints(),
               testing::ElementsAre(expected_endpoint));
 
-  EXPECT_THAT(results.addresses().value().dns_aliases(),
+  EXPECT_THAT(results.legacy_addresses().value().dns_aliases(),
               testing::ElementsAre("fourth.test", "third.test", "second.test",
                                    "first.test"));
 }
@@ -1037,11 +1039,11 @@
             DnsResponseResultExtractor::ExtractionError::kOk);
 
   EXPECT_THAT(results.error(), test::IsOk());
-  ASSERT_TRUE(results.addresses());
-  EXPECT_THAT(results.addresses().value().endpoints(),
+  ASSERT_TRUE(results.legacy_addresses());
+  EXPECT_THAT(results.legacy_addresses().value().endpoints(),
               testing::ElementsAre(expected_endpoint));
 
-  EXPECT_THAT(results.addresses().value().dns_aliases(),
+  EXPECT_THAT(results.legacy_addresses().value().dns_aliases(),
               testing::ElementsAre("fourth.test", "third.test", "second.test",
                                    "first.test"));
 }
@@ -1083,11 +1085,11 @@
             DnsResponseResultExtractor::ExtractionError::kOk);
 
   EXPECT_THAT(results.error(), test::IsOk());
-  ASSERT_TRUE(results.addresses());
-  EXPECT_THAT(results.addresses().value().endpoints(),
+  ASSERT_TRUE(results.legacy_addresses());
+  EXPECT_THAT(results.legacy_addresses().value().endpoints(),
               testing::ElementsAre(expected_endpoint));
 
-  EXPECT_THAT(results.addresses().value().dns_aliases(),
+  EXPECT_THAT(results.legacy_addresses().value().dns_aliases(),
               testing::ElementsAre("zfourth.test", "athird.test",
                                    "qsecond.test", "first.test"));
 }
@@ -1110,7 +1112,7 @@
   EXPECT_THAT(results.error(), test::IsOk());
   EXPECT_THAT(results.text_records(),
               testing::Optional(testing::ElementsAre("foo")));
-  EXPECT_FALSE(results.addresses());
+  EXPECT_FALSE(results.legacy_addresses());
 }
 
 TEST(DnsResponseResultExtractorTest,
@@ -1133,11 +1135,11 @@
 
   EXPECT_THAT(results.error(), test::IsOk());
   EXPECT_FALSE(results.text_records());
-  ASSERT_TRUE(results.addresses());
-  EXPECT_THAT(results.addresses().value().endpoints(),
+  ASSERT_TRUE(results.legacy_addresses());
+  EXPECT_THAT(results.legacy_addresses().value().endpoints(),
               testing::ElementsAre(expected_endpoint));
 
-  EXPECT_THAT(results.addresses().value().dns_aliases(),
+  EXPECT_THAT(results.legacy_addresses().value().dns_aliases(),
               testing::ElementsAre("fourth.test", "third.test", "second.test",
                                    "first.test"));
 }
@@ -1171,8 +1173,8 @@
             DnsResponseResultExtractor::ExtractionError::kOk);
 
   EXPECT_THAT(results.error(), test::IsError(ERR_NAME_NOT_RESOLVED));
-  ASSERT_TRUE(results.addresses());
-  EXPECT_TRUE(results.addresses().value().dns_aliases().empty());
+  ASSERT_TRUE(results.legacy_addresses());
+  EXPECT_TRUE(results.legacy_addresses().value().dns_aliases().empty());
 }
 
 TEST(DnsResponseResultExtractorTest, RejectsCnameChainWithLoop) {
diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc
index f37d5fa1..f560d1c 100644
--- a/net/dns/dns_transaction.cc
+++ b/net/dns/dns_transaction.cc
@@ -1037,7 +1037,8 @@
 
         if (extraction_error ==
                 DnsResponseResultExtractor::ExtractionError::kOk &&
-            results.addresses() && !results.addresses().value().empty()) {
+            results.legacy_addresses() &&
+            !results.legacy_addresses().value().empty()) {
           // The DoH probe queries don't go through the standard DnsAttempt
           // path, so the ServerStats have not been updated yet.
           context_->RecordServerSuccess(
diff --git a/net/dns/dns_util.cc b/net/dns/dns_util.cc
index debf173..435b1b9 100644
--- a/net/dns/dns_util.cc
+++ b/net/dns/dns_util.cc
@@ -20,7 +20,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "build/build_config.h"
-#include "net/base/address_list.h"
 #include "net/base/url_util.h"
 #include "net/dns/public/dns_protocol.h"
 #include "net/dns/public/doh_provider_entry.h"
@@ -239,46 +238,6 @@
   return out;
 }
 
-AddressListDeltaType FindAddressListDeltaType(const AddressList& a,
-                                              const AddressList& b) {
-  bool pairwise_mismatch = false;
-  bool any_match = false;
-  bool any_missing = false;
-  bool same_size = a.size() == b.size();
-
-  for (size_t i = 0; i < a.size(); ++i) {
-    bool this_match = false;
-    for (size_t j = 0; j < b.size(); ++j) {
-      if (a[i] == b[j]) {
-        any_match = true;
-        this_match = true;
-        // If there is no match before, and the current match, this means
-        // DELTA_OVERLAP.
-        if (any_missing)
-          return DELTA_OVERLAP;
-      } else if (i == j) {
-        pairwise_mismatch = true;
-      }
-    }
-    if (!this_match) {
-      any_missing = true;
-      // If any match has occurred before, then there is no need to compare the
-      // remaining addresses. This means DELTA_OVERLAP.
-      if (any_match)
-        return DELTA_OVERLAP;
-    }
-  }
-
-  if (same_size && !pairwise_mismatch)
-    return DELTA_IDENTICAL;
-  else if (same_size && !any_missing)
-    return DELTA_REORDERED;
-  else if (any_match)
-    return DELTA_OVERLAP;
-  else
-    return DELTA_DISJOINT;
-}
-
 std::string CreateNamePointer(uint16_t offset) {
   DCHECK_EQ(offset & ~dns_protocol::kOffsetMask, 0);
   char buf[2];
diff --git a/net/dns/dns_util.h b/net/dns/dns_util.h
index ce465776..f72a1da6 100644
--- a/net/dns/dns_util.h
+++ b/net/dns/dns_util.h
@@ -26,8 +26,6 @@
 
 namespace net {
 
-class AddressList;
-
 // DNSDomainFromDot - convert a domain string to DNS format. From DJB's
 // public domain DNS library. |dotted| may include only characters a-z, A-Z,
 // 0-9, -, and _.
@@ -96,26 +94,6 @@
     base::TimeDelta default_delta,
     NetworkChangeNotifier::ConnectionType connection_type);
 
-// How similar or different two AddressLists are (see values for details).
-// Used in histograms; do not modify existing values.
-enum AddressListDeltaType {
-  // Both lists contain the same addresses in the same order.
-  DELTA_IDENTICAL = 0,
-  // Both lists contain the same addresses in a different order.
-  DELTA_REORDERED = 1,
-  // The two lists have at least one address in common, but not all of them.
-  DELTA_OVERLAP = 2,
-  // The two lists have no addresses in common.
-  DELTA_DISJOINT = 3,
-  MAX_DELTA_TYPE
-};
-
-// Compares two AddressLists to see how similar or different their addresses
-// are. (See |AddressListDeltaType| for details of exactly what's checked.)
-NET_EXPORT
-AddressListDeltaType FindAddressListDeltaType(const AddressList& a,
-                                              const AddressList& b);
-
 // Creates a 2-byte string that represents the name pointer defined in Section
 // 4.1.1 of RFC 1035 for the given offset. The first two bits in the first byte
 // of the name pointer are ones, and the rest 14 bits are given to |offset|,
diff --git a/net/dns/host_cache.cc b/net/dns/host_cache.cc
index 9c1ce87a..6900b24 100644
--- a/net/dns/host_cache.cc
+++ b/net/dns/host_cache.cc
@@ -5,9 +5,13 @@
 #include "net/dns/host_cache.h"
 
 #include <algorithm>
+#include <iterator>
+#include <map>
 #include <ostream>
 #include <string>
+#include <type_traits>
 #include <unordered_set>
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
@@ -15,6 +19,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/time/default_tick_clock.h"
@@ -24,6 +29,8 @@
 #include "net/base/ip_endpoint.h"
 #include "net/base/trace_constants.h"
 #include "net/dns/host_resolver.h"
+#include "net/dns/https_record_rdata.h"
+#include "net/dns/public/dns_protocol.h"
 #include "net/dns/public/host_resolver_source.h"
 #include "net/log/net_log.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -57,11 +64,77 @@
 const char kPinnedKey[] = "pinned";
 const char kNetworkChangesKey[] = "network_changes";
 const char kNetErrorKey[] = "net_error";
+const char kIpEndpointsKey[] = "ip_endpoints";
+const char kEndpointAddressKey[] = "endpoint_address";
+const char kEndpointPortKey[] = "endpoint_port";
+const char kEndpointMetadatasKey[] = "endpoint_metadatas";
+const char kEndpointMetadataWeightKey[] = "endpoint_metadata_weight";
+const char kEndpointMetadataValueKey[] = "endpoint_metadata_value";
+const char kAliasesKey[] = "aliases";
 const char kAddressesKey[] = "addresses";
 const char kTextRecordsKey[] = "text_records";
 const char kHostnameResultsKey[] = "hostname_results";
 const char kHostPortsKey[] = "host_ports";
 
+base::Value IpEndpointToValue(const IPEndPoint& endpoint) {
+  base::Value::DictStorage dictionary;
+  dictionary.emplace(kEndpointAddressKey, endpoint.ToStringWithoutPort());
+  dictionary.emplace(kEndpointPortKey, int{endpoint.port()});
+  return base::Value(std::move(dictionary));
+}
+
+absl::optional<IPEndPoint> IpEndpointFromValue(const base::Value& value) {
+  if (!value.is_dict())
+    return absl::nullopt;
+
+  const std::string* ip_str = value.FindStringKey(kEndpointAddressKey);
+  absl::optional<int> port = value.FindIntKey(kEndpointPortKey);
+
+  if (!ip_str || !port ||
+      !base::IsValueInRangeForNumericType<uint16_t>(port.value())) {
+    return absl::nullopt;
+  }
+
+  IPAddress ip;
+  if (!ip.AssignFromIPLiteral(*ip_str))
+    return absl::nullopt;
+
+  return IPEndPoint(ip, base::checked_cast<uint16_t>(port.value()));
+}
+
+base::Value EndpointMetadataPairToValue(
+    const std::pair<HttpsRecordPriority, ConnectionEndpointMetadata>& pair) {
+  base::Value::DictStorage dictionary;
+  dictionary.emplace(kEndpointMetadataWeightKey, int{pair.first});
+  dictionary.emplace(kEndpointMetadataValueKey, pair.second.ToValue());
+  return base::Value(std::move(dictionary));
+}
+
+absl::optional<std::pair<HttpsRecordPriority, ConnectionEndpointMetadata>>
+EndpointMetadataPairFromValue(const base::Value& value) {
+  if (!value.is_dict())
+    return absl::nullopt;
+
+  absl::optional<int> priority = value.FindIntKey(kEndpointMetadataWeightKey);
+  const base::Value* metadata_value = value.FindKey(kEndpointMetadataValueKey);
+
+  if (!priority || !base::IsValueInRangeForNumericType<HttpsRecordPriority>(
+                       priority.value())) {
+    return absl::nullopt;
+  }
+
+  if (!metadata_value)
+    return absl::nullopt;
+  absl::optional<ConnectionEndpointMetadata> metadata =
+      ConnectionEndpointMetadata::FromValue(*metadata_value);
+  if (!metadata)
+    return absl::nullopt;
+
+  return std::make_pair(
+      base::checked_cast<HttpsRecordPriority>(priority.value()),
+      std::move(metadata).value());
+}
+
 bool AddressListFromListValue(const base::Value* value,
                               absl::optional<AddressList>* out_list) {
   if (!value) {
@@ -91,6 +164,16 @@
   }
 }
 
+template <typename T>
+void MergeContainers(absl::optional<T>& target,
+                     const absl::optional<T>& source) {
+  if (target.has_value() && source.has_value()) {
+    target->insert(source->begin(), source->end());
+  } else if (source) {
+    target = source;
+  }
+}
+
 // Used to reject empty and IP literal (whether or not surrounded by brackets)
 // hostnames.
 bool IsValidHostname(base::StringPiece hostname) {
@@ -175,6 +258,14 @@
   // If |ttl| has a value, must not be negative.
   DCHECK_GE(ttl.value_or(base::TimeDelta()), base::TimeDelta());
   DCHECK_NE(OK, error_);
+
+  // host_cache.h defines its own `HttpsRecordPriority` due to
+  // https_record_rdata.h not being allowed in the same places, but the types
+  // should still be the same thing.
+  static_assert(std::is_same<net::HttpsRecordPriority,
+                             HostCache::Entry::HttpsRecordPriority>::value,
+                "`net::HttpsRecordPriority` and "
+                "`HostCache::Entry::HttpsRecordPriority` must be same type");
 }
 
 HostCache::Entry::Entry(const Entry& entry) = default;
@@ -183,6 +274,36 @@
 
 HostCache::Entry::~Entry() = default;
 
+absl::optional<std::vector<HostResolverEndpointResult>>
+HostCache::Entry::GetEndpoints() const {
+  if (!ip_endpoints_.has_value())
+    return absl::nullopt;
+
+  std::vector<HostResolverEndpointResult> endpoints;
+
+  if (ip_endpoints_.value().empty())
+    return endpoints;
+
+  if (endpoint_metadatas_.has_value()) {
+    HttpsRecordPriority last_priority = 0;
+    for (const auto& metadata : endpoint_metadatas_.value()) {
+      // Ensure metadatas are iterated in priority order.
+      DCHECK_GE(metadata.first, last_priority);
+      last_priority = metadata.first;
+
+      endpoints.emplace_back();
+      endpoints.back().ip_endpoints = ip_endpoints_.value();
+      endpoints.back().metadata = metadata.second;
+    }
+  }
+
+  // Add a final non-protocol endpoint at the end.
+  endpoints.emplace_back();
+  endpoints.back().ip_endpoints = ip_endpoints_.value();
+
+  return endpoints;
+}
+
 absl::optional<base::TimeDelta> HostCache::Entry::GetOptionalTtl() const {
   if (has_ttl())
     return ttl();
@@ -201,6 +322,9 @@
   front.error_ =
       front.error() == OK || back.error() == OK ? OK : ERR_NAME_NOT_RESOLVED;
 
+  MergeLists(&front.ip_endpoints_, back.ip_endpoints_);
+  MergeContainers(front.endpoint_metadatas_, back.endpoint_metadatas_);
+  MergeContainers(front.aliases_, back.aliases_);
   front.MergeAddressesFrom(back);
   MergeLists(&front.text_records_, back.text_records());
   MergeLists(&front.hostnames_, back.hostnames());
@@ -234,31 +358,25 @@
 HostCache::Entry HostCache::Entry::CopyWithDefaultPort(uint16_t port) const {
   Entry copy(*this);
 
-  if (addresses() &&
-      std::any_of(addresses().value().begin(), addresses().value().end(),
-                  [](const IPEndPoint& e) { return e.port() == 0; })) {
-    AddressList addresses_with_port;
-    addresses_with_port.SetDnsAliases(addresses()->dns_aliases());
-    for (const IPEndPoint& endpoint : addresses().value()) {
+  if (copy.ip_endpoints_) {
+    for (IPEndPoint& endpoint : copy.ip_endpoints_.value()) {
       if (endpoint.port() == 0)
-        addresses_with_port.push_back(IPEndPoint(endpoint.address(), port));
-      else
-        addresses_with_port.push_back(endpoint);
+        endpoint = IPEndPoint(endpoint.address(), port);
     }
-    copy.set_addresses(addresses_with_port);
   }
 
-  if (hostnames() &&
-      std::any_of(hostnames().value().begin(), hostnames().value().end(),
-                  [](const HostPortPair& h) { return h.port() == 0; })) {
-    std::vector<HostPortPair> hostnames_with_port;
-    for (const HostPortPair& hostname : hostnames().value()) {
-      if (hostname.port() == 0)
-        hostnames_with_port.push_back(HostPortPair(hostname.host(), port));
-      else
-        hostnames_with_port.push_back(hostname);
+  if (copy.legacy_addresses_) {
+    for (IPEndPoint& endpoint : copy.legacy_addresses_.value().endpoints()) {
+      if (endpoint.port() == 0)
+        endpoint = IPEndPoint(endpoint.address(), port);
     }
-    copy.set_hostnames(std::move(hostnames_with_port));
+  }
+
+  if (copy.hostnames_) {
+    for (HostPortPair& hostname : copy.hostnames_.value()) {
+      if (hostname.port() == 0)
+        hostname = HostPortPair(hostname.host(), port);
+    }
   }
 
   return copy;
@@ -273,7 +391,10 @@
                         base::TimeDelta ttl,
                         int network_changes)
     : error_(entry.error()),
-      addresses_(entry.addresses()),
+      ip_endpoints_(entry.ip_endpoints_),
+      endpoint_metadatas_(entry.endpoint_metadatas_),
+      aliases_(base::OptionalFromPtr(entry.aliases())),
+      legacy_addresses_(entry.legacy_addresses()),
       text_records_(entry.text_records()),
       hostnames_(entry.hostnames()),
       experimental_results_(entry.experimental_results()),
@@ -285,7 +406,12 @@
 
 HostCache::Entry::Entry(
     int error,
-    const absl::optional<AddressList>& addresses,
+    absl::optional<std::vector<IPEndPoint>> ip_endpoints,
+    absl::optional<
+        std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>>
+        endpoint_metadatas,
+    absl::optional<std::set<std::string>> aliases,
+    const absl::optional<AddressList>& legacy_addresses,
     absl::optional<std::vector<std::string>>&& text_records,
     absl::optional<std::vector<HostPortPair>>&& hostnames,
     absl::optional<std::vector<bool>>&& experimental_results,
@@ -293,7 +419,10 @@
     base::TimeTicks expires,
     int network_changes)
     : error_(error),
-      addresses_(addresses),
+      ip_endpoints_(std::move(ip_endpoints)),
+      endpoint_metadatas_(std::move(endpoint_metadatas)),
+      aliases_(std::move(aliases)),
+      legacy_addresses_(legacy_addresses),
       text_records_(std::move(text_records)),
       hostnames_(std::move(hostnames)),
       experimental_results_(std::move(experimental_results)),
@@ -333,13 +462,13 @@
 }
 
 void HostCache::Entry::MergeAddressesFrom(const HostCache::Entry& source) {
-  MergeLists(&addresses_, source.addresses());
-  if (!addresses_ || addresses_->size() <= 1)
+  MergeLists(&legacy_addresses_, source.legacy_addresses());
+  if (!legacy_addresses_ || legacy_addresses_->size() <= 1)
     return;  // Nothing to do.
 
-  addresses_->Deduplicate();
+  legacy_addresses_->Deduplicate();
 
-  std::stable_sort(addresses_->begin(), addresses_->end(),
+  std::stable_sort(legacy_addresses_->begin(), legacy_addresses_->end(),
                    [](const IPEndPoint& lhs, const IPEndPoint& rhs) {
                      // Return true iff |lhs < rhs|.
                      return lhs.GetFamily() == ADDRESS_FAMILY_IPV6 &&
@@ -349,37 +478,38 @@
 
 void HostCache::Entry::MergeDnsAliasesFrom(const HostCache::Entry& source) {
   // No aliases to merge if source has no AddressList.
-  if (!source.addresses())
+  if (!source.legacy_addresses())
     return;
 
   // We expect this to be true because the address merging should have already
   // created the AddressList if the source had one but the target didn't.
-  DCHECK(addresses());
+  DCHECK(legacy_addresses());
 
   // Nothing to merge.
-  if (source.addresses()->dns_aliases().empty())
+  if (source.legacy_addresses()->dns_aliases().empty())
     return;
 
   // No aliases pre-existing in target, so simply set target's aliases to
   // source's. This takes care of the case where target does not have a usable
   // canonical name, but source does.
-  if (addresses()->dns_aliases().empty()) {
-    addresses_->SetDnsAliases(source.addresses()->dns_aliases());
+  if (legacy_addresses()->dns_aliases().empty()) {
+    legacy_addresses_->SetDnsAliases(source.legacy_addresses()->dns_aliases());
     return;
   }
 
-  DCHECK(addresses()->dns_aliases() != std::vector<std::string>({""}));
-  DCHECK(source.addresses()->dns_aliases() != std::vector<std::string>({""}));
+  DCHECK(legacy_addresses()->dns_aliases() != std::vector<std::string>({""}));
+  DCHECK(source.legacy_addresses()->dns_aliases() !=
+         std::vector<std::string>({""}));
 
   // We need to check for possible blanks and duplicates in the source's
   // aliases.
   std::unordered_set<std::string> aliases_seen;
   std::vector<std::string> deduplicated_source_aliases;
 
-  aliases_seen.insert(addresses()->dns_aliases().begin(),
-                      addresses()->dns_aliases().end());
+  aliases_seen.insert(legacy_addresses()->dns_aliases().begin(),
+                      legacy_addresses()->dns_aliases().end());
 
-  for (const auto& alias : source.addresses()->dns_aliases()) {
+  for (const auto& alias : source.legacy_addresses()->dns_aliases()) {
     if (alias != "" && aliases_seen.find(alias) == aliases_seen.end()) {
       aliases_seen.insert(alias);
       deduplicated_source_aliases.push_back(alias);
@@ -388,7 +518,7 @@
 
   // The first entry of target's aliases must remain in place,
   // as it's the canonical name, so we append source's aliases to the back.
-  addresses_->AppendDnsAliases(std::move(deduplicated_source_aliases));
+  legacy_addresses_->AppendDnsAliases(std::move(deduplicated_source_aliases));
 }
 
 base::Value HostCache::Entry::GetAsValue(bool include_staleness) const {
@@ -419,10 +549,37 @@
   if (error() != OK) {
     entry_dict.SetIntKey(kNetErrorKey, error());
   } else {
-    if (addresses()) {
+    if (ip_endpoints_) {
+      base::Value::ListStorage ip_endpoints_list;
+      for (const IPEndPoint& ip_endpoint : ip_endpoints_.value()) {
+        ip_endpoints_list.push_back(IpEndpointToValue(ip_endpoint));
+      }
+      entry_dict.SetKey(kIpEndpointsKey,
+                        base::Value(std::move(ip_endpoints_list)));
+    }
+
+    if (endpoint_metadatas_) {
+      base::Value::ListStorage endpoint_metadatas_list;
+      for (const auto& endpoint_metadata_pair : endpoint_metadatas_.value()) {
+        endpoint_metadatas_list.push_back(
+            EndpointMetadataPairToValue(endpoint_metadata_pair));
+      }
+      entry_dict.SetKey(kEndpointMetadatasKey,
+                        base::Value(std::move(endpoint_metadatas_list)));
+    }
+
+    if (aliases()) {
+      base::Value::ListStorage alias_list;
+      for (const std::string& alias : *aliases()) {
+        alias_list.emplace_back(alias);
+      }
+      entry_dict.SetKey(kAliasesKey, base::Value(std::move(alias_list)));
+    }
+
+    if (legacy_addresses()) {
       // Append all of the resolved addresses.
       base::ListValue addresses_value;
-      for (const IPEndPoint& address : addresses().value()) {
+      for (const IPEndPoint& address : legacy_addresses().value()) {
         addresses_value.Append(address.ToStringWithoutPort());
       }
       entry_dict.SetKey(kAddressesKey, std::move(addresses_value));
@@ -585,60 +742,9 @@
   if (it != entries_.end()) {
     has_active_pin = HasActivePin(it->second);
 
-    absl::optional<AddressListDeltaType> addresses_delta;
-    if (entry.addresses() || it->second.addresses()) {
-      if (entry.addresses() && it->second.addresses()) {
-        addresses_delta = FindAddressListDeltaType(
-            it->second.addresses().value(), entry.addresses().value());
-      } else {
-        addresses_delta = DELTA_DISJOINT;
-      }
-    }  // Else no addresses in old or new, so nullopt delta.
-
-    // For non-address results, delta is only considered for whole-list
-    // equality. The meaning of partial list equality varies too much depending
-    // on the context of a DNS record.
-    absl::optional<AddressListDeltaType> nonaddress_delta;
-    if (entry.text_records() || it->second.text_records() ||
-        entry.hostnames() || it->second.hostnames()) {
-      if (entry.text_records() == it->second.text_records() &&
-          entry.hostnames() == it->second.hostnames()) {
-        nonaddress_delta = DELTA_IDENTICAL;
-      } else if (entry.text_records() == it->second.text_records() ||
-                 entry.hostnames() == it->second.hostnames()) {
-        nonaddress_delta = DELTA_OVERLAP;
-      } else {
-        nonaddress_delta = DELTA_DISJOINT;
-      }
-    }  // Else no nonaddress results in old or new, so nullopt delta.
-
-    AddressListDeltaType overall_delta;
-    if (!addresses_delta && !nonaddress_delta) {
-      // No results in old or new is IDENTICAL.
-      overall_delta = DELTA_IDENTICAL;
-    } else if (!addresses_delta) {
-      overall_delta = nonaddress_delta.value();
-    } else if (!nonaddress_delta) {
-      overall_delta = addresses_delta.value();
-    } else if (addresses_delta == DELTA_DISJOINT &&
-               nonaddress_delta == DELTA_DISJOINT) {
-      overall_delta = DELTA_DISJOINT;
-    } else if (addresses_delta == DELTA_DISJOINT ||
-               nonaddress_delta == DELTA_DISJOINT) {
-      // If only some result types are DISJOINT, some match and we have OVERLAP.
-      overall_delta = DELTA_OVERLAP;
-    } else {
-      // No DISJOINT result types, so we have at least partial match.  Take the
-      // least matching amount (highest enum value).
-      overall_delta =
-          std::max(addresses_delta.value(), nonaddress_delta.value());
-    }
-
     // TODO(juliatuttle): Remember some old metadata (hit count or frequency or
     // something like that) if it's useful for better eviction algorithms?
-    result_changed =
-        entry.error() == OK && (it->second.error() != entry.error() ||
-                                overall_delta != DELTA_IDENTICAL);
+    result_changed = entry.error() == OK && !it->second.ContentsEqual(entry);
     entries_.erase(it);
   } else {
     result_changed = true;
@@ -849,7 +955,10 @@
     bool secure = entry_dict.FindBoolKey(kSecureKey).value_or(false);
 
     int error = OK;
-    const base::Value* addresses_value = nullptr;
+    const base::Value* ip_endpoints_value = nullptr;
+    const base::Value* endpoint_metadatas_value = nullptr;
+    const base::Value* aliases_value = nullptr;
+    const base::Value* legacy_addresses_value = nullptr;
     const base::Value* text_records_value = nullptr;
     const base::Value* hostname_records_value = nullptr;
     const base::Value* host_ports_value = nullptr;
@@ -858,7 +967,10 @@
     if (maybe_error.has_value()) {
       error = maybe_error.value();
     } else {
-      addresses_value = entry_dict.FindListKey(kAddressesKey);
+      ip_endpoints_value = entry_dict.FindListKey(kIpEndpointsKey);
+      endpoint_metadatas_value = entry_dict.FindListKey(kEndpointMetadatasKey);
+      aliases_value = entry_dict.FindListKey(kAliasesKey);
+      legacy_addresses_value = entry_dict.FindListKey(kAddressesKey);
       text_records_value = entry_dict.FindListKey(kTextRecordsKey);
       hostname_records_value = entry_dict.FindListKey(kHostnameResultsKey);
       host_ports_value = entry_dict.FindListKey(kHostPortsKey);
@@ -877,8 +989,48 @@
         tick_clock_->NowTicks() -
         (base::Time::Now() - base::Time::FromInternalValue(time_internal));
 
-    absl::optional<AddressList> address_list;
-    if (!AddressListFromListValue(addresses_value, &address_list)) {
+    absl::optional<std::vector<IPEndPoint>> ip_endpoints;
+    if (ip_endpoints_value) {
+      ip_endpoints.emplace();
+      for (const base::Value& ip_endpoint_value :
+           ip_endpoints_value->GetList()) {
+        absl::optional<IPEndPoint> ip_endpoint =
+            IpEndpointFromValue(ip_endpoint_value);
+        if (!ip_endpoint)
+          return false;
+        ip_endpoints->push_back(std::move(ip_endpoint).value());
+      }
+    }
+
+    absl::optional<
+        std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>>
+        endpoint_metadatas;
+    if (endpoint_metadatas_value) {
+      endpoint_metadatas.emplace();
+      for (const base::Value& endpoint_metadata_value :
+           endpoint_metadatas_value->GetList()) {
+        absl::optional<
+            std::pair<HttpsRecordPriority, ConnectionEndpointMetadata>>
+            pair = EndpointMetadataPairFromValue(endpoint_metadata_value);
+        if (!pair)
+          return false;
+        endpoint_metadatas->insert(std::move(pair).value());
+      }
+    }
+
+    absl::optional<std::set<std::string>> aliases;
+    if (aliases_value) {
+      aliases.emplace();
+      for (const base::Value& alias_value : aliases_value->GetList()) {
+        if (!alias_value.is_string())
+          return false;
+        aliases->insert(alias_value.GetString());
+      }
+    }
+
+    absl::optional<AddressList> legacy_address_list;
+    if (!AddressListFromListValue(legacy_addresses_value,
+                                  &legacy_address_list)) {
       return false;
     }
 
@@ -919,9 +1071,9 @@
     absl::optional<std::vector<bool>> experimental_results;
 
     // Assume an empty address list if we have an address type and no results.
-    if (IsAddressType(dns_query_type) && !address_list && !text_records &&
-        !hostname_records) {
-      address_list.emplace();
+    if (IsAddressType(dns_query_type) && !ip_endpoints &&
+        !legacy_address_list && !text_records && !hostname_records) {
+      legacy_address_list.emplace();
     }
 
     Key key(std::move(host), dns_query_type, flags,
@@ -933,9 +1085,11 @@
     // replace the entry.
     auto found = entries_.find(key);
     if (found == entries_.end()) {
-      Entry entry(error, address_list, std::move(text_records),
-                  std::move(hostname_records), std::move(experimental_results),
-                  Entry::SOURCE_UNKNOWN, expiration_time, network_changes_ - 1);
+      Entry entry(error, std::move(ip_endpoints), std::move(endpoint_metadatas),
+                  std::move(aliases), legacy_address_list,
+                  std::move(text_records), std::move(hostname_records),
+                  std::move(experimental_results), Entry::SOURCE_UNKNOWN,
+                  expiration_time, network_changes_ - 1);
       entry.set_pinning(maybe_pinned.value_or(false));
       AddEntry(key, std::move(entry));
       restore_size_++;
diff --git a/net/dns/host_cache.h b/net/dns/host_cache.h
index 7592240d..1c86a14 100644
--- a/net/dns/host_cache.h
+++ b/net/dns/host_cache.h
@@ -11,6 +11,7 @@
 #include <map>
 #include <memory>
 #include <ostream>
+#include <set>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -20,17 +21,22 @@
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/numerics/clamped_math.h"
+#include "base/stl_util.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "net/base/address_family.h"
 #include "net/base/address_list.h"
+#include "net/base/connection_endpoint_metadata.h"
 #include "net/base/expiring_cache.h"
 #include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_export.h"
 #include "net/base/network_isolation_key.h"
 #include "net/dns/dns_util.h"
+#include "net/dns/host_resolver_results.h"
+#include "net/dns/public/dns_protocol.h"
 #include "net/dns/public/dns_query_type.h"
 #include "net/dns/public/host_resolver_source.h"
 #include "net/log/net_log_capture_mode.h"
@@ -150,15 +156,44 @@
     Entry& operator=(const Entry& entry);
     Entry& operator=(Entry&& entry);
 
+    bool operator==(const Entry& other) const {
+      return ContentsEqual(other) &&
+             std::tie(source_, pinning_, ttl_, expires_, network_changes_,
+                      total_hits_, stale_hits_) ==
+                 std::tie(other.source_, other.pinning_, other.ttl_,
+                          other.expires_, other.network_changes_,
+                          other.total_hits_, other.stale_hits_);
+    }
+
+    bool ContentsEqual(const Entry& other) const {
+      return std::tie(error_, ip_endpoints_, endpoint_metadatas_, aliases_,
+                      legacy_addresses_, text_records_, hostnames_,
+                      experimental_results_) ==
+             std::tie(other.error_, other.ip_endpoints_,
+                      other.endpoint_metadatas_, other.aliases_,
+                      other.legacy_addresses_, other.text_records_,
+                      other.hostnames_, other.experimental_results_);
+    }
+
     int error() const { return error_; }
     bool did_complete() const {
       return error_ != ERR_NETWORK_CHANGED &&
              error_ != ERR_HOST_RESOLVER_QUEUE_TOO_LARGE;
     }
     void set_error(int error) { error_ = error; }
-    const absl::optional<AddressList>& addresses() const { return addresses_; }
-    void set_addresses(const absl::optional<AddressList>& addresses) {
-      addresses_ = addresses;
+    absl::optional<std::vector<HostResolverEndpointResult>> GetEndpoints()
+        const;
+    const std::set<std::string>* aliases() const {
+      return base::OptionalOrNullptr(aliases_);
+    }
+    void set_aliases(std::set<std::string> aliases) {
+      aliases_ = std::move(aliases);
+    }
+    const absl::optional<AddressList>& legacy_addresses() const {
+      return legacy_addresses_;
+    }
+    void set_legacy_addresses(const absl::optional<AddressList>& addresses) {
+      legacy_addresses_ = addresses;
     }
     const absl::optional<std::vector<std::string>>& text_records() const {
       return text_records_;
@@ -198,8 +233,8 @@
     // for the same overall host resolution query.
     //
     // Merges lists, placing elements from |front| before elements from |back|.
-    // Further, dedupes address lists and moves IPv6 addresses before IPv4
-    // addresses (maintaining stable order otherwise).
+    // Further, dedupes legacy address lists and moves IPv6 addresses before
+    // IPv4 addresses (maintaining stable order otherwise).
     //
     // Fields that cannot be merged take precedence from |front|.
     static Entry MergeEntries(Entry front, Entry back);
@@ -212,6 +247,8 @@
     HostCache::Entry CopyWithDefaultPort(uint16_t port) const;
 
    private:
+    using HttpsRecordPriority = uint16_t;
+
     friend class HostCache;
 
     Entry(const Entry& entry,
@@ -220,7 +257,12 @@
           int network_changes);
 
     Entry(int error,
-          const absl::optional<AddressList>& addresses,
+          absl::optional<std::vector<IPEndPoint>> ip_endpoints,
+          absl::optional<
+              std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>>
+              endpoint_metadatas,
+          absl::optional<std::set<std::string>> aliases,
+          const absl::optional<AddressList>& legacy_addresses,
           absl::optional<std::vector<std::string>>&& text_results,
           absl::optional<std::vector<HostPortPair>>&& hostnames,
           absl::optional<std::vector<bool>>&& experimental_results,
@@ -230,7 +272,17 @@
 
     void PrepareForCacheInsertion();
 
-    void SetResult(AddressList addresses) { addresses_ = std::move(addresses); }
+    void SetResult(std::vector<IPEndPoint> ip_endpoints) {
+      ip_endpoints_ = std::move(ip_endpoints);
+    }
+    void SetResult(
+        std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>
+            endpoint_metadatas) {
+      endpoint_metadatas_ = std::move(endpoint_metadatas);
+    }
+    void SetResult(AddressList addresses) {
+      legacy_addresses_ = std::move(addresses);
+    }
     void SetResult(std::vector<std::string> text_records) {
       text_records_ = std::move(text_records);
     }
@@ -250,9 +302,9 @@
                       int network_changes,
                       EntryStaleness* out) const;
 
-    // Merges addresses from |source| into the stored list of addresses and
-    // deduplicates. The address list can be accessed with |addresses()|. This
-    // method performs a stable sort to ensure IPv6 addresses precede IPv4
+    // Merges legacy addresses from |source| into the stored list of addresses
+    // and deduplicates. The address list can be accessed with |addresses()|.
+    // This method performs a stable sort to ensure IPv6 addresses precede IPv4
     // addresses. IP versions being equal, addresses from |*this| will precede
     // those from |source|.
     //
@@ -263,15 +315,20 @@
     // non-empty and therefore OK.
     void MergeAddressesFrom(const HostCache::Entry& source);
 
-    // Merges DNS aliases from |source| into the stored list of DNS aliases and
-    // deduplicates.
+    // Merges the legacy DNS aliases list from `source` into the stored list of
+    // DNS aliases and deduplicates.
     void MergeDnsAliasesFrom(const HostCache::Entry& source);
 
     base::Value GetAsValue(bool include_staleness) const;
 
     // The resolve results for this entry.
     int error_ = ERR_FAILED;
-    absl::optional<AddressList> addresses_;
+    absl::optional<std::vector<IPEndPoint>> ip_endpoints_;
+    absl::optional<
+        std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>>
+        endpoint_metadatas_;
+    absl::optional<std::set<std::string>> aliases_;
+    absl::optional<AddressList> legacy_addresses_;
     absl::optional<std::vector<std::string>> text_records_;
     absl::optional<std::vector<HostPortPair>> hostnames_;
     absl::optional<std::vector<bool>> experimental_results_;
diff --git a/net/dns/host_cache_unittest.cc b/net/dns/host_cache_unittest.cc
index e10d61d..0b54e13 100644
--- a/net/dns/host_cache_unittest.cc
+++ b/net/dns/host_cache_unittest.cc
@@ -5,6 +5,7 @@
 #include "net/dns/host_cache.h"
 
 #include <algorithm>
+#include <map>
 #include <string>
 #include <utility>
 
@@ -13,13 +14,18 @@
 #include "base/callback_helpers.h"
 #include "base/cxx17_backports.h"
 #include "base/format_macros.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
+#include "net/base/connection_endpoint_metadata.h"
 #include "net/base/network_isolation_key.h"
 #include "net/base/schemeful_site.h"
+#include "net/dns/host_resolver_results.h"
+#include "net/dns/host_resolver_results_test_util.h"
+#include "net/dns/https_record_rdata.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -29,6 +35,7 @@
 using ::testing::_;
 using ::testing::ElementsAre;
 using ::testing::ElementsAreArray;
+using ::testing::IsEmpty;
 using ::testing::Optional;
 using ::testing::Pair;
 using ::testing::Pointee;
@@ -62,6 +69,13 @@
   int num_changes_ = 0;
 };
 
+MATCHER_P(EntryContentsEqual,
+          entry,
+          base::StrCat({"contents ", negation ? "!=" : "==", " contents of ",
+                        testing::PrintToString(entry)})) {
+  return arg.ContentsEqual(entry);
+}
+
 }  // namespace
 
 TEST(HostCacheTest, Basic) {
@@ -126,6 +140,63 @@
   EXPECT_FALSE(cache.Lookup(key2, now));
 }
 
+TEST(HostCacheTest, GetEndpoints) {
+  std::vector<IPEndPoint> ip_endpoints = {IPEndPoint(IPAddress(1, 1, 1, 1), 0),
+                                          IPEndPoint(IPAddress(2, 2, 2, 2), 0)};
+  HostCache::Entry entry(OK, ip_endpoints, HostCache::Entry::SOURCE_DNS);
+
+  EXPECT_THAT(entry.GetEndpoints(),
+              Optional(ElementsAre(ExpectEndpointResult(ip_endpoints))));
+}
+
+TEST(HostCacheTest, GetEmptyEndpoints) {
+  HostCache::Entry entry(ERR_NAME_NOT_RESOLVED, std::vector<IPEndPoint>(),
+                         HostCache::Entry::SOURCE_DNS);
+  EXPECT_THAT(entry.GetEndpoints(), Optional(IsEmpty()));
+}
+
+TEST(HostCacheTest, GetEmptyEndpointsWithMetadata) {
+  HostCache::Entry entry(ERR_NAME_NOT_RESOLVED, std::vector<IPEndPoint>(),
+                         HostCache::Entry::SOURCE_DNS);
+
+  // Merge in non-empty metadata.
+  ConnectionEndpointMetadata metadata;
+  metadata.supported_protocol_alpns = {"h3", "h2"};
+  HostCache::Entry metadata_entry(
+      OK,
+      std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>{
+          {1u, metadata}},
+      HostCache::Entry::SOURCE_DNS);
+
+  auto merged_entry = HostCache::Entry::MergeEntries(entry, metadata_entry);
+
+  // Result should still be empty.
+  EXPECT_THAT(merged_entry.GetEndpoints(), Optional(IsEmpty()));
+}
+
+TEST(HostCacheTest, GetMissingEndpoints) {
+  HostCache::Entry entry(ERR_NAME_NOT_RESOLVED, HostCache::Entry::SOURCE_DNS);
+  EXPECT_FALSE(entry.GetEndpoints());
+}
+
+TEST(HostCacheTest, GetMissingEndpointsWithMetadata) {
+  HostCache::Entry entry(ERR_NAME_NOT_RESOLVED, HostCache::Entry::SOURCE_DNS);
+
+  // Merge in non-empty metadata.
+  ConnectionEndpointMetadata metadata;
+  metadata.supported_protocol_alpns = {"h3", "h2"};
+  HostCache::Entry metadata_entry(
+      OK,
+      std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>{
+          {1u, metadata}},
+      HostCache::Entry::SOURCE_DNS);
+
+  auto merged_entry = HostCache::Entry::MergeEntries(entry, metadata_entry);
+
+  // Result should still be `nullopt`.
+  EXPECT_FALSE(merged_entry.GetEndpoints());
+}
+
 // Test that Keys without scheme are allowed and treated as completely different
 // from similar Keys with scheme.
 TEST(HostCacheTest, HandlesKeysWithoutScheme) {
@@ -880,14 +951,12 @@
   base::TimeTicks now;
 
   // Make entry1 and entry2, identical except for IP and pinned flag.
-  IPAddress address1(192, 0, 2, 1);
-  IPAddress address2(192, 0, 2, 2);
-  IPEndPoint endpoint1(address1, 0);
-  IPEndPoint endpoint2(address2, 0);
-  HostCache::Entry entry1 = HostCache::Entry(OK, AddressList(endpoint1),
-                                             HostCache::Entry::SOURCE_UNKNOWN);
-  HostCache::Entry entry2 = HostCache::Entry(OK, AddressList(endpoint2),
-                                             HostCache::Entry::SOURCE_UNKNOWN);
+  IPEndPoint endpoint1(IPAddress(192, 0, 2, 1), 0);
+  IPEndPoint endpoint2(IPAddress(192, 0, 2, 2), 0);
+  HostCache::Entry entry1 = HostCache::Entry(
+      OK, std::vector<IPEndPoint>{endpoint1}, HostCache::Entry::SOURCE_UNKNOWN);
+  HostCache::Entry entry2 = HostCache::Entry(
+      OK, std::vector<IPEndPoint>{endpoint2}, HostCache::Entry::SOURCE_UNKNOWN);
   entry1.set_pinning(true);
 
   HostCache::Key key = Key("foobar.com");
@@ -898,7 +967,9 @@
   const auto* pair1 = cache.Lookup(key, now);
   ASSERT_TRUE(pair1);
   const HostCache::Entry& result1 = pair1->second;
-  EXPECT_EQ(endpoint1, result1.addresses()->front());
+  EXPECT_THAT(
+      result1.GetEndpoints(),
+      Optional(ElementsAre(ExpectEndpointResult(ElementsAre(endpoint1)))));
   EXPECT_THAT(result1.pinning(), Optional(true));
 
   // Insert |entry2|, and verify that it when it is retrieved, it
@@ -907,7 +978,9 @@
   const auto* pair2 = cache.Lookup(key, now);
   ASSERT_TRUE(pair2);
   const HostCache::Entry& result2 = pair2->second;
-  EXPECT_EQ(endpoint2, result2.addresses()->front());
+  EXPECT_THAT(
+      result2.GetEndpoints(),
+      Optional(ElementsAre(ExpectEndpointResult(ElementsAre(endpoint2)))));
   EXPECT_THAT(result2.pinning(), Optional(true));
 }
 
@@ -918,14 +991,12 @@
   base::TimeTicks now;
 
   // Make entry1 and entry2, identical except for IP and "pinned" flag.
-  IPAddress address1(192, 0, 2, 1);
-  IPAddress address2(192, 0, 2, 2);
-  IPEndPoint endpoint1(address1, 0);
-  IPEndPoint endpoint2(address2, 0);
-  HostCache::Entry entry1 = HostCache::Entry(OK, AddressList(endpoint1),
-                                             HostCache::Entry::SOURCE_UNKNOWN);
-  HostCache::Entry entry2 = HostCache::Entry(OK, AddressList(endpoint2),
-                                             HostCache::Entry::SOURCE_UNKNOWN);
+  IPEndPoint endpoint1(IPAddress(192, 0, 2, 1), 0);
+  IPEndPoint endpoint2(IPAddress(192, 0, 2, 2), 0);
+  HostCache::Entry entry1 = HostCache::Entry(
+      OK, std::vector<IPEndPoint>{endpoint1}, HostCache::Entry::SOURCE_UNKNOWN);
+  HostCache::Entry entry2 = HostCache::Entry(
+      OK, std::vector<IPEndPoint>{endpoint2}, HostCache::Entry::SOURCE_UNKNOWN);
   entry1.set_pinning(true);
 
   HostCache::Key key = Key("foobar.com");
@@ -936,7 +1007,9 @@
   const auto* pair1 = cache.Lookup(key, now);
   ASSERT_TRUE(pair1);
   const HostCache::Entry& result1 = pair1->second;
-  EXPECT_EQ(endpoint1, result1.addresses()->front());
+  EXPECT_THAT(
+      result1.GetEndpoints(),
+      Optional(ElementsAre(ExpectEndpointResult(ElementsAre(endpoint1)))));
   EXPECT_THAT(result1.pinning(), Optional(true));
 
   // Make entry1 obsolete.
@@ -948,7 +1021,9 @@
   const auto* pair2 = cache.Lookup(key, now);
   ASSERT_TRUE(pair2);
   const HostCache::Entry& result2 = pair2->second;
-  EXPECT_EQ(endpoint2, result2.addresses()->front());
+  EXPECT_THAT(
+      result2.GetEndpoints(),
+      Optional(ElementsAre(ExpectEndpointResult(ElementsAre(endpoint2)))));
   EXPECT_THAT(result2.pinning(), Optional(false));
 }
 
@@ -960,14 +1035,12 @@
   base::TimeTicks now;
 
   // Make entry1 and entry2, identical except for IP and pinned flag.
-  IPAddress address1(192, 0, 2, 1);
-  IPAddress address2(192, 0, 2, 2);
-  IPEndPoint endpoint1(address1, 0);
-  IPEndPoint endpoint2(address2, 0);
-  HostCache::Entry entry1 = HostCache::Entry(OK, AddressList(endpoint1),
-                                             HostCache::Entry::SOURCE_UNKNOWN);
-  HostCache::Entry entry2 = HostCache::Entry(OK, AddressList(endpoint2),
-                                             HostCache::Entry::SOURCE_UNKNOWN);
+  IPEndPoint endpoint1(IPAddress(192, 0, 2, 1), 0);
+  IPEndPoint endpoint2(IPAddress(192, 0, 2, 2), 0);
+  HostCache::Entry entry1 = HostCache::Entry(
+      OK, std::vector<IPEndPoint>{endpoint1}, HostCache::Entry::SOURCE_UNKNOWN);
+  HostCache::Entry entry2 = HostCache::Entry(
+      OK, std::vector<IPEndPoint>{endpoint2}, HostCache::Entry::SOURCE_UNKNOWN);
   entry1.set_pinning(true);
   entry2.set_pinning(false);
 
@@ -979,7 +1052,9 @@
   const auto* pair1 = cache.Lookup(key, now);
   ASSERT_TRUE(pair1);
   const HostCache::Entry& result1 = pair1->second;
-  EXPECT_EQ(endpoint1, result1.addresses()->front());
+  EXPECT_THAT(
+      result1.GetEndpoints(),
+      Optional(ElementsAre(ExpectEndpointResult(ElementsAre(endpoint1)))));
   EXPECT_THAT(result1.pinning(), Optional(true));
 
   // Insert |entry2|, and verify that it when it is retrieved, it
@@ -988,7 +1063,9 @@
   const auto* pair2 = cache.Lookup(key, now);
   ASSERT_TRUE(pair2);
   const HostCache::Entry& result2 = pair2->second;
-  EXPECT_EQ(endpoint2, result2.addresses()->front());
+  EXPECT_THAT(
+      result2.GetEndpoints(),
+      Optional(ElementsAre(ExpectEndpointResult(ElementsAre(endpoint2)))));
   EXPECT_THAT(result2.pinning(), Optional(false));
 }
 
@@ -1146,7 +1223,164 @@
   }
 }
 
-TEST(HostCacheTest, SerializeAndDeserialize) {
+TEST(HostCacheTest, SerializeAndDeserializeWithExpirations) {
+  const base::TimeDelta kTTL = base::Seconds(10);
+
+  HostCache cache(kMaxCacheEntries);
+
+  // Start at t=0.
+  base::TimeTicks now;
+
+  HostCache::Key expire_by_time_key = Key("expire.by.time.test");
+  HostCache::Key expire_by_changes_key = Key("expire.by.changes.test");
+
+  IPEndPoint endpoint(IPAddress(1, 2, 3, 4), 0);
+  HostCache::Entry entry = HostCache::Entry(
+      OK, std::vector<IPEndPoint>{endpoint}, HostCache::Entry::SOURCE_UNKNOWN);
+
+  EXPECT_EQ(0u, cache.size());
+
+  // Add an entry for `expire_by_time_key` at t=0.
+  EXPECT_FALSE(cache.Lookup(expire_by_time_key, now));
+  cache.Set(expire_by_time_key, entry, now, kTTL);
+  EXPECT_THAT(cache.Lookup(expire_by_time_key, now),
+              Pointee(Pair(expire_by_time_key, EntryContentsEqual(entry))));
+
+  EXPECT_EQ(1u, cache.size());
+
+  // Advance to t=5.
+  now += base::Seconds(5);
+
+  // Add entry for `expire_by_changes_key` at t=5.
+  EXPECT_FALSE(cache.Lookup(expire_by_changes_key, now));
+  cache.Set(expire_by_changes_key, entry, now, kTTL);
+  EXPECT_TRUE(cache.Lookup(expire_by_changes_key, now));
+  EXPECT_EQ(2u, cache.size());
+
+  EXPECT_EQ(0u, cache.last_restore_size());
+
+  // Advance to t=12, and serialize/deserialize the cache.
+  now += base::Seconds(7);
+
+  base::Value serialized_cache(base::Value::Type::LIST);
+  cache.GetList(&serialized_cache, false /* include_staleness */,
+                HostCache::SerializationType::kRestorable);
+  HostCache restored_cache(kMaxCacheEntries);
+
+  restored_cache.RestoreFromListValue(serialized_cache);
+
+  HostCache::EntryStaleness stale;
+
+  // The `expire_by_time_key` entry is stale due to both network changes and
+  // expiration time.
+  EXPECT_FALSE(restored_cache.Lookup(expire_by_time_key, now));
+  EXPECT_THAT(restored_cache.LookupStale(expire_by_time_key, now, &stale),
+              Pointee(Pair(expire_by_time_key, EntryContentsEqual(entry))));
+  EXPECT_EQ(1, stale.network_changes);
+  // Time to TimeTicks conversion is fuzzy, so just check that expected and
+  // actual expiration times are close.
+  EXPECT_GT(base::Milliseconds(100),
+            (base::Seconds(2) - stale.expired_by).magnitude());
+
+  // The `expire_by_changes_key` entry is stale only due to network changes.
+  EXPECT_FALSE(restored_cache.Lookup(expire_by_changes_key, now));
+  EXPECT_THAT(restored_cache.LookupStale(expire_by_changes_key, now, &stale),
+              Pointee(Pair(expire_by_changes_key, EntryContentsEqual(entry))));
+  EXPECT_EQ(1, stale.network_changes);
+  EXPECT_GT(base::Milliseconds(100),
+            (base::Seconds(-3) - stale.expired_by).magnitude());
+
+  EXPECT_EQ(2u, restored_cache.last_restore_size());
+}
+
+// Test that any changes between serialization and restore are preferred over
+// old restored entries.
+TEST(HostCacheTest, SerializeAndDeserializeWithChanges) {
+  const base::TimeDelta kTTL = base::Seconds(10);
+
+  HostCache cache(kMaxCacheEntries);
+
+  // Start at t=0.
+  base::TimeTicks now;
+
+  HostCache::Key to_serialize_key1 = Key("to.serialize1.test");
+  HostCache::Key to_serialize_key2 = Key("to.serialize2.test");
+  HostCache::Key other_key = Key("other.test");
+
+  IPEndPoint endpoint(IPAddress(1, 1, 1, 1), 0);
+  HostCache::Entry serialized_entry = HostCache::Entry(
+      OK, std::vector<IPEndPoint>{endpoint}, HostCache::Entry::SOURCE_UNKNOWN);
+
+  IPEndPoint replacement_endpoint(IPAddress(2, 2, 2, 2), 0);
+  HostCache::Entry replacement_entry =
+      HostCache::Entry(OK, std::vector<IPEndPoint>{replacement_endpoint},
+                       HostCache::Entry::SOURCE_UNKNOWN);
+
+  IPEndPoint other_endpoint(IPAddress(3, 3, 3, 3), 0);
+  HostCache::Entry other_entry =
+      HostCache::Entry(OK, std::vector<IPEndPoint>{other_endpoint},
+                       HostCache::Entry::SOURCE_UNKNOWN);
+
+  EXPECT_EQ(0u, cache.size());
+
+  // Add `to_serialize_key1` and `to_serialize_key2`
+  EXPECT_FALSE(cache.Lookup(to_serialize_key1, now));
+  cache.Set(to_serialize_key1, serialized_entry, now, kTTL);
+  EXPECT_THAT(
+      cache.Lookup(to_serialize_key1, now),
+      Pointee(Pair(to_serialize_key1, EntryContentsEqual(serialized_entry))));
+  EXPECT_FALSE(cache.Lookup(to_serialize_key2, now));
+  cache.Set(to_serialize_key2, serialized_entry, now, kTTL);
+  EXPECT_THAT(
+      cache.Lookup(to_serialize_key2, now),
+      Pointee(Pair(to_serialize_key2, EntryContentsEqual(serialized_entry))));
+  EXPECT_EQ(2u, cache.size());
+
+  // Serialize the cache.
+  base::Value serialized_cache(base::Value::Type::LIST);
+  cache.GetList(&serialized_cache, false /* include_staleness */,
+                HostCache::SerializationType::kRestorable);
+  HostCache restored_cache(kMaxCacheEntries);
+
+  // Add entries for `to_serialize_key1` and `other_key` to the new cache
+  // before restoring the serialized one. The `to_serialize_key1` result is
+  // different from the original.
+  EXPECT_FALSE(restored_cache.Lookup(to_serialize_key1, now));
+  restored_cache.Set(to_serialize_key1, replacement_entry, now, kTTL);
+  EXPECT_THAT(
+      restored_cache.Lookup(to_serialize_key1, now),
+      Pointee(Pair(to_serialize_key1, EntryContentsEqual(replacement_entry))));
+  EXPECT_EQ(1u, restored_cache.size());
+
+  EXPECT_FALSE(restored_cache.Lookup(other_key, now));
+  restored_cache.Set(other_key, other_entry, now, kTTL);
+  EXPECT_THAT(restored_cache.Lookup(other_key, now),
+              Pointee(Pair(other_key, EntryContentsEqual(other_entry))));
+  EXPECT_EQ(2u, restored_cache.size());
+
+  EXPECT_EQ(0u, restored_cache.last_restore_size());
+
+  restored_cache.RestoreFromListValue(serialized_cache);
+  EXPECT_EQ(1u, restored_cache.last_restore_size());
+
+  HostCache::EntryStaleness stale;
+
+  // Expect `to_serialize_key1` has the replacement entry.
+  EXPECT_THAT(
+      restored_cache.Lookup(to_serialize_key1, now),
+      Pointee(Pair(to_serialize_key1, EntryContentsEqual(replacement_entry))));
+
+  // Expect `to_serialize_key2` has the original entry.
+  EXPECT_THAT(
+      restored_cache.LookupStale(to_serialize_key2, now, &stale),
+      Pointee(Pair(to_serialize_key2, EntryContentsEqual(serialized_entry))));
+
+  // Expect no change for `other_key`.
+  EXPECT_THAT(restored_cache.Lookup(other_key, now),
+              Pointee(Pair(other_key, EntryContentsEqual(other_entry))));
+}
+
+TEST(HostCacheTest, SerializeAndDeserializeLegacyAddresses) {
   const base::TimeDelta kTTL = base::Seconds(10);
 
   HostCache cache(kMaxCacheEntries);
@@ -1235,12 +1469,12 @@
       restored_cache.LookupStale(key1, now, &stale);
   EXPECT_TRUE(result1);
   EXPECT_TRUE(result1->first.secure);
-  ASSERT_TRUE(result1->second.addresses());
+  ASSERT_TRUE(result1->second.legacy_addresses());
   EXPECT_FALSE(result1->second.text_records());
   EXPECT_FALSE(result1->second.hostnames());
-  EXPECT_EQ(1u, result1->second.addresses().value().size());
+  EXPECT_EQ(1u, result1->second.legacy_addresses().value().size());
   EXPECT_EQ(address_ipv4,
-            result1->second.addresses().value().front().address());
+            result1->second.legacy_addresses().value().front().address());
   EXPECT_EQ(1, stale.network_changes);
   // Time to TimeTicks conversion is fuzzy, so just check that expected and
   // actual expiration times are close.
@@ -1253,11 +1487,12 @@
       restored_cache.LookupStale(key2, now, &stale);
   EXPECT_TRUE(result2);
   EXPECT_FALSE(result2->first.secure);
-  ASSERT_TRUE(result2->second.addresses());
-  EXPECT_EQ(2u, result2->second.addresses().value().size());
+  ASSERT_TRUE(result2->second.legacy_addresses());
+  EXPECT_EQ(2u, result2->second.legacy_addresses().value().size());
   EXPECT_EQ(address_ipv6,
-            result2->second.addresses().value().front().address());
-  EXPECT_EQ(address_ipv4, result2->second.addresses().value().back().address());
+            result2->second.legacy_addresses().value().front().address());
+  EXPECT_EQ(address_ipv4,
+            result2->second.legacy_addresses().value().back().address());
   EXPECT_EQ(1, stale.network_changes);
   EXPECT_GT(base::Milliseconds(100),
             (base::Seconds(-3) - stale.expired_by).magnitude());
@@ -1266,19 +1501,19 @@
   const std::pair<const HostCache::Key, HostCache::Entry>* result3 =
       restored_cache.Lookup(key3, now);
   EXPECT_TRUE(result3);
-  ASSERT_TRUE(result3->second.addresses());
-  EXPECT_EQ(1u, result3->second.addresses().value().size());
+  ASSERT_TRUE(result3->second.legacy_addresses());
+  EXPECT_EQ(1u, result3->second.legacy_addresses().value().size());
   EXPECT_EQ(address_ipv4,
-            result3->second.addresses().value().front().address());
+            result3->second.legacy_addresses().value().front().address());
 
   // The "foobar4.com" entry is still present and usable.
   const std::pair<const HostCache::Key, HostCache::Entry>* result4 =
       restored_cache.Lookup(key4, now);
   EXPECT_TRUE(result4);
-  ASSERT_TRUE(result4->second.addresses());
-  EXPECT_EQ(1u, result4->second.addresses().value().size());
+  ASSERT_TRUE(result4->second.legacy_addresses());
+  EXPECT_EQ(1u, result4->second.legacy_addresses().value().size());
   EXPECT_EQ(address_ipv4,
-            result4->second.addresses().value().front().address());
+            result4->second.legacy_addresses().value().front().address());
 
   EXPECT_EQ(2u, restored_cache.last_restore_size());
 }
@@ -1288,8 +1523,8 @@
 
   HostCache::Key key("host.test", DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, NetworkIsolationKey());
-  HostCache::Entry entry =
-      HostCache::Entry(OK, AddressList(), HostCache::Entry::SOURCE_UNKNOWN);
+  HostCache::Entry entry = HostCache::Entry(OK, std::vector<IPEndPoint>(),
+                                            HostCache::Entry::SOURCE_UNKNOWN);
 
   base::TimeTicks now;
   HostCache cache(kMaxCacheEntries);
@@ -1307,7 +1542,7 @@
 
   HostCache::EntryStaleness staleness;
   EXPECT_THAT(restored_cache.LookupStale(key, now, &staleness),
-              Pointee(Pair(key, _)));
+              Pointee(Pair(key, EntryContentsEqual(entry))));
 }
 
 TEST(HostCacheTest, SerializeAndDeserializeWithNetworkIsolationKey) {
@@ -1324,10 +1559,10 @@
                       HostResolverSource::ANY, kNetworkIsolationKey);
   HostCache::Key key2(kHost, DnsQueryType::UNSPECIFIED, 0,
                       HostResolverSource::ANY, kOpaqueNetworkIsolationKey);
-  IPEndPoint endpoint(IPAddress(1, 2, 3, 4), 0);
 
-  HostCache::Entry entry = HostCache::Entry(OK, AddressList(endpoint),
-                                            HostCache::Entry::SOURCE_UNKNOWN);
+  IPEndPoint endpoint(IPAddress(1, 2, 3, 4), 0);
+  HostCache::Entry entry = HostCache::Entry(
+      OK, std::vector<IPEndPoint>{endpoint}, HostCache::Entry::SOURCE_UNKNOWN);
 
   base::TimeTicks now;
   HostCache cache(kMaxCacheEntries);
@@ -1351,14 +1586,8 @@
   EXPECT_EQ(1u, restored_cache.size());
 
   HostCache::EntryStaleness stale;
-  const std::pair<const HostCache::Key, HostCache::Entry>* result =
-      restored_cache.LookupStale(key1, now, &stale);
-  ASSERT_TRUE(result);
-  EXPECT_EQ(kNetworkIsolationKey, result->first.network_isolation_key);
-  EXPECT_THAT(result->first.host,
-              testing::VariantWith<url::SchemeHostPort>(kHost));
-  ASSERT_EQ(1u, result->second.addresses().value().size());
-  EXPECT_EQ(endpoint, result->second.addresses().value().front());
+  EXPECT_THAT(restored_cache.LookupStale(key1, now, &stale),
+              Pointee(Pair(key1, EntryContentsEqual(entry))));
   EXPECT_FALSE(restored_cache.Lookup(key2, now));
 }
 
@@ -1370,10 +1599,10 @@
 
   HostCache::Key key(kHost, DnsQueryType::UNSPECIFIED, 0,
                      HostResolverSource::ANY, kNetworkIsolationKey);
-  IPEndPoint endpoint(IPAddress(1, 2, 3, 4), 0);
 
-  HostCache::Entry entry = HostCache::Entry(OK, AddressList(endpoint),
-                                            HostCache::Entry::SOURCE_UNKNOWN);
+  IPEndPoint endpoint(IPAddress(1, 2, 3, 4), 0);
+  HostCache::Entry entry = HostCache::Entry(
+      OK, std::vector<IPEndPoint>{endpoint}, HostCache::Entry::SOURCE_UNKNOWN);
 
   base::TimeTicks now;
   HostCache cache(kMaxCacheEntries);
@@ -1426,12 +1655,8 @@
   HostCache::EntryStaleness stale;
   const std::pair<const HostCache::Key, HostCache::Entry>* result =
       restored_cache.LookupStale(key, now, &stale);
-  ASSERT_TRUE(result);
-  EXPECT_TRUE(result->first.secure);
-  EXPECT_FALSE(result->second.addresses());
-  ASSERT_TRUE(result->second.text_records());
-  EXPECT_FALSE(result->second.hostnames());
-  EXPECT_EQ(text_records, result->second.text_records().value());
+  EXPECT_THAT(result, Pointee(Pair(key, EntryContentsEqual(entry))));
+  EXPECT_THAT(result->second.text_records(), Optional(text_records));
 }
 
 TEST(HostCacheTest, SerializeAndDeserialize_Hostname) {
@@ -1460,12 +1685,65 @@
   HostCache::EntryStaleness stale;
   const std::pair<const HostCache::Key, HostCache::Entry>* result =
       restored_cache.LookupStale(key, now, &stale);
+  EXPECT_THAT(result, Pointee(Pair(key, EntryContentsEqual(entry))));
+  EXPECT_THAT(result->second.hostnames(), Optional(hostnames));
+}
+
+TEST(HostCacheTest, SerializeAndDeserializeEndpointResult) {
+  base::TimeTicks now;
+
+  base::TimeDelta ttl = base::Seconds(99);
+  HostCache::Key key(url::SchemeHostPort(url::kHttpsScheme, "example.com", 443),
+                     DnsQueryType::A, 0, HostResolverSource::DNS,
+                     NetworkIsolationKey());
+
+  std::vector<IPEndPoint> ip_endpoints = {
+      IPEndPoint(IPAddress(1, 1, 1, 1), 800),
+      IPEndPoint(IPAddress(2, 2, 2, 2), 900),
+      IPEndPoint(IPAddress(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4),
+                 100)};
+  HostCache::Entry entry(OK, ip_endpoints, HostCache::Entry::SOURCE_DNS, ttl);
+  std::set<std::string> aliases = {"ipv4_alias.test", "ipv6_alias.test",
+                                   "other_alias.test"};
+  entry.set_aliases(aliases);
+  EXPECT_TRUE(entry.GetEndpoints());
+
+  ConnectionEndpointMetadata metadata1;
+  metadata1.supported_protocol_alpns = {"h3", "h2"};
+  metadata1.ech_config_list = {'f', 'o', 'o'};
+  ConnectionEndpointMetadata metadata2;
+  metadata2.supported_protocol_alpns = {"h2", "h4"};
+  HostCache::Entry metadata_entry(
+      OK,
+      std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>{
+          {1u, metadata1}, {2u, metadata2}},
+      HostCache::Entry::SOURCE_DNS);
+
+  auto merged_entry = HostCache::Entry::MergeEntries(entry, metadata_entry);
+
+  HostCache cache(kMaxCacheEntries);
+  cache.Set(key, merged_entry, now, ttl);
+  EXPECT_EQ(1u, cache.size());
+
+  base::Value serialized_cache(base::Value::Type::LIST);
+  cache.GetList(&serialized_cache, false /* include_staleness */,
+                HostCache::SerializationType::kRestorable);
+  HostCache restored_cache(kMaxCacheEntries);
+  restored_cache.RestoreFromListValue(serialized_cache);
+
+  ASSERT_EQ(1u, restored_cache.size());
+  HostCache::EntryStaleness stale;
+  const std::pair<const HostCache::Key, HostCache::Entry>* result =
+      restored_cache.LookupStale(key, now, &stale);
+
   ASSERT_TRUE(result);
-  EXPECT_FALSE(result->first.secure);
-  EXPECT_FALSE(result->second.addresses());
-  EXPECT_FALSE(result->second.text_records());
-  ASSERT_TRUE(result->second.hostnames());
-  EXPECT_EQ(hostnames, result->second.hostnames().value());
+  EXPECT_THAT(result, Pointee(Pair(key, EntryContentsEqual(merged_entry))));
+  EXPECT_THAT(
+      result->second.GetEndpoints(),
+      Optional(ElementsAre(ExpectEndpointResult(ip_endpoints, metadata1),
+                           ExpectEndpointResult(ip_endpoints, metadata2),
+                           ExpectEndpointResult(ip_endpoints))));
+  EXPECT_THAT(result->second.aliases(), Pointee(aliases));
 }
 
 TEST(HostCacheTest, PersistenceDelegate) {
@@ -1477,21 +1755,14 @@
   HostCache::Key key1 = Key("foobar.com");
   HostCache::Key key2 = Key("foobar2.com");
 
-  IPAddress address_ipv4(1, 2, 3, 4);
-  IPAddress address_ipv6(0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-  IPEndPoint endpoint_ipv4(address_ipv4, 0);
-  IPEndPoint endpoint_ipv6(address_ipv6, 0);
-
-  HostCache::Entry entry1 = HostCache::Entry(OK, AddressList(endpoint_ipv4),
-                                             HostCache::Entry::SOURCE_UNKNOWN);
-  AddressList addresses2 = AddressList(endpoint_ipv6);
-  addresses2.push_back(endpoint_ipv4);
-  HostCache::Entry entry2 =
-      HostCache::Entry(OK, addresses2, HostCache::Entry::SOURCE_UNKNOWN);
-  HostCache::Entry entry3 = HostCache::Entry(
+  HostCache::Entry ok_entry = HostCache::Entry(
+      OK, std::vector<IPEndPoint>(), HostCache::Entry::SOURCE_UNKNOWN);
+  std::vector<IPEndPoint> other_endpoints = {
+      IPEndPoint(IPAddress(1, 1, 1, 1), 300)};
+  HostCache::Entry other_entry(OK, std::move(other_endpoints),
+                               HostCache::Entry::SOURCE_UNKNOWN);
+  HostCache::Entry error_entry = HostCache::Entry(
       ERR_NAME_NOT_RESOLVED, AddressList(), HostCache::Entry::SOURCE_UNKNOWN);
-  HostCache::Entry entry4 =
-      HostCache::Entry(OK, AddressList(), HostCache::Entry::SOURCE_UNKNOWN);
 
   // Start at t=0.
   base::TimeTicks now;
@@ -1499,13 +1770,13 @@
 
   // Add two entries at t=0.
   EXPECT_FALSE(cache.Lookup(key1, now));
-  cache.Set(key1, entry1, now, kTTL);
+  cache.Set(key1, ok_entry, now, kTTL);
   EXPECT_TRUE(cache.Lookup(key1, now));
   EXPECT_EQ(1u, cache.size());
   EXPECT_EQ(1, delegate.num_changes());
 
   EXPECT_FALSE(cache.Lookup(key2, now));
-  cache.Set(key2, entry3, now, kTTL);
+  cache.Set(key2, error_entry, now, kTTL);
   EXPECT_TRUE(cache.Lookup(key2, now));
   EXPECT_EQ(2u, cache.size());
   EXPECT_EQ(2, delegate.num_changes());
@@ -1516,14 +1787,14 @@
   // Changes that shouldn't trigger a write:
   // Add an entry for "foobar.com" with different expiration time.
   EXPECT_TRUE(cache.Lookup(key1, now));
-  cache.Set(key1, entry1, now, kTTL);
+  cache.Set(key1, ok_entry, now, kTTL);
   EXPECT_TRUE(cache.Lookup(key1, now));
   EXPECT_EQ(2u, cache.size());
   EXPECT_EQ(2, delegate.num_changes());
 
   // Add an entry for "foobar.com" with different TTL.
   EXPECT_TRUE(cache.Lookup(key1, now));
-  cache.Set(key1, entry1, now, kTTL - base::Seconds(5));
+  cache.Set(key1, ok_entry, now, kTTL - base::Seconds(5));
   EXPECT_TRUE(cache.Lookup(key1, now));
   EXPECT_EQ(2u, cache.size());
   EXPECT_EQ(2, delegate.num_changes());
@@ -1531,20 +1802,20 @@
   // Changes that should trigger a write:
   // Add an entry for "foobar.com" with different address list.
   EXPECT_TRUE(cache.Lookup(key1, now));
-  cache.Set(key1, entry2, now, kTTL);
+  cache.Set(key1, other_entry, now, kTTL);
   EXPECT_TRUE(cache.Lookup(key1, now));
   EXPECT_EQ(2u, cache.size());
   EXPECT_EQ(3, delegate.num_changes());
 
   // Add an entry for "foobar2.com" with different error.
   EXPECT_TRUE(cache.Lookup(key1, now));
-  cache.Set(key2, entry4, now, kTTL);
+  cache.Set(key2, ok_entry, now, kTTL);
   EXPECT_TRUE(cache.Lookup(key1, now));
   EXPECT_EQ(2u, cache.size());
   EXPECT_EQ(4, delegate.num_changes());
 }
 
-TEST(HostCacheTest, MergeEntries) {
+TEST(HostCacheTest, MergeLegacyAddressEntries) {
   const IPAddress kAddressFront(1, 2, 3, 4);
   const IPEndPoint kEndpointFront(kAddressFront, 0);
   std::vector<std::string> aliases_front({"alias1", "alias2", "alias3"});
@@ -1572,7 +1843,7 @@
   EXPECT_EQ(HostCache::Entry::SOURCE_DNS, result.source());
 
   // Expect the IPv6 address to precede the IPv4 address.
-  EXPECT_THAT(result.addresses(),
+  EXPECT_THAT(result.legacy_addresses(),
               Optional(Property(&AddressList::endpoints,
                                 ElementsAre(kEndpointBack, kEndpointFront))));
   EXPECT_THAT(result.text_records(), Optional(ElementsAre("text1", "text2")));
@@ -1580,8 +1851,8 @@
   EXPECT_THAT(result.hostnames(),
               Optional(ElementsAre(kHostnameFront, kHostnameBack)));
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_THAT(result.addresses().value().dns_aliases(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_THAT(result.legacy_addresses().value().dns_aliases(),
               ElementsAre("alias1", "alias2", "alias3", "alias4", "alias5"));
 }
 
@@ -1605,7 +1876,7 @@
   return out;
 }
 
-TEST(HostCacheTest, SortsAndDeduplicatesAddresses) {
+TEST(HostCacheTest, SortsAndDeduplicatesLegacyAddresses) {
   IPAddressList front_addresses = MakeIPList({"0.0.0.1", "0.0.0.1", "0.0.0.2"});
   IPAddressList back_addresses =
       MakeIPList({"0.0.0.2", "0.0.0.2", "::3", "::3"});
@@ -1628,17 +1899,17 @@
   EXPECT_EQ(HostCache::Entry::SOURCE_DNS, result.source());
 
   EXPECT_THAT(
-      result.addresses(),
+      result.legacy_addresses(),
       Optional(Property(
           &AddressList::endpoints,
           ElementsAreArray(MakeEndpoints({"::3", "0.0.0.1", "0.0.0.2"})))));
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_THAT(result.addresses().value().dns_aliases(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_THAT(result.legacy_addresses().value().dns_aliases(),
               ElementsAre("front", "back"));
 }
 
-TEST(HostCacheTest, PrefersAddressesWithIpv6) {
+TEST(HostCacheTest, PrefersLegacyAddressesWithIpv6) {
   IPAddressList front_addresses = MakeIPList({"::1", "0.0.0.2", "0.0.0.4"});
   IPAddressList back_addresses =
       MakeIPList({"0.0.0.2", "0.0.0.2", "::3", "::3", "0.0.0.4"});
@@ -1657,17 +1928,133 @@
   HostCache::Entry result =
       HostCache::Entry::MergeEntries(std::move(front), std::move(back));
 
-  EXPECT_THAT(result.addresses(),
+  EXPECT_THAT(result.legacy_addresses(),
               Optional(Property(&AddressList::endpoints,
                                 ElementsAreArray(MakeEndpoints(
                                     {"::1", "::3", "0.0.0.2", "0.0.0.4"})))));
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_THAT(result.addresses().value().dns_aliases(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_THAT(result.legacy_addresses().value().dns_aliases(),
               ElementsAre("front", "back"));
 }
 
-TEST(HostCacheTest, MergeEntries_frontEmpty) {
+TEST(HostCacheTest, MergeEndpoints) {
+  std::vector<IPEndPoint> front_endpoints = {
+      IPEndPoint(IPAddress(1, 1, 1, 1), 800),
+      IPEndPoint(IPAddress(2, 2, 2, 2), 900)};
+  HostCache::Entry front(OK, front_endpoints, HostCache::Entry::SOURCE_DNS);
+
+  std::vector<IPEndPoint> back_endpoints = {IPEndPoint(
+      IPAddress(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4), 100)};
+  HostCache::Entry back(OK, back_endpoints, HostCache::Entry::SOURCE_DNS);
+
+  std::vector<IPEndPoint> expected_endpoints = {
+      IPEndPoint(IPAddress(1, 1, 1, 1), 800),
+      IPEndPoint(IPAddress(2, 2, 2, 2), 900),
+      IPEndPoint(IPAddress(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4),
+                 100)};
+  HostCache::Entry expected(OK, expected_endpoints,
+                            HostCache::Entry::SOURCE_DNS);
+
+  HostCache::Entry result = HostCache::Entry::MergeEntries(front, back);
+  EXPECT_EQ(result, expected);
+}
+
+TEST(HostCacheTest, MergeMetadatas) {
+  ConnectionEndpointMetadata front_metadata;
+  front_metadata.supported_protocol_alpns = {"h5", "h6", "monster truck rally"};
+  front_metadata.ech_config_list = {'h', 'i'};
+  std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>
+      front_metadata_map{{4u, front_metadata}};
+  HostCache::Entry front(OK, front_metadata_map, HostCache::Entry::SOURCE_DNS);
+
+  ConnectionEndpointMetadata back_metadata;
+  back_metadata.supported_protocol_alpns = {"h5"};
+  std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>
+      back_metadata_map{{2u, back_metadata}};
+  HostCache::Entry back(OK, back_metadata_map, HostCache::Entry::SOURCE_DNS);
+
+  HostCache::Entry result = HostCache::Entry::MergeEntries(front, back);
+
+  // Expect `GetEndpoints()` to ignore metadatas if no `IPEndPoint`s.
+  EXPECT_FALSE(result.GetEndpoints());
+
+  // Expect order irrelevant for endpoint metadata merging.
+  result = HostCache::Entry::MergeEntries(back, front);
+  EXPECT_FALSE(result.GetEndpoints());
+}
+
+TEST(HostCacheTest, MergeMetadatasWithIpEndpoints) {
+  ConnectionEndpointMetadata front_metadata;
+  front_metadata.supported_protocol_alpns = {"h5", "h6", "monster truck rally"};
+  front_metadata.ech_config_list = {'h', 'i'};
+  std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>
+      front_metadata_map{{4u, front_metadata}};
+  HostCache::Entry front(OK, front_metadata_map, HostCache::Entry::SOURCE_DNS);
+
+  ConnectionEndpointMetadata back_metadata;
+  back_metadata.supported_protocol_alpns = {"h5"};
+  std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>
+      back_metadata_map{{2u, back_metadata}};
+  HostCache::Entry back(OK, back_metadata_map, HostCache::Entry::SOURCE_DNS);
+
+  HostCache::Entry merged_metadatas =
+      HostCache::Entry::MergeEntries(front, back);
+  HostCache::Entry reversed_merged_metadatas =
+      HostCache::Entry::MergeEntries(back, front);
+
+  // Expect `GetEndpoints()` to always ignore metadatas with no `IPEndPoint`s.
+  EXPECT_FALSE(merged_metadatas.GetEndpoints());
+  EXPECT_FALSE(reversed_merged_metadatas.GetEndpoints());
+
+  // Merge in an `IPEndPoint`.
+  IPEndPoint ip_endpoint(IPAddress(1, 1, 1, 1), 0);
+  HostCache::Entry with_ip_endpoint(OK, std::vector<IPEndPoint>{ip_endpoint},
+                                    HostCache::Entry::SOURCE_DNS);
+
+  HostCache::Entry result =
+      HostCache::Entry::MergeEntries(merged_metadatas, with_ip_endpoint);
+
+  // Expect `back_metadata` before `front_metadata` because it has lower
+  // priority number.
+  EXPECT_THAT(
+      result.GetEndpoints(),
+      Optional(ElementsAre(
+          ExpectEndpointResult(ElementsAre(ip_endpoint), back_metadata),
+          ExpectEndpointResult(ElementsAre(ip_endpoint), front_metadata),
+          ExpectEndpointResult(ElementsAre(ip_endpoint)))));
+
+  // Expect merge order irrelevant.
+  EXPECT_EQ(result, HostCache::Entry::MergeEntries(reversed_merged_metadatas,
+                                                   with_ip_endpoint));
+  EXPECT_EQ(result,
+            HostCache::Entry::MergeEntries(with_ip_endpoint, merged_metadatas));
+  EXPECT_EQ(result, HostCache::Entry::MergeEntries(with_ip_endpoint,
+                                                   reversed_merged_metadatas));
+}
+
+TEST(HostCacheTest, MergeAliases) {
+  HostCache::Entry front(OK, std::vector<IPEndPoint>(),
+                         HostCache::Entry::SOURCE_DNS);
+  front.set_aliases({"foo1.test", "foo2.test", "foo3.test"});
+
+  HostCache::Entry back(OK, std::vector<IPEndPoint>(),
+                        HostCache::Entry::SOURCE_DNS);
+  back.set_aliases({"foo2.test", "foo4.test"});
+
+  HostCache::Entry expected(OK, std::vector<IPEndPoint>(),
+                            HostCache::Entry::SOURCE_DNS);
+  expected.set_aliases({"foo1.test", "foo2.test", "foo3.test", "foo4.test"});
+
+  HostCache::Entry result = HostCache::Entry::MergeEntries(front, back);
+  EXPECT_EQ(result, expected);
+
+  // Expect order irrelevant for alias merging.
+  result = HostCache::Entry::MergeEntries(back, front);
+  EXPECT_EQ(result, expected);
+}
+
+TEST(HostCacheTest, MergeLegacyAddressEntries_frontEmpty) {
   HostCache::Entry front(ERR_NAME_NOT_RESOLVED, HostCache::Entry::SOURCE_DNS);
 
   const IPAddress kAddressBack(0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -1686,20 +2073,20 @@
   EXPECT_EQ(OK, result.error());
   EXPECT_EQ(HostCache::Entry::SOURCE_DNS, result.source());
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_THAT(result.addresses().value().endpoints(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_THAT(result.legacy_addresses().value().endpoints(),
               ElementsAre(kEndpointBack));
   EXPECT_THAT(result.text_records(), Optional(ElementsAre("text2")));
   EXPECT_THAT(result.hostnames(), Optional(ElementsAre(kHostnameBack)));
 
   EXPECT_EQ(base::Hours(4), result.ttl());
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_THAT(result.addresses().value().dns_aliases(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_THAT(result.legacy_addresses().value().dns_aliases(),
               ElementsAre("alias1", "alias2", "alias3"));
 }
 
-TEST(HostCacheTest, MergeEntries_backEmpty) {
+TEST(HostCacheTest, MergeLegacyAddressEntries_backEmpty) {
   const IPAddress kAddressFront(1, 2, 3, 4);
   const IPEndPoint kEndpointFront(kAddressFront, 0);
   std::vector<std::string> aliases_front({"alias1", "alias2", "alias3"});
@@ -1718,20 +2105,20 @@
   EXPECT_EQ(OK, result.error());
   EXPECT_EQ(HostCache::Entry::SOURCE_DNS, result.source());
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_THAT(result.addresses().value().endpoints(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_THAT(result.legacy_addresses().value().endpoints(),
               ElementsAre(kEndpointFront));
   EXPECT_THAT(result.text_records(), Optional(ElementsAre("text1")));
   EXPECT_THAT(result.hostnames(), Optional(ElementsAre(kHostnameFront)));
 
   EXPECT_EQ(base::Minutes(5), result.ttl());
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_THAT(result.addresses().value().dns_aliases(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_THAT(result.legacy_addresses().value().dns_aliases(),
               ElementsAre("alias1", "alias2", "alias3"));
 }
 
-TEST(HostCacheTest, MergeEntries_bothEmpty) {
+TEST(HostCacheTest, MergeLegacyAddressEntries_bothEmpty) {
   HostCache::Entry front(ERR_NAME_NOT_RESOLVED, HostCache::Entry::SOURCE_DNS);
   HostCache::Entry back(ERR_NAME_NOT_RESOLVED, HostCache::Entry::SOURCE_DNS);
 
@@ -1741,18 +2128,19 @@
   EXPECT_EQ(ERR_NAME_NOT_RESOLVED, result.error());
   EXPECT_EQ(HostCache::Entry::SOURCE_DNS, result.source());
 
-  EXPECT_FALSE(result.addresses());
+  EXPECT_FALSE(result.legacy_addresses());
   EXPECT_FALSE(result.text_records());
   EXPECT_FALSE(result.hostnames());
   EXPECT_FALSE(result.has_ttl());
 }
 
-TEST(HostCacheTest, MergeEntries_frontWithAliasesNoAddressesBackWithBoth) {
+TEST(HostCacheTest,
+     MergeLegacyAddressEntries_frontWithAliasesNoAddressesBackWithBoth) {
   HostCache::Entry front(ERR_NAME_NOT_RESOLVED, HostCache::Entry::SOURCE_DNS);
   AddressList front_addresses;
   std::vector<std::string> aliases_front({"alias0", "alias1", "alias2"});
   front_addresses.SetDnsAliases(std::move(aliases_front));
-  front.set_addresses(front_addresses);
+  front.set_legacy_addresses(front_addresses);
 
   const IPAddress kAddressBack(0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                0);
@@ -1767,24 +2155,25 @@
   EXPECT_EQ(OK, result.error());
   EXPECT_EQ(HostCache::Entry::SOURCE_DNS, result.source());
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_THAT(result.addresses().value().endpoints(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_THAT(result.legacy_addresses().value().endpoints(),
               ElementsAre(kEndpointBack));
 
   EXPECT_EQ(base::Hours(4), result.ttl());
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_THAT(result.addresses().value().dns_aliases(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_THAT(result.legacy_addresses().value().dns_aliases(),
               ElementsAre("alias0", "alias1", "alias2", "alias3"));
 }
 
-TEST(HostCacheTest, MergeEntries_backWithAliasesNoAddressesFrontWithBoth) {
+TEST(HostCacheTest,
+     MergeLegacyAddressEntries_backWithAliasesNoAddressesFrontWithBoth) {
   HostCache::Entry back(ERR_NAME_NOT_RESOLVED, HostCache::Entry::SOURCE_DNS);
   AddressList back_addresses;
   std::vector<std::string> aliases_back({"alias1", "alias2", "alias3"});
 
   back_addresses.SetDnsAliases(std::move(aliases_back));
-  back.set_addresses(back_addresses);
+  back.set_legacy_addresses(back_addresses);
 
   const IPAddress kAddressFront(0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                 0);
@@ -1800,18 +2189,19 @@
   EXPECT_EQ(OK, result.error());
   EXPECT_EQ(HostCache::Entry::SOURCE_DNS, result.source());
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_THAT(result.addresses().value().endpoints(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_THAT(result.legacy_addresses().value().endpoints(),
               ElementsAre(kEndpointFront));
 
   EXPECT_EQ(base::Hours(4), result.ttl());
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_THAT(result.addresses().value().dns_aliases(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_THAT(result.legacy_addresses().value().dns_aliases(),
               ElementsAre("alias0", "alias1", "alias2", "alias3"));
 }
 
-TEST(HostCacheTest, MergeEntries_frontWithAddressesNoAliasesBackWithBoth) {
+TEST(HostCacheTest,
+     MergeLegacyAddressEntries_frontWithAddressesNoAliasesBackWithBoth) {
   const IPAddress kAddressFront(1, 2, 3, 4);
   const IPEndPoint kEndpointFront(kAddressFront, 0);
   HostCache::Entry front(OK, AddressList(kEndpointFront),
@@ -1830,18 +2220,19 @@
   EXPECT_EQ(OK, result.error());
   EXPECT_EQ(HostCache::Entry::SOURCE_DNS, result.source());
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_THAT(result.addresses().value().endpoints(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_THAT(result.legacy_addresses().value().endpoints(),
               ElementsAre(kEndpointBack, kEndpointFront));
 
   EXPECT_EQ(base::Hours(4), result.ttl());
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_THAT(result.addresses().value().dns_aliases(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_THAT(result.legacy_addresses().value().dns_aliases(),
               ElementsAre("alias1", "alias2", "alias3"));
 }
 
-TEST(HostCacheTest, MergeEntries_backWithAddressesNoAliasesFrontWithBoth) {
+TEST(HostCacheTest,
+     MergeLegacyAddressEntries_backWithAddressesNoAliasesFrontWithBoth) {
   const IPAddress kAddressFront(1, 2, 3, 4);
   const IPEndPoint kEndpointFront(kAddressFront, 0);
   std::vector<std::string> aliases_front({"alias1", "alias2", "alias3"});
@@ -1861,14 +2252,14 @@
   EXPECT_EQ(OK, result.error());
   EXPECT_EQ(HostCache::Entry::SOURCE_DNS, result.source());
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_THAT(result.addresses().value().endpoints(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_THAT(result.legacy_addresses().value().endpoints(),
               ElementsAre(kEndpointBack, kEndpointFront));
 
   EXPECT_EQ(base::Hours(4), result.ttl());
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_THAT(result.addresses().value().dns_aliases(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_THAT(result.legacy_addresses().value().dns_aliases(),
               ElementsAre("alias1", "alias2", "alias3"));
 }
 
@@ -1884,7 +2275,7 @@
   EXPECT_EQ(base::Seconds(42), result.ttl());
 }
 
-TEST(HostCacheTest, MergeEntries_FrontCannonnamePreserved) {
+TEST(HostCacheTest, MergeLegacyAddressEntries_FrontCannonnamePreserved) {
   AddressList addresses_front;
   const std::string kCanonicalNameFront = "name1";
   std::vector<std::string> front_aliases({kCanonicalNameFront});
@@ -1900,14 +2291,15 @@
   HostCache::Entry result =
       HostCache::Entry::MergeEntries(std::move(front), std::move(back));
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_EQ(kCanonicalNameFront, result.addresses().value().GetCanonicalName());
-  EXPECT_THAT(result.addresses().value().dns_aliases(),
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_EQ(kCanonicalNameFront,
+            result.legacy_addresses().value().GetCanonicalName());
+  EXPECT_THAT(result.legacy_addresses().value().dns_aliases(),
               ElementsAre("name1", "name2"));
 }
 
 // Test that the back canonname can be used if there is no front cannonname.
-TEST(HostCacheTest, MergeEntries_BackCannonnameUsable) {
+TEST(HostCacheTest, MergeLegacyAddressEntries_BackCannonnameUsable) {
   AddressList addresses_front;
   const std::string kCanonicalNameFront = "";
   std::vector<std::string> front_aliases({kCanonicalNameFront});
@@ -1923,9 +2315,11 @@
   HostCache::Entry result =
       HostCache::Entry::MergeEntries(std::move(front), std::move(back));
 
-  ASSERT_TRUE(result.addresses());
-  EXPECT_EQ(kCanonicalNameBack, result.addresses().value().GetCanonicalName());
-  EXPECT_THAT(result.addresses().value().dns_aliases(), ElementsAre("name2"));
+  ASSERT_TRUE(result.legacy_addresses());
+  EXPECT_EQ(kCanonicalNameBack,
+            result.legacy_addresses().value().GetCanonicalName());
+  EXPECT_THAT(result.legacy_addresses().value().dns_aliases(),
+              ElementsAre("name2"));
 }
 
 }  // namespace net
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index f8a3f5e..cba6bfdc 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -727,20 +727,21 @@
   const absl::optional<AddressList>& GetAddressResults() const override {
     DCHECK(complete_);
     static const base::NoDestructor<absl::optional<AddressList>> nullopt_result;
-    return results_ ? results_.value().addresses() : *nullopt_result;
+    return results_ ? results_.value().legacy_addresses() : *nullopt_result;
   }
 
   absl::optional<std::vector<HostResolverEndpointResult>> GetEndpointResults()
       const override {
     DCHECK(complete_);
 
-    if (!results_.has_value() || !results_.value().addresses().has_value())
+    if (!results_.has_value() ||
+        !results_.value().legacy_addresses().has_value())
       return absl::nullopt;
 
     // TODO(crbug.com/1264933): Use HostResolverEndpointResult internally
     // instead of converting on output.
     return HostResolver::AddressListToEndpointResults(
-        results_.value().addresses().value());
+        results_.value().legacy_addresses().value());
   }
 
   const absl::optional<std::vector<std::string>>& GetTextResults()
@@ -896,20 +897,20 @@
   void FixupDnsAliasResults() {
     // If there are no address results, if there are no aliases, or if there
     // are already fixed up alias results, there is nothing to do.
-    if (!results_ || !results_.value().addresses() ||
-        results_.value().addresses()->dns_aliases().empty() ||
+    if (!results_ || !results_.value().legacy_addresses() ||
+        results_.value().legacy_addresses()->dns_aliases().empty() ||
         fixed_up_dns_alias_results_) {
       return;
     }
 
     fixed_up_dns_alias_results_.emplace(
-        results_.value().addresses()->dns_aliases().begin(),
-        results_.value().addresses()->dns_aliases().end());
+        results_.value().legacy_addresses()->dns_aliases().begin(),
+        results_.value().legacy_addresses()->dns_aliases().end());
 
     // Skip fixups for `include_canonical_name` requests. Just use the
     // canonical name exactly as it was received from the system resolver.
     if (parameters().include_canonical_name) {
-      DCHECK_LE(results_.value().addresses()->dns_aliases().size(), 1u);
+      DCHECK_LE(results_.value().legacy_addresses()->dns_aliases().size(), 1u);
     } else {
       fixed_up_dns_alias_results_ = dns_alias_utility::FixUpDnsAliases(
           fixed_up_dns_alias_results_.value());
@@ -1763,16 +1764,18 @@
     // If there are multiple addresses, and at least one is IPv6, need to
     // sort them.
     bool at_least_one_ipv6_address =
-        results.addresses() && !results.addresses().value().empty() &&
-        (results.addresses().value()[0].GetFamily() == ADDRESS_FAMILY_IPV6 ||
-         std::any_of(results.addresses().value().begin(),
-                     results.addresses().value().end(), [](auto& e) {
+        results.legacy_addresses() &&
+        !results.legacy_addresses().value().empty() &&
+        (results.legacy_addresses().value()[0].GetFamily() ==
+             ADDRESS_FAMILY_IPV6 ||
+         std::any_of(results.legacy_addresses().value().begin(),
+                     results.legacy_addresses().value().end(), [](auto& e) {
                        return e.GetFamily() == ADDRESS_FAMILY_IPV6;
                      }));
 
     if (at_least_one_ipv6_address) {
       // Sort addresses if needed.  Sort could complete synchronously.
-      AddressList addresses = results.addresses().value();
+      AddressList addresses = results.legacy_addresses().value();
       client_->GetAddressSorter()->Sort(
           addresses,
           base::BindOnce(&DnsTask::OnSortComplete, AsWeakPtr(),
@@ -1788,7 +1791,7 @@
                       bool secure,
                       bool success,
                       const AddressList& addr_list) {
-    results.set_addresses(addr_list);
+    results.set_legacy_addresses(addr_list);
 
     if (!success) {
       OnFailure(ERR_DNS_SORT_ERROR, results.GetOptionalTtl());
@@ -1994,7 +1997,7 @@
 //-----------------------------------------------------------------------------
 
 struct HostResolverManager::JobKey {
-  JobKey(ResolveContext* resolve_context)
+  explicit JobKey(ResolveContext* resolve_context)
       : resolve_context(resolve_context->AsSafeRef()) {}
 
   bool operator<(const JobKey& other) const {
@@ -2593,7 +2596,8 @@
     // find address results, but DnsTask may claim success if any transaction,
     // e.g. a supplemental HTTPS transaction, finds results.
     if (key_.query_type == DnsQueryType::UNSPECIFIED && results.error() == OK &&
-        (!results.addresses() || results.addresses().value().empty())) {
+        (!results.legacy_addresses() ||
+         results.legacy_addresses().value().empty())) {
       results.set_error(ERR_NAME_NOT_RESOLVED);
     }
 
@@ -2616,8 +2620,8 @@
     base::TimeDelta bounded_ttl =
         std::max(results.ttl(), base::Seconds(kMinimumTTLSeconds));
 
-    if (results.addresses() &&
-        ContainsIcannNameCollisionIp(results.addresses().value())) {
+    if (results.legacy_addresses() &&
+        ContainsIcannNameCollisionIp(results.legacy_addresses().value())) {
       CompleteRequestsWithError(ERR_ICANN_NAME_COLLISION);
       return;
     }
@@ -2689,8 +2693,8 @@
     // TODO(crbug.com/846423): Consider adding MDNS-specific logging.
 
     HostCache::Entry results = mdns_task_->GetResults();
-    if (results.addresses() &&
-        ContainsIcannNameCollisionIp(results.addresses().value())) {
+    if (results.legacy_addresses() &&
+        ContainsIcannNameCollisionIp(results.legacy_addresses().value())) {
       CompleteRequestsWithError(ERR_ICANN_NAME_COLLISION);
     } else {
       // MDNS uses a separate cache, so skip saving result to cache.
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index d06d312..5f2aa1c 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -13943,13 +13943,17 @@
   base::test::ScopedFeatureList features;
 };
 
-std::vector<IPAddress> IPAddresses(const AddressList& addresses) {
+std::vector<IPAddress> IPAddresses(const std::vector<IPEndPoint>& endpoints) {
   std::vector<IPAddress> ip_addresses;
-  base::ranges::transform(addresses, std::back_inserter(ip_addresses),
+  base::ranges::transform(endpoints, std::back_inserter(ip_addresses),
                           &IPEndPoint::address);
   return ip_addresses;
 }
 
+std::vector<IPAddress> IPAddresses(const AddressList& addresses) {
+  return IPAddresses(addresses.endpoints());
+}
+
 MATCHER_P(AddressesMatch, expected, "Matches addresses between AddressLists") {
   return testing::Matches(testing::UnorderedElementsAreArray(
       IPAddresses(expected)))(IPAddresses(arg));
@@ -13967,6 +13971,9 @@
   EXPECT_THAT(bootstrap_response.result_error(), IsOk());
   EXPECT_THAT(bootstrap_response.request()->GetAddressResults(),
               Optional(AddressesMatch(kRemoteAddrs)));
+  EXPECT_THAT(bootstrap_response.request()->GetEndpointResults(),
+              testing::Optional(testing::ElementsAre(
+                  ExpectEndpointResult(AddressesMatch(kRemoteAddrs)))));
 }
 
 TEST_F(HostResolverManagerBootstrapTest, InsecureCacheEntry) {
@@ -13982,6 +13989,9 @@
   EXPECT_THAT(bootstrap_response.result_error(), IsOk());
   EXPECT_THAT(bootstrap_response.request()->GetAddressResults(),
               Optional(AddressesMatch(kCacheAddrs)));
+  EXPECT_THAT(bootstrap_response.request()->GetEndpointResults(),
+              testing::Optional(testing::ElementsAre(
+                  ExpectEndpointResult(AddressesMatch(kCacheAddrs)))));
 }
 
 TEST_F(HostResolverManagerBootstrapTest, SecureCacheEntry) {
@@ -13997,6 +14007,9 @@
   EXPECT_THAT(bootstrap_response.result_error(), IsOk());
   EXPECT_THAT(bootstrap_response.request()->GetAddressResults(),
               Optional(AddressesMatch(kCacheAddrs)));
+  EXPECT_THAT(bootstrap_response.request()->GetEndpointResults(),
+              testing::Optional(testing::ElementsAre(
+                  ExpectEndpointResult(AddressesMatch(kCacheAddrs)))));
 }
 
 TEST_F(HostResolverManagerBootstrapTest, OnlyBootstrap) {
@@ -14012,6 +14025,9 @@
   EXPECT_THAT(bootstrap_response.result_error(), IsOk());
   EXPECT_THAT(bootstrap_response.request()->GetAddressResults(),
               Optional(AddressesMatch(kBootstrapAddrs)));
+  EXPECT_THAT(bootstrap_response.request()->GetEndpointResults(),
+              testing::Optional(testing::ElementsAre(
+                  ExpectEndpointResult(AddressesMatch(kBootstrapAddrs)))));
 
   // Run the followup query.
   RunUntilIdle();
@@ -14020,7 +14036,7 @@
   const auto* secure_result = resolve_context_->host_cache()->Lookup(
       MakeCacheKey(/*secure=*/true), GetMockTickClock()->NowTicks());
   ASSERT_THAT(secure_result, testing::NotNull());
-  EXPECT_THAT(secure_result->second.addresses(),
+  EXPECT_THAT(secure_result->second.legacy_addresses(),
               Optional(AddressesMatch(kRemoteAddrs)));
 }
 
@@ -14040,6 +14056,9 @@
   EXPECT_THAT(bootstrap_response.result_error(), IsOk());
   EXPECT_THAT(bootstrap_response.request()->GetAddressResults(),
               Optional(AddressesMatch(kBootstrapAddrs)));
+  EXPECT_THAT(bootstrap_response.request()->GetEndpointResults(),
+              testing::Optional(testing::ElementsAre(
+                  ExpectEndpointResult(AddressesMatch(kBootstrapAddrs)))));
 
   // Run the followup query.
   RunUntilIdle();
@@ -14048,7 +14067,7 @@
   const auto* secure_result = resolve_context_->host_cache()->Lookup(
       MakeCacheKey(/*secure=*/true), GetMockTickClock()->NowTicks());
   ASSERT_THAT(secure_result, testing::NotNull());
-  EXPECT_THAT(secure_result->second.addresses(),
+  EXPECT_THAT(secure_result->second.legacy_addresses(),
               Optional(AddressesMatch(kRemoteAddrs)));
 }
 
@@ -14068,6 +14087,9 @@
   EXPECT_THAT(bootstrap_response.result_error(), IsOk());
   EXPECT_THAT(bootstrap_response.request()->GetAddressResults(),
               Optional(AddressesMatch(kCacheAddrs)));
+  EXPECT_THAT(bootstrap_response.request()->GetEndpointResults(),
+              testing::Optional(testing::ElementsAre(
+                  ExpectEndpointResult(AddressesMatch(kCacheAddrs)))));
 }
 
 TEST_F(HostResolverManagerBootstrapTest, BlankSlateFailure) {
@@ -14099,6 +14121,9 @@
   EXPECT_THAT(bootstrap_response.result_error(), IsOk());
   EXPECT_THAT(bootstrap_response.request()->GetAddressResults(),
               Optional(AddressesMatch(kBootstrapAddrs)));
+  EXPECT_THAT(bootstrap_response.request()->GetEndpointResults(),
+              testing::Optional(testing::ElementsAre(
+                  ExpectEndpointResult(AddressesMatch(kBootstrapAddrs)))));
 
   // Run the followup query.
   RunUntilIdle();
@@ -14149,6 +14174,9 @@
   EXPECT_THAT(bootstrap_response2.result_error(), IsOk());
   EXPECT_THAT(bootstrap_response2.request()->GetAddressResults(),
               Optional(AddressesMatch(kRemoteAddrs)));
+  EXPECT_THAT(bootstrap_response2.request()->GetEndpointResults(),
+              testing::Optional(testing::ElementsAre(
+                  ExpectEndpointResult(AddressesMatch(kRemoteAddrs)))));
 }
 
 TEST_F(HostResolverManagerBootstrapTest, BootstrapFollowupFailureTwice) {
@@ -14171,6 +14199,9 @@
   EXPECT_THAT(bootstrap_response2.result_error(), IsOk());
   EXPECT_THAT(bootstrap_response2.request()->GetAddressResults(),
               Optional(AddressesMatch(kBootstrapAddrs)));
+  EXPECT_THAT(bootstrap_response2.request()->GetEndpointResults(),
+              testing::Optional(testing::ElementsAre(
+                  ExpectEndpointResult(AddressesMatch(kBootstrapAddrs)))));
 
   // Run the followup query again.
   RunUntilIdle();
@@ -14194,6 +14225,9 @@
   EXPECT_THAT(bootstrap_response1.result_error(), IsOk());
   EXPECT_THAT(bootstrap_response1.request()->GetAddressResults(),
               Optional(AddressesMatch(kBootstrapAddrs)));
+  EXPECT_THAT(bootstrap_response1.request()->GetEndpointResults(),
+              testing::Optional(testing::ElementsAre(
+                  ExpectEndpointResult(AddressesMatch(kBootstrapAddrs)))));
 
   ResolveHostResponseHelper bootstrap_response2(resolver_->CreateRequest(
       kEndpoint, kIsolationKey, NetLogWithSource(), bootstrap_params(),
@@ -14203,6 +14237,9 @@
   EXPECT_THAT(bootstrap_response2.result_error(), IsOk());
   EXPECT_THAT(bootstrap_response2.request()->GetAddressResults(),
               Optional(AddressesMatch(kBootstrapAddrs)));
+  EXPECT_THAT(bootstrap_response2.request()->GetEndpointResults(),
+              testing::Optional(testing::ElementsAre(
+                  ExpectEndpointResult(AddressesMatch(kBootstrapAddrs)))));
 
   // Run the followup query.
   RunUntilIdle();
@@ -14211,7 +14248,7 @@
   const auto* secure_result = resolve_context_->host_cache()->Lookup(
       MakeCacheKey(/*secure=*/true), GetMockTickClock()->NowTicks());
   ASSERT_THAT(secure_result, testing::NotNull());
-  EXPECT_THAT(secure_result->second.addresses(),
+  EXPECT_THAT(secure_result->second.legacy_addresses(),
               Optional(AddressesMatch(kRemoteAddrs)));
 }
 
diff --git a/net/dns/host_resolver_mdns_listener_impl.cc b/net/dns/host_resolver_mdns_listener_impl.cc
index 9c4fa1b..fd9e34e 100644
--- a/net/dns/host_resolver_mdns_listener_impl.cc
+++ b/net/dns/host_resolver_mdns_listener_impl.cc
@@ -80,10 +80,11 @@
       break;
     case DnsQueryType::A:
     case DnsQueryType::AAAA:
-      DCHECK(parsed_entry.addresses());
-      DCHECK_EQ(1u, parsed_entry.addresses().value().size());
-      delegate_->OnAddressResult(ConvertUpdateType(update), query_type_,
-                                 parsed_entry.addresses().value().front());
+      DCHECK(parsed_entry.legacy_addresses());
+      DCHECK_EQ(1u, parsed_entry.legacy_addresses().value().size());
+      delegate_->OnAddressResult(
+          ConvertUpdateType(update), query_type_,
+          parsed_entry.legacy_addresses().value().front());
       break;
     case DnsQueryType::TXT:
       DCHECK(parsed_entry.text_records());
diff --git a/net/dns/host_resolver_results.cc b/net/dns/host_resolver_results.cc
index 91be75a..324d4d8 100644
--- a/net/dns/host_resolver_results.cc
+++ b/net/dns/host_resolver_results.cc
@@ -4,6 +4,18 @@
 
 #include "net/dns/host_resolver_results.h"
 
+#include <stdint.h>
+
+#include <string>
+#include <utility>
+
+#include "base/numerics/safe_conversions.h"
+#include "base/values.h"
+#include "net/base/connection_endpoint_metadata.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
 namespace net {
 
 HostResolverEndpointResult::HostResolverEndpointResult() = default;
diff --git a/net/dns/host_resolver_results.h b/net/dns/host_resolver_results.h
index a03442f..972afcf 100644
--- a/net/dns/host_resolver_results.h
+++ b/net/dns/host_resolver_results.h
@@ -6,6 +6,7 @@
 #define NET_DNS_HOST_RESOLVER_RESULTS_H_
 
 #include <string>
+#include <tuple>
 #include <vector>
 
 #include "net/base/connection_endpoint_metadata.h"
@@ -26,6 +27,14 @@
   HostResolverEndpointResult(HostResolverEndpointResult&&);
   HostResolverEndpointResult& operator=(HostResolverEndpointResult&&) = default;
 
+  bool operator==(const HostResolverEndpointResult& other) const {
+    return std::tie(ip_endpoints, metadata) ==
+           std::tie(other.ip_endpoints, other.metadata);
+  }
+  bool operator!=(const HostResolverEndpointResult& other) const {
+    return !(*this == other);
+  }
+
   // IP endpoints at which to connect to the service.
   std::vector<net::IPEndPoint> ip_endpoints;
 
diff --git a/net/dns/https_record_rdata.cc b/net/dns/https_record_rdata.cc
index 109259d..e16df6e4 100644
--- a/net/dns/https_record_rdata.cc
+++ b/net/dns/https_record_rdata.cc
@@ -252,7 +252,7 @@
 constexpr uint16_t ServiceFormHttpsRecordRdata::kSupportedKeys[];
 
 ServiceFormHttpsRecordRdata::ServiceFormHttpsRecordRdata(
-    uint16_t priority,
+    HttpsRecordPriority priority,
     std::string service_name,
     std::set<uint16_t> mandatory_keys,
     std::vector<std::string> alpn_ids,
@@ -330,7 +330,7 @@
 
   if (reader.remaining() == 0) {
     return std::make_unique<ServiceFormHttpsRecordRdata>(
-        priority, std::move(service_name).value(),
+        HttpsRecordPriority{priority}, std::move(service_name).value(),
         std::set<uint16_t>() /* mandatory_keys */,
         std::vector<std::string>() /* alpn_ids */, true /* default_alpn */,
         absl::nullopt /* port */, std::vector<IPAddress>() /* ipv4_hint */,
@@ -446,9 +446,10 @@
   }
 
   return std::make_unique<ServiceFormHttpsRecordRdata>(
-      priority, std::move(service_name).value(), std::move(mandatory_keys),
-      std::move(alpn_ids), default_alpn, port, std::move(ipv4_hint),
-      std::move(ech_config), std::move(ipv6_hint), std::move(unparsed_params));
+      HttpsRecordPriority{priority}, std::move(service_name).value(),
+      std::move(mandatory_keys), std::move(alpn_ids), default_alpn, port,
+      std::move(ipv4_hint), std::move(ech_config), std::move(ipv6_hint),
+      std::move(unparsed_params));
 }
 
 bool ServiceFormHttpsRecordRdata::IsCompatible() const {
diff --git a/net/dns/https_record_rdata.h b/net/dns/https_record_rdata.h
index 5dcea961..dbef3dd9 100644
--- a/net/dns/https_record_rdata.h
+++ b/net/dns/https_record_rdata.h
@@ -21,6 +21,8 @@
 
 namespace net {
 
+using HttpsRecordPriority = uint16_t;
+
 class AliasFormHttpsRecordRdata;
 class ServiceFormHttpsRecordRdata;
 
@@ -110,7 +112,7 @@
   bool IsEqual(const HttpsRecordRdata* other) const override;
   bool IsAlias() const override;
 
-  uint16_t priority() const { return priority_; }
+  HttpsRecordPriority priority() const { return priority_; }
   base::StringPiece service_name() const { return service_name_; }
   const std::set<uint16_t>& mandatory_keys() const { return mandatory_keys_; }
   const std::vector<std::string>& alpn_ids() const { return alpn_ids_; }
@@ -132,7 +134,7 @@
  private:
   static bool IsSupportedKey(uint16_t key);
 
-  const uint16_t priority_;
+  const HttpsRecordPriority priority_;
   const std::string service_name_;
 
   // Supported service parameters.
diff --git a/net/dns/mock_host_resolver.cc b/net/dns/mock_host_resolver.cc
index 721e6ed..2f9698b 100644
--- a/net/dns/mock_host_resolver.cc
+++ b/net/dns/mock_host_resolver.cc
@@ -996,7 +996,7 @@
       rv = cache_result->second.error();
       if (rv == OK) {
         *addresses = AddressList::CopyWithPort(
-            cache_result->second.addresses().value(), GetPort(endpoint));
+            cache_result->second.legacy_addresses().value(), GetPort(endpoint));
         *out_stale_info = std::move(stale_info);
       }
 
diff --git a/net/extras/sqlite/sqlite_persistent_cookie_store_unittest.cc b/net/extras/sqlite/sqlite_persistent_cookie_store_unittest.cc
index cc0780dd..cbce4650 100644
--- a/net/extras/sqlite/sqlite_persistent_cookie_store_unittest.cc
+++ b/net/extras/sqlite/sqlite_persistent_cookie_store_unittest.cc
@@ -1385,7 +1385,8 @@
   // Create a cookie on a scheme that doesn't handle cookies by default,
   // and save it.
   std::unique_ptr<CookieMonster> cookie_monster =
-      std::make_unique<CookieMonster>(store_.get(), nullptr);
+      std::make_unique<CookieMonster>(store_.get(), /*net_log=*/nullptr,
+                                      /*first_party_sets_enabled=*/false);
   ResultSavingCookieCallback<bool> cookie_scheme_callback1;
   cookie_monster->SetCookieableSchemes({"ftp", "http"},
                                        cookie_scheme_callback1.MakeCallback());
@@ -1429,7 +1430,8 @@
   // instances, so they should complete before the new PersistentCookieStore
   // starts looking at the state on disk.
   Create(false, false, true /* want current thread to invoke cookie monster */);
-  cookie_monster = std::make_unique<CookieMonster>(store_.get(), nullptr);
+  cookie_monster = std::make_unique<CookieMonster>(
+      store_.get(), /*net_log=*/nullptr, /*first_party_sets_enabled=*/false);
   ResultSavingCookieCallback<bool> cookie_scheme_callback2;
   cookie_monster->SetCookieableSchemes({"ftp", "http"},
                                        cookie_scheme_callback2.MakeCallback());
@@ -1459,7 +1461,8 @@
       base::CreateDirectory(temp_dir_.GetPath().Append(kCookieFilename)));
   Create(false, false, true /* want current thread to invoke cookie monster */);
   std::unique_ptr<CookieMonster> cookie_monster =
-      std::make_unique<CookieMonster>(store_.get(), nullptr);
+      std::make_unique<CookieMonster>(store_.get(), /*net_log=*/nullptr,
+                                      /*first_party_sets_enabled=*/false);
 
   ResultSavingCookieCallback<CookieAccessResult> set_cookie_callback;
   GURL url("http://www.example.com/");
diff --git a/net/server/http_server_unittest.cc b/net/server/http_server_unittest.cc
index 4c59764..b0cd2cb 100644
--- a/net/server/http_server_unittest.cc
+++ b/net/server/http_server_unittest.cc
@@ -29,6 +29,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/test/repeating_test_future.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "net/base/address_list.h"
@@ -167,13 +168,17 @@
   std::unique_ptr<TCPClientSocket> socket_;
 };
 
+struct ReceivedRequest {
+  HttpServerRequestInfo info;
+  int connection_id;
+};
+
 }  // namespace
 
 class HttpServerTest : public TestWithTaskEnvironment,
                        public HttpServer::Delegate {
  public:
-  HttpServerTest()
-      : quit_after_request_count_(0), quit_on_close_connection_(-1) {}
+  HttpServerTest() : quit_on_close_connection_(-1) {}
 
   void SetUp() override {
     std::unique_ptr<ServerSocket> server_socket(
@@ -199,9 +204,7 @@
 
   void OnHttpRequest(int connection_id,
                      const HttpServerRequestInfo& info) override {
-    requests_.push_back(std::make_pair(info, connection_id));
-    if (requests_.size() == quit_after_request_count_)
-      std::move(run_loop_quit_func_).Run();
+    received_requests_.AddValue({.info = info, .connection_id = connection_id});
   }
 
   void OnWebSocketRequest(int connection_id,
@@ -220,18 +223,9 @@
       std::move(run_loop_quit_func_).Run();
   }
 
-  void RunUntilRequestsReceived(size_t count) {
-    quit_after_request_count_ = count;
-    if (requests_.size() == count)
-      return;
+  ReceivedRequest WaitForRequest() { return received_requests_.Take(); }
 
-    base::RunLoop run_loop;
-    base::AutoReset<base::OnceClosure> run_loop_quit_func(
-        &run_loop_quit_func_, run_loop.QuitClosure());
-    run_loop.Run();
-
-    ASSERT_EQ(requests_.size(), count);
-  }
+  bool HasRequest() const { return !received_requests_.IsEmpty(); }
 
   // Connections should only be created using this method, which waits until
   // both the server and the client have received the connected socket.
@@ -261,16 +255,6 @@
     ASSERT_FALSE(iter->second);
   }
 
-  HttpServerRequestInfo GetRequest(size_t request_index) {
-    return requests_[request_index].first;
-  }
-
-  size_t num_requests() const { return requests_.size(); }
-
-  int GetConnectionId(size_t request_index) {
-    return requests_[request_index].second;
-  }
-
   void HandleAcceptResult(std::unique_ptr<StreamSocket> socket) {
     ASSERT_FALSE(quit_on_create_loop_);
     quit_on_create_loop_ = std::make_unique<base::RunLoop>();
@@ -286,12 +270,11 @@
   std::unique_ptr<HttpServer> server_;
   IPEndPoint server_address_;
   base::OnceClosure run_loop_quit_func_;
-  std::vector<std::pair<HttpServerRequestInfo, int> > requests_;
   std::unordered_map<int /* connection_id */, bool /* connected */>
       connection_map_;
 
  private:
-  size_t quit_after_request_count_;
+  base::test::RepeatingTestFuture<ReceivedRequest> received_requests_;
   std::unique_ptr<base::RunLoop> quit_on_create_loop_;
   int quit_on_close_connection_;
 };
@@ -321,27 +304,13 @@
   }
 
   void OnWebSocketMessage(int connection_id, std::string data) override {
-    message_ = data;
-    got_message_ = true;
-    if (run_loop_) {
-      run_loop_->Quit();
-    }
+    messages_.AddValue(data);
   }
 
-  const std::string& GetMessage() {
-    if (!got_message_) {
-      run_loop_ = std::make_unique<base::RunLoop>();
-      run_loop_->Run();
-      run_loop_.reset();
-    }
-    got_message_ = false;
-    return message_;
-  }
+  std::string GetMessage() { return messages_.Take(); }
 
  private:
-  std::string message_;
-  std::unique_ptr<base::RunLoop> run_loop_;
-  bool got_message_ = false;
+  base::test::RepeatingTestFuture<std::string> messages_;
 };
 
 std::string EncodeFrame(std::string message,
@@ -370,12 +339,12 @@
   TestHttpClient client;
   CreateConnection(&client);
   client.Send("GET /test HTTP/1.1\r\n\r\n");
-  RunUntilRequestsReceived(1);
-  ASSERT_EQ("GET", GetRequest(0).method);
-  ASSERT_EQ("/test", GetRequest(0).path);
-  ASSERT_EQ("", GetRequest(0).data);
-  ASSERT_EQ(0u, GetRequest(0).headers.size());
-  ASSERT_TRUE(base::StartsWith(GetRequest(0).peer.ToString(), "127.0.0.1",
+  ReceivedRequest request = WaitForRequest();
+  ASSERT_EQ("GET", request.info.method);
+  ASSERT_EQ("/test", request.info.path);
+  ASSERT_EQ("", request.info.data);
+  ASSERT_EQ(0u, request.info.headers.size());
+  ASSERT_TRUE(base::StartsWith(request.info.peer.ToString(), "127.0.0.1",
                                base::CompareCase::SENSITIVE));
 }
 
@@ -384,7 +353,7 @@
   CreateConnection(&client);
   client.Send("GET /test HTTP/1.1\r\n\r)");
   RunUntilConnectionIdClosed(1);
-  EXPECT_EQ(0u, num_requests());
+  EXPECT_FALSE(HasRequest());
   client.ExpectUsedThenDisconnectedWithNoData();
 }
 
@@ -401,20 +370,19 @@
       {"HeaderWithNonASCII", ":  ", "\xf7"},
   };
   std::string headers;
-  for (size_t i = 0; i < base::size(kHeaders); ++i) {
-    headers +=
-        std::string(kHeaders[i][0]) + kHeaders[i][1] + kHeaders[i][2] + "\r\n";
+  for (const auto& header : kHeaders) {
+    headers += std::string(header[0]) + header[1] + header[2] + "\r\n";
   }
 
   client.Send("GET /test HTTP/1.1\r\n" + headers + "\r\n");
-  RunUntilRequestsReceived(1);
-  ASSERT_EQ("", GetRequest(0).data);
+  auto request = WaitForRequest();
+  ASSERT_EQ("", request.info.data);
 
-  for (size_t i = 0; i < base::size(kHeaders); ++i) {
-    std::string field = base::ToLowerASCII(std::string(kHeaders[i][0]));
-    std::string value = kHeaders[i][2];
-    ASSERT_EQ(1u, GetRequest(0).headers.count(field)) << field;
-    ASSERT_EQ(value, GetRequest(0).headers[field]) << kHeaders[i][0];
+  for (const auto& header : kHeaders) {
+    std::string field = base::ToLowerASCII(std::string(header[0]));
+    std::string value = header[2];
+    ASSERT_EQ(1u, request.info.headers.count(field)) << field;
+    ASSERT_EQ(value, request.info.headers[field]) << header[0];
   }
 }
 
@@ -422,27 +390,28 @@
   TestHttpClient client;
   CreateConnection(&client);
   const char* const kHeaders[][3] = {
+      // clang-format off
       {"FirstHeader", ": ", "1"},
       {"DuplicateHeader", ": ", "2"},
       {"MiddleHeader", ": ", "3"},
       {"DuplicateHeader", ": ", "4"},
       {"LastHeader", ": ", "5"},
+      // clang-format on
   };
   std::string headers;
-  for (size_t i = 0; i < base::size(kHeaders); ++i) {
-    headers +=
-        std::string(kHeaders[i][0]) + kHeaders[i][1] + kHeaders[i][2] + "\r\n";
+  for (const auto& header : kHeaders) {
+    headers += std::string(header[0]) + header[1] + header[2] + "\r\n";
   }
 
   client.Send("GET /test HTTP/1.1\r\n" + headers + "\r\n");
-  RunUntilRequestsReceived(1);
-  ASSERT_EQ("", GetRequest(0).data);
+  auto request = WaitForRequest();
+  ASSERT_EQ("", request.info.data);
 
-  for (size_t i = 0; i < base::size(kHeaders); ++i) {
-    std::string field = base::ToLowerASCII(std::string(kHeaders[i][0]));
-    std::string value = (field == "duplicateheader") ? "2,4" : kHeaders[i][2];
-    ASSERT_EQ(1u, GetRequest(0).headers.count(field)) << field;
-    ASSERT_EQ(value, GetRequest(0).headers[field]) << kHeaders[i][0];
+  for (const auto& header : kHeaders) {
+    std::string field = base::ToLowerASCII(std::string(header[0]));
+    std::string value = (field == "duplicateheader") ? "2,4" : header[2];
+    ASSERT_EQ(1u, request.info.headers.count(field)) << field;
+    ASSERT_EQ(value, request.info.headers[field]) << header[0];
   }
 }
 
@@ -461,41 +430,40 @@
       "HeaderWithNonASCII:  \xf7",
   };
   std::string headers;
-  for (size_t i = 0; i < base::size(kHeaders); ++i) {
-    headers += std::string(kHeaders[i]) + "\r\n";
+  for (const char* header : kHeaders) {
+    headers += std::string(header) + "\r\n";
   }
 
   client.Send("GET /test HTTP/1.1\r\n" + headers + "\r\n");
-  RunUntilRequestsReceived(1);
-  ASSERT_EQ("", GetRequest(0).data);
+  auto request = WaitForRequest();
+  ASSERT_EQ("", request.info.data);
 
-  ASSERT_TRUE(GetRequest(0).HasHeaderValue("header", "abcd"));
-  ASSERT_FALSE(GetRequest(0).HasHeaderValue("header", "bc"));
-  ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithnowhitespace", "e"));
-  ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithwhitespace", "f"));
-  ASSERT_TRUE(GetRequest(0).HasHeaderValue("duplicateheader", "g"));
-  ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithcomma", "h"));
-  ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithcomma", "i"));
-  ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithcomma", "j"));
-  ASSERT_TRUE(GetRequest(0).HasHeaderValue("duplicateheader", "k"));
-  ASSERT_FALSE(GetRequest(0).HasHeaderValue("emptyheader", "x"));
-  ASSERT_FALSE(GetRequest(0).HasHeaderValue("emptyheaderwithwhitespace", "x"));
-  ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithnonascii", "\xf7"));
+  ASSERT_TRUE(request.info.HasHeaderValue("header", "abcd"));
+  ASSERT_FALSE(request.info.HasHeaderValue("header", "bc"));
+  ASSERT_TRUE(request.info.HasHeaderValue("headerwithnowhitespace", "e"));
+  ASSERT_TRUE(request.info.HasHeaderValue("headerwithwhitespace", "f"));
+  ASSERT_TRUE(request.info.HasHeaderValue("duplicateheader", "g"));
+  ASSERT_TRUE(request.info.HasHeaderValue("headerwithcomma", "h"));
+  ASSERT_TRUE(request.info.HasHeaderValue("headerwithcomma", "i"));
+  ASSERT_TRUE(request.info.HasHeaderValue("headerwithcomma", "j"));
+  ASSERT_TRUE(request.info.HasHeaderValue("duplicateheader", "k"));
+  ASSERT_FALSE(request.info.HasHeaderValue("emptyheader", "x"));
+  ASSERT_FALSE(request.info.HasHeaderValue("emptyheaderwithwhitespace", "x"));
+  ASSERT_TRUE(request.info.HasHeaderValue("headerwithnonascii", "\xf7"));
 }
 
 TEST_F(HttpServerTest, RequestWithBody) {
   TestHttpClient client;
   CreateConnection(&client);
   std::string body = "a" + std::string(1 << 10, 'b') + "c";
-  client.Send(base::StringPrintf(
-      "GET /test HTTP/1.1\r\n"
-      "SomeHeader: 1\r\n"
-      "Content-Length: %" PRIuS "\r\n\r\n%s",
-      body.length(),
-      body.c_str()));
-  RunUntilRequestsReceived(1);
-  ASSERT_EQ(2u, GetRequest(0).headers.size());
-  ASSERT_EQ(body.length(), GetRequest(0).data.length());
+  client.Send(
+      base::StringPrintf("GET /test HTTP/1.1\r\n"
+                         "SomeHeader: 1\r\n"
+                         "Content-Length: %" PRIuS "\r\n\r\n%s",
+                         body.length(), body.c_str()));
+  auto request = WaitForRequest();
+  ASSERT_EQ(2u, request.info.headers.size());
+  ASSERT_EQ(body.length(), request.info.data.length());
   ASSERT_EQ('a', body[0]);
   ASSERT_EQ('c', *body.rbegin());
 }
@@ -510,7 +478,7 @@
       "Upgrade: h2c\r\n"
       "Connection: SomethingElse, Upgrade\r\n"
       "\r\n");
-  RunUntilRequestsReceived(1);
+  WaitForRequest();
 }
 
 TEST_F(WebSocketTest, RequestWebSocket) {
@@ -523,7 +491,7 @@
       "Sec-WebSocket-Version: 8\r\n"
       "Sec-WebSocket-Key: key\r\n"
       "\r\n");
-  RunUntilRequestsReceived(1);
+  WaitForRequest();
 }
 
 TEST_F(WebSocketTest, RequestWebSocketTrailingJunk) {
@@ -550,7 +518,7 @@
       "Connection: SomethingElse, Upgrade\r\n"
       "Sec-WebSocket-Version: 8\r\n"
       "Sec-WebSocket-Key: key\r\n\r\n");
-  RunUntilRequestsReceived(1);
+  WaitForRequest();
   ASSERT_TRUE(client.ReadResponse(&response));
   const std::string message = "";
   const std::string ping_frame =
@@ -574,7 +542,7 @@
       "Connection: SomethingElse, Upgrade\r\n"
       "Sec-WebSocket-Version: 8\r\n"
       "Sec-WebSocket-Key: key\r\n\r\n");
-  RunUntilRequestsReceived(1);
+  WaitForRequest();
   ASSERT_TRUE(client.ReadResponse(&response));
   const std::string message = "hello";
   const std::string ping_frame =
@@ -598,7 +566,7 @@
       "Connection: SomethingElse, Upgrade\r\n"
       "Sec-WebSocket-Version: 8\r\n"
       "Sec-WebSocket-Key: key\r\n\r\n");
-  RunUntilRequestsReceived(1);
+  WaitForRequest();
   ASSERT_TRUE(client.ReadResponse(&response));
   const std::string ping_frame = EncodeFrame(
       /* message= */ "", WebSocketFrameHeader::OpCodeEnum::kOpCodePing,
@@ -625,7 +593,7 @@
       "Connection: SomethingElse, Upgrade\r\n"
       "Sec-WebSocket-Version: 8\r\n"
       "Sec-WebSocket-Key: key\r\n\r\n");
-  RunUntilRequestsReceived(1);
+  WaitForRequest();
   ASSERT_TRUE(client.ReadResponse(&response));
   constexpr int kFrameSize = 100000;
   const std::string text_frame(kFrameSize, 'a');
@@ -655,7 +623,7 @@
       "Connection: SomethingElse, Upgrade\r\n"
       "Sec-WebSocket-Version: 8\r\n"
       "Sec-WebSocket-Key: key\r\n\r\n");
-  RunUntilRequestsReceived(1);
+  WaitForRequest();
   ASSERT_TRUE(client.ReadResponse(&response));
   const std::string text_frame_first = "foo";
   const std::string continuation_frame_first = "bar";
@@ -700,7 +668,7 @@
       "Connection: SomethingElse, Upgrade\r\n"
       "Sec-WebSocket-Version: 8\r\n"
       "Sec-WebSocket-Key: key\r\n\r\n");
-  RunUntilRequestsReceived(1);
+  WaitForRequest();
   ASSERT_TRUE(client.ReadResponse(&response));
 
   const std::string ping_message_first = "";
@@ -741,7 +709,7 @@
       "Connection: SomethingElse, Upgrade\r\n"
       "Sec-WebSocket-Version: 8\r\n"
       "Sec-WebSocket-Key: key\r\n\r\n");
-  RunUntilRequestsReceived(1);
+  WaitForRequest();
   ASSERT_TRUE(client.ReadResponse(&response));
 
   const std::string text_frame = "foo";
@@ -781,7 +749,7 @@
       "Connection: SomethingElse, Upgrade\r\n"
       "Sec-WebSocket-Version: 8\r\n"
       "Sec-WebSocket-Key: key\r\n\r\n");
-  RunUntilRequestsReceived(1);
+  WaitForRequest();
   ASSERT_TRUE(client.ReadResponse(&response));
 
   const std::string text_frame = "foo";
@@ -821,7 +789,7 @@
       "Connection: SomethingElse, Upgrade\r\n"
       "Sec-WebSocket-Version: 8\r\n"
       "Sec-WebSocket-Key: key\r\n\r\n");
-  RunUntilRequestsReceived(1);
+  WaitForRequest();
   ASSERT_TRUE(client.ReadResponse(&response));
 
   const std::string text_frame = "foo";
@@ -856,7 +824,7 @@
       "Connection: SomethingElse, Upgrade\r\n"
       "Sec-WebSocket-Version: 8\r\n"
       "Sec-WebSocket-Key: key\r\n\r\n");
-  RunUntilRequestsReceived(1);
+  WaitForRequest();
   ASSERT_TRUE(client.ReadResponse(&response));
 
   const std::string text_frame = "foo";
@@ -919,8 +887,8 @@
   TestHttpClient client;
   CreateConnection(&client);
   client.Send("GET /test HTTP/1.1\r\n\r\n");
-  RunUntilRequestsReceived(1);
-  server_->Send200(GetConnectionId(0), "Response!", "text/plain",
+  auto request = WaitForRequest();
+  server_->Send200(request.connection_id, "Response!", "text/plain",
                    TRAFFIC_ANNOTATION_FOR_TESTS);
 
   std::string response;
@@ -935,12 +903,12 @@
   TestHttpClient client;
   CreateConnection(&client);
   client.Send("GET /test HTTP/1.1\r\n\r\n");
-  RunUntilRequestsReceived(1);
-  server_->SendRaw(GetConnectionId(0), "Raw Data ",
+  auto request = WaitForRequest();
+  server_->SendRaw(request.connection_id, "Raw Data ",
                    TRAFFIC_ANNOTATION_FOR_TESTS);
-  server_->SendRaw(GetConnectionId(0), "More Data",
+  server_->SendRaw(request.connection_id, "More Data",
                    TRAFFIC_ANNOTATION_FOR_TESTS);
-  server_->SendRaw(GetConnectionId(0), "Third Piece of Data",
+  server_->SendRaw(request.connection_id, "Third Piece of Data",
                    TRAFFIC_ANNOTATION_FOR_TESTS);
 
   const std::string expected_response("Raw Data More DataThird Piece of Data");
@@ -956,17 +924,17 @@
       "GET /test \r\n\r\n",
   };
 
-  for (size_t i = 0; i < base::size(kBadProtocolRequests); ++i) {
+  for (const char* bad_request : kBadProtocolRequests) {
     TestHttpClient client;
     CreateConnection(&client);
 
-    client.Send(kBadProtocolRequests[i]);
+    client.Send(bad_request);
     client.ExpectUsedThenDisconnectedWithNoData();
 
     // Assert that the delegate was updated properly.
     ASSERT_EQ(1u, connection_map().size());
     ASSERT_FALSE(connection_map().begin()->second);
-    EXPECT_EQ(0ul, requests_.size());
+    EXPECT_FALSE(HasRequest());
 
     // Reset the state of the connection map.
     connection_map().clear();
@@ -1030,8 +998,8 @@
       return ERR_IO_PENDING;
     }
     DCHECK_GT(buf_len, 0);
-    int read_len = std::min(static_cast<int>(pending_read_data_.size()),
-                            buf_len);
+    int read_len =
+        std::min(static_cast<int>(pending_read_data_.size()), buf_len);
     memcpy(buf->data(), pending_read_data_.data(), read_len);
     pending_read_data_.erase(0, read_len);
     return read_len;
@@ -1080,13 +1048,12 @@
       "GET /test HTTP/1.1\r\n"
       "SomeHeader: 1\r\n"
       "Content-Length: %" PRIuS "\r\n\r\n%s",
-      body.length(),
-      body.c_str());
+      body.length(), body.c_str());
   socket->DidRead(request_text.c_str(), request_text.length() - 2);
-  ASSERT_EQ(0u, requests_.size());
+  ASSERT_FALSE(HasRequest());
   socket->DidRead(request_text.c_str() + request_text.length() - 2, 2);
-  ASSERT_EQ(1u, requests_.size());
-  ASSERT_EQ(body, GetRequest(0).data);
+  ASSERT_TRUE(HasRequest());
+  ASSERT_EQ(body, WaitForRequest().info.data);
 }
 
 TEST_F(HttpServerTest, MultipleRequestsOnSameConnection) {
@@ -1095,15 +1062,14 @@
   TestHttpClient client;
   CreateConnection(&client);
   std::string body = "body";
-  client.Send(base::StringPrintf(
-      "GET /test HTTP/1.1\r\n"
-      "Content-Length: %" PRIuS "\r\n\r\n%s",
-      body.length(),
-      body.c_str()));
-  RunUntilRequestsReceived(1);
-  ASSERT_EQ(body, GetRequest(0).data);
+  client.Send(
+      base::StringPrintf("GET /test HTTP/1.1\r\n"
+                         "Content-Length: %" PRIuS "\r\n\r\n%s",
+                         body.length(), body.c_str()));
+  auto first_request = WaitForRequest();
+  ASSERT_EQ(body, first_request.info.data);
 
-  int client_connection_id = GetConnectionId(0);
+  int client_connection_id = first_request.connection_id;
   server_->Send200(client_connection_id, "Content for /test", "text/plain",
                    TRAFFIC_ANNOTATION_FOR_TESTS);
   std::string response1;
@@ -1114,10 +1080,10 @@
                              base::CompareCase::SENSITIVE));
 
   client.Send("GET /test2 HTTP/1.1\r\n\r\n");
-  RunUntilRequestsReceived(2);
-  ASSERT_EQ("/test2", GetRequest(1).path);
+  auto second_request = WaitForRequest();
+  ASSERT_EQ("/test2", second_request.info.path);
 
-  ASSERT_EQ(client_connection_id, GetConnectionId(1));
+  ASSERT_EQ(client_connection_id, second_request.connection_id);
   server_->Send404(client_connection_id, TRAFFIC_ANNOTATION_FOR_TESTS);
   std::string response2;
   ASSERT_TRUE(client.ReadResponse(&response2));
@@ -1125,10 +1091,10 @@
                                base::CompareCase::SENSITIVE));
 
   client.Send("GET /test3 HTTP/1.1\r\n\r\n");
-  RunUntilRequestsReceived(3);
-  ASSERT_EQ("/test3", GetRequest(2).path);
+  auto third_request = WaitForRequest();
+  ASSERT_EQ("/test3", third_request.info.path);
 
-  ASSERT_EQ(client_connection_id, GetConnectionId(2));
+  ASSERT_EQ(client_connection_id, third_request.connection_id);
   server_->Send200(client_connection_id, "Content for /test3", "text/plain",
                    TRAFFIC_ANNOTATION_FOR_TESTS);
   std::string response3;
@@ -1165,7 +1131,7 @@
   EXPECT_EQ(1ul, connection_ids_.size());
   // OnHttpRequest() should never have been called, since the connection was
   // closed without reading from it.
-  EXPECT_EQ(0ul, requests_.size());
+  EXPECT_FALSE(HasRequest());
 }
 
 }  // namespace
diff --git a/net/socket/udp_socket_posix.cc b/net/socket/udp_socket_posix.cc
index c6453b31..a826160 100644
--- a/net/socket/udp_socket_posix.cc
+++ b/net/socket/udp_socket_posix.cc
@@ -291,12 +291,19 @@
 #if BUILDFLAG(IS_MAC)
   // Attempt to clear errors on the socket so that they are not returned by
   // close(). See https://crbug.com/1151048.
-  // TODO(ricea): Remove this if it doesn't work, or when the OS bug is fixed.
   int value = 0;
   socklen_t value_len = sizeof(value);
   HANDLE_EINTR(getsockopt(socket_, SOL_SOCKET, SO_ERROR, &value, &value_len));
 
-  PCHECK(IGNORE_EINTR(guarded_close_np(socket_, &kSocketFdGuard)) == 0);
+  if (IGNORE_EINTR(guarded_close_np(socket_, &kSocketFdGuard)) != 0) {
+    // There is a bug in the Mac OS kernel that it can return an
+    // ENOTCONN error. In this case we don't know whether the file
+    // descriptor is still allocated or not. We cannot safely close the
+    // file descriptor because it may have been reused by another
+    // thread in the meantime. We may leak file handles here and cause
+    // a crash indirectly later. See https://crbug.com/1151048.
+    PCHECK(errno == ENOTCONN);
+  }
 #else
   PCHECK(IGNORE_EINTR(close(socket_)) == 0);
 #endif  // BUILDFLAG(IS_MAC)
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc
index 5917125d..502ec41 100644
--- a/net/url_request/url_request_context_builder.cc
+++ b/net/url_request/url_request_context_builder.cc
@@ -367,8 +367,8 @@
   if (cookie_store_set_by_client_) {
     storage->set_cookie_store(std::move(cookie_store_));
   } else {
-    std::unique_ptr<CookieStore> cookie_store(
-        new CookieMonster(nullptr /* store */, context->net_log()));
+    std::unique_ptr<CookieStore> cookie_store(new CookieMonster(
+        nullptr /* store */, context->net_log(), first_party_sets_enabled_));
     storage->set_cookie_store(std::move(cookie_store));
   }
 
diff --git a/net/url_request/url_request_context_builder.h b/net/url_request/url_request_context_builder.h
index cb08ba7..3799bd5 100644
--- a/net/url_request/url_request_context_builder.h
+++ b/net/url_request/url_request_context_builder.h
@@ -270,6 +270,9 @@
   void set_throttling_enabled(bool throttling_enabled) {
     throttling_enabled_ = throttling_enabled;
   }
+  void set_first_party_sets_enabled(bool enabled) {
+    first_party_sets_enabled_ = enabled;
+  }
 
   void set_ct_policy_enforcer(
       std::unique_ptr<CTPolicyEnforcer> ct_policy_enforcer);
@@ -349,6 +352,7 @@
   bool throttling_enabled_ = false;
   bool cookie_store_set_by_client_ = false;
   bool suppress_setting_socket_performance_watcher_factory_for_testing_ = false;
+  bool first_party_sets_enabled_ = false;
 
   HttpCacheParams http_cache_params_;
   HttpNetworkSessionParams http_network_session_params_;
diff --git a/net/url_request/url_request_http_job_unittest.cc b/net/url_request/url_request_http_job_unittest.cc
index 4c9fb80..bd4dd66 100644
--- a/net/url_request/url_request_http_job_unittest.cc
+++ b/net/url_request/url_request_http_job_unittest.cc
@@ -1568,7 +1568,8 @@
   base::HistogramTester histograms;
   const std::string test_histogram = "Cookie.CookieSchemeRequestScheme";
 
-  CookieMonster cm(nullptr, nullptr);
+  CookieMonster cm(/*store=*/nullptr, /*net_log=*/nullptr,
+                   /*first_party_sets_enabled=*/false);
   TestURLRequestContext context(true);
   context.set_cookie_store(&cm);
   context.Init();
@@ -1661,7 +1662,8 @@
   ASSERT_TRUE(test_server.Start());
 
   FilteringTestNetworkDelegate network_delegate;
-  CookieMonster cm(nullptr, nullptr);
+  CookieMonster cm(/*store=*/nullptr, /*net_log=*/nullptr,
+                   /*first_party_sets_enabled=*/false);
   TestURLRequestContext context(true);
   context.set_cookie_store(&cm);
   context.set_network_delegate(&network_delegate);
@@ -1732,7 +1734,8 @@
   FilteringTestNetworkDelegate network_delegate;
   network_delegate.set_block_get_cookies_by_name(true);
   network_delegate.SetCookieFilter("blocked_");
-  CookieMonster cm(nullptr, nullptr);
+  CookieMonster cm(/*store=*/nullptr, /*net_log=*/nullptr,
+                   /*first_party_sets_enabled=*/false);
   TestURLRequestContext context(true);
   context.set_cookie_store(&cm);
   context.set_network_delegate(&network_delegate);
@@ -1809,7 +1812,8 @@
   ASSERT_TRUE(https_test.Start());
 
   TestURLRequestContext context;
-  CookieMonster cookie_monster(nullptr, nullptr);
+  CookieMonster cookie_monster(nullptr, nullptr,
+                               false /* first_party_sets_enabled */);
   context.set_cookie_store(&cookie_monster);
 
   TestDelegate delegate;
@@ -1891,7 +1895,8 @@
       kOwnerSite, std::set<SchemefulSite>({kOwnerSite, kMemberSite})));
 
   TestURLRequestContext context;
-  CookieMonster cookie_monster(nullptr, nullptr);
+  CookieMonster cookie_monster(nullptr, nullptr,
+                               false /* first_party_sets_enabled */);
   auto cookie_access_delegate = std::make_unique<TestCookieAccessDelegate>();
   cookie_access_delegate->SetFirstPartySets(first_party_sets);
   cookie_monster.SetCookieAccessDelegate(std::move(cookie_access_delegate));
diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc
index dc2a274..acf5e13 100644
--- a/net/url_request/url_request_test_util.cc
+++ b/net/url_request/url_request_test_util.cc
@@ -125,7 +125,8 @@
   // In-memory cookie store.
   if (!cookie_store()) {
     context_storage_.set_cookie_store(std::make_unique<CookieMonster>(
-        nullptr /* store */, nullptr /* netlog */));
+        nullptr /* store */, nullptr /* netlog */,
+        false /* first_party_sets_enabled */));
   }
 
   if (!http_user_agent_settings()) {
@@ -195,8 +196,10 @@
   builder->SetHttpAuthHandlerFactory(HttpAuthHandlerFactory::CreateDefault());
   builder->SetHttpServerProperties(std::make_unique<HttpServerProperties>());
   builder->set_quic_context(std::make_unique<QuicContext>());
-  builder->SetCookieStore(std::make_unique<CookieMonster>(/*store=*/nullptr,
-                                                          /*netlog=*/nullptr));
+  builder->SetCookieStore(
+      std::make_unique<CookieMonster>(/*store=*/nullptr,
+                                      /*netlog=*/nullptr,
+                                      /*first_party_sets_enabled=*/false));
   builder->set_http_user_agent_settings(
       std::make_unique<StaticHttpUserAgentSettings>("en-us,fr", std::string()));
   return builder;
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 5940231..b55b551 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -1982,8 +1982,8 @@
   TestDelegate async_delegate;
 
   TestURLRequestContext sync_context;
-  std::unique_ptr<CookieMonster> cm =
-      std::make_unique<CookieMonster>(nullptr, nullptr);
+  std::unique_ptr<CookieMonster> cm = std::make_unique<CookieMonster>(
+      nullptr, nullptr, false /* first_party_sets_enabled */);
   sync_context.set_cookie_store(cm.get());
   FilteringTestNetworkDelegate sync_filter_network_delegate;
   sync_filter_network_delegate.SetCookieFilter("CookieBlockedOnCanGetCookie");
@@ -3216,7 +3216,7 @@
   auto cad = std::make_unique<TestCookieAccessDelegate>();
   cad->SetIgnoreSameSiteRestrictionsScheme("chrome", true);
 
-  CookieMonster cm(nullptr, nullptr);
+  CookieMonster cm(nullptr, nullptr, false /* first_party_sets_enabled */);
   cm.SetCookieAccessDelegate(std::move(cad));
 
   TestURLRequestContext context(true);
@@ -8050,7 +8050,7 @@
 
   FilteringTestNetworkDelegate network_delegate;
   network_delegate.SetCookieFilter("blockeduserpreference");
-  CookieMonster cm(nullptr, nullptr);
+  CookieMonster cm(nullptr, nullptr, false /* first_party_sets_enabled */);
   TestURLRequestContext context(true);
   context.set_cookie_store(&cm);
   context.set_network_delegate(&network_delegate);
@@ -8303,8 +8303,8 @@
     TestURLRequestContext context(true);
     context.set_network_delegate(&filtering_network_delegate);
 
-    std::unique_ptr<CookieMonster> cm =
-        std::make_unique<CookieMonster>(nullptr, nullptr);
+    std::unique_ptr<CookieMonster> cm = std::make_unique<CookieMonster>(
+        nullptr, nullptr, false /* first_party_sets_enabled */);
     auto another_cookie = CanonicalCookie::Create(
         url_requiring_auth_wo_cookies, "another_cookie=true", base::Time::Now(),
         absl::nullopt /* server_time */,
@@ -8728,8 +8728,8 @@
     filtering_network_delegate.set_block_annotate_cookies();
     TestURLRequestContext context(true);
     context.set_network_delegate(&filtering_network_delegate);
-    std::unique_ptr<CookieMonster> cm =
-        std::make_unique<CookieMonster>(nullptr, nullptr);
+    std::unique_ptr<CookieMonster> cm = std::make_unique<CookieMonster>(
+        nullptr, nullptr, false /* first_party_sets_enabled */);
     auto another_cookie = CanonicalCookie::Create(
         original_url, "another_cookie=true", base::Time::Now(),
         absl::nullopt /* server_time */,
diff --git a/pdf/pdf_view_plugin_base.cc b/pdf/pdf_view_plugin_base.cc
index 63f10ce..50e72cba 100644
--- a/pdf/pdf_view_plugin_base.cc
+++ b/pdf/pdf_view_plugin_base.cc
@@ -1632,15 +1632,12 @@
 }
 
 void PdfViewPluginBase::SendThumbnail(base::Value reply, Thumbnail thumbnail) {
-  const SkBitmap& bitmap = thumbnail.bitmap();
-  base::Value image_data(base::make_span(
-      static_cast<uint8_t*>(bitmap.getPixels()), bitmap.computeByteSize()));
-
   DCHECK_EQ(*reply.FindStringKey("type"), "getThumbnailReply");
   DCHECK(reply.FindStringKey("messageId"));
-  reply.SetKey("imageData", std::move(image_data));
-  reply.SetIntKey("width", bitmap.width());
-  reply.SetIntKey("height", bitmap.height());
+
+  reply.SetKey("imageData", base::Value(thumbnail.TakeData()));
+  reply.SetIntKey("width", thumbnail.image_size().width());
+  reply.SetIntKey("height", thumbnail.image_size().height());
   SendMessage(std::move(reply));
 }
 
diff --git a/pdf/pdfium/pdfium_page.cc b/pdf/pdfium/pdfium_page.cc
index 230ebbb..6e8e303e8 100644
--- a/pdf/pdfium/pdfium_page.cc
+++ b/pdf/pdfium/pdfium_page.cc
@@ -32,7 +32,6 @@
 #include "third_party/pdfium/public/cpp/fpdf_scopers.h"
 #include "third_party/pdfium/public/fpdf_annot.h"
 #include "third_party/pdfium/public/fpdf_catalog.h"
-#include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/rect.h"
@@ -1555,22 +1554,22 @@
   gfx::Size page_size(base::saturated_cast<int>(FPDF_GetPageWidthF(page)),
                       base::saturated_cast<int>(FPDF_GetPageHeightF(page)));
   Thumbnail thumbnail(page_size, device_pixel_ratio);
+  const gfx::Size& image_size = thumbnail.image_size();
 
-  SkBitmap& sk_bitmap = thumbnail.bitmap();
   ScopedFPDFBitmap fpdf_bitmap(FPDFBitmap_CreateEx(
-      sk_bitmap.width(), sk_bitmap.height(), FPDFBitmap_BGRA,
-      sk_bitmap.getPixels(), sk_bitmap.rowBytes()));
+      image_size.width(), image_size.height(), FPDFBitmap_BGRA,
+      thumbnail.GetImageData().data(), thumbnail.stride()));
 
   // Clear the bitmap.
   FPDFBitmap_FillRect(fpdf_bitmap.get(), /*left=*/0, /*top=*/0,
-                      sk_bitmap.width(), sk_bitmap.height(),
+                      image_size.width(), image_size.height(),
                       /*color=*/0xFFFFFFFF);
 
   // The combination of the `FPDF_REVERSE_BYTE_ORDER` rendering flag and the
   // `FPDFBitmap_BGRA` format when initializing `fpdf_bitmap` results in an RGBA
   // rendering, which is the format required by HTML <canvas>.
   FPDF_RenderPageBitmap(fpdf_bitmap.get(), GetPage(), /*start_x=*/0,
-                        /*start_y=*/0, sk_bitmap.width(), sk_bitmap.height(),
+                        /*start_y=*/0, image_size.width(), image_size.height(),
                         /*rotate=*/0, FPDF_ANNOT | FPDF_REVERSE_BYTE_ORDER);
 
   return thumbnail;
diff --git a/pdf/pdfium/pdfium_page_unittest.cc b/pdf/pdfium/pdfium_page_unittest.cc
index 92b85755..946eaf4 100644
--- a/pdf/pdfium/pdfium_page_unittest.cc
+++ b/pdf/pdfium/pdfium_page_unittest.cc
@@ -27,6 +27,7 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/geometry/size_f.h"
+#include "ui/gfx/geometry/skia_conversions.h"
 #include "ui/gfx/geometry/test/geometry_util.h"
 #include "ui/gfx/range/range.h"
 
@@ -762,15 +763,25 @@
                              const std::string& expectation_file_prefix) {
     PDFiumPage& page = GetPDFiumPageForTest(engine, page_index);
     Thumbnail thumbnail = page.GenerateThumbnail(device_pixel_ratio);
-    EXPECT_EQ(expected_thumbnail_size, gfx::Size(thumbnail.bitmap().width(),
-                                                 thumbnail.bitmap().height()));
+    EXPECT_EQ(expected_thumbnail_size, thumbnail.image_size());
     EXPECT_EQ(device_pixel_ratio, thumbnail.device_pixel_ratio());
 
+    auto image_info =
+        SkImageInfo::Make(gfx::SizeToSkISize(thumbnail.image_size()),
+                          kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+    int stride = thumbnail.stride();
+    ASSERT_GT(stride, 0);
+    ASSERT_EQ(image_info.minRowBytes(), static_cast<size_t>(stride));
+    std::vector<uint8_t> data = thumbnail.TakeData();
+    SkBitmap bitmap;
+    EXPECT_TRUE(bitmap.installPixels(image_info, data.data(),
+                                     image_info.minRowBytes()));
+
     base::FilePath expectation_png_file_path = GetThumbnailTestData(
         expectation_file_prefix, page_index, device_pixel_ratio);
 
     EXPECT_TRUE(cc::MatchesPNGFile(
-        thumbnail.bitmap(), GetTestDataFilePath(expectation_png_file_path),
+        bitmap, GetTestDataFilePath(expectation_png_file_path),
         cc::ExactPixelComparator(/*discard_alpha=*/false)))
         << "Reference: " << expectation_png_file_path;
   }
diff --git a/pdf/ui/thumbnail.cc b/pdf/ui/thumbnail.cc
index 092e48f..b45225e 100644
--- a/pdf/ui/thumbnail.cc
+++ b/pdf/ui/thumbnail.cc
@@ -4,15 +4,17 @@
 
 #include "pdf/ui/thumbnail.h"
 
+#include <stddef.h>
+
 #include <algorithm>
 #include <cmath>
+#include <utility>
 
 #include "base/check.h"
 #include "base/check_op.h"
 #include "base/cxx17_backports.h"
-#include "base/numerics/safe_math.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkImageInfo.h"
+#include "base/numerics/checked_math.h"
+#include "base/values.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace chrome_pdf {
@@ -92,24 +94,28 @@
   return scaled_size;
 }
 
+int CalculateStride(int width) {
+  base::CheckedNumeric<size_t> stride = kImageColorChannels;
+  stride *= width;
+  return stride.ValueOrDie<int>();
+}
+
+size_t CalculateImageDataSize(int stride, int height) {
+  base::CheckedNumeric<int> size = stride;
+  size *= height;
+  return size.ValueOrDie<size_t>();
+}
+
 }  // namespace
 
 Thumbnail::Thumbnail(const gfx::Size& page_size, float device_pixel_ratio)
     : device_pixel_ratio_(base::clamp(device_pixel_ratio,
                                       kMinDevicePixelRatio,
-                                      kMaxDevicePixelRatio)) {
-  const gfx::Size thumbnail_size_device_pixels =
-      CalculateBestFitSize(page_size, device_pixel_ratio_);
-
-  // Note that <canvas> can only hold data in RGBA format. It is the
-  // responsibility of the thumbnail's renderer to fill `bitmap_` with RGBA
-  // data.
-  const SkImageInfo info =
-      SkImageInfo::Make(thumbnail_size_device_pixels.width(),
-                        thumbnail_size_device_pixels.height(),
-                        kRGBA_8888_SkColorType, kPremul_SkAlphaType);
-  bool success = bitmap_.tryAllocPixels(info, info.minRowBytes());
-  DCHECK(success);
+                                      kMaxDevicePixelRatio)),
+      image_size_(CalculateBestFitSize(page_size, device_pixel_ratio_)),
+      stride_(CalculateStride(image_size_.width())),
+      image_data_(CalculateImageDataSize(stride(), image_size().height())) {
+  DCHECK(!image_data_.empty());
 }
 
 Thumbnail::Thumbnail(Thumbnail&& other) = default;
@@ -118,4 +124,14 @@
 
 Thumbnail::~Thumbnail() = default;
 
+base::Value::BlobStorage& Thumbnail::GetImageData() {
+  DCHECK(!image_data_.empty());
+  return image_data_;
+}
+
+base::Value::BlobStorage Thumbnail::TakeData() {
+  DCHECK(!image_data_.empty());
+  return std::move(image_data_);
+}
+
 }  // namespace chrome_pdf
diff --git a/pdf/ui/thumbnail.h b/pdf/ui/thumbnail.h
index 8171f85..580ecf6 100644
--- a/pdf/ui/thumbnail.h
+++ b/pdf/ui/thumbnail.h
@@ -5,11 +5,8 @@
 #ifndef PDF_UI_THUMBNAIL_H_
 #define PDF_UI_THUMBNAIL_H_
 
-#include "third_party/skia/include/core/SkBitmap.h"
-
-namespace gfx {
-class Size;
-}  // namespace gfx
+#include "base/values.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace chrome_pdf {
 
@@ -20,19 +17,30 @@
   Thumbnail& operator=(Thumbnail&& other);
   ~Thumbnail();
 
-  SkBitmap& bitmap() { return bitmap_; }
-
   float device_pixel_ratio() const { return device_pixel_ratio_; }
 
- private:
-  // Raw image data of the thumbnail.
-  SkBitmap bitmap_;
+  int stride() const { return stride_; }
 
+  const gfx::Size& image_size() const { return image_size_; }
+
+  // Note that <canvas> can only hold data in RGBA format. It is the
+  // responsibility of the thumbnail's renderer to fill the data with RGBA data.
+  base::Value::BlobStorage& GetImageData();
+
+  // Transfers the internal image data to the caller. After calling TakeData(),
+  // this Thumbnail instance should not be used.
+  base::Value::BlobStorage TakeData();
+
+ private:
   // Intended resolution of the thumbnail image. The dimensions of `bitmap_`
   // are the dimensions of the thumbnail in CSS pixels multiplied by
   // `device_pixel_ratio_`.
   // Only values between 0.25 and 2 are supported.
   float device_pixel_ratio_;
+
+  gfx::Size image_size_;  // In pixels.
+  int stride_;
+  base::Value::BlobStorage image_data_;
 };
 
 }  // namespace chrome_pdf
diff --git a/pdf/ui/thumbnail_unittest.cc b/pdf/ui/thumbnail_unittest.cc
index 25f78c8..bfab5bf 100644
--- a/pdf/ui/thumbnail_unittest.cc
+++ b/pdf/ui/thumbnail_unittest.cc
@@ -22,8 +22,7 @@
 
 void TestBestFitSize(const BestFitSizeParams& params) {
   Thumbnail thumbnail(params.page_size, params.device_pixel_ratio);
-  EXPECT_EQ(gfx::Size(thumbnail.bitmap().width(), thumbnail.bitmap().height()),
-            params.expected_thumbnail_size)
+  EXPECT_EQ(thumbnail.image_size(), params.expected_thumbnail_size)
       << "Failed for page of size of " << params.page_size.ToString()
       << " and device to pixel ratio of " << params.device_pixel_ratio;
 }
diff --git a/remoting/host/linux/linux_me2me_host.py b/remoting/host/linux/linux_me2me_host.py
index c93ab21..65fb4935 100755
--- a/remoting/host/linux/linux_me2me_host.py
+++ b/remoting/host/linux/linux_me2me_host.py
@@ -829,12 +829,13 @@
     """Send SIGTERM to all procs and wait for them to exit. Will fallback to
     SIGKILL if a process doesn't exit within 10 seconds.
     """
-    for proc, name in [(self.x_proc, "X server"),
-                       (self.pre_session_proc, "pre-session"),
+    for proc, name in [(self.host_proc, "host"),
                        (self.session_proc, "session"),
-                       (self.host_proc, "host")]:
+                       (self.pre_session_proc, "pre-session"),
+                       (self.x_proc, "X server")]:
+      logging.info("Shutting down %s: %s", name, proc and proc.pid)
       if proc is not None:
-        logging.info("Terminating " + name)
+        logging.info("Sending SIGTERM to %s proc.", name)
         try:
           psutil_proc = psutil.Process(proc.pid)
           psutil_proc.terminate()
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index 0b1e23f1..44271ba 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -283,6 +283,8 @@
 
   void StartOnNetworkThread();
 
+  void ShutdownOnNetworkThread();
+
 #if defined(OS_POSIX)
   // Callback passed to RegisterSignalHandler() to handle SIGTERM events.
   void SigTermHandler(int signal_number);
@@ -500,7 +502,7 @@
   // We might be getting deleted on one of the threads the |host_context| owns,
   // so we need to post it back to the caller thread to safely join & delete the
   // threads it contains.  This will go away when we move to AutoThread.
-  // |context_release()| will null |context_| before the method is invoked, so
+  // |context_.release()| will null |context_| before the method is invoked, so
   // we need to pull out the task-runner on which to call DeleteSoon first.
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
       context_->ui_task_runner();
@@ -736,6 +738,14 @@
 #endif  // defined(OS_POSIX)
 }
 
+void HostProcess::ShutdownOnNetworkThread() {
+  DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
+  config_watcher_.reset();
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+  cert_watcher_.reset();
+#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
+}
+
 #if defined(OS_POSIX)
 void HostProcess::SigTermHandler(int signal_number) {
   DCHECK(signal_number == SIGTERM);
@@ -793,13 +803,13 @@
 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
     if (!cert_watcher_) {
       cert_watcher_ = std::make_unique<CertificateWatcher>(
-          base::BindRepeating(&HostProcess::ShutdownHost, this,
-                              kSuccessExitCode),
+          base::BindRepeating(&HostProcess::ShutdownHost,
+                              base::Unretained(this), kSuccessExitCode),
           context_->file_task_runner());
       cert_watcher_->Start();
     }
     cert_watcher_->SetMonitor(host_->status_monitor());
-#endif
+#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
 
     scoped_refptr<protocol::TokenValidatorFactory> token_validator_factory =
         new TokenValidatorFactoryImpl(third_party_auth_config_, key_pair_,
@@ -812,7 +822,7 @@
 #if defined(OS_POSIX)
   // On Linux and Mac, perform a PAM authorization step after authentication.
   factory = std::make_unique<PamAuthorizationFactory>(std::move(factory));
-#endif
+#endif  // defined(OS_POSIX)
   host_->SetAuthenticatorFactory(std::move(factory));
 }
 
@@ -951,6 +961,9 @@
 void HostProcess::ShutdownOnUiThread() {
   DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
 
+  context_->network_task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&HostProcess::ShutdownOnNetworkThread, this));
+
   // Tear down resources that need to be torn down on the UI thread.
   desktop_environment_factory_.reset();
   policy_watcher_.reset();
@@ -968,6 +981,10 @@
   // TODO(wez): DesktopEnvironmentFactory should own the pipe reader.
   // See crbug.com/161373 and crbug.com/104544.
   AudioCapturerLinux::InitializePipeReader(nullptr, base::FilePath());
+
+  context_->input_task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce([]() { delete ui::X11EventSource::GetInstance(); }));
 #endif
 }
 
@@ -1575,6 +1592,7 @@
       context_->url_loader_factory());
   zombie_host_detector_ = std::make_unique<ZombieHostDetector>(base::BindOnce(
       &HostProcess::OnZombieStateDetected, base::Unretained(this)));
+
   auto ftl_signal_strategy = std::make_unique<FtlSignalStrategy>(
       std::make_unique<OAuthTokenGetterProxy>(
           oauth_token_getter_->GetWeakPtr()),
@@ -1884,7 +1902,7 @@
   // Need to prime the host OS version value for linux to prevent IO on the
   // network thread. base::GetLinuxDistro() caches the result.
   base::GetLinuxDistro();
-#endif
+#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
 
   base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Me2Me");
 
@@ -1892,7 +1910,7 @@
   base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
   base::RunLoop run_loop;
   std::unique_ptr<ChromotingHostContext> context =
-      ChromotingHostContext::Create(new AutoThreadTaskRunner(
+      ChromotingHostContext::Create(base::MakeRefCounted<AutoThreadTaskRunner>(
           main_task_executor.task_runner(), run_loop.QuitClosure()));
   if (!context)
     return kInitializationFailed;
@@ -1906,10 +1924,9 @@
   // (x11::Connection::Get()) can dispatch X events.
   auto event_source =
       std::make_unique<ui::X11EventSource>(x11::Connection::Get());
-  auto input_task_runner = context->input_task_runner();
-  input_task_runner->PostTask(FROM_HERE, base::BindOnce([]() {
-                                new ui::X11EventSource(x11::Connection::Get());
-                              }));
+  context->input_task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce([]() { new ui::X11EventSource(x11::Connection::Get()); }));
 #endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
 
   // Create & start the HostProcess using these threads.
@@ -1922,12 +1939,6 @@
   // Run the main (also UI) task executor until the host no longer needs it.
   run_loop.Run();
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
-  input_task_runner->PostTask(FROM_HERE, base::BindOnce([]() {
-                                delete ui::X11EventSource::GetInstance();
-                              }));
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
-
   // Block until tasks blocking shutdown have completed their execution.
   base::ThreadPoolInstance::Get()->Shutdown();
 
diff --git a/remoting/remoting_locales.gni b/remoting/remoting_locales.gni
index 3dc3ed8..2defd4b 100644
--- a/remoting/remoting_locales.gni
+++ b/remoting/remoting_locales.gni
@@ -75,8 +75,10 @@
 if (is_chromeos_ash || is_chromeos_lacros) {
   # Support Icelandic in ChromeOS
   remoting_locales_without_pseudolocales += [
+    "af",
     "en-US",
     "is",
+    "zu",
   ]
 }
 
diff --git a/remoting/resources/remoting_strings.grd b/remoting/resources/remoting_strings.grd
index 52e0b5f7..d717637 100644
--- a/remoting/resources/remoting_strings.grd
+++ b/remoting/resources/remoting_strings.grd
@@ -7,6 +7,9 @@
       <emit emit_type="prepend"/>
     </output>
 
+    <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="remoting/resources/af.pak" lang="af" type="data_package"/>
+    </if>
     <output filename="remoting/resources/am.pak" lang="am" type="data_package"/>
     <output filename="remoting/resources/ar.pak" lang="ar" type="data_package"/>
     <output filename="remoting/resources/bg.pak" lang="bg" type="data_package"/>
@@ -79,11 +82,17 @@
     <output filename="remoting/resources/vi.pak" lang="vi" type="data_package"/>
     <output filename="remoting/resources/zh-CN.pak" lang="zh-CN" type="data_package"/>
     <output filename="remoting/resources/zh-TW.pak" lang="zh-TW" type="data_package"/>
+    <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="remoting/resources/zu.pak" lang="zu" type="data_package"/>
+    </if>
 
     <!-- Pseudolocales -->
     <output filename="remoting/resources/ar-XB.pak" lang="ar-XB" type="data_package"/>
     <output filename="remoting/resources/en-XA.pak" lang="en-XA" type="data_package"/>
 
+    <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="remoting/resources/_locales/af/messages.json" lang="af" type="chrome_messages_json"/>
+    </if>
     <output filename="remoting/resources/_locales/am/messages.json" lang="am" type="chrome_messages_json"/>
     <output filename="remoting/resources/_locales/ar/messages.json" lang="ar" type="chrome_messages_json"/>
     <output filename="remoting/resources/_locales/bg/messages.json" lang="bg" type="chrome_messages_json"/>
@@ -157,6 +166,9 @@
     <output filename="remoting/resources/_locales/vi/messages.json" lang="vi" type="chrome_messages_json"/>
     <output filename="remoting/resources/_locales/zh_CN/messages.json" lang="zh-CN" type="chrome_messages_json"/>
     <output filename="remoting/resources/_locales/zh_TW/messages.json" lang="zh-TW" type="chrome_messages_json"/>
+    <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="remoting/resources/_locales/zu/messages.json" lang="zu" type="chrome_messages_json"/>
+    </if>
 
     <!-- Pseudolocales -->
     <output filename="remoting/resources/_locales/ar_XB/messages.json" lang="ar-XB" type="chrome_messages_json"/>
diff --git a/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc b/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc
index 60d6323..e18aec23 100644
--- a/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc
+++ b/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc
@@ -18,8 +18,6 @@
 #include <sys/utsname.h>
 #include <unistd.h>
 
-#include "base/memory/raw_ptr.h"
-
 #if defined(ANDROID)
 // Work-around for buggy headers in Android's NDK
 #define __user
@@ -28,6 +26,7 @@
 
 #include "base/bind.h"
 #include "base/check.h"
+#include "base/memory/raw_ptr.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/system/sys_info.h"
@@ -2087,7 +2086,7 @@
 }
 
 // Android does not expose pread64 nor pwrite64.
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 
 bool FullPwrite64(int fd, const char* buffer, size_t count, off64_t offset) {
   while (count > 0) {
@@ -2168,7 +2167,7 @@
   BPF_ASSERT(pread_64_was_forwarded);
 }
 
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 void* TsyncApplyToTwoThreadsFunc(void* cond_ptr) {
   base::WaitableEvent* event = static_cast<base::WaitableEvent*>(cond_ptr);
diff --git a/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc b/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
index 0d6b6b3..376e855 100644
--- a/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
+++ b/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
@@ -310,7 +310,8 @@
 
 // Only use syscall(...) on x64 to avoid having to reimplement a libc-like
 // layer that uses different syscalls on different architectures.
-#if (defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)) && \
+#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
+     BUILDFLAG(IS_ANDROID)) &&                        \
     defined(__x86_64__)
 #define DIRECT_SYSCALLER_ENABLED
 #endif
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
index f9eea2783..74ce4c75 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
@@ -150,7 +150,7 @@
     return Allow();
   }
 
-#if defined(__NR_rseq) && !defined(OS_ANDROID)
+#if defined(__NR_rseq) && !BUILDFLAG(IS_ANDROID)
   // See https://crbug.com/1104160. Rseq can only be disabled right before an
   // execve, because glibc registers it with the kernel and so far it's unclear
   // whether shared libraries (which, during initialization, may observe that
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
index 8021eea..ea405e4 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
@@ -186,7 +186,7 @@
 }
 
 // Rseq should be enabled if it exists (i.e. shouldn't receive EPERM).
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 BPF_TEST_C(BaselinePolicy, RseqEnabled, BaselinePolicy) {
   errno = 0;
   int res = syscall(__NR_rseq, 0, 0, 0, 0);
@@ -196,7 +196,7 @@
   // EINVAL, or ENOSYS if the kernel is too old to recognize the system call.
   BPF_ASSERT(EINVAL == errno || ENOSYS == errno);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 BPF_DEATH_TEST_C(BaselinePolicy,
                  DisallowedCloneFlagCrashes,
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
index 36221d2..33112b7c 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -35,8 +35,8 @@
 #include "sandbox/linux/system_headers/linux_syscalls.h"
 #include "sandbox/linux/system_headers/linux_time.h"
 
-#if (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \
-    !defined(__arm__) && !defined(__aarch64__) &&           \
+#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \
+    !defined(__arm__) && !defined(__aarch64__) &&             \
     !defined(PTRACE_GET_THREAD_AREA)
 // Also include asm/ptrace-abi.h since ptrace.h in older libc (for instance
 // the one in Ubuntu 16.04 LTS) is missing PTRACE_GET_THREAD_AREA.
@@ -45,13 +45,13 @@
 #include <asm/ptrace-abi.h>
 #endif
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 
 #if !defined(F_DUPFD_CLOEXEC)
 #define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6)
 #endif
 
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
 #if defined(__arm__) && !defined(MAP_STACK)
 #define MAP_STACK 0x20000  // Daisy build environment has old headers.
@@ -87,7 +87,7 @@
 }
 
 inline bool IsAndroid() {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   return true;
 #else
   return false;
@@ -172,7 +172,7 @@
   const Arg<int> option(0);
   return Switch(option)
       .CASES((PR_GET_NAME, PR_SET_NAME, PR_GET_DUMPABLE, PR_SET_DUMPABLE
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
               , PR_SET_VMA, PR_SET_PTRACER, PR_SET_TIMERSLACK
               , PR_GET_NO_NEW_PRIVS, PR_PAC_RESET_KEYS
 
@@ -202,7 +202,7 @@
               , PR_SET_TIMERSLACK_PID_1
               , PR_SET_TIMERSLACK_PID_2
               , PR_SET_TIMERSLACK_PID_3
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
               ),
              Allow())
       .Default(CrashSIGSYSPrctl());
@@ -397,7 +397,7 @@
               CLOCK_THREAD_CPUTIME_ID),
              Allow())
       .Default(CrashSIGSYS()))
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     // Allow per-pid and per-tid clocks.
     .ElseIf((clockid & CPUCLOCK_CLOCK_MASK) != CLOCKFD, Allow())
 #endif
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
index 4adc80b..67da5d1 100644
--- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
@@ -95,7 +95,7 @@
   CheckClock(CLOCK_REALTIME);
   CheckClock(CLOCK_REALTIME_COARSE);
   CheckClock(CLOCK_THREAD_CPUTIME_ID);
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   clockid_t clock_id;
   pthread_getcpuclockid(pthread_self(), &clock_id);
   CheckClock(clock_id);
@@ -140,7 +140,7 @@
   syscall(SYS_clock_nanosleep, (~0) | CLOCKFD, 0, &ts, &out_ts);
 }
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 BPF_DEATH_TEST_C(ParameterRestrictions,
                  clock_gettime_crash_cpu_clock,
                  DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
@@ -154,7 +154,7 @@
   struct timespec ts;
   clock_gettime(kInitCPUClockID, &ts);
 }
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 class RestrictSchedPolicy : public bpf_dsl::Policy {
  public:
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
index 7069cd7..30911a5 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -58,7 +58,7 @@
 // flags that are unlikely to ever be used by the kernel. A normal kernel would
 // return -EINVAL, but a buggy LG kernel would return 1.
 bool KernelHasLGBug() {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // sys_set_media will see this as NULL, which should be a safe (non-crashing)
   // way to invoke it. A genuine seccomp syscall will see it as
   // SECCOMP_SET_MODE_STRICT.
@@ -73,7 +73,7 @@
   if (rv != -1) {
     return true;
   }
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
   return false;
 }
diff --git a/sandbox/linux/services/credentials.h b/sandbox/linux/services/credentials.h
index 189d1613..213d8d1 100644
--- a/sandbox/linux/services/credentials.h
+++ b/sandbox/linux/services/credentials.h
@@ -7,9 +7,9 @@
 
 #include "build/build_config.h"
 // Link errors are tedious to track, raise a compile-time error instead.
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #error "Android is not supported."
-#endif  // defined(OS_ANDROID).
+#endif  // BUILDFLAG(IS_ANDROID).
 
 #include <string>
 #include <vector>
diff --git a/sandbox/linux/services/resource_limits_unittests.cc b/sandbox/linux/services/resource_limits_unittests.cc
index b79404d..bac5221 100644
--- a/sandbox/linux/services/resource_limits_unittests.cc
+++ b/sandbox/linux/services/resource_limits_unittests.cc
@@ -10,6 +10,7 @@
 #include <unistd.h>
 
 #include "base/check_op.h"
+#include "build/build_config.h"
 #include "sandbox/linux/tests/test_utils.h"
 #include "sandbox/linux/tests/unit_tests.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -19,7 +20,7 @@
 namespace {
 
 // Fails on Android: crbug.com/459158
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 #define MAYBE_NoFork DISABLE_ON_ASAN(NoFork)
 #else
 #define MAYBE_NoFork DISABLED_NoFork
diff --git a/sandbox/linux/syscall_broker/broker_client.cc b/sandbox/linux/syscall_broker/broker_client.cc
index 0147524..17a8513 100644
--- a/sandbox/linux/syscall_broker/broker_client.cc
+++ b/sandbox/linux/syscall_broker/broker_client.cc
@@ -22,7 +22,7 @@
 #include "sandbox/linux/syscall_broker/broker_permission_list.h"
 #include "sandbox/linux/syscall_broker/broker_simple_message.h"
 
-#if defined(OS_ANDROID) && !defined(MSG_CMSG_CLOEXEC)
+#if BUILDFLAG(IS_ANDROID) && !defined(MSG_CMSG_CLOEXEC)
 #define MSG_CMSG_CLOEXEC 0x40000000
 #endif
 
diff --git a/sandbox/linux/syscall_broker/broker_process.cc b/sandbox/linux/syscall_broker/broker_process.cc
index 51e1e9d5..7dd4688 100644
--- a/sandbox/linux/syscall_broker/broker_process.cc
+++ b/sandbox/linux/syscall_broker/broker_process.cc
@@ -117,44 +117,44 @@
   // and are default disabled in Android. So, we should refuse to broker them
   // to be consistent with the platform's restrictions.
   switch (sysno) {
-#if !defined(__aarch64__) && !defined(OS_ANDROID)
+#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID)
     case __NR_access:
 #endif
     case __NR_faccessat:
     case __NR_faccessat2:
       return !fast_check || policy_->allowed_command_set.test(COMMAND_ACCESS);
 
-#if !defined(__aarch64__) && !defined(OS_ANDROID)
+#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID)
     case __NR_mkdir:
 #endif
     case __NR_mkdirat:
       return !fast_check || policy_->allowed_command_set.test(COMMAND_MKDIR);
 
-#if !defined(__aarch64__) && !defined(OS_ANDROID)
+#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID)
     case __NR_open:
 #endif
     case __NR_openat:
       return !fast_check || policy_->allowed_command_set.test(COMMAND_OPEN);
 
-#if !defined(__aarch64__) && !defined(OS_ANDROID)
+#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID)
     case __NR_readlink:
 #endif
     case __NR_readlinkat:
       return !fast_check || policy_->allowed_command_set.test(COMMAND_READLINK);
 
-#if !defined(__aarch64__) && !defined(OS_ANDROID)
+#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID)
     case __NR_rename:
 #endif
     case __NR_renameat:
     case __NR_renameat2:
       return !fast_check || policy_->allowed_command_set.test(COMMAND_RENAME);
 
-#if !defined(__aarch64__) && !defined(OS_ANDROID)
+#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID)
     case __NR_rmdir:
       return !fast_check || policy_->allowed_command_set.test(COMMAND_RMDIR);
 #endif
 
-#if !defined(__aarch64__) && !defined(OS_ANDROID)
+#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID)
     case __NR_stat:
     case __NR_lstat:
 #endif
@@ -179,7 +179,7 @@
       return !fast_check || policy_->allowed_command_set.test(COMMAND_STAT);
 #endif
 
-#if !defined(__aarch64__) && !defined(OS_ANDROID)
+#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID)
     case __NR_unlink:
       return !fast_check || policy_->allowed_command_set.test(COMMAND_UNLINK);
 #endif
diff --git a/sandbox/linux/syscall_broker/broker_process_unittest.cc b/sandbox/linux/syscall_broker/broker_process_unittest.cc
index 310d705..ad6e502 100644
--- a/sandbox/linux/syscall_broker/broker_process_unittest.cc
+++ b/sandbox/linux/syscall_broker/broker_process_unittest.cc
@@ -586,7 +586,7 @@
   // expected.
 }
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 // Flaky on Linux NG bots: https://crbug.com/595199.
 #define MAYBE_RecvMsgDescriptorLeak DISABLED_RecvMsgDescriptorLeak
 #else
@@ -1752,54 +1752,53 @@
 TEST(BrokerProcess, IsSyscallAllowed) {
   const base::flat_map<BrokerCommand, base::flat_set<int>> kSysnosForCommand = {
       {COMMAND_ACCESS,
-       {__NR_faccessat,
-        __NR_faccessat2,
-#if defined(__NR_access) && !defined(OS_ANDROID)
+       {__NR_faccessat, __NR_faccessat2,
+#if defined(__NR_access) && !BUILDFLAG(IS_ANDROID)
         __NR_access
 #endif
        }},
       {COMMAND_MKDIR,
        {__NR_mkdirat,
-#if defined(__NR_mkdir) && !defined(OS_ANDROID)
+#if defined(__NR_mkdir) && !BUILDFLAG(IS_ANDROID)
         __NR_mkdir
 #endif
        }},
       {COMMAND_OPEN,
        {__NR_openat,
-#if defined(__NR_open) && !defined(OS_ANDROID)
+#if defined(__NR_open) && !BUILDFLAG(IS_ANDROID)
         __NR_open
 #endif
        }},
       {COMMAND_READLINK,
        {__NR_readlinkat,
-#if defined(__NR_readlink) && !defined(OS_ANDROID)
+#if defined(__NR_readlink) && !BUILDFLAG(IS_ANDROID)
         __NR_readlink
 #endif
        }},
       {COMMAND_RENAME,
        {__NR_renameat,
-#if defined(__NR_rename) && !defined(OS_ANDROID)
+#if defined(__NR_rename) && !BUILDFLAG(IS_ANDROID)
         __NR_rename
 #endif
        }},
       {COMMAND_UNLINK,
        {__NR_unlinkat,
-#if defined(__NR_unlink) && !defined(OS_ANDROID)
+#if defined(__NR_unlink) && !BUILDFLAG(IS_ANDROID)
         __NR_unlink
 #endif
        }},
       {COMMAND_RMDIR,
        {__NR_unlinkat,
-#if defined(__NR_rmdir) && !defined(OS_ANDROID)
+#if defined(__NR_rmdir) && !BUILDFLAG(IS_ANDROID)
         __NR_rmdir
 #endif
        }},
       {COMMAND_STAT,
        {
-#if defined(__NR_stat) && !defined(OS_ANDROID)
+#if defined(__NR_stat) && !BUILDFLAG(IS_ANDROID)
            __NR_stat,
 #endif
-#if defined(__NR_lstat) && !defined(OS_ANDROID)
+#if defined(__NR_lstat) && !BUILDFLAG(IS_ANDROID)
            __NR_lstat,
 #endif
 #if defined(__NR_fstatat)
diff --git a/sandbox/linux/system_headers/linux_prctl.h b/sandbox/linux/system_headers/linux_prctl.h
index 50f639b..b2b4bae 100644
--- a/sandbox/linux/system_headers/linux_prctl.h
+++ b/sandbox/linux/system_headers/linux_prctl.h
@@ -15,14 +15,14 @@
 #define PR_SET_TIMERSLACK 29
 #endif
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 
 // https://android.googlesource.com/platform/bionic/+/lollipop-release/libc/private/bionic_prctl.h
 #if !defined(PR_SET_VMA)
 #define PR_SET_VMA 0x53564d41
 #endif
 
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
 #if !defined(PR_SET_PTRACER)
 #define PR_SET_PTRACER 0x59616d61
diff --git a/sandbox/linux/tests/scoped_temporary_file.cc b/sandbox/linux/tests/scoped_temporary_file.cc
index 1b8d028..380e475 100644
--- a/sandbox/linux/tests/scoped_temporary_file.cc
+++ b/sandbox/linux/tests/scoped_temporary_file.cc
@@ -15,11 +15,11 @@
 namespace sandbox {
 
 ScopedTemporaryFile::ScopedTemporaryFile() : fd_(-1) {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   static const char file_template[] = "/data/local/tmp/ScopedTempFileXXXXXX";
 #else
   static const char file_template[] = "/tmp/ScopedTempFileXXXXXX";
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
   static_assert(sizeof(full_file_name_) >= sizeof(file_template),
                 "full_file_name is not large enough");
   memcpy(full_file_name_, file_template, sizeof(file_template));
diff --git a/sandbox/linux/tests/unit_tests.cc b/sandbox/linux/tests/unit_tests.cc
index 35ff4ea3..2f6ac04b 100644
--- a/sandbox/linux/tests/unit_tests.cc
+++ b/sandbox/linux/tests/unit_tests.cc
@@ -62,7 +62,7 @@
 namespace sandbox {
 
 bool IsAndroid() {
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   return true;
 #else
   return false;
@@ -165,7 +165,7 @@
     struct rlimit no_core = {0};
     setrlimit(RLIMIT_CORE, &no_core);
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
     // On Android Oreo and higher, the system applies a seccomp filter to all
     // processes. It has its own SIGSYS handler that is un-hooked here in the
     // test child process, so that the Chromium handler can be used. This
@@ -275,7 +275,7 @@
 
 // In official builds CHECK messages are dropped, look for SIGABRT or SIGTRAP.
 // See https://crbug.com/437312 and https://crbug.com/612507.
-#if defined(OFFICIAL_BUILD) && defined(NDEBUG) && !defined(OS_ANDROID)
+#if defined(OFFICIAL_BUILD) && defined(NDEBUG) && !BUILDFLAG(IS_ANDROID)
   if (subprocess_exited_without_matching_message) {
     static const char kSigTrapMessage[] = "Received signal 5";
     static const char kSigAbortMessage[] = "Received signal 6";
@@ -293,7 +293,7 @@
   std::string details(TestFailedMessage(msg));
   const char* expected_msg = static_cast<const char*>(aux);
 
-#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS)
   // This hack is required for ChromeOS since its signal handler does not
   // return indicating the handled signal, but with a simple _exit(1) only.
   const bool subprocess_got_sigsegv =
diff --git a/sandbox/linux/tests/unit_tests.h b/sandbox/linux/tests/unit_tests.h
index 397434e..efaf564f 100644
--- a/sandbox/linux/tests/unit_tests.h
+++ b/sandbox/linux/tests/unit_tests.h
@@ -52,7 +52,7 @@
 #define DISABLE_ON_SANITIZERS(test_name) test_name
 #endif
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #define DISABLE_ON_ANDROID(test_name) DISABLED_##test_name
 #else
 #define DISABLE_ON_ANDROID(test_name) test_name
diff --git a/sandbox/policy/features.cc b/sandbox/policy/features.cc
index 1a19cdd..f89d9ca7 100644
--- a/sandbox/policy/features.cc
+++ b/sandbox/policy/features.cc
@@ -12,14 +12,14 @@
 namespace policy {
 namespace features {
 
-#if !defined(OS_MAC) && !defined(OS_FUCHSIA)
+#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_FUCHSIA)
 // Enables network service sandbox.
 // (Only causes an effect when feature kNetworkService is enabled.)
 const base::Feature kNetworkServiceSandbox{"NetworkServiceSandbox",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
-#endif  // !defined(OS_MAC) && !defined(OS_FUCHSIA)
+#endif  // !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_FUCHSIA)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 // Emergency "off switch" for new Windows KTM security mitigation,
 // sandbox::MITIGATION_KTM_COMPONENT.
 const base::Feature kWinSboxDisableKtmComponent{
@@ -41,12 +41,12 @@
 const base::Feature kRendererAppContainer{"RendererAppContainer",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 // Controls whether the isolated XR service is sandboxed.
 const base::Feature kXRSandbox{"XRSandbox", base::FEATURE_ENABLED_BY_DEFAULT};
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // Controls whether the Spectre variant 2 mitigation is enabled. We use a USE
@@ -62,7 +62,7 @@
     "ForceSpectreVariant2Mitigation", base::FEATURE_DISABLED_BY_DEFAULT};
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 bool IsWinNetworkServiceSandboxSupported() {
   // Since some APIs used for LPAC are unsupported below Windows 10 RS2 (1703
   // build 15063) so place a check here in a central place.
@@ -70,19 +70,19 @@
     return false;
   return true;
 }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 bool IsNetworkSandboxEnabled() {
-#if defined(OS_MAC) || defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_FUCHSIA)
   return true;
 #else
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   if (!IsWinNetworkServiceSandboxSupported())
     return false;
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
   // Check feature status.
   return base::FeatureList::IsEnabled(kNetworkServiceSandbox);
-#endif  // defined(OS_MAC) || defined(OS_FUCHSIA)
+#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_FUCHSIA)
 }
 
 }  // namespace features
diff --git a/sandbox/policy/features.h b/sandbox/policy/features.h
index 34f4bb5..a9c557e 100644
--- a/sandbox/policy/features.h
+++ b/sandbox/policy/features.h
@@ -17,21 +17,21 @@
 namespace policy {
 namespace features {
 
-#if !defined(OS_MAC) && !defined(OS_FUCHSIA)
+#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_FUCHSIA)
 SANDBOX_POLICY_EXPORT extern const base::Feature kNetworkServiceSandbox;
 #endif
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 SANDBOX_POLICY_EXPORT extern const base::Feature kWinSboxDisableKtmComponent;
 SANDBOX_POLICY_EXPORT extern const base::Feature kWinSboxDisableExtensionPoints;
 SANDBOX_POLICY_EXPORT extern const base::Feature kGpuAppContainer;
 SANDBOX_POLICY_EXPORT extern const base::Feature kGpuLPAC;
 SANDBOX_POLICY_EXPORT extern const base::Feature kRendererAppContainer;
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
-#if !defined(OS_ANDROID)
+#if !BUILDFLAG(IS_ANDROID)
 SANDBOX_POLICY_EXPORT extern const base::Feature kXRSandbox;
-#endif  // !defined(OS_ANDROID)
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 SANDBOX_POLICY_EXPORT extern const base::Feature kSpectreVariant2Mitigation;
@@ -39,7 +39,7 @@
     kForceSpectreVariant2Mitigation;
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 // Returns whether the Network Service Sandbox is supported by the current
 // Windows platform. Call this function rather than checking the
 // kNetworkServiceSandbox feature directly.
diff --git a/sandbox/policy/linux/bpf_gpu_policy_linux.cc b/sandbox/policy/linux/bpf_gpu_policy_linux.cc
index 48b16ab..f8df9dc 100644
--- a/sandbox/policy/linux/bpf_gpu_policy_linux.cc
+++ b/sandbox/policy/linux/bpf_gpu_policy_linux.cc
@@ -102,7 +102,7 @@
       break;
   }
 
-#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
   if (SyscallSets::IsSystemVSharedMemory(sysno))
     return Allow();
 #endif
diff --git a/sandbox/policy/sandbox.cc b/sandbox/policy/sandbox.cc
index 3b31605..d76a616 100644
--- a/sandbox/policy/sandbox.cc
+++ b/sandbox/policy/sandbox.cc
@@ -9,37 +9,37 @@
 #include "sandbox/policy/mojom/sandbox.mojom.h"
 #include "sandbox/policy/switches.h"
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
 #include "base/android/jni_android.h"
-#endif  // defined(OS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include "sandbox/policy/linux/sandbox_linux.h"
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 #include "sandbox/mac/seatbelt.h"
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "base/process/process_info.h"
 #include "sandbox/policy/win/sandbox_win.h"
 #include "sandbox/win/src/sandbox.h"
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 namespace sandbox {
 namespace policy {
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 bool Sandbox::Initialize(sandbox::mojom::Sandbox sandbox_type,
                          SandboxLinux::PreSandboxHook hook,
                          const SandboxLinux::Options& options) {
   return SandboxLinux::GetInstance()->InitializeSandbox(
       sandbox_type, std::move(hook), options);
 }
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 bool Sandbox::Initialize(sandbox::mojom::Sandbox sandbox_type,
                          SandboxInterfaceInfo* sandbox_info) {
   BrokerServices* broker_services = sandbox_info->broker_services;
@@ -65,7 +65,7 @@
   return IsUnsandboxedSandboxType(sandbox_type) ||
          SandboxWin::InitTargetServices(sandbox_info->target_services);
 }
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
 // static
 bool Sandbox::IsProcessSandboxed() {
@@ -80,7 +80,7 @@
     return true;
   }
 
-#if defined(OS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
   // Note that this does not check the status of the Seccomp sandbox. Call
   // https://developer.android.com/reference/android/os/Process#isIsolated().
   JNIEnv* env = base::android::AttachCurrentThread();
@@ -90,12 +90,12 @@
       base::android::MethodID::Get<base::android::MethodID::TYPE_STATIC>(
           env, process_class.obj(), "isIsolated", "()Z");
   return env->CallStaticBooleanMethod(process_class.obj(), is_isolated);
-#elif defined(OS_FUCHSIA)
+#elif BUILDFLAG(IS_FUCHSIA)
   // TODO(https://crbug.com/1071420): Figure out what to do here. Process
   // launching controls the sandbox and there are no ambient capabilities, so
   // basically everything but the browser is considered sandboxed.
   return !is_browser;
-#elif defined(OS_LINUX) || defined(OS_CHROMEOS)
+#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   int status = SandboxLinux::GetInstance()->GetStatus();
   constexpr int kLayer1Flags = SandboxLinux::Status::kSUID |
                                SandboxLinux::Status::kPIDNS |
@@ -103,9 +103,9 @@
   constexpr int kLayer2Flags =
       SandboxLinux::Status::kSeccompBPF | SandboxLinux::Status::kSeccompTSYNC;
   return (status & kLayer1Flags) != 0 && (status & kLayer2Flags) != 0;
-#elif defined(OS_MAC)
+#elif BUILDFLAG(IS_MAC)
   return Seatbelt::IsSandboxed();
-#elif defined(OS_WIN)
+#elif BUILDFLAG(IS_WIN)
   return base::GetCurrentProcessIntegrityLevel() < base::MEDIUM_INTEGRITY;
 #else
   return false;
diff --git a/sandbox/policy/sandbox.h b/sandbox/policy/sandbox.h
index 4212646..b003a1f 100644
--- a/sandbox/policy/sandbox.h
+++ b/sandbox/policy/sandbox.h
@@ -8,7 +8,7 @@
 #include "build/build_config.h"
 #include "sandbox/policy/export.h"
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include "sandbox/policy/linux/sandbox_linux.h"
 #endif
 
@@ -32,16 +32,16 @@
 
 class SANDBOX_POLICY_EXPORT Sandbox {
  public:
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   static bool Initialize(sandbox::mojom::Sandbox sandbox_type,
                          SandboxLinux::PreSandboxHook hook,
                          const SandboxLinux::Options& options);
-#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   static bool Initialize(sandbox::mojom::Sandbox sandbox_type,
                          SandboxInterfaceInfo* sandbox_info);
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 
   // Returns true if the current process is running with a sandbox, and false
   // if the process is not sandboxed. This should be used to assert that code is
diff --git a/sandbox/policy/sandbox_delegate.h b/sandbox/policy/sandbox_delegate.h
index 991eb065..46913ae0 100644
--- a/sandbox/policy/sandbox_delegate.h
+++ b/sandbox/policy/sandbox_delegate.h
@@ -27,7 +27,7 @@
   // Sandbox::kNoSandbox to run without a sandbox policy.
   virtual sandbox::mojom::Sandbox GetSandboxType() = 0;
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   // Whether to disable the default policy specified in
   // AddPolicyForSandboxedProcess.
   virtual bool DisableDefaultPolicy() = 0;
@@ -48,7 +48,7 @@
   // Whether this process will be compatible with Control-flow Enforcement
   // Technology (CET) / Hardware-enforced Stack Protection.
   virtual bool CetCompatible() = 0;
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 };
 
 }  // namespace policy
diff --git a/sandbox/policy/sandbox_type.cc b/sandbox/policy/sandbox_type.cc
index 3c517f9..5bdd5d9b 100644
--- a/sandbox/policy/sandbox_type.cc
+++ b/sandbox/policy/sandbox_type.cc
@@ -26,7 +26,7 @@
   switch (sandbox_type) {
     case Sandbox::kNoSandbox:
       return true;
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     case Sandbox::kNoSandboxAndElevatedPrivileges:
       return true;
     case Sandbox::kXrCompositing:
@@ -39,7 +39,7 @@
 #endif
     case Sandbox::kAudio:
       return false;
-#if defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_FUCHSIA)
     case Sandbox::kVideoCapture:
       return false;
 #endif
@@ -58,7 +58,7 @@
     case Sandbox::kPrintBackend:
 #endif
     case Sandbox::kPrintCompositor:
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
     case Sandbox::kMirroring:
     case Sandbox::kNaClLoader:
 #endif
@@ -70,7 +70,7 @@
     case Sandbox::kLibassistant:
 #endif  // BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
 #endif  // // BUILDFLAG(IS_CHROMEOS_ASH)
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     case Sandbox::kZygoteIntermediateSandbox:
 #endif
     case Sandbox::kSpeechRecognition:
@@ -122,17 +122,17 @@
 #endif
     case Sandbox::kPrintCompositor:
     case Sandbox::kAudio:
-#if defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_FUCHSIA)
     case Sandbox::kVideoCapture:
 #endif
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     case Sandbox::kNoSandboxAndElevatedPrivileges:
     case Sandbox::kXrCompositing:
     case Sandbox::kPdfConversion:
     case Sandbox::kIconReader:
     case Sandbox::kMediaFoundationCdm:
     case Sandbox::kWindowsSystemProxyResolver:
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     case Sandbox::kHardwareVideoDecoding:
     case Sandbox::kIme:
@@ -141,9 +141,9 @@
     case Sandbox::kLibassistant:
 #endif  // BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
     case Sandbox::kMirroring:
-#endif  // defined(OS_MAC)
+#endif  // BUILDFLAG(IS_MAC)
     case Sandbox::kSpeechRecognition:
       DCHECK(command_line->GetSwitchValueASCII(switches::kProcessType) ==
              switches::kUtilityProcess);
@@ -152,11 +152,11 @@
           switches::kServiceSandboxType,
           StringFromUtilitySandboxType(sandbox_type));
       break;
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
     case Sandbox::kNaClLoader:
       break;
-#endif  // defined(OS_MAC)
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     case Sandbox::kZygoteIntermediateSandbox:
       break;
 #endif
@@ -193,7 +193,7 @@
 
   // NaCl tests on all platforms use the loader process.
   if (process_type == switches::kNaClLoaderProcess) {
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
     return Sandbox::kNaClLoader;
 #else
     return Sandbox::kUtility;
@@ -203,13 +203,13 @@
   if (process_type == switches::kNaClBrokerProcess)
     return Sandbox::kNoSandbox;
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   // Intermediate process gains a sandbox later.
   if (process_type == switches::kZygoteProcessType)
     return Sandbox::kZygoteIntermediateSandbox;
 #endif
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   if (process_type == switches::kRelauncherProcessType)
     return Sandbox::kNoSandbox;
 #endif
@@ -228,10 +228,10 @@
   switch (sandbox_type) {
     case Sandbox::kNoSandbox:
       return switches::kNoneSandbox;
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     case Sandbox::kNoSandboxAndElevatedPrivileges:
       return switches::kNoneSandboxAndElevatedPrivileges;
-#endif  // defined(OS_WIN)
+#endif  // BUILDFLAG(IS_WIN)
     case Sandbox::kNetwork:
       return switches::kNetworkSandbox;
 #if BUILDFLAG(ENABLE_PLUGINS)
@@ -250,7 +250,7 @@
       return switches::kUtilitySandbox;
     case Sandbox::kAudio:
       return switches::kAudioSandbox;
-#if defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_FUCHSIA)
     case Sandbox::kVideoCapture:
       return switches::kVideoCaptureSandbox;
 #endif
@@ -260,7 +260,7 @@
       return switches::kServiceSandboxWithJit;
     case Sandbox::kSpeechRecognition:
       return switches::kSpeechRecognitionSandbox;
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     case Sandbox::kXrCompositing:
       return switches::kXrCompositingSandbox;
     case Sandbox::kPdfConversion:
@@ -271,8 +271,8 @@
       return switches::kMediaFoundationCdmSandbox;
     case Sandbox::kWindowsSystemProxyResolver:
       return switches::kWindowsSystemProxyResolverSandbox;
-#endif  // defined(OS_WIN)
-#if defined(OS_MAC)
+#endif  // BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_MAC)
     case Sandbox::kMirroring:
       return switches::kMirroringSandbox;
 #endif
@@ -291,10 +291,10 @@
       // The following are not utility processes so should not occur.
     case Sandbox::kRenderer:
     case Sandbox::kGpu:
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
     case Sandbox::kNaClLoader:
-#endif  // defined(OS_MAC)
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#endif  // BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
     case Sandbox::kZygoteIntermediateSandbox:
 #endif
       NOTREACHED();
@@ -318,7 +318,7 @@
   if (sandbox_string == switches::kNoneSandbox)
     return Sandbox::kNoSandbox;
   if (sandbox_string == switches::kNoneSandboxAndElevatedPrivileges) {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
     return Sandbox::kNoSandboxAndElevatedPrivileges;
 #else
     return Sandbox::kNoSandbox;
@@ -338,7 +338,7 @@
 #endif
   if (sandbox_string == switches::kPrintCompositorSandbox)
     return Sandbox::kPrintCompositor;
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   if (sandbox_string == switches::kXrCompositingSandbox)
     return Sandbox::kXrCompositing;
   if (sandbox_string == switches::kPdfConversionSandbox)
@@ -350,7 +350,7 @@
   if (sandbox_string == switches::kWindowsSystemProxyResolverSandbox)
     return Sandbox::kWindowsSystemProxyResolver;
 #endif
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
   if (sandbox_string == switches::kMirroringSandbox)
     return Sandbox::kMirroring;
 #endif
@@ -358,7 +358,7 @@
     return Sandbox::kAudio;
   if (sandbox_string == switches::kSpeechRecognitionSandbox)
     return Sandbox::kSpeechRecognition;
-#if defined(OS_FUCHSIA)
+#if BUILDFLAG(IS_FUCHSIA)
   if (sandbox_string == switches::kVideoCaptureSandbox)
     return Sandbox::kVideoCapture;
 #endif
diff --git a/sandbox/policy/sandbox_type_unittest.cc b/sandbox/policy/sandbox_type_unittest.cc
index 8f8a5a3..187043a 100644
--- a/sandbox/policy/sandbox_type_unittest.cc
+++ b/sandbox/policy/sandbox_type_unittest.cc
@@ -86,7 +86,7 @@
   EXPECT_EQ(Sandbox::kSpeechRecognition,
             SandboxTypeFromCommandLine(command_line9));
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   base::CommandLine command_line10(command_line);
   SetCommandLineFlagsForSandboxType(&command_line10, Sandbox::kXrCompositing);
   EXPECT_EQ(Sandbox::kXrCompositing,
@@ -188,7 +188,7 @@
   // specific default to no sandbox on non Windows platforms.
   Sandbox elevated_type =
       UtilitySandboxTypeFromString(switches::kNoneSandboxAndElevatedPrivileges);
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
   EXPECT_EQ(Sandbox::kNoSandboxAndElevatedPrivileges, elevated_type);
 #else
   EXPECT_EQ(Sandbox::kNoSandbox, elevated_type);
diff --git a/sandbox/policy/switches.cc b/sandbox/policy/switches.cc
index 2064e21a..bbb8e9b9 100644
--- a/sandbox/policy/switches.cc
+++ b/sandbox/policy/switches.cc
@@ -8,7 +8,7 @@
 #include "build/chromeos_buildflags.h"
 #include "printing/buildflags/buildflags.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "base/command_line.h"
 #include "base/win/windows_version.h"
 #endif
@@ -39,7 +39,7 @@
 const char kSpeechRecognitionSandbox[] = "speech_recognition";
 const char kVideoCaptureSandbox[] = "video_capture";
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 const char kPdfConversionSandbox[] = "pdf_conversion";
 const char kXrCompositingSandbox[] = "xr_compositing";
 const char kIconReaderSandbox[] = "icon_reader";
@@ -47,7 +47,7 @@
 const char kWindowsSystemProxyResolverSandbox[] = "proxy_resolver_win";
 #endif  // OS_WIN
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 const char kMirroringSandbox[] = "mirroring";
 #endif  // OS_MAC
 
@@ -95,13 +95,13 @@
 // Meant to be used as a browser-level switch for testing purposes only.
 const char kNoSandbox[] = "no-sandbox";
 
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 // Instructs the zygote to launch without a sandbox. Processes forked from this
 // type of zygote will apply their own custom sandboxes later.
 const char kNoZygoteSandbox[] = "no-zygote-sandbox";
 #endif
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 // Allows third party modules to inject by disabling the BINARY_SIGNATURE
 // mitigation policy on Win10+. Also has other effects in ELF.
 const char kAllowThirdPartyModules[] = "allow-third-party-modules";
@@ -114,7 +114,7 @@
 const char kAddXrAppContainerCaps[] = "add-xr-appcontainer-caps";
 #endif
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 // Cause the OS X sandbox write to syslog every time an access to a resource
 // is denied by the sandbox.
 const char kEnableSandboxLogging[] = "enable-sandbox-logging";
diff --git a/sandbox/policy/switches.h b/sandbox/policy/switches.h
index 5b346a5b1..e1a8b81 100644
--- a/sandbox/policy/switches.h
+++ b/sandbox/policy/switches.h
@@ -40,7 +40,7 @@
 SANDBOX_POLICY_EXPORT extern const char kSpeechRecognitionSandbox[];
 SANDBOX_POLICY_EXPORT extern const char kVideoCaptureSandbox[];
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 SANDBOX_POLICY_EXPORT extern const char kPdfConversionSandbox[];
 SANDBOX_POLICY_EXPORT extern const char kXrCompositingSandbox[];
 SANDBOX_POLICY_EXPORT extern const char kIconReaderSandbox[];
@@ -48,7 +48,7 @@
 SANDBOX_POLICY_EXPORT extern const char kWindowsSystemProxyResolverSandbox[];
 #endif  // OS_WIN
 
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 SANDBOX_POLICY_EXPORT extern const char kMirroringSandbox[];
 #endif  // OS_MAC
 
@@ -71,15 +71,15 @@
 SANDBOX_POLICY_EXPORT extern const char kGpuSandboxAllowSysVShm[];
 SANDBOX_POLICY_EXPORT extern const char kGpuSandboxFailuresFatal[];
 SANDBOX_POLICY_EXPORT extern const char kNoSandbox[];
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 SANDBOX_POLICY_EXPORT extern const char kNoZygoteSandbox[];
 #endif
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 SANDBOX_POLICY_EXPORT extern const char kAllowThirdPartyModules[];
 SANDBOX_POLICY_EXPORT extern const char kAddGpuAppContainerCaps[];
 SANDBOX_POLICY_EXPORT extern const char kAddXrAppContainerCaps[];
 #endif
-#if defined(OS_MAC)
+#if BUILDFLAG(IS_MAC)
 SANDBOX_POLICY_EXPORT extern const char kEnableSandboxLogging[];
 SANDBOX_POLICY_EXPORT extern const char kDisableMetalShaderCache[];
 #endif
diff --git a/sandbox/policy/win/sandbox_policy_feature_test.h b/sandbox/policy/win/sandbox_policy_feature_test.h
index ca29735f..4af74f8 100644
--- a/sandbox/policy/win/sandbox_policy_feature_test.h
+++ b/sandbox/policy/win/sandbox_policy_feature_test.h
@@ -12,7 +12,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "base/win/windows_version.h"
 #include "sandbox/policy/win/sandbox_test_utils.h"
 #include "sandbox/policy/win/sandbox_win.h"
diff --git a/sandbox/win/src/app_container.h b/sandbox/win/src/app_container.h
index fc3f17fd..1552a59 100644
--- a/sandbox/win/src/app_container.h
+++ b/sandbox/win/src/app_container.h
@@ -10,6 +10,7 @@
 #include "base/win/sid.h"
 #include "base/win/windows_types.h"
 #include "sandbox/win/src/acl.h"
+#include "sandbox/win/src/security_capabilities.h"
 
 namespace sandbox {
 
@@ -70,6 +71,16 @@
   virtual bool GetEnableLowPrivilegeAppContainer() = 0;
 
   virtual AppContainerType GetAppContainerType() = 0;
+
+  // Get a vector of capabilities.
+  virtual const std::vector<base::win::Sid>& GetCapabilities() = 0;
+
+  // Get a vector of impersonation only capabilities. Used if the process needs
+  // a more privileged token to start.
+  virtual const std::vector<base::win::Sid>& GetImpersonationCapabilities() = 0;
+
+  // Get an allocated SecurityCapabilities object for this App Container.
+  virtual std::unique_ptr<SecurityCapabilities> GetSecurityCapabilities() = 0;
 };
 
 }  // namespace sandbox
diff --git a/sandbox/win/src/app_container_base.h b/sandbox/win/src/app_container_base.h
index 1215cf9..fabfe81 100644
--- a/sandbox/win/src/app_container_base.h
+++ b/sandbox/win/src/app_container_base.h
@@ -15,7 +15,6 @@
 #include "base/win/windows_types.h"
 #include "sandbox/win/src/app_container.h"
 #include "sandbox/win/src/sandbox_types.h"
-#include "sandbox/win/src/security_capabilities.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace sandbox {
@@ -47,20 +46,13 @@
   void SetEnableLowPrivilegeAppContainer(bool enable) override;
   bool GetEnableLowPrivilegeAppContainer() override;
   AppContainerType GetAppContainerType() override;
+  const std::vector<base::win::Sid>& GetCapabilities() override;
+  const std::vector<base::win::Sid>& GetImpersonationCapabilities() override;
+  std::unique_ptr<SecurityCapabilities> GetSecurityCapabilities() override;
 
   // Get the package SID for this AC.
   const base::win::Sid& GetPackageSid() const;
 
-  // Get an allocated SecurityCapabilities object for this App Container.
-  std::unique_ptr<SecurityCapabilities> GetSecurityCapabilities();
-
-  // Get a vector of capabilities.
-  const std::vector<base::win::Sid>& GetCapabilities();
-
-  // Get a vector of impersonation only capabilities. Used if the process needs
-  // a more privileged token to start.
-  const std::vector<base::win::Sid>& GetImpersonationCapabilities();
-
   // Creates a new AppContainer object. This will create a new profile
   // if it doesn't already exist. The profile must be deleted manually using
   // the Delete method if it's no longer required.
diff --git a/sandbox/win/src/broker_services.cc b/sandbox/win/src/broker_services.cc
index 99d04df..089a41c 100644
--- a/sandbox/win/src/broker_services.cc
+++ b/sandbox/win/src/broker_services.cc
@@ -520,8 +520,7 @@
   for (HANDLE handle : policy_handle_list)
     startup_info->AddInheritedHandle(handle);
 
-  scoped_refptr<AppContainerBase> container =
-      policy_base->GetAppContainerBase();
+  scoped_refptr<AppContainer> container = policy_base->GetAppContainer();
   if (container)
     startup_info->SetAppContainer(container);
 
diff --git a/sandbox/win/src/filesystem_policy.cc b/sandbox/win/src/filesystem_policy.cc
index bbf03802..b188503 100644
--- a/sandbox/win/src/filesystem_policy.cc
+++ b/sandbox/win/src/filesystem_policy.cc
@@ -82,6 +82,7 @@
     return false;
   }
 
+  bool is_pipe = IsPipe(mod_name);
   if (!PreProcessName(&mod_name)) {
     // The path to be added might contain a reparse point.
     NOTREACHED();
@@ -171,7 +172,7 @@
     return false;
   }
 
-  if ((rule_to_add & kCallNtSetInfoRename) &&
+  if ((rule_to_add & kCallNtSetInfoRename) && !is_pipe &&
       (!rename.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) ||
        !policy->AddRule(IpcTag::NTSETINFO_RENAME, &rename))) {
     return false;
diff --git a/sandbox/win/src/policy_target_test.cc b/sandbox/win/src/policy_target_test.cc
index b490cc4..b8b6859 100644
--- a/sandbox/win/src/policy_target_test.cc
+++ b/sandbox/win/src/policy_target_test.cc
@@ -9,6 +9,7 @@
 #include "base/strings/string_util.h"
 #include "base/win/scoped_process_information.h"
 #include "base/win/windows_version.h"
+#include "build/build_config.h"
 #include "sandbox/win/src/sandbox.h"
 #include "sandbox/win/src/sandbox_factory.h"
 #include "sandbox/win/src/sandbox_utils.h"
@@ -16,7 +17,7 @@
 #include "sandbox/win/tests/common/controller.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
 #include "base/win/win_util.h"
 #endif
 
diff --git a/sandbox/win/src/sandbox_policy_base.cc b/sandbox/win/src/sandbox_policy_base.cc
index 4b1dee7e..54ba38e 100644
--- a/sandbox/win/src/sandbox_policy_base.cc
+++ b/sandbox/win/src/sandbox_policy_base.cc
@@ -643,7 +643,7 @@
 }
 
 scoped_refptr<AppContainer> PolicyBase::GetAppContainer() {
-  return GetAppContainerBase();
+  return app_container_;
 }
 
 void PolicyBase::SetEffectiveToken(HANDLE token) {
@@ -651,10 +651,6 @@
   effective_token_ = token;
 }
 
-scoped_refptr<AppContainerBase> PolicyBase::GetAppContainerBase() {
-  return app_container_;
-}
-
 ResultCode PolicyBase::SetupAllInterceptions(TargetProcess& target) {
   InterceptionManager manager(target, relaxed_interceptions_);
 
diff --git a/sandbox/win/src/sandbox_policy_base.h b/sandbox/win/src/sandbox_policy_base.h
index f7a3ca88..7297412 100644
--- a/sandbox/win/src/sandbox_policy_base.h
+++ b/sandbox/win/src/sandbox_policy_base.h
@@ -85,9 +85,6 @@
   void SetAllowNoSandboxJob() override;
   bool GetAllowNoSandboxJob() override;
 
-  // Get the AppContainer profile as its internal type.
-  scoped_refptr<AppContainerBase> GetAppContainerBase();
-
   // Creates a Job object with the level specified in a previous call to
   // SetJobLevel().
   ResultCode MakeJobObject(base::win::ScopedHandle* job);
diff --git a/sandbox/win/src/startup_information_helper.cc b/sandbox/win/src/startup_information_helper.cc
index 7251205..d85c1ff 100644
--- a/sandbox/win/src/startup_information_helper.cc
+++ b/sandbox/win/src/startup_information_helper.cc
@@ -68,7 +68,7 @@
 }
 
 void StartupInformationHelper::SetAppContainer(
-    scoped_refptr<AppContainerBase> container) {
+    scoped_refptr<AppContainer> container) {
   // Only supported for Windows 8+.
   DCHECK(base::win::GetVersion() >= base::win::Version::WIN8);
   // LowPrivilegeAppContainer only supported for Windows 10+
diff --git a/sandbox/win/src/startup_information_helper.h b/sandbox/win/src/startup_information_helper.h
index 87034d2ec..166f2fc 100644
--- a/sandbox/win/src/startup_information_helper.h
+++ b/sandbox/win/src/startup_information_helper.h
@@ -43,7 +43,7 @@
   // Create PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES and
   //        PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY
   // based on |container|. |container| should be valid.
-  void SetAppContainer(scoped_refptr<AppContainerBase> container);
+  void SetAppContainer(scoped_refptr<AppContainer> container);
   // Creates PROC_THREAD_ATTRIBUTE_JOB_LIST with |job_handle|. Not valid before
   // Windows 10.
   void AddJobToAssociate(HANDLE job_handle);
@@ -69,7 +69,7 @@
   int CountAttributes();
 
   // Fields that are not passed into CreateProcessAsUserW().
-  scoped_refptr<AppContainerBase> app_container_;
+  scoped_refptr<AppContainer> app_container_;
   bool restrict_child_process_creation_ = false;
   HANDLE stdout_handle_ = INVALID_HANDLE_VALUE;
   HANDLE stderr_handle_ = INVALID_HANDLE_VALUE;
diff --git a/services/network/cookie_manager_unittest.cc b/services/network/cookie_manager_unittest.cc
index beb8dc2d..c4fd0f1 100644
--- a/services/network/cookie_manager_unittest.cc
+++ b/services/network/cookie_manager_unittest.cc
@@ -20,7 +20,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
-#include "net/base/features.h"
 #include "net/cookies/canonical_cookie_test_helpers.h"
 #include "net/cookies/cookie_access_result.h"
 #include "net/cookies/cookie_constants.h"
@@ -409,7 +408,7 @@
 
     connection_error_seen_ = false;
     cookie_monster_ = std::make_unique<net::CookieMonster>(
-        std::move(store), nullptr /* netlog */);
+        std::move(store), nullptr /* netlog */, first_party_sets_enabled_);
     url_request_context_ = std::make_unique<net::URLRequestContext>();
     url_request_context_->set_cookie_store(cookie_monster_.get());
     cookie_service_ = std::make_unique<CookieManager>(
@@ -430,6 +429,7 @@
   void OnConnectionError() { connection_error_seen_ = true; }
 
   bool connection_error_seen_;
+  const bool first_party_sets_enabled_ = true;
 
   base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<net::CookieMonster> cookie_monster_;
@@ -844,8 +844,6 @@
 }
 
 TEST_F(CookieManagerTest, GetCookieListSameParty) {
-  scoped_feature_list().Reset();
-  scoped_feature_list().InitAndEnableFeature(net::features::kFirstPartySets);
   // Create SameParty & non-SameParty cookies for each valid SameSite choice.
   // Unspecified:
   ASSERT_TRUE(SetCanonicalCookie(
diff --git a/services/network/cors/cors_url_loader_factory.cc b/services/network/cors/cors_url_loader_factory.cc
index af0d8e6..62a413f 100644
--- a/services/network/cors/cors_url_loader_factory.cc
+++ b/services/network/cors/cors_url_loader_factory.cc
@@ -588,6 +588,7 @@
     if (request.net_log_create_info) {
       mojo::ReportBadMessage(
           "CorsURLLoaderFactory: net_log_create_info field is not expected.");
+      return false;
     }
 
     // `net_log_reference_info` field is expected to be used within network
@@ -596,6 +597,7 @@
       mojo::ReportBadMessage(
           "CorsURLLoaderFactory: net_log_reference_info field is not "
           "expected.");
+      return false;
     }
   }
 
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index d1ad7581..4402b71 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -708,7 +708,8 @@
       std::make_unique<RestrictedCookieManager>(
           role, url_request_context_->cookie_store(),
           cookie_manager_->cookie_settings(), origin, isolation_info,
-          std::move(cookie_observer)),
+          std::move(cookie_observer),
+          network_service_->first_party_sets()->is_enabled()),
       std::move(receiver));
 }
 
@@ -2208,8 +2209,9 @@
 
   if (session_cleanup_cookie_store) {
     std::unique_ptr<net::CookieMonster> cookie_store =
-        std::make_unique<net::CookieMonster>(session_cleanup_cookie_store.get(),
-                                             net_log);
+        std::make_unique<net::CookieMonster>(
+            session_cleanup_cookie_store.get(), net_log,
+            network_service_->first_party_sets()->is_enabled());
     if (params_->persist_session_cookies)
       cookie_store->SetPersistSessionCookies(true);
 
@@ -2420,6 +2422,9 @@
   builder.set_host_mapping_rules(
       command_line->GetSwitchValueASCII(switches::kHostResolverRules));
 
+  builder.set_first_party_sets_enabled(
+      network_service_->first_party_sets()->is_enabled());
+
   auto result =
       URLRequestContextOwner(std::move(pref_service), builder.Build());
 
diff --git a/services/network/public/cpp/simple_url_loader_unittest.cc b/services/network/public/cpp/simple_url_loader_unittest.cc
index f747894..dde9c97 100644
--- a/services/network/public/cpp/simple_url_loader_unittest.cc
+++ b/services/network/public/cpp/simple_url_loader_unittest.cc
@@ -2391,7 +2391,13 @@
 // This test tries closing the client pipe / completing the request in most
 // possible valid orders relative to read events (Which always occur in the same
 // order).
-TEST_P(SimpleURLLoaderTest, CloseClientPipeOrder) {
+// TODO(crbug.com/1286251): Flakes on ios simulator.
+#if defined(OS_IOS)
+#define MAYBE_CloseClientPipeOrder DISABLED_CloseClientPipeOrder
+#else
+#define MAYBE_CloseClientPipeOrder CloseClientPipeOrder
+#endif
+TEST_P(SimpleURLLoaderTest, MAYBE_CloseClientPipeOrder) {
   enum class ClientCloseOrder {
     kBeforeData,
     kDuringData,
diff --git a/services/network/restricted_cookie_manager.cc b/services/network/restricted_cookie_manager.cc
index f1829d7..89dea656 100644
--- a/services/network/restricted_cookie_manager.cc
+++ b/services/network/restricted_cookie_manager.cc
@@ -225,14 +225,16 @@
            const url::Origin& top_frame_origin,
            const absl::optional<net::CookiePartitionKey>& cookie_partition_key,
            net::CookieOptions options,
-           mojo::PendingRemote<mojom::CookieChangeListener> mojo_listener)
+           mojo::PendingRemote<mojom::CookieChangeListener> mojo_listener,
+           const bool first_party_sets_enabled)
       : cookie_store_(cookie_store),
         restricted_cookie_manager_(restricted_cookie_manager),
         url_(url),
         site_for_cookies_(site_for_cookies),
         top_frame_origin_(top_frame_origin),
         options_(options),
-        mojo_listener_(std::move(mojo_listener)) {
+        mojo_listener_(std::move(mojo_listener)),
+        first_party_sets_enabled_(first_party_sets_enabled) {
     // TODO(pwnall): add a constructor w/options to net::CookieChangeDispatcher.
     cookie_store_subscription_ =
         cookie_store->GetChangeDispatcher().AddCallbackForUrl(
@@ -269,7 +271,8 @@
     // CookieChangeDispatcher doesn't check for inclusion against `options_`, so
     // we need to double-check that.
     net::CookieSamePartyStatus same_party_status =
-        net::cookie_util::GetSamePartyStatus(change.cookie, options_);
+        net::cookie_util::GetSamePartyStatus(change.cookie, options_,
+                                             first_party_sets_enabled_);
 
     if (!change.cookie
              .IncludeForRequestURL(
@@ -318,6 +321,8 @@
 
   mojo::Remote<mojom::CookieChangeListener> mojo_listener_;
 
+  const bool first_party_sets_enabled_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 };
 
@@ -327,13 +332,15 @@
     const CookieSettings& cookie_settings,
     const url::Origin& origin,
     const net::IsolationInfo& isolation_info,
-    mojo::PendingRemote<mojom::CookieAccessObserver> cookie_observer)
+    mojo::PendingRemote<mojom::CookieAccessObserver> cookie_observer,
+    const bool first_party_sets_enabled)
     : role_(role),
       cookie_store_(cookie_store),
       cookie_settings_(cookie_settings),
       origin_(origin),
       isolation_info_(isolation_info),
-      cookie_observer_(std::move(cookie_observer)) {
+      cookie_observer_(std::move(cookie_observer)),
+      first_party_sets_enabled_(first_party_sets_enabled) {
   DCHECK(cookie_store);
   CHECK(origin_ == isolation_info_.frame_origin().value() ||
         role_ != mojom::RestrictedCookieManagerRole::SCRIPT);
@@ -666,7 +673,8 @@
       cookie_store_->cookie_access_delegate());
   auto listener = std::make_unique<Listener>(
       cookie_store_, this, url, site_for_cookies, top_frame_origin,
-      cookie_partition_key_, net_options, std::move(mojo_listener));
+      cookie_partition_key_, net_options, std::move(mojo_listener),
+      first_party_sets_enabled_);
 
   listener->mojo_listener().set_disconnect_handler(
       base::BindOnce(&RestrictedCookieManager::RemoveChangeListener,
diff --git a/services/network/restricted_cookie_manager.h b/services/network/restricted_cookie_manager.h
index d265048..037481a8 100644
--- a/services/network/restricted_cookie_manager.h
+++ b/services/network/restricted_cookie_manager.h
@@ -73,7 +73,8 @@
       const CookieSettings& cookie_settings,
       const url::Origin& origin,
       const net::IsolationInfo& isolation_info,
-      mojo::PendingRemote<mojom::CookieAccessObserver> cookie_observer);
+      mojo::PendingRemote<mojom::CookieAccessObserver> cookie_observer,
+      bool first_party_sets_enabled);
 
   RestrictedCookieManager(const RestrictedCookieManager&) = delete;
   RestrictedCookieManager& operator=(const RestrictedCookieManager&) = delete;
@@ -216,6 +217,8 @@
   // update filtering.
   CookieAccessesByURLAndSite recent_cookie_accesses_;
 
+  const bool first_party_sets_enabled_;
+
   base::WeakPtrFactory<RestrictedCookieManager> weak_ptr_factory_{this};
 };
 
diff --git a/services/network/restricted_cookie_manager_unittest.cc b/services/network/restricted_cookie_manager_unittest.cc
index 64aa3e3b..ced5cfd 100644
--- a/services/network/restricted_cookie_manager_unittest.cc
+++ b/services/network/restricted_cookie_manager_unittest.cc
@@ -237,7 +237,9 @@
     : public testing::TestWithParam<mojom::RestrictedCookieManagerRole> {
  public:
   RestrictedCookieManagerTest()
-      : cookie_monster_(nullptr, nullptr /* netlog */),
+      : cookie_monster_(/*store=*/nullptr,
+                        /*net_log=*/nullptr,
+                        kFirstPartySetsEnabled),
         isolation_info_(kDefaultIsolationInfo),
         service_(std::make_unique<RestrictedCookieManager>(
             GetParam(),
@@ -245,7 +247,8 @@
             cookie_settings_,
             kDefaultOrigin,
             isolation_info_,
-            recording_client_.GetRemote())),
+            recording_client_.GetRemote(),
+            kFirstPartySetsEnabled)),
         receiver_(service_.get(),
                   service_remote_.BindNewPipeAndPassReceiver()) {
     sync_service_ =
@@ -369,6 +372,7 @@
                                  kDefaultOrigin,
                                  kDefaultOrigin,
                                  net::SiteForCookies());
+  const bool kFirstPartySetsEnabled = true;
 
   base::test::SingleThreadTaskEnvironment task_environment_{
       base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
@@ -387,8 +391,8 @@
 class SamePartyEnabledRestrictedCookieManagerTest
     : public RestrictedCookieManagerTest {
  public:
-  SamePartyEnabledRestrictedCookieManagerTest() : first_party_sets_(true) {
-    feature_list_.InitAndEnableFeature(net::features::kFirstPartySets);
+  SamePartyEnabledRestrictedCookieManagerTest()
+      : first_party_sets_(/*enabled=*/true) {
     first_party_sets_.SetManuallySpecifiedSet(
         "https://example.com,https://member1.com");
     auto cookie_access_delegate = std::make_unique<CookieAccessDelegateImpl>(
diff --git a/services/network/ssl_config_service_mojo_unittest.cc b/services/network/ssl_config_service_mojo_unittest.cc
index 848124f1..977b072 100644
--- a/services/network/ssl_config_service_mojo_unittest.cc
+++ b/services/network/ssl_config_service_mojo_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/files/file_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
+#include "base/test/repeating_test_future.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
 #include "crypto/sha2.h"
@@ -49,17 +50,14 @@
   }
 
   ~TestSSLConfigServiceObserver() override {
-    EXPECT_EQ(observed_changes_, changes_to_wait_for_);
+    EXPECT_TRUE(config_changed_calls_.IsEmpty())
+        << "Unexpected calls to OnSSLContextConfigChanged()";
     ssl_config_service_->RemoveObserver(this);
   }
 
   // net::SSLConfigService::Observer implementation:
   void OnSSLContextConfigChanged() override {
-    ++observed_changes_;
-    ssl_context_config_during_change_ =
-        ssl_config_service_->GetSSLContextConfig();
-    if (run_loop_)
-      run_loop_->Quit();
+    config_changed_calls_.AddValue(ssl_config_service_->GetSSLContextConfig());
   }
 
   // Waits for a SSLContextConfig change. The first time it's called, waits for
@@ -67,38 +65,25 @@
   // waits for the second, etc. Must be called once for each change that
   // happens, and fails if more than once change happens between calls, or
   // during a call.
-  void WaitForChange() {
-    EXPECT_FALSE(run_loop_);
-    ++changes_to_wait_for_;
-    if (changes_to_wait_for_ == observed_changes_)
-      return;
-    EXPECT_LT(observed_changes_, changes_to_wait_for_);
-
-    run_loop_ = std::make_unique<base::RunLoop>();
-    run_loop_->Run();
-    run_loop_.reset();
-    EXPECT_EQ(observed_changes_, changes_to_wait_for_);
+  net::SSLContextConfig WaitForChange() {
+    EXPECT_TRUE(config_changed_calls_.Wait())
+        << "Missing call to OnSSLContextConfigChanged()";
+    return config_changed_calls_.Take();
   }
 
-  const net::SSLContextConfig& ssl_context_config_during_change() const {
-    return ssl_context_config_during_change_;
-  }
-
-  int observed_changes() const { return observed_changes_; }
-
  private:
   const raw_ptr<net::SSLConfigService> ssl_config_service_;
-  int observed_changes_ = 0;
-  int changes_to_wait_for_ = 0;
-  net::SSLContextConfig ssl_context_config_during_change_;
-  std::unique_ptr<base::RunLoop> run_loop_;
+
+  // All calls to OnSSLContextConfigChanged().
+  base::test::RepeatingTestFuture<net::SSLContextConfig> config_changed_calls_;
 };
 
 class TestCertVerifierConfigObserver : public net::CertVerifier {
  public:
   TestCertVerifierConfigObserver() = default;
   ~TestCertVerifierConfigObserver() override {
-    EXPECT_EQ(observed_changes_, changes_to_wait_for_);
+    EXPECT_TRUE(set_config_calls_.IsEmpty())
+        << "Unexpected call to SetConfig()";
   }
 
   // CertVerifier implementation:
@@ -111,40 +96,21 @@
     return net::ERR_FAILED;
   }
   void SetConfig(const Config& config) override {
-    ++observed_changes_;
-    verifier_config_during_change_ = config;
-    if (run_loop_)
-      run_loop_->Quit();
+    set_config_calls_.AddValue(config);
   }
 
   // Waits for a SSLConfig change. The first time it's called, waits for the
   // first change, if one hasn't been observed already, the second time, waits
   // for the second, etc. Must be called once for each change that happens, and
   // fails it more than once change happens between calls, or during a call.
-  void WaitForChange() {
-    EXPECT_FALSE(run_loop_);
-    ++changes_to_wait_for_;
-    if (changes_to_wait_for_ == observed_changes_)
-      return;
-    EXPECT_LT(observed_changes_, changes_to_wait_for_);
-
-    run_loop_ = std::make_unique<base::RunLoop>();
-    run_loop_->Run();
-    run_loop_.reset();
-    EXPECT_EQ(observed_changes_, changes_to_wait_for_);
+  net::CertVerifier::Config WaitForChange() {
+    EXPECT_TRUE(set_config_calls_.Wait()) << "Missing call to SetConfig()";
+    return set_config_calls_.Take();
   }
 
-  const net::CertVerifier::Config& verifier_config_during_change() const {
-    return verifier_config_during_change_;
-  }
-
-  int observed_changes() const { return observed_changes_; }
-
  private:
-  int observed_changes_ = 0;
-  int changes_to_wait_for_ = 0;
-  net::CertVerifier::Config verifier_config_during_change_;
-  std::unique_ptr<base::RunLoop> run_loop_;
+  // All calls to SetConfig().
+  base::test::RepeatingTestFuture<Config> set_config_calls_;
 };
 
 class NetworkServiceSSLConfigServiceTest : public testing::Test {
@@ -210,22 +176,22 @@
     TestSSLConfigServiceObserver observer(
         network_context_->url_request_context()->ssl_config_service());
     ssl_config_client_->OnSSLConfigUpdated(mojom::SSLConfig::New());
-    observer.WaitForChange();
+    net::SSLContextConfig config_during_change = observer.WaitForChange();
     EXPECT_TRUE(net::SSLConfigService::SSLContextConfigsAreEqualForTesting(
         GetSSLContextConfig(), net::SSLContextConfig()));
     EXPECT_TRUE(net::SSLConfigService::SSLContextConfigsAreEqualForTesting(
-        observer.ssl_context_config_during_change(), net::SSLContextConfig()));
+        config_during_change, net::SSLContextConfig()));
     // Sanity check.
     EXPECT_FALSE(net::SSLConfigService::SSLContextConfigsAreEqualForTesting(
         GetSSLContextConfig(), expected_net_config));
 
     // Set the configuration to |mojo_config| again, and check the results.
     ssl_config_client_->OnSSLConfigUpdated(mojo_config.Clone());
-    observer.WaitForChange();
+    config_during_change = observer.WaitForChange();
     EXPECT_TRUE(net::SSLConfigService::SSLContextConfigsAreEqualForTesting(
         GetSSLContextConfig(), expected_net_config));
     EXPECT_TRUE(net::SSLConfigService::SSLContextConfigsAreEqualForTesting(
-        observer.ssl_context_config_during_change(), expected_net_config));
+        config_during_change, expected_net_config));
   }
 
   // Runs two conversion tests for |mojo_config|.  Uses it as an initial
@@ -248,24 +214,22 @@
     SetUpNetworkContext(std::move(network_context_params));
 
     // Make sure the initial configuration is set.
-    observer.WaitForChange();
-    EXPECT_EQ(observer.verifier_config_during_change(), expected_net_config);
+    net::CertVerifier::Config config_during_change = observer.WaitForChange();
+    EXPECT_EQ(config_during_change, expected_net_config);
     // Sanity check.
-    EXPECT_NE(observer.verifier_config_during_change(),
-              net::CertVerifier::Config());
+    EXPECT_NE(config_during_change, net::CertVerifier::Config());
 
     // Reset the configuration to the default ones, and check the results.
     ssl_config_client_->OnSSLConfigUpdated(mojom::SSLConfig::New());
-    observer.WaitForChange();
-    EXPECT_EQ(observer.verifier_config_during_change(),
-              net::CertVerifier::Config());
+    config_during_change = observer.WaitForChange();
+    EXPECT_EQ(config_during_change, net::CertVerifier::Config());
     // Sanity check.
-    EXPECT_NE(observer.verifier_config_during_change(), expected_net_config);
+    EXPECT_NE(config_during_change, expected_net_config);
 
     // Set the configuration to |mojo_config| again, and check the results.
     ssl_config_client_->OnSSLConfigUpdated(mojo_config.Clone());
-    observer.WaitForChange();
-    EXPECT_EQ(observer.verifier_config_during_change(), expected_net_config);
+    config_during_change = observer.WaitForChange();
+    EXPECT_EQ(config_during_change, expected_net_config);
 
     // Reset the CertVerifier for subsequent invocations.
     NetworkContext::SetCertVerifierForTesting(nullptr);
@@ -317,10 +281,10 @@
   network_context_params->initial_ssl_config = mojom::SSLConfig::New();
   SetUpNetworkContext(std::move(network_context_params));
 
-  observer.WaitForChange();
+  net::CertVerifier::Config config_during_change = observer.WaitForChange();
 
   net::CertVerifier::Config default_config;
-  EXPECT_EQ(observer.verifier_config_during_change(), default_config);
+  EXPECT_EQ(config_during_change, default_config);
 
   NetworkContext::SetCertVerifierForTesting(nullptr);
 }
diff --git a/services/network/websocket.cc b/services/network/websocket.cc
index eef15ca..4d863631 100644
--- a/services/network/websocket.cc
+++ b/services/network/websocket.cc
@@ -453,7 +453,7 @@
       throttling_profile_id_(throttling_profile_id) {
   DCHECK(handshake_client_);
   // |delay| should be zero if this connection is not throttled.
-  DCHECK(pending_connection_tracker.has_value() || delay.is_zero());
+  DCHECK(pending_connection_tracker_.has_value() || delay.is_zero());
   if (auth_handler_) {
     // Make sure the request dies if |auth_handler_| has an error, otherwise
     // requests can hang.
diff --git a/services/services_strings.grd b/services/services_strings.grd
index 756fe8f..a13b881 100644
--- a/services/services_strings.grd
+++ b/services/services_strings.grd
@@ -41,7 +41,9 @@
       <output filename="services_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="services_strings_af.pak" type="data_package" lang="af" />
       <output filename="services_strings_is.pak" type="data_package" lang="is" />
+      <output filename="services_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="services_strings_am.pak" type="data_package" lang="am" />
     <output filename="services_strings_ar.pak" type="data_package" lang="ar" />
diff --git a/services/tracing/perfetto/privacy_filtered_fields-inl.h b/services/tracing/perfetto/privacy_filtered_fields-inl.h
index 01853ce..e51ce4c 100644
--- a/services/tracing/perfetto/privacy_filtered_fields-inl.h
+++ b/services/tracing/perfetto/privacy_filtered_fields-inl.h
@@ -318,13 +318,17 @@
 constexpr MessageInfo kRendererMainThreadTaskExecution = {
     kRendererMainThreadTaskExecutionIndices, nullptr};
 
+// Proto Message: EventLatency
+constexpr int kEventLatencyIndices[] = {1, -1};
+constexpr MessageInfo kEventLatency = {kEventLatencyIndices, nullptr};
+
 // Proto Message: TrackEvent
 constexpr int kTrackEventIndices[] = {
     1,    2,    3,    5,    6,    9,    10,   11,   12,   16,   17,   22,
     23,   24,   25,   26,   27,   28,   29,   30,   31,   32,   33,   34,
     35,   36,   38,   39,   40,   41,   42,   43,   1001, 1002, 1003, 1004,
     1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016,
-    1017, 1018, 1019, 1020, 1021, 1023, 1024, 1025, 1031, -1};
+    1017, 1018, 1019, 1020, 1021, 1023, 1024, 1025, 1031, 1032, -1};
 constexpr MessageInfo const* kTrackEventComplexMessages[] = {
     nullptr,
     nullptr,
@@ -382,7 +386,8 @@
     &kParkableStringCompressInBackground,
     &kParkableStringUnpark,
     &kChromeSamplingProfilerSampleCollected,
-    &kRendererMainThreadTaskExecution};
+    &kRendererMainThreadTaskExecution,
+    &kEventLatency};
 constexpr MessageInfo kTrackEvent = {kTrackEventIndices,
                                      kTrackEventComplexMessages};
 
diff --git a/storage/browser/blob/blob_registry_impl_unittest.cc b/storage/browser/blob/blob_registry_impl_unittest.cc
index e7de3b8..90a5b6b 100644
--- a/storage/browser/blob/blob_registry_impl_unittest.cc
+++ b/storage/browser/blob/blob_registry_impl_unittest.cc
@@ -7,13 +7,13 @@
 #include <limits>
 #include <memory>
 #include <string>
+#include <tuple>
 #include <utility>
 #include <vector>
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/files/scoped_temp_dir.h"
-#include "base/ignore_result.h"
 #include "base/memory/raw_ptr.h"
 #include "base/rand_util.h"
 #include "base/run_loop.h"
@@ -334,7 +334,7 @@
 
   std::vector<blink::mojom::DataElementPtr> elements;
   mojo::PendingRemote<blink::mojom::Blob> referenced_blob_remote;
-  ignore_result(referenced_blob_remote.InitWithNewPipeAndPassReceiver());
+  std::ignore = referenced_blob_remote.InitWithNewPipeAndPassReceiver();
   elements.push_back(
       blink::mojom::DataElement::NewBlob(blink::mojom::DataElementBlob::New(
           std::move(referenced_blob_remote), 0, 16)));
@@ -975,7 +975,7 @@
   const std::string kId = "id";
 
   mojo::PendingRemote<blink::mojom::BytesProvider> bytes_provider_remote;
-  ignore_result(bytes_provider_remote.InitWithNewPipeAndPassReceiver());
+  std::ignore = bytes_provider_remote.InitWithNewPipeAndPassReceiver();
 
   std::vector<blink::mojom::DataElementPtr> elements;
   elements.push_back(
diff --git a/storage/browser/blob/scoped_file.cc b/storage/browser/blob/scoped_file.cc
index 9a9a2f9..98cac278 100644
--- a/storage/browser/blob/scoped_file.cc
+++ b/storage/browser/blob/scoped_file.cc
@@ -27,7 +27,7 @@
   DCHECK(path.empty() || policy != DELETE_ON_SCOPE_OUT ||
          file_task_runner_.get())
       << "path:" << path.value() << " policy:" << policy
-      << " runner:" << file_task_runner.get();
+      << " runner:" << file_task_runner_.get();
 }
 
 ScopedFile::ScopedFile(ScopedFile&& other) {
diff --git a/styleguide/c++/c++11.md b/styleguide/c++/c++11.md
index f6f0020..7a41fdf 100644
--- a/styleguide/c++/c++11.md
+++ b/styleguide/c++/c++11.md
@@ -465,6 +465,23 @@
 See [migration task](https://bugs.chromium.org/p/chromium/issues/detail?id=1283907).
 ***
 
+### constexpr if <sup>[allowed]</sup>
+
+```c++
+if constexpr (cond) { ...
+```
+
+**Description:** Write code that is instantiated depending on a compile-time
+condition.
+
+**Documentation:**
+[if statement](https://en.cppreference.com/w/cpp/language/if)
+
+**Notes:**
+*** promo
+See [discussion thread](https://groups.google.com/a/chromium.org/g/cxx/c/op2ePZnjP0w).
+***
+
 ## C++17 Allowed Library Features {#library-allowlist-17}
 
 The following C++17 language features are allowed in the Chromium codebase.
@@ -721,23 +738,6 @@
 None
 ***
 
-### constexpr if <sup>[tbd]</sup>
-
-```c++
-if constexpr (cond) { ...
-```
-
-**Description:** Write code that is instantiated depending on a compile-time
-condition.
-
-**Documentation:**
-[if statement](https://en.cppreference.com/w/cpp/language/if)
-
-**Notes:**
-*** promo
-None
-***
-
 ### UTF-8 character literals <sup>[tbd]</sup>
 
 ```c++
diff --git a/testing/buildbot/README.md b/testing/buildbot/README.md
index 51897b64..ccb75176 100644
--- a/testing/buildbot/README.md
+++ b/testing/buildbot/README.md
@@ -366,9 +366,7 @@
 * `swarming`: a dictionary specifying Swarming parameters to be applied to all
   tests that run on the bot.
 
-* `os_type`: the type of OS this bot tests. The only useful value currently is
-  `'android'`, and enables outputting of certain Android-specific entries into
-  the JSON files.
+* `os_type`: the type of OS this bot tests.
 
 * `skip_cipd_packages`: (Android-specific) when True, disables emission of the
   `'cipd_packages'` Swarming dictionary entry. Not commonly used; further use is
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index 3bd6201a..fd949888 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -1752,7 +1752,7 @@
       {
         "args": [],
         "cros_board": "atlas",
-        "cros_img": "atlas-release/R99-14447.0.0",
+        "cros_img": "atlas-release/R99-14450.0.0",
         "name": "lacros_all_tast_tests_ATLAS_LKGM",
         "resultdb": {
           "enable": true,
@@ -1767,7 +1767,7 @@
       {
         "args": [],
         "cros_board": "atlas",
-        "cros_img": "atlas-release/R99-14440.0.0",
+        "cros_img": "atlas-release/R99-14447.0.0",
         "name": "lacros_all_tast_tests_ATLAS_DEV",
         "resultdb": {
           "enable": true,
@@ -1782,7 +1782,7 @@
       {
         "args": [],
         "cros_board": "atlas",
-        "cros_img": "atlas-release/R97-14324.56.0",
+        "cros_img": "atlas-release/R98-14388.24.0",
         "name": "lacros_all_tast_tests_ATLAS_BETA",
         "resultdb": {
           "enable": true,
@@ -1812,7 +1812,7 @@
       {
         "args": [],
         "cros_board": "eve",
-        "cros_img": "eve-release/R99-14447.0.0",
+        "cros_img": "eve-release/R99-14450.0.0",
         "name": "lacros_all_tast_tests_EVE_LKGM",
         "resultdb": {
           "enable": true,
@@ -1827,7 +1827,7 @@
       {
         "args": [],
         "cros_board": "eve",
-        "cros_img": "eve-release/R99-14440.0.0",
+        "cros_img": "eve-release/R99-14447.0.0",
         "name": "lacros_all_tast_tests_EVE_DEV",
         "resultdb": {
           "enable": true,
@@ -1842,7 +1842,7 @@
       {
         "args": [],
         "cros_board": "eve",
-        "cros_img": "eve-release/R97-14324.56.0",
+        "cros_img": "eve-release/R98-14388.24.0",
         "name": "lacros_all_tast_tests_EVE_BETA",
         "resultdb": {
           "enable": true,
@@ -1917,7 +1917,7 @@
       {
         "args": [],
         "cros_board": "kevin",
-        "cros_img": "kevin-release/R99-14447.0.0",
+        "cros_img": "kevin-release/R99-14450.0.0",
         "name": "lacros_all_tast_tests_KEVIN_LKGM",
         "resultdb": {
           "enable": true,
@@ -1932,7 +1932,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R99-14447.0.0",
+        "cros_img": "hana-release/R99-14450.0.0",
         "name": "lacros_all_tast_tests_HANA_LKGM",
         "resultdb": {
           "enable": true,
@@ -1947,7 +1947,7 @@
       {
         "args": [],
         "cros_board": "kevin",
-        "cros_img": "kevin-release/R99-14447.0.0",
+        "cros_img": "kevin-release/R99-14450.0.0",
         "name": "ozone_unittests_KEVIN_LKGM",
         "resultdb": {
           "enable": true,
@@ -1961,7 +1961,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R99-14447.0.0",
+        "cros_img": "hana-release/R99-14450.0.0",
         "name": "ozone_unittests_HANA_LKGM",
         "resultdb": {
           "enable": true,
@@ -1975,7 +1975,7 @@
       {
         "args": [],
         "cros_board": "kevin",
-        "cros_img": "kevin-release/R99-14447.0.0",
+        "cros_img": "kevin-release/R99-14450.0.0",
         "name": "viz_unittests_KEVIN_LKGM",
         "resultdb": {
           "enable": true,
@@ -1989,7 +1989,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R99-14447.0.0",
+        "cros_img": "hana-release/R99-14450.0.0",
         "name": "viz_unittests_HANA_LKGM",
         "resultdb": {
           "enable": true,
@@ -2049,6 +2049,29 @@
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test_id_prefix": "ninja://chrome/test:chrome_sizes/"
+      },
+      {
+        "isolate_name": "variations_smoke_tests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "variations_smoke_tests",
+        "resultdb": {
+          "enable": true,
+          "result_format": "single"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04",
+              "pool": "chrome.tests"
+            }
+          ],
+          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:variations_smoke_tests/"
       }
     ]
   },
@@ -3932,6 +3955,30 @@
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test_id_prefix": "ninja://chrome/test:chrome_sizes/"
+      },
+      {
+        "isolate_name": "variations_smoke_tests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "variations_smoke_tests",
+        "resultdb": {
+          "enable": true,
+          "result_format": "single"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-10.15",
+              "pool": "chrome.tests"
+            }
+          ],
+          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:variations_smoke_tests/"
       }
     ]
   },
@@ -3982,6 +4029,29 @@
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test_id_prefix": "ninja://chrome/test:chrome_sizes/"
+      },
+      {
+        "isolate_name": "variations_smoke_tests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "variations_smoke_tests",
+        "resultdb": {
+          "enable": true,
+          "result_format": "single"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Windows-10-19042",
+              "pool": "chrome.tests"
+            }
+          ],
+          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:variations_smoke_tests/"
       }
     ]
   },
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index cb036ee..2203c3d1 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -5413,49 +5413,9 @@
           "expiration": 18000,
           "hard_timeout": 14400,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 15
+          "shards": 18
         },
         "test_id_prefix": "ninja://android_webview/test:system_webview_wpt/"
-      },
-      {
-        "experiment_percentage": 100,
-        "isolate_name": "weblayer_shell_wpt",
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "weblayer_shell_wpt",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "results_handler": "layout tests",
-        "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": [
-            {
-              "device_os": "PQ3A.190801.002",
-              "device_os_flavor": "google",
-              "device_os_type": "userdebug",
-              "device_type": "walleye",
-              "os": "Android"
-            }
-          ],
-          "expiration": 18000,
-          "hard_timeout": 14400,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 15
-        },
-        "test_id_prefix": "ninja://weblayer/shell/android:weblayer_shell_wpt/"
       }
     ]
   },
@@ -5463,6 +5423,8 @@
     "isolated_scripts": [
       {
         "args": [
+          "--log-wptreport",
+          "--processes=2",
           "--avd-config=../../tools/android/avd/proto/generic_android28.textpb"
         ],
         "experiment_percentage": 100,
@@ -6131,6 +6093,7 @@
     "isolated_scripts": [
       {
         "args": [
+          "--processes=2",
           "--avd-config=../../tools/android/avd/proto/generic_android28.textpb"
         ],
         "experiment_percentage": 100,
@@ -6196,6 +6159,7 @@
     "isolated_scripts": [
       {
         "args": [
+          "--processes=2",
           "--avd-config=../../tools/android/avd/proto/generic_android28.textpb",
           "--default-exclude",
           "--include-file=../../third_party/blink/web_tests/android/WPTSmokeTestCases"
@@ -6252,8 +6216,7 @@
               }
             ]
           },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
         "test_id_prefix": "ninja://weblayer/shell/android:weblayer_shell_wpt/"
       }
@@ -6264,7 +6227,8 @@
       {
         "args": [
           "--avd-config=../../tools/android/avd/proto/generic_android28.textpb",
-          "--log-wptreport"
+          "--log-wptreport",
+          "--processes=2"
         ],
         "experiment_percentage": 100,
         "isolate_name": "system_webview_wpt",
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index 79b786d..a31dc19 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -9119,6 +9119,24 @@
         "test_id_prefix": "ninja://ui/color:color_unittests/"
       },
       {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "components_browsertests",
+        "test_id_prefix": "ninja://components:components_browsertests/"
+      },
+      {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.components_unittests.filter"
         ],
@@ -9637,8 +9655,7 @@
       },
       {
         "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter",
-          "--use-run"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter"
         ],
         "merge": {
           "args": [],
@@ -10823,6 +10840,25 @@
         "test_id_prefix": "ninja://ui/color:color_unittests/"
       },
       {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "components_browsertests",
+        "test_id_prefix": "ninja://components:components_browsertests/"
+      },
+      {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.components_unittests.filter"
         ],
@@ -11255,8 +11291,7 @@
       },
       {
         "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter",
-          "--use-run"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter"
         ],
         "merge": {
           "args": [],
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 2d8285c..d3fd1b1 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -18550,6 +18550,24 @@
       },
       {
         "args": [
+          "--ram-size-mb=16384",
+          "--code-coverage",
+          "--code-coverage-dir=${ISOLATED_OUTDIR}"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "components_browsertests",
+        "test_id_prefix": "ninja://components:components_browsertests/"
+      },
+      {
+        "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.components_unittests.filter",
           "--ram-size-mb=16384",
           "--code-coverage",
@@ -19827,6 +19845,25 @@
         "test_id_prefix": "ninja://ui/color:color_unittests/"
       },
       {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "components_browsertests",
+        "test_id_prefix": "ninja://components:components_browsertests/"
+      },
+      {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.components_unittests.filter"
         ],
@@ -20265,8 +20302,7 @@
       },
       {
         "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter",
-          "--use-run"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter"
         ],
         "merge": {
           "args": [],
@@ -21051,6 +21087,28 @@
       },
       {
         "args": [
+          "--device=aemu"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "components_browsertests",
+        "test_id_prefix": "ninja://components:components_browsertests/"
+      },
+      {
+        "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.components_unittests.filter",
           "--device=aemu"
         ],
@@ -21541,7 +21599,6 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter",
-          "--use-run",
           "--device=aemu"
         ],
         "merge": {
@@ -22065,408 +22122,6 @@
           "shards": 12
         },
         "test_id_prefix": "ninja://:blink_web_tests/"
-      },
-      {
-        "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=web-engine-shell",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
-          "--device=aemu"
-        ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "context_lost_validating_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "depth_capture",
-          "--show-stdout",
-          "--browser=web-engine-shell",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--device=aemu"
-        ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "depth_capture_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=web-engine-shell",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--device=aemu"
-        ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "gpu_process_launch_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=web-engine-shell",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--device=aemu"
-        ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "hardware_accelerated_feature_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=web-engine-shell",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--expected-vendor-id",
-          "0",
-          "--expected-device-id",
-          "0",
-          "--device=aemu"
-        ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "info_collection_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "maps",
-          "--show-stdout",
-          "--browser=web-engine-shell",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--device=aemu",
-          "--git-revision=${got_revision}"
-        ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "maps_tests",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "mediapipe",
-          "--show-stdout",
-          "--browser=web-engine-shell",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating",
-          "--device=aemu"
-        ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "mediapipe_validating_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=web-engine-shell",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--device=aemu",
-          "--git-revision=${got_revision}"
-        ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "pixel_tests",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=web-engine-shell",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--device=aemu"
-        ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "screenshot_sync_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=web-engine-shell",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--device=aemu"
-        ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "trace_test",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=web-engine-shell",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--device=aemu"
-        ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgl_conformance_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 18
-        },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
       }
     ]
   },
@@ -22476,25 +22131,6 @@
     ],
     "gtest_tests": [
       {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm64",
-              "inside_docker": "1",
-              "os": "Ubuntu-20.04"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "components_browsertests",
-        "test_id_prefix": "ninja://components:components_browsertests/"
-      },
-      {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.content_browsertests.filter",
           "--",
@@ -22840,6 +22476,24 @@
         "test_id_prefix": "ninja://ui/color:color_unittests/"
       },
       {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-18.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "components_browsertests",
+        "test_id_prefix": "ninja://components:components_browsertests/"
+      },
+      {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.components_unittests.filter"
         ],
@@ -23358,8 +23012,7 @@
       },
       {
         "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter",
-          "--use-run"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter"
         ],
         "merge": {
           "args": [],
@@ -24142,6 +23795,24 @@
         "test_id_prefix": "ninja://ui/color:color_unittests/"
       },
       {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-18.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "components_browsertests",
+        "test_id_prefix": "ninja://components:components_browsertests/"
+      },
+      {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.components_unittests.filter"
         ],
@@ -24660,8 +24331,7 @@
       },
       {
         "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter",
-          "--use-run"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter"
         ],
         "merge": {
           "args": [],
@@ -25152,24 +24822,6 @@
     ],
     "gtest_tests": [
       {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-18.04"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "components_browsertests",
-        "test_id_prefix": "ninja://components:components_browsertests/"
-      },
-      {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.content_browsertests.filter",
           "--",
@@ -25227,7 +24879,7 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25249,7 +24901,7 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
@@ -25260,7 +24912,7 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25282,7 +24934,7 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
@@ -25293,7 +24945,7 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25315,7 +24967,7 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
@@ -25326,7 +24978,7 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25348,7 +25000,7 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
@@ -25363,7 +25015,7 @@
           "--expected-device-id",
           "0"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25385,7 +25037,7 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
@@ -25394,15 +25046,18 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
           "--git-revision=${got_revision}"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "maps_tests",
+        "name": "maps_pixel_validating_test",
         "precommit_args": [
           "--gerrit-issue=${patch_issue}",
           "--gerrit-patchset=${patch_set}",
@@ -25424,7 +25079,7 @@
           "idempotent": false,
           "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
@@ -25435,7 +25090,7 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25457,7 +25112,7 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
@@ -25466,15 +25121,18 @@
           "--browser=web-engine-shell",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
           "--git-revision=${got_revision}"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "pixel_tests",
+        "name": "pixel_skia_gold_validating_test",
         "precommit_args": [
           "--gerrit-issue=${patch_issue}",
           "--gerrit-patchset=${patch_set}",
@@ -25496,7 +25154,7 @@
           "idempotent": false,
           "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
@@ -25507,7 +25165,7 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25529,7 +25187,41 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=web-engine-shell",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--dont-restore-color-profile-after-test"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "screenshot_sync_validating_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-18.04"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
@@ -25540,7 +25232,7 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25562,7 +25254,7 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
@@ -25573,7 +25265,7 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25596,7 +25288,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 18
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       }
     ]
   },
@@ -25604,27 +25296,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--custom-image=workstation.qemu-x64-release"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "kvm": "1",
-              "os": "Ubuntu-18.04"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "components_browsertests",
-        "test_id_prefix": "ninja://components:components_browsertests/"
-      },
-      {
-        "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.content_browsertests.filter",
           "--",
           "--disable-gpu",
@@ -25678,13 +25349,13 @@
         "args": [
           "context_lost",
           "--show-stdout",
-          "--browser=web-engine-shell",
+          "--browser=fuchsia-chrome",
           "--passthrough",
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
           "--custom-image=workstation.qemu-x64-release"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25706,19 +25377,19 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
           "depth_capture",
           "--show-stdout",
-          "--browser=web-engine-shell",
+          "--browser=fuchsia-chrome",
           "--passthrough",
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
           "--custom-image=workstation.qemu-x64-release"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25740,19 +25411,19 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
           "gpu_process",
           "--show-stdout",
-          "--browser=web-engine-shell",
+          "--browser=fuchsia-chrome",
           "--passthrough",
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
           "--custom-image=workstation.qemu-x64-release"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25774,19 +25445,19 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
           "hardware_accelerated_feature",
           "--show-stdout",
-          "--browser=web-engine-shell",
+          "--browser=fuchsia-chrome",
           "--passthrough",
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
           "--custom-image=workstation.qemu-x64-release"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25808,13 +25479,13 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
           "info_collection",
           "--show-stdout",
-          "--browser=web-engine-shell",
+          "--browser=fuchsia-chrome",
           "--passthrough",
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
@@ -25824,7 +25495,7 @@
           "0",
           "--custom-image=workstation.qemu-x64-release"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25846,25 +25517,28 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
           "maps",
           "--show-stdout",
-          "--browser=web-engine-shell",
+          "--browser=fuchsia-chrome",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
           "--custom-image=workstation.qemu-x64-release",
           "--git-revision=${got_revision}"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "maps_tests",
+        "name": "maps_pixel_validating_test",
         "precommit_args": [
           "--gerrit-issue=${patch_issue}",
           "--gerrit-patchset=${patch_set}",
@@ -25886,19 +25560,19 @@
           "idempotent": false,
           "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
           "mediapipe",
           "--show-stdout",
-          "--browser=web-engine-shell",
+          "--browser=fuchsia-chrome",
           "--passthrough",
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating",
           "--custom-image=workstation.qemu-x64-release"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25920,25 +25594,28 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
           "pixel",
           "--show-stdout",
-          "--browser=web-engine-shell",
+          "--browser=fuchsia-chrome",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
           "--custom-image=workstation.qemu-x64-release",
           "--git-revision=${got_revision}"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "pixel_tests",
+        "name": "pixel_skia_gold_validating_test",
         "precommit_args": [
           "--gerrit-issue=${patch_issue}",
           "--gerrit-patchset=${patch_set}",
@@ -25960,19 +25637,19 @@
           "idempotent": false,
           "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
           "screenshot_sync",
           "--show-stdout",
-          "--browser=web-engine-shell",
+          "--browser=fuchsia-chrome",
           "--passthrough",
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
           "--custom-image=workstation.qemu-x64-release"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -25994,19 +25671,54 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=fuchsia-chrome",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--dont-restore-color-profile-after-test",
+          "--custom-image=workstation.qemu-x64-release"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "screenshot_sync_validating_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-18.04"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
           "trace_test",
           "--show-stdout",
-          "--browser=web-engine-shell",
+          "--browser=fuchsia-chrome",
           "--passthrough",
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
           "--custom-image=workstation.qemu-x64-release"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -26028,19 +25740,19 @@
           "idempotent": false,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       },
       {
         "args": [
           "webgl_conformance",
           "--show-stdout",
-          "--browser=web-engine-shell",
+          "--browser=fuchsia-chrome",
           "--passthrough",
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
           "--custom-image=workstation.qemu-x64-release"
         ],
-        "isolate_name": "fuchsia_telemetry_gpu_integration_test",
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -26063,7 +25775,7 @@
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 18
         },
-        "test_id_prefix": "ninja://content/test:fuchsia_telemetry_gpu_integration_test/"
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
       }
     ]
   },
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index d7fbdfee..18d5df8 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -4655,6 +4655,54 @@
       }
     ]
   },
+  "Android FYI Release (Pixel 6)": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "noop_sleep",
+          "--show-stdout",
+          "--browser=android-chromium",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test_android_chrome",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "noop_sleep_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "device_os": "S",
+              "device_os_type": "userdebug",
+              "device_type": "oriole",
+              "os": "Android",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_android_chrome/"
+      }
+    ]
+  },
   "Android FYI SkiaRenderer GL (Nexus 5X)": {
     "isolated_scripts": [
       {
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 42e49e7..6f7e4a8 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -2171,6 +2171,25 @@
         "test_id_prefix": "ninja://ui/color:color_unittests/"
       },
       {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "inside_docker": "1",
+              "os": "Ubuntu-20.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "components_browsertests",
+        "test_id_prefix": "ninja://components:components_browsertests/"
+      },
+      {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.components_unittests.filter"
         ],
@@ -2603,8 +2622,7 @@
       },
       {
         "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter",
-          "--use-run"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter"
         ],
         "merge": {
           "args": [],
@@ -3369,6 +3387,24 @@
         "test_id_prefix": "ninja://ui/color:color_unittests/"
       },
       {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1",
+              "os": "Ubuntu-18.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "components_browsertests",
+        "test_id_prefix": "ninja://components:components_browsertests/"
+      },
+      {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.components_unittests.filter"
         ],
@@ -3887,8 +3923,7 @@
       },
       {
         "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter",
-          "--use-run"
+          "--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter"
         ],
         "merge": {
           "args": [],
diff --git a/testing/buildbot/chromium.webrtc.fyi.json b/testing/buildbot/chromium.webrtc.fyi.json
index 4394f0b7..640566ff 100644
--- a/testing/buildbot/chromium.webrtc.fyi.json
+++ b/testing/buildbot/chromium.webrtc.fyi.json
@@ -773,162 +773,5 @@
         "test_id_prefix": "ninja://remoting:remoting_unittests/"
       }
     ]
-  },
-  "WebRTC Chromium FYI Win8 Tester": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter",
-          "--run-manual",
-          "--test-launcher-jobs=1"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "browser_tests_functional",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-8.1-SP0"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "browser_tests",
-        "test_id_prefix": "ninja://chrome/test:browser_tests/"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtcApprtcBrowserTest.*",
-          "--run-manual",
-          "--test-launcher-jobs=1"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-8.1-SP0"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "browser_tests_apprtc",
-        "test_id_prefix": "ninja://chrome/test:browser_tests_apprtc/"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtc*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-8.1-SP0"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "content_browsertests",
-        "test_id_prefix": "ninja://content/test:content_browsertests/"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtc*MANUAL*:-UsingRealWebcam*",
-          "--run-manual",
-          "--ui-test-action-max-timeout=110000",
-          "--test-launcher-timeout=120000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "content_browsertests_stress",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-8.1-SP0"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "content_browsertests",
-        "test_id_prefix": "ninja://content/test:content_browsertests/"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc.content_unittests.filter"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-8.1-SP0"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "content_unittests",
-        "test_id_prefix": "ninja://content/test:content_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-8.1-SP0"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "jingle_unittests",
-        "test_id_prefix": "ninja://jingle:jingle_unittests/"
-      },
-      {
-        "args": [
-          "--gtest_filter=Webrtc*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-8.1-SP0"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "remoting_unittests",
-        "test_id_prefix": "ninja://remoting:remoting_unittests/"
-      }
-    ]
   }
 }
diff --git a/testing/buildbot/chromium.webrtc.json b/testing/buildbot/chromium.webrtc.json
index f5df0c3..ac6d68d 100644
--- a/testing/buildbot/chromium.webrtc.json
+++ b/testing/buildbot/chromium.webrtc.json
@@ -948,190 +948,5 @@
         "test_id_prefix": "ninja://remoting:remoting_unittests/"
       }
     ]
-  },
-  "WebRTC Chromium Win8 Tester": {
-    "gtest_tests": [
-      {
-        "annotate": "graphing",
-        "args": [
-          "--gtest_filter=WebRtcStatsPerfBrowserTest.*:WebRtcVideoDisplayPerfBrowserTests*:WebRtcVideoQualityBrowserTests*:WebRtcVideoHighBitrateBrowserTest*:WebRtcWebcamBrowserTests*",
-          "--run-manual",
-          "--ui-test-action-max-timeout=300000",
-          "--test-launcher-timeout=350000",
-          "--test-launcher-jobs=1",
-          "--test-launcher-bot-mode",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "perf_builder_name_alias": "chromium-webrtc-rel-win8",
-        "perf_config": {
-          "a_default_rev": "r_webrtc_git",
-          "r_webrtc_git": "${webrtc_got_rev}"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "browser_tests",
-        "test_id_prefix": "ninja://chrome/test:browser_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter",
-          "--run-manual",
-          "--test-launcher-jobs=1"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "browser_tests_functional",
-        "perf_builder_name_alias": "chromium-webrtc-rel-win8",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "browser_tests",
-        "test_id_prefix": "ninja://chrome/test:browser_tests/"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtcApprtcBrowserTest.*",
-          "--run-manual",
-          "--test-launcher-jobs=1"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "perf_builder_name_alias": "chromium-webrtc-rel-win8",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "browser_tests_apprtc",
-        "test_id_prefix": "ninja://chrome/test:browser_tests_apprtc/"
-      },
-      {
-        "args": [
-          "--enable-logging",
-          "--v=1",
-          "--test-launcher-jobs=1",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "perf_builder_name_alias": "chromium-webrtc-rel-win8",
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "capture_unittests",
-        "test_id_prefix": "ninja://media/capture:capture_unittests/"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtc*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "perf_builder_name_alias": "chromium-webrtc-rel-win8",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "content_browsertests",
-        "test_id_prefix": "ninja://content/test:content_browsertests/"
-      },
-      {
-        "args": [
-          "--gtest_filter=UsingRealWebcam*",
-          "--run-manual",
-          "--test-launcher-jobs=1"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "content_browsertests_sequential",
-        "perf_builder_name_alias": "chromium-webrtc-rel-win8",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "content_browsertests",
-        "test_id_prefix": "ninja://content/test:content_browsertests/"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtc*MANUAL*:-UsingRealWebcam*",
-          "--run-manual",
-          "--ui-test-action-max-timeout=110000",
-          "--test-launcher-timeout=120000"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "content_browsertests_stress",
-        "perf_builder_name_alias": "chromium-webrtc-rel-win8",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "content_browsertests",
-        "test_id_prefix": "ninja://content/test:content_browsertests/"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc.content_unittests.filter"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "perf_builder_name_alias": "chromium-webrtc-rel-win8",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "content_unittests",
-        "test_id_prefix": "ninja://content/test:content_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "perf_builder_name_alias": "chromium-webrtc-rel-win8",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "jingle_unittests",
-        "test_id_prefix": "ninja://jingle:jingle_unittests/"
-      },
-      {
-        "args": [
-          "--gtest_filter=Webrtc*"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "perf_builder_name_alias": "chromium-webrtc-rel-win8",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "remoting_unittests",
-        "test_id_prefix": "ninja://remoting:remoting_unittests/"
-      }
-    ]
   }
 }
diff --git a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter
index 3f112df..16918ed 100644
--- a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter
+++ b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter
@@ -20,7 +20,12 @@
 -All/PendingUpdateScreenTest.*
 -All/PublicSessionOobeTest.*
 -All/PublicSessionWithTermsOfServiceOobeTest.*
+-All/SAMLDeviceAttestationEnrolledTest.*
+-All/SAMLDeviceAttestationTest.*
+-All/SAMLEnrollmentTest.*
 -All/SAMLPasswordAttributesTest.*
+-All/SAMLPolicyTest.*
+-All/SamlTestWithFeatures.*
 -All/SshWarningTest.*
 -All/SyncConsentPolicyDisabledTest.*
 -All/SyncConsentTestWithParams.*
@@ -131,10 +136,6 @@
 -ResetTestWithTpmFirmwareUpdatePreserve.*
 -ResetTestWithTpmFirmwareUpdateRequested.*
 -RestoreOnStartupTestChromeOS.*
--SAMLDeviceAttestationTest.*
--SAMLEnrollmentTest.*
--SAMLPolicyTest.*
--SamlTest.*
 -SecurityTokenSamlTest.*
 -SigninToUserProfileSwitchTest.*
 -SiteIsolationFlagHandlingTest.*
diff --git a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter
index 6ccab6e..09f51deb 100644
--- a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter
+++ b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter
@@ -20,7 +20,12 @@
 All/PendingUpdateScreenTest.*
 All/PublicSessionOobeTest.*
 All/PublicSessionWithTermsOfServiceOobeTest.*
+All/SAMLDeviceAttestationEnrolledTest.*
+All/SAMLDeviceAttestationTest.*
+All/SAMLEnrollmentTest.*
 All/SAMLPasswordAttributesTest.*
+All/SAMLPolicyTest.*
+All/SamlTestWithFeatures.*
 All/SshWarningTest.*
 All/SyncConsentPolicyDisabledTest.*
 All/SyncConsentTestWithParams.*
@@ -131,10 +136,6 @@
 ResetTestWithTpmFirmwareUpdatePreserve.*
 ResetTestWithTpmFirmwareUpdateRequested.*
 RestoreOnStartupTestChromeOS.*
-SAMLDeviceAttestationTest.*
-SAMLEnrollmentTest.*
-SAMLPolicyTest.*
-SamlTest.*
 SecurityTokenSamlTest.*
 SigninToUserProfileSwitchTest.*
 SiteIsolationFlagHandlingTest.*
diff --git a/testing/buildbot/filters/fuchsia.content_browsertests.filter b/testing/buildbot/filters/fuchsia.content_browsertests.filter
index 72ef74e..1c33f7b 100644
--- a/testing/buildbot/filters/fuchsia.content_browsertests.filter
+++ b/testing/buildbot/filters/fuchsia.content_browsertests.filter
@@ -9,8 +9,6 @@
 -All/WorkerTest.WebSocketSharedWorker/*
 -AutoPictureInPictureContentBrowserTest.AutoPictureInPictureTriggeredWhenFullscreen
 -BackForwardCacheBrowserTest.DoesNotCacheIfSpeechRecognitionIsStarted
--BackForwardCacheBrowserTest.WebSocketCachedIfClosed
--BackForwardCacheBrowserTest.WebSocketNotCached
 -ContentBrowserTest.BrowserCrashCallStack
 -ContentBrowserTest.RendererCrashCallStack
 -CrossPlatformAccessibilityBrowserTest.ControlsIdsForDateTimePopup
@@ -45,19 +43,6 @@
 -WebRtcCaptureFromElementBrowserTest.VerifyCanvasCaptureWebGLFrames
 -WebRtcVideoCaptureServiceBrowserTest.FramesSentThroughTextureVirtualDeviceGetDisplayedOnPage
 
-# crbug.com/1254563: run-test-component specific issues
--All/SignedExchangeExpectCTReportBrowserTest.CTFailureSendsExpectCTReport/0
--All/SignedExchangeExpectCTReportBrowserTest.CTFailureSendsExpectCTReport/1
--All/SignedExchangeReportingBrowserTest.CertErrorSendsReport/0
--All/SignedExchangeReportingBrowserTest.CertErrorSendsReport/1
--All/SitePerProcessBrowserTest.RenderFrameProxyNotRecreatedDuringProcessShutdown/1
--BackForwardCacheBrowserTest.NonTrivialRTCPeerConnectionNotCached
--BackForwardCacheBrowserTest.TrivialRTCPeerConnectionCached
--NavigationRequestHostResolutionFailureTest.HostResolutionFailure
--NetInfoBrowserTest.TwoRenderViewsInOneProcess
--WebRtcIPPermissionDeniedTest.GatherLocalCandidates
--WebRtcIPPolicyPublicAndPrivateInterfacesTest.GatherLocalCandidates
-
 # crbug.com/1260245: Failing due to using jitless v8 out of renderer
 -DataDecoderBrowserTest.DecodeImage
 -DataDecoderBrowserTest.DecodeImageIsolated
@@ -102,21 +87,3 @@
 # crbug.com/1280308: Tests are passing on a NUC but failing on the ARM64 bots.
 -MSE_ClearKey/EncryptedMediaTest.Playback_VideoOnly_WebM_VP9Profile2/0
 -MSE_ClearKey/EncryptedMediaTest.Playback_VideoOnly_MP4_VP9Profile2/0
-
-# crbug.com/1285060
--All/AccessibilityHitTestingBrowserTest.CachingAsyncHitTest_WithPinchZoom/ZoomFactor2_UseZoomForDSFOn
--All/AccessibilityHitTestingBrowserTest.CachingAsyncHitTest/ZoomFactor2_UseZoomForDSFOn
--All/AccessibilityHitTestingBrowserTest.CachingAsyncHitTestMissesElement_WithPinchZoom/ZoomFactor2_UseZoomForDSFOn
--All/AccessibilityHitTestingBrowserTest.CachingAsyncHitTestMissesElement/ZoomFactor2_UseZoomForDSFOn
--All/AccessibilityHitTestingBrowserTest.HitTest_WithPinchZoom/ZoomFactor2_UseZoomForDSFOn
--All/AccessibilityHitTestingBrowserTest.HitTest/ZoomFactor2_UseZoomForDSFOn
--All/AccessibilityHitTestingCrossProcessBrowserTest.HitTestingInCrossProcessIframeWithScrolling/ZoomFactor2_UseZoomForDSFOn
--All/MAYBE_SitePerProcessAccessibilityDeviceScaleFactorBrowserTest.CrossSiteIframeCoordinates/0
--All/MAYBE_SitePerProcessAccessibilityDeviceScaleFactorBrowserTest.CrossSiteIframeCoordinates/1
-
-# crbug.com/1285058
--AccessibilityActionBrowserTest.ScrollIntoView
-
-# crbug.com/1285057
--AttributionsBrowserTest*
--AttributionsPrerenderBrowserTest*
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index 72701d1..5add3211 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -399,6 +399,9 @@
   def is_chromeos(self, tester_config):
     return tester_config.get('os_type') == 'chromeos'
 
+  def is_fuchsia(self, tester_config):
+    return tester_config.get('os_type') == 'fuchsia'
+
   def is_lacros(self, tester_config):
     return tester_config.get('os_type') == 'lacros'
 
@@ -997,6 +1000,8 @@
       return (
           'telemetry_gpu_integration_test' +
           BROWSER_CONFIG_TO_TARGET_SUFFIX_MAP[tester_config['browser_config']])
+    elif self.is_fuchsia(tester_config):
+      return 'telemetry_gpu_integration_test_fuchsia'
     else:
       return 'telemetry_gpu_integration_test'
 
diff --git a/testing/buildbot/generate_buildbot_json_unittest.py b/testing/buildbot/generate_buildbot_json_unittest.py
index 4297cfb9..51927d9 100755
--- a/testing/buildbot/generate_buildbot_json_unittest.py
+++ b/testing/buildbot/generate_buildbot_json_unittest.py
@@ -407,6 +407,32 @@
 ]
 """
 
+FOO_GPU_TELEMETRY_TEST_WATERFALL_FUCHSIA = """\
+[
+  {
+    'project': 'chromium',
+    'bucket': 'ci',
+    'name': 'chromium.test',
+    'machines': {
+      'Fake Tester': {
+        'os_type': 'fuchsia',
+        'browser_config': 'fuchsia-chrome',
+        'swarming': {
+          'dimension_sets': [
+            {
+              'kvm': '1',
+            },
+          ],
+        },
+        'test_suites': {
+          'gpu_telemetry_tests': 'composition_tests',
+        },
+      },
+    },
+  },
+]
+"""
+
 NVIDIA_GPU_TELEMETRY_TEST_WATERFALL = """\
 [
   {
@@ -1720,6 +1746,44 @@
 }
 """
 
+GPU_TELEMETRY_TEST_OUTPUT_FUCHSIA = """\
+{
+  "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
+  "AAAAA2 See generate_buildbot_json.py to make changes": {},
+  "Fake Tester": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "foo",
+          "--show-stdout",
+          "--browser=fuchsia-chrome",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test_fuchsia",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "foo_tests",
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "kvm": "1"
+            }
+          ],
+          "idempotent": false
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_fuchsia/"
+      }
+    ]
+  }
+}
+"""
+
 NVIDIA_GPU_TELEMETRY_TEST_OUTPUT = """\
 {
   "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
@@ -2331,6 +2395,15 @@
 }
 """
 
+GPU_TELEMETRY_GN_ISOLATE_MAP_FUCHSIA = """\
+{
+  'telemetry_gpu_integration_test_fuchsia': {
+    'label': '//chrome/test:telemetry_gpu_integration_test_fuchsia',
+    'type': 'script',
+      }
+}
+"""
+
 GN_ISOLATE_MAP_KEY_LABEL_MISMATCH="""\
 {
   'foo_test': {
@@ -2703,6 +2776,20 @@
     fbb.check_output_file_consistency(verbose=True)
     self.assertFalse(fbb.printed_lines)
 
+  def test_gpu_telemetry_tests_fuchsia(self):
+    fbb = FakeBBGen(self.args,
+                    FOO_GPU_TELEMETRY_TEST_WATERFALL_FUCHSIA,
+                    COMPOSITION_SUITE_WITH_NAME_NOT_ENDING_IN_TEST,
+                    LUCI_MILO_CFG,
+                    exceptions=NO_BAR_TEST_EXCEPTIONS,
+                    gn_isolate_map=GPU_TELEMETRY_GN_ISOLATE_MAP_FUCHSIA)
+    self.create_testing_buildbot_json_file('chromium.test.json',
+                                           GPU_TELEMETRY_TEST_OUTPUT_FUCHSIA)
+    self.create_testing_buildbot_json_file('chromium.ci.json',
+                                           GPU_TELEMETRY_TEST_OUTPUT_FUCHSIA)
+    fbb.check_output_file_consistency(verbose=True)
+    self.assertFalse(fbb.printed_lines)
+
   def test_nvidia_gpu_telemetry_tests(self):
     fbb = FakeBBGen(self.args,
                     NVIDIA_GPU_TELEMETRY_TEST_WATERFALL,
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 8ebf7dc3..58145ad 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -1729,6 +1729,16 @@
     "script": "//testing/scripts/run_gpu_integration_test_as_googletest.py",
     "type": "script",
   },
+  "telemetry_gpu_integration_test_fuchsia": {
+    "args": [
+      "../../content/test/gpu/run_gpu_integration_test_fuchsia.py",
+      "--logs-dir",
+      "${ISOLATED_OUTDIR}",
+    ],
+    "label": "//chrome/test:telemetry_gpu_integration_test_fuchsia",
+    "script": "//testing/scripts/run_gpu_integration_test_as_googletest.py",
+    "type": "script",
+  },
   "telemetry_gpu_integration_test_scripts_only": {
     "label": "//chrome/test:telemetry_gpu_integration_test_scripts_only",
     "type": "additional_compile_target",
diff --git a/testing/buildbot/internal.chromeos.fyi.json b/testing/buildbot/internal.chromeos.fyi.json
index 67a0845e..2524ee182 100644
--- a/testing/buildbot/internal.chromeos.fyi.json
+++ b/testing/buildbot/internal.chromeos.fyi.json
@@ -1135,7 +1135,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R99-14447.0.0",
+        "cros_img": "octopus-release/R99-14450.0.0",
         "name": "lacros_fyi_tast_tests_OCTOPUS_LKGM",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1146,7 +1146,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R99-14440.0.0",
+        "cros_img": "octopus-release/R99-14447.0.0",
         "name": "lacros_fyi_tast_tests_OCTOPUS_DEV",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1157,7 +1157,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R97-14324.49.0",
+        "cros_img": "octopus-release/R98-14388.24.0",
         "name": "lacros_fyi_tast_tests_OCTOPUS_BETA",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && !informational)",
@@ -1179,7 +1179,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R99-14447.0.0",
+        "cros_img": "octopus-release/R99-14450.0.0",
         "name": "ozone_unittests_OCTOPUS_LKGM",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1189,7 +1189,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R99-14440.0.0",
+        "cros_img": "octopus-release/R99-14447.0.0",
         "name": "ozone_unittests_OCTOPUS_DEV",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1199,7 +1199,7 @@
       {
         "args": [],
         "cros_board": "octopus",
-        "cros_img": "octopus-release/R97-14324.49.0",
+        "cros_img": "octopus-release/R98-14388.24.0",
         "name": "ozone_unittests_OCTOPUS_BETA",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1227,7 +1227,7 @@
       {
         "args": [],
         "cros_board": "kevin",
-        "cros_img": "kevin-release/R99-14447.0.0",
+        "cros_img": "kevin-release/R99-14450.0.0",
         "name": "lacros_all_tast_tests_KEVIN_LKGM",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && \"dep:lacros_unstable\")",
@@ -1238,7 +1238,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R99-14447.0.0",
+        "cros_img": "hana-release/R99-14450.0.0",
         "name": "lacros_all_tast_tests_HANA_LKGM",
         "swarming": {},
         "tast_expr": "(\"group:mainline\" && \"dep:lacros\" && \"dep:lacros_unstable\")",
@@ -1249,7 +1249,7 @@
       {
         "args": [],
         "cros_board": "kevin",
-        "cros_img": "kevin-release/R99-14447.0.0",
+        "cros_img": "kevin-release/R99-14450.0.0",
         "name": "ozone_unittests_KEVIN_LKGM",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1259,7 +1259,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R99-14447.0.0",
+        "cros_img": "hana-release/R99-14450.0.0",
         "name": "ozone_unittests_HANA_LKGM",
         "swarming": {},
         "test": "ozone_unittests",
@@ -1269,7 +1269,7 @@
       {
         "args": [],
         "cros_board": "kevin",
-        "cros_img": "kevin-release/R99-14447.0.0",
+        "cros_img": "kevin-release/R99-14450.0.0",
         "name": "viz_unittests_KEVIN_LKGM",
         "swarming": {},
         "test": "viz_unittests",
@@ -1279,7 +1279,7 @@
       {
         "args": [],
         "cros_board": "hana",
-        "cros_img": "hana-release/R99-14447.0.0",
+        "cros_img": "hana-release/R99-14450.0.0",
         "name": "viz_unittests_HANA_LKGM",
         "swarming": {},
         "test": "viz_unittests",
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index ad452e52..5c72a8f2 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -214,6 +214,13 @@
       },
     },
   },
+  'android_s': {
+    'swarming': {
+      'dimensions': {
+        'device_os': 'S',
+      },
+    },
+  },
   'arm64': {
     'swarming': {
       'dimensions': {
@@ -332,9 +339,6 @@
   'chromium-webrtc-rel-win10': {
     'perf_builder_name_alias': 'chromium-webrtc-rel-win10',
   },
-  'chromium-webrtc-rel-win8': {
-    'perf_builder_name_alias': 'chromium-webrtc-rel-win8',
-  },
   # Used for invert CQ tests selection. Adding ci_only: False to
   # test_suite_exceptions.pyl to select tests that are allowed on CQ builders.
   'ci_only': {
@@ -921,6 +925,15 @@
       },
     },
   },
+  'oriole': {
+    # Pixel 6
+    'swarming': {
+      'dimensions': {
+        'device_type': 'oriole',
+        'os': 'Android',
+      },
+    },
+  },
   'out_dir_arg': {
     '$mixin_append': {
       'args': [
@@ -1138,13 +1151,6 @@
       },
     },
   },
-  'win8': {
-    'swarming': {
-      'dimensions': {
-        'os': 'Windows-8.1-SP0',
-      },
-    },
-  },
   'x86-32': {
     'swarming': {
       'dimensions': {
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index ca15da0..6cdf7ba3 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1071,15 +1071,6 @@
       }
     }
   },
-  'chrome_public_wpt': {
-    'modifications': {
-      'android-web-platform-pie-x86-fyi-rel': {
-        'swarming': {
-          'shards': 18,
-        },
-      },
-    },
-  },
   'chrome_sizes': {
     'modifications': {
       'lacros-amd64-generic-chrome': {
@@ -2343,14 +2334,6 @@
         },
       },
     },
-    'replacements': {
-      # TODO(crbug.com/1254563): Remove when suite can use run-test-component.
-      'fuchsia-code-coverage': {
-        'args': {
-          '--use-run': None,
-        },
-      },
-    },
   },
   'not_site_per_process_blink_web_tests': {
     'remove_from': [
@@ -2864,10 +2847,8 @@
       'android-webview-pie-x86-wpt-fyi-rel': {
         'args': [
           '--log-wptreport',
+          '--processes=2',
         ],
-        'swarming': {
-          'shards': 18,
-        },
       },
     },
   },
@@ -3489,19 +3470,13 @@
   },
   'weblayer_shell_wpt': {
     'modifications': {
-      # TODO(crbug.com/1171555): remove this when test can run with more emulators
-      'android-weblayer-pie-x86-wpt-fyi-rel': {
-        'swarming': {
-          'shards': 18,
-        },
-      },
       'android-weblayer-pie-x86-wpt-smoketest': {
         'args': [
           '--default-exclude',
-          '--include-file=../../third_party/blink/web_tests/android/WPTSmokeTestCases'
+          '--include-file=../../third_party/blink/web_tests/android/WPTSmokeTestCases',
         ],
         'swarming': {
-          'shards': 3,
+          'shards': 1,
         },
       },
     },
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index fd3d07b..70f7dd93 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -540,6 +540,13 @@
           'has_native_resultdb_integration',
         ],
       },
+      'variations_smoke_tests': {
+        'isolate_name': 'variations_smoke_tests',
+        'resultdb': {
+          'enable': True,
+          'result_format': 'single'
+        },
+      },
     },
 
     'chrome_public_tests': {
@@ -563,8 +570,12 @@
 
     'chrome_public_wpt': {
       'chrome_public_wpt': {
+        'args': [
+          '--log-wptreport',
+          '--processes=2',
+        ],
         'swarming': {
-          'shards': 15,
+          'shards': 18,
           'expiration': 18000,
           'hard_timeout': 14400,
         },
@@ -1660,6 +1671,7 @@
       'boringssl_ssl_tests': {},
       'capture_unittests': {},
       'color_unittests': {},
+      'components_browsertests': {},
       'components_unittests': {
         'swarming': {
           'shards': 2,
@@ -1706,8 +1718,6 @@
         },
         'args': [
           '--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.net_unittests.filter',
-          # TODO(crbug.com/1254563): Fix net_unittests to work under run-test-component.
-          '--use-run',
         ],
       },
       'ozone_gl_unittests': {
@@ -1760,7 +1770,6 @@
     },
 
     'fuchsia_experimental_gtests': {
-      'components_browsertests': {},
       'content_browsertests': {
         'args': [
           '--test-launcher-filter-file=../../testing/buildbot/filters/fuchsia.content_browsertests.filter',
@@ -2777,6 +2786,37 @@
       },
     },
 
+    'gpu_fuchsia_common_telemetry_tests': {
+      'info_collection': {
+        'args': [
+          '--expected-vendor-id',
+          '${gpu_vendor_id}',
+          '$$MAGIC_SUBSTITUTION_GPUExpectedDeviceId',
+        ],
+        'mixins': [
+          'has_native_resultdb_integration',
+        ],
+      },
+      'screenshot_sync': {
+        'mixins': [
+          'has_native_resultdb_integration',
+        ],
+      },
+      'trace_test': {
+        'mixins': [
+          'has_native_resultdb_integration',
+        ],
+      },
+      'webgl_conformance': {
+        'mixins': [
+          'has_native_resultdb_integration',
+        ],
+        'swarming': {
+          'shards': 18,
+        },
+      }
+    },
+
     'gpu_fyi_and_optional_non_linux_gtests': {
       # gpu_unittests is killing the Swarmed Linux GPU bots similarly to
       # how content_unittests was: http://crbug.com/763498 .
@@ -5125,7 +5165,7 @@
     'system_webview_wpt': {
       'system_webview_wpt': {
         'swarming': {
-          'shards': 15,
+          'shards': 18,
           'expiration': 18000,
           'hard_timeout': 14400,
         },
@@ -5313,6 +5353,16 @@
       'upload_trace_processor': {},
     },
 
+    'variations_smoke_tests': {
+      'variations_smoke_tests': {
+        'isolate_name': 'variations_smoke_tests',
+        'resultdb': {
+          'enable': True,
+          'result_format': 'single'
+        },
+      },
+    },
+
     # Not applicable for android x86 & x64 since the targets here assert
     # "enable_vr" in GN which is only true for android arm & arm64.
     # For details, see the following files:
@@ -5403,8 +5453,11 @@
 
     'weblayer_shell_wpt': {
       'weblayer_shell_wpt': {
+        'args': [
+          '--processes=2',
+        ],
         'swarming': {
-          'shards': 15,
+          'shards': 18,
           'expiration': 18000,
           'hard_timeout': 14400,
         },
@@ -5985,11 +6038,6 @@
       'webview_ui_instrumentation_tests',
     ],
 
-    'android_wpt_scripts': [
-      'system_webview_wpt',
-      'weblayer_shell_wpt',
-    ],
-
     'backuprefptr_gtests': [
       'backuprefptr_generic_gtests',
     ],
@@ -6477,6 +6525,12 @@
       'rendering_desktop_representative_perf_tests_isolated_scripts',
     ],
 
+    'gpu_fuchsia_telemetry_tests': [
+      'gpu_fuchsia_common_telemetry_tests',
+      'gpu_mediapipe_validating_telemetry_tests',
+      'gpu_validating_telemetry_tests',
+    ],
+
     'gpu_fyi_android_gtests': [
       'gpu_angle_unit_gtests',
       'gpu_common_gtests_validating',
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 9ce8df2..f453747 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -563,8 +563,8 @@
   'CROS_ATLAS_LKGM': {
     'skylab': {
       'cros_board': 'atlas',
-      'cros_chrome_version': '99.0.4815.0',
-      'cros_img': 'atlas-release/R99-14447.0.0',
+      'cros_chrome_version': '99.0.4817.0',
+      'cros_img': 'atlas-release/R99-14450.0.0',
     },
     'enabled': True,
     'identifier': 'ATLAS_LKGM',
@@ -572,8 +572,8 @@
   'CROS_ATLAS_DEV': {
     'skylab': {
       'cros_board': 'atlas',
-      'cros_chrome_version': '99.0.4807.0',
-      'cros_img': 'atlas-release/R99-14440.0.0',
+      'cros_chrome_version': '99.0.4815.0',
+      'cros_img': 'atlas-release/R99-14447.0.0',
     },
     'enabled': True,
     'identifier': 'ATLAS_DEV',
@@ -581,8 +581,8 @@
   'CROS_ATLAS_BETA': {
     'skylab': {
       'cros_board': 'atlas',
-      'cros_chrome_version': '97.0.4692.70',
-      'cros_img': 'atlas-release/R97-14324.56.0',
+      'cros_chrome_version': '98.0.4758.46',
+      'cros_img': 'atlas-release/R98-14388.24.0',
     },
     'enabled': True,
     'identifier': 'ATLAS_BETA',
@@ -599,8 +599,8 @@
   'CROS_EVE_LKGM': {
     'skylab': {
       'cros_board': 'eve',
-      'cros_chrome_version': '99.0.4815.0',
-      'cros_img': 'eve-release/R99-14447.0.0',
+      'cros_chrome_version': '99.0.4817.0',
+      'cros_img': 'eve-release/R99-14450.0.0',
     },
     'enabled': True,
     'identifier': 'EVE_LKGM',
@@ -608,8 +608,8 @@
   'CROS_EVE_DEV': {
     'skylab': {
       'cros_board': 'eve',
-      'cros_chrome_version': '99.0.4807.0',
-      'cros_img': 'eve-release/R99-14440.0.0',
+      'cros_chrome_version': '99.0.4815.0',
+      'cros_img': 'eve-release/R99-14447.0.0',
     },
     'enabled': True,
     'identifier': 'EVE_DEV',
@@ -617,8 +617,8 @@
   'CROS_EVE_BETA': {
     'skylab': {
       'cros_board': 'eve',
-      'cros_chrome_version': '97.0.4692.70',
-      'cros_img': 'eve-release/R97-14324.56.0',
+      'cros_chrome_version': '98.0.4758.46',
+      'cros_img': 'eve-release/R98-14388.24.0',
     },
     'enabled': True,
     'identifier': 'EVE_BETA',
@@ -635,8 +635,8 @@
   'CROS_KEVIN_LKGM': {
     'skylab': {
       'cros_board': 'kevin',
-      'cros_chrome_version': '99.0.4815.0',
-      'cros_img': 'kevin-release/R99-14447.0.0',
+      'cros_chrome_version': '99.0.4817.0',
+      'cros_img': 'kevin-release/R99-14450.0.0',
     },
     'enabled': True,
     'identifier': 'KEVIN_LKGM',
@@ -644,8 +644,8 @@
   'CROS_HANA_LKGM': {
     'skylab': {
       'cros_board': 'hana',
-      'cros_chrome_version': '99.0.4815.0',
-      'cros_img': 'hana-release/R99-14447.0.0',
+      'cros_chrome_version': '99.0.4817.0',
+      'cros_img': 'hana-release/R99-14450.0.0',
     },
     'enabled': True,
     'identifier': 'HANA_LKGM',
@@ -653,8 +653,8 @@
   'CROS_OCTOPUS_LKGM': {
     'skylab': {
       'cros_board': 'octopus',
-      'cros_chrome_version': '99.0.4815.0',
-      'cros_img': 'octopus-release/R99-14447.0.0',
+      'cros_chrome_version': '99.0.4817.0',
+      'cros_img': 'octopus-release/R99-14450.0.0',
     },
     'enabled': True,
     'identifier': 'OCTOPUS_LKGM',
@@ -662,8 +662,8 @@
   'CROS_OCTOPUS_DEV': {
     'skylab': {
       'cros_board': 'octopus',
-      'cros_chrome_version': '99.0.4807.0',
-      'cros_img': 'octopus-release/R99-14440.0.0',
+      'cros_chrome_version': '99.0.4815.0',
+      'cros_img': 'octopus-release/R99-14447.0.0',
     },
     'enabled': True,
     'identifier': 'OCTOPUS_DEV',
@@ -671,8 +671,8 @@
   'CROS_OCTOPUS_BETA': {
     'skylab': {
       'cros_board': 'octopus',
-      'cros_chrome_version': '97.0.4692.63',
-      'cros_img': 'octopus-release/R97-14324.49.0',
+      'cros_chrome_version': '98.0.4758.46',
+      'cros_img': 'octopus-release/R98-14388.24.0',
     },
     'enabled': True,
     'identifier': 'OCTOPUS_BETA',
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index f343eba..c91ea74 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1139,7 +1139,7 @@
           'walleye',
         ],
         'test_suites': {
-          'isolated_scripts': 'android_wpt_scripts',
+          'isolated_scripts': 'system_webview_wpt',
         },
         'use_swarming': True,
         'os_type': 'android',
@@ -1735,7 +1735,7 @@
           'all',
         ],
         'browser_config': 'web-engine-shell',
-        'os_type': 'linux',
+        'os_type': 'fuchsia',
         'mixins': [
           'linux-xenial',
         ],
@@ -2818,10 +2818,7 @@
         'additional_compile_targets': [
           'all',
         ],
-        'browser_config': 'web-engine-shell',
-        'os_type': 'linux',
         'test_suites': {
-          'gpu_telemetry_tests': 'fuchsia_gpu_telemetry_tests',
           'gtest_tests': 'fuchsia_web_engine_non_graphical_gtests',
           'isolated_scripts': 'fuchsia_isolated_scripts',
         },
@@ -2885,7 +2882,7 @@
           'all',
         ],
         'browser_config': 'web-engine-shell',
-        'os_type': 'linux',
+        'os_type': 'fuchsia',
         'mixins': [
           'linux-bionic',
         ],
@@ -2897,13 +2894,13 @@
           ],
         },
         'test_suites': {
-          'gpu_telemetry_tests': 'fuchsia_gpu_telemetry_tests',
+          'gpu_telemetry_tests': 'gpu_fuchsia_telemetry_tests',
           'gtest_tests': 'fuchsia_experimental_gtests',
         },
       },
       'fuchsia-fyi-x64-wst': {
-        'browser_config': 'web-engine-shell',
-        'os_type': 'linux',
+        'browser_config': 'fuchsia-chrome',
+        'os_type': 'fuchsia',
         'mixins': [
           'linux-bionic',
         ],
@@ -2918,7 +2915,7 @@
           ],
         },
         'test_suites': {
-          'gpu_telemetry_tests': 'fuchsia_gpu_telemetry_tests',
+          'gpu_telemetry_tests': 'gpu_fuchsia_telemetry_tests',
           'gtest_tests': 'fuchsia_experimental_gtests',
         },
       },
@@ -4085,6 +4082,20 @@
           'android_webview_gpu_telemetry_tests': 'android_webview_gpu_telemetry_tests',
         },
       },
+      'Android FYI Release (Pixel 6)': {
+        'browser_config': 'android-chromium',
+        'os_type': 'android',
+        'skip_merge_script': True,
+        'mixins': [
+          'android_s',
+          'has_native_resultdb_integration',
+          'oriole',
+          'gpu-swarming-pool',
+        ],
+        'test_suites': {
+          'gpu_telemetry_tests': 'gpu_noop_sleep_telemetry_test'
+        },
+      },
       'Android FYI SkiaRenderer GL (Nexus 5X)': {
         'os_type': 'android',
         'browser_config': 'android-chromium',
@@ -4836,7 +4847,7 @@
           'all',
         ],
         'browser_config': 'web-engine-shell',
-        'os_type': 'linux',
+        'os_type': 'fuchsia',
         'mixins': [
           'linux-bionic',
         ],
@@ -5926,14 +5937,6 @@
           'gtest_tests': 'webrtc_chromium_tests_with_baremetal_tests',
         },
       },
-      'WebRTC Chromium Win8 Tester': {
-        'mixins': [
-          'chromium-webrtc-rel-win8',
-        ],
-        'test_suites': {
-          'gtest_tests': 'webrtc_chromium_tests_with_baremetal_tests',
-        },
-      },
     },
   },
   {
@@ -6038,15 +6041,6 @@
           'gtest_tests': 'webrtc_chromium_gtests',
         },
       },
-      'WebRTC Chromium FYI Win8 Tester': {
-        'mixins': [
-          'x86-64',
-          'win8',
-        ],
-        'test_suites': {
-          'gtest_tests': 'webrtc_chromium_gtests',
-        },
-      },
     },
   },
   {
diff --git a/testing/scripts/run_variations_smoke_tests.py b/testing/scripts/run_variations_smoke_tests.py
index e80022d9..e60cbe4 100755
--- a/testing/scripts/run_variations_smoke_tests.py
+++ b/testing/scripts/run_variations_smoke_tests.py
@@ -18,10 +18,14 @@
 from threading import Thread
 
 import common
+import six
 import variations_seed_access_helper as seed_helper
 from variations_http_test_server import HTTPServer
 from variations_http_test_server import HTTPHandler
 
+if six.PY3:
+  import http
+
 _THIS_DIR = os.path.dirname(os.path.abspath(__file__))
 _SRC_DIR = os.path.join(_THIS_DIR, os.path.pardir, os.path.pardir)
 _WEBDRIVER_PATH = os.path.join(_SRC_DIR, 'third_party', 'webdriver', 'pylib')
@@ -46,7 +50,7 @@
         'expected_text': 'Success',
     },
     {
-        'url': 'https://localhost:8000',
+        'url': 'http://localhost:8000',
         'expected_id': 'sites-chrome-userheader-title',
         'expected_text': 'The Chromium Projects',
     },
@@ -56,9 +60,17 @@
 def _get_httpd():
   """Returns a HTTPServer instance."""
   hostname = "localhost"
-  port = 0
+  port = 8000
   directory = os.path.join(_THIS_DIR, _VARIATIONS_TEST_DATA, "http_server")
-  httpd = HTTPServer(directory, (hostname, port))
+  httpd = None
+  if six.PY3:
+    handler = partial(http.server.SimpleHTTPRequestHandler, directory=directory)
+    httpd = http.server.HTTPServer((hostname, port), handler)
+    httpd.timeout = 0.5
+    httpd.allow_reuse_address = True
+    httpd.server_bind()
+  else:
+    httpd = HTTPServer(directory, (hostname, port))
   return httpd
 
 
@@ -250,9 +262,15 @@
     A local http.server.HTTPServer.
   """
   httpd = _get_httpd()
-  logging.info("%s is used as local http server.", httpd.address_string())
-  thread = Thread(target=httpd.server_forever)
-  thread.daemon = True
+  thread = None
+  if six.PY3:
+    address = "http://{}:{}".format(httpd.server_name, httpd.server_port)
+    logging.info("%s is used as local http server.", address)
+    thread = Thread(target=httpd.serve_forever)
+    thread.setDaemon(True)
+  else:
+    thread = Thread(target=httpd.serve_forever)
+    thread.daemon = True
   thread.start()
   return httpd
 
diff --git a/testing/scripts/variations_http_test_server.py b/testing/scripts/variations_http_test_server.py
index e0817d70..b910688 100644
--- a/testing/scripts/variations_http_test_server.py
+++ b/testing/scripts/variations_http_test_server.py
@@ -5,6 +5,7 @@
 serves content from a base_path.
 """
 
+import os
 try:
   # Python 2
   from SimpleHTTPServer import SimpleHTTPRequestHandler
@@ -30,6 +31,7 @@
     self.stop = False
     BaseHTTPServer.__init__(self, server_address, RequestHandlerClass)
 
+  #pylint: disable=unused-argument
   def serve_forever(self, poll_interval=0.1):
     self.stop = False
     while not self.stop:
diff --git a/testing/scripts/wpt_android_lib.py b/testing/scripts/wpt_android_lib.py
index af802c3..9726a68 100644
--- a/testing/scripts/wpt_android_lib.py
+++ b/testing/scripts/wpt_android_lib.py
@@ -83,7 +83,7 @@
     env = os.environ.copy()
     if 'GTEST_SHARD_INDEX' in env:
       shard_index = int(env['GTEST_SHARD_INDEX'])
-      return 'wpt_reports_%s_%d.json' % (self.options.product, shard_index)
+      return 'wpt_reports_%s_%02d.json' % (self.options.product, shard_index)
     else:
       return 'wpt_reports_%s.json' % self.options.product
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 1f1c47b0..fa831b9 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -624,6 +624,21 @@
             ]
         }
     ],
+    "AndroidRDSGlobal": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "RequestDesktopSiteGlobal"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidRequestMobileSiteOption": [
         {
             "platforms": [
@@ -1498,7 +1513,6 @@
                         "file_system_api_supported": "true",
                         "foreground_cache_size": "2",
                         "grace_period_to_finish_loading_in_seconds": "60",
-                        "max_buffered_bytes": "200000",
                         "max_buffered_bytes_per_process": "1024000"
                     },
                     "enable_features": [
@@ -1532,7 +1546,6 @@
                         "file_system_api_supported": "true",
                         "foreground_cache_size": "2",
                         "grace_period_to_finish_loading_in_seconds": "60",
-                        "max_buffered_bytes": "200000",
                         "max_buffered_bytes_per_process": "1024000",
                         "supported_features": "MediaSessionImplOnServiceCreated",
                         "unload_support": "opt_in_header_required"
@@ -1721,6 +1734,23 @@
             ]
         }
     ],
+    "ButterForPasswordsRevisedOptInFlow": [
+        {
+            "platforms": [
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_20211203",
+                    "enable_features": [
+                        "PasswordsAccountStorageRevisedOptInFlow"
+                    ]
+                }
+            ]
+        }
+    ],
     "CPSS": [
         {
             "platforms": [
@@ -2323,21 +2353,6 @@
             ]
         }
     ],
-    "ClankDetailedLanguageSettings": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "DetailedLanguageSettings"
-                    ]
-                }
-            ]
-        }
-    ],
     "ClientSideDetectionReferrerChain": [
         {
             "platforms": [
@@ -2556,34 +2571,6 @@
             ]
         }
     ],
-    "ConsumeCodeCacheOffThread": [
-        {
-            "platforms": [
-                "android",
-                "android_weblayer",
-                "android_webview",
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ConsumeCodeCacheOffThread"
-                    ]
-                },
-                {
-                    "name": "Disabled",
-                    "disable_features": [
-                        "ConsumeCodeCacheOffThread"
-                    ]
-                }
-            ]
-        }
-    ],
     "ContentCaptureExperiment": [
         {
             "platforms": [
@@ -2677,6 +2664,21 @@
             ]
         }
     ],
+    "ContextMenuPopupStyleAndroid": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ContextMenuPopupStyle"
+                    ]
+                }
+            ]
+        }
+    ],
     "ContextMenuShopWithGoogleLens": [
         {
             "platforms": [
@@ -2775,7 +2777,7 @@
             ]
         }
     ],
-    "CriticalPersistedTabData_V2": [
+    "CriticalPersistedTabDataV2": [
         {
             "platforms": [
                 "android"
@@ -2924,6 +2926,23 @@
                     ]
                 }
             ]
+        },
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "DrDc",
+                    "enable_features": [
+                        "EnableDrDc"
+                    ],
+                    "disable_features": [
+                        "DefaultPassthroughCommandDecoder",
+                        "RawDraw"
+                    ]
+                }
+            ]
         }
     ],
     "DeprioritizeTimersUntilDOMContentLoadedExperiment": [
@@ -3608,22 +3627,6 @@
             ]
         }
     ],
-    "EnableDrDc": [
-        {
-            "platforms": [
-                "android",
-                "android_weblayer"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "EnableDrDc"
-                    ]
-                }
-            ]
-        }
-    ],
     "EnableDuplicateDownloadDialog": [
         {
             "platforms": [
@@ -3755,26 +3758,6 @@
             ]
         }
     ],
-    "FirstInputDelayWithoutEventListener": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "FirstInputDelayWithoutEventListener"
-                    ]
-                }
-            ]
-        }
-    ],
     "FirstPartySetsOriginTrial": [
         {
             "platforms": [
@@ -3798,26 +3781,6 @@
             ]
         }
     ],
-    "FixFirstInputDelayForDesktop": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "FixFirstInputDelayForDesktop"
-                    ]
-                }
-            ]
-        }
-    ],
     "FocusHelpBubbleScreenReaderPromo": [
         {
             "platforms": [
@@ -4592,6 +4555,27 @@
             ]
         }
     ],
+    "IncludeIpcOverheadInNavigationStart": [
+        {
+            "platforms": [
+                "android",
+                "android_weblayer",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "IncludeIpcOverheadInNavigationStart"
+                    ]
+                }
+            ]
+        }
+    ],
     "IncompatibleApplicationsWarning": [
         {
             "platforms": [
@@ -5352,26 +5336,6 @@
             ]
         }
     ],
-    "NetworkRequestUsesOnlyPerProcessBufferLimit": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "chromeos_lacros",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "NetworkRequestUsesOnlyPerProcessBufferLimit"
-                    ]
-                }
-            ]
-        }
-    ],
     "NoWakeUpsForCanceledTasks": [
         {
             "platforms": [
@@ -5489,21 +5453,6 @@
             ]
         }
     ],
-    "OfflinePageServiceTurndownM91": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Turndown_Disabled",
-                    "disable_features": [
-                        "OfflinePagesPrefetching"
-                    ]
-                }
-            ]
-        }
-    ],
     "OmniboxBundledExperimentV1": [
         {
             "platforms": [
@@ -6161,6 +6110,21 @@
             ]
         }
     ],
+    "PasswordChangeFromLeakWarning": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "PasswordChange"
+                    ]
+                }
+            ]
+        }
+    ],
     "PdfUnseasoned": [
         {
             "platforms": [
@@ -7032,29 +6996,27 @@
             ]
         }
     ],
-    "SafeBrowsingCsdComponentVersion25": [
+    "SafeBrowsingCompareCSDDesktop": [
         {
             "platforms": [
-                "chromeos",
-                "chromeos_lacros",
+                "windows",
                 "linux",
-                "mac",
-                "windows"
+                "mac"
             ],
             "experiments": [
                 {
-                    "name": "Enabled25_052021",
+                    "name": "EnabledTfLiteAndBaseline_20220111",
                     "params": {
-                        "reporter_omaha_tag": "25"
+                        "reporter_omaha_tag": "27.4"
                     },
                     "enable_features": [
                         "ClientSideDetectionTag"
                     ]
                 },
                 {
-                    "name": "Enabled26_052021",
+                    "name": "EnabledTfLiteOnly_20220111",
                     "params": {
-                        "reporter_omaha_tag": "26"
+                        "reporter_omaha_tag": "27.3"
                     },
                     "enable_features": [
                         "ClientSideDetectionTag"
@@ -8212,6 +8174,119 @@
             ]
         }
     ],
+    "V8ConcurrentSparkplug": [
+        {
+            "platforms": [
+                "linux",
+                "windows",
+                "mac",
+                "chromeos_lacros",
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "1Thread",
+                    "params": {
+                        "V8ConcurrentSparkplugMaxThreads": "1"
+                    },
+                    "enable_features": [
+                        "V8ConcurrentSparkplug",
+                        "V8Sparkplug"
+                    ]
+                },
+                {
+                    "name": "Control",
+                    "disable_features": [
+                        "V8ConcurrentSparkplug"
+                    ]
+                },
+                {
+                    "name": "2Threads",
+                    "params": {
+                        "V8ConcurrentSparkplugMaxThreads": "2"
+                    },
+                    "enable_features": [
+                        "V8ConcurrentSparkplug",
+                        "V8Sparkplug"
+                    ]
+                },
+                {
+                    "name": "ManyThreads",
+                    "params": {
+                        "V8ConcurrentSparkplugMaxThreads": "0"
+                    },
+                    "enable_features": [
+                        "V8ConcurrentSparkplug",
+                        "V8Sparkplug"
+                    ]
+                },
+                {
+                    "name": "NoSparkplug",
+                    "disable_features": [
+                        "V8ConcurrentSparkplug",
+                        "V8Sparkplug"
+                    ]
+                }
+            ]
+        }
+    ],
+    "V8ConcurrentSparkplug-Android": [
+        {
+            "platforms": [
+                "android",
+                "android_weblayer",
+                "android_webview"
+            ],
+            "experiments": [
+                {
+                    "name": "1Thread",
+                    "params": {
+                        "V8ConcurrentSparkplugMaxThreads": "1"
+                    },
+                    "enable_features": [
+                        "V8ConcurrentSparkplug",
+                        "V8Sparkplug"
+                    ]
+                },
+                {
+                    "name": "Control",
+                    "disable_features": [
+                        "V8ConcurrentSparkplug",
+                        "V8Sparkplug"
+                    ]
+                },
+                {
+                    "name": "2Threads",
+                    "params": {
+                        "V8ConcurrentSparkplugMaxThreads": "2"
+                    },
+                    "enable_features": [
+                        "V8ConcurrentSparkplug",
+                        "V8Sparkplug"
+                    ]
+                },
+                {
+                    "name": "ManyThreads",
+                    "params": {
+                        "V8ConcurrentSparkplugMaxThreads": "0"
+                    },
+                    "enable_features": [
+                        "V8ConcurrentSparkplug",
+                        "V8Sparkplug"
+                    ]
+                },
+                {
+                    "name": "WithSparkplug",
+                    "enable_features": [
+                        "V8Sparkplug"
+                    ],
+                    "disable_features": [
+                        "V8ConcurrentSparkplug"
+                    ]
+                }
+            ]
+        }
+    ],
     "V8FastApiCalls": [
         {
             "platforms": [
@@ -8700,20 +8775,6 @@
             ]
         }
     ],
-    "WebRTC-Aec3ConservativeTailFreqResponse": [
-        {
-            "platforms": [
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled"
-                }
-            ]
-        }
-    ],
     "WebRTC-Aec3DelayEstimateSmoothingDelayFoundOverride": [
         {
             "platforms": [
@@ -8910,6 +8971,25 @@
             ]
         }
     ],
+    "WebRtcMetronomeTaskQueue": [
+        {
+            "platforms": [
+                "windows",
+                "mac",
+                "chromeos",
+                "chromeos_lacros",
+                "linux"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_V1",
+                    "enable_features": [
+                        "WebRtcMetronomeTaskQueue"
+                    ]
+                }
+            ]
+        }
+    ],
     "WebUICodeCache": [
         {
             "platforms": [
diff --git a/third_party/abseil-cpp/symbols_arm64_rel.def b/third_party/abseil-cpp/symbols_arm64_rel.def
index 883c8cf..d7695d2d 100644
--- a/third_party/abseil-cpp/symbols_arm64_rel.def
+++ b/third_party/abseil-cpp/symbols_arm64_rel.def
@@ -84,6 +84,8 @@
     ??$SetEdge@$0A@@CordRepBtree@cord_internal@absl@@QEAA?AUOpResult@012@_NPEAUCordRep@12@_K@Z
     ??$StrReplaceAll@V?$initializer_list@U?$pair@Vstring_view@absl@@V12@@__1@std@@@std@@@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@Vstring_view@0@AEBV?$initializer_list@U?$pair@Vstring_view@absl@@V12@@__1@std@@@3@@Z
     ??$StrReplaceAll@V?$initializer_list@U?$pair@Vstring_view@absl@@V12@@__1@std@@@std@@@absl@@YAHAEBV?$initializer_list@U?$pair@Vstring_view@absl@@V12@@__1@std@@@std@@PEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@2@@Z
+    ??$ToChronoDuration@V?$duration@_JV?$ratio@$00$0DOI@@__1@std@@@chrono@__1@std@@@time_internal@absl@@YA?AV?$duration@_JV?$ratio@$00$0DOI@@__1@std@@@chrono@__1@std@@VDuration@1@@Z
+    ??$ToChronoDuration@V?$duration@_JV?$ratio@$00$0PECEA@@__1@std@@@chrono@__1@std@@@time_internal@absl@@YA?AV?$duration@_JV?$ratio@$00$0PECEA@@__1@std@@@chrono@__1@std@@VDuration@1@@Z
     ??$__construct_node_hash@AEBUpiecewise_construct_t@__1@std@@V?$tuple@AEBV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@23@V?$tuple@$$V@23@@?$__hash_table@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__1@std@@V?$__unordered_map_hasher@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@23@$00@23@V?$__unordered_map_equal@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@23@$00@23@V?$allocator@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__1@std@@@23@@__1@std@@AEAA?AV?$unique_ptr@U?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__1@std@@PEAX@__1@std@@V?$__hash_node_destructor@V?$allocator@U?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__1@std@@PEAX@__1@std@@@__1@std@@@23@@12@_KAEBUpiecewise_construct_t@12@$$QEAV?$tuple@AEBV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@12@$$QEAV?$tuple@$$V@12@@Z
     ??$__emplace_back_slow_path@AEAVstring_view@absl@@AEBV12@AEA_K@?$vector@UViableSubstitution@strings_internal@absl@@V?$allocator@UViableSubstitution@strings_internal@absl@@@__1@std@@@__1@std@@AEAAXAEAVstring_view@absl@@AEBV34@AEA_K@Z
     ??$__emplace_unique_key_args@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@AEBUpiecewise_construct_t@23@V?$tuple@AEBV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@23@V?$tuple@$$V@23@@?$__hash_table@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__1@std@@V?$__unordered_map_hasher@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@23@$00@23@V?$__unordered_map_equal@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@23@U?$equal_to@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@23@U?$hash@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@23@$00@23@V?$allocator@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__1@std@@@23@@__1@std@@QEAA?AU?$pair@V?$__hash_iterator@PEAU?$__hash_node@U?$__hash_value_type@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@PEBVImpl@time_zone@cctz@time_internal@absl@@@__1@std@@PEAX@__1@std@@@__1@std@@_N@12@AEBV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@12@AEBUpiecewise_construct_t@12@$$QEAV?$tuple@AEBV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@12@$$QEAV?$tuple@$$V@12@@Z
diff --git a/third_party/abseil-cpp/symbols_x64_rel.def b/third_party/abseil-cpp/symbols_x64_rel.def
index 034462b..4d392c6 100644
--- a/third_party/abseil-cpp/symbols_x64_rel.def
+++ b/third_party/abseil-cpp/symbols_x64_rel.def
@@ -204,7 +204,6 @@
     ??Gdetail@cctz@time_internal@absl@@YA?AV?$civil_time@Uday_tag@detail@cctz@time_internal@absl@@@0123@V40123@_J@Z
     ??Gdetail@cctz@time_internal@absl@@YA?AV?$civil_time@Usecond_tag@detail@cctz@time_internal@absl@@@0123@V40123@_J@Z
     ??Hdetail@cctz@time_internal@absl@@YA?AV?$civil_time@Uday_tag@detail@cctz@time_internal@absl@@@0123@V40123@_J@Z
-    ??Hdetail@cctz@time_internal@absl@@YA?AV?$civil_time@Usecond_tag@detail@cctz@time_internal@absl@@@0123@V40123@_J@Z
     ??Kabsl@@YA?AVint128@0@V10@0@Z
     ??Kabsl@@YA?AVuint128@0@V10@0@Z
     ??Labsl@@YA?AVint128@0@V10@0@Z
@@ -912,7 +911,6 @@
     ?max@?$RandenPool@G@random_internal@absl@@SAGXZ
     ?max@?$RandenPool@I@random_internal@absl@@SAIXZ
     ?max@?$RandenPool@_K@random_internal@absl@@SA_KXZ
-    ?max@?$civil_time@Usecond_tag@detail@cctz@time_internal@absl@@@detail@cctz@time_internal@absl@@SA?AV12345@XZ
     ?memcasecmp@strings_internal@absl@@YAHPEBD0_K@Z
     ?memcspn@strings_internal@absl@@YA_KPEBD_K0@Z
     ?memdup@strings_internal@absl@@YAPEADPEBD_K@Z
diff --git a/third_party/abseil-cpp/symbols_x64_rel_asan.def b/third_party/abseil-cpp/symbols_x64_rel_asan.def
index b675dff8..01bc1fd 100644
--- a/third_party/abseil-cpp/symbols_x64_rel_asan.def
+++ b/third_party/abseil-cpp/symbols_x64_rel_asan.def
@@ -213,7 +213,6 @@
     ??Gdetail@cctz@time_internal@absl@@YA?AV?$civil_time@Uday_tag@detail@cctz@time_internal@absl@@@0123@V40123@_J@Z
     ??Gdetail@cctz@time_internal@absl@@YA?AV?$civil_time@Usecond_tag@detail@cctz@time_internal@absl@@@0123@V40123@_J@Z
     ??Hdetail@cctz@time_internal@absl@@YA?AV?$civil_time@Uday_tag@detail@cctz@time_internal@absl@@@0123@V40123@_J@Z
-    ??Hdetail@cctz@time_internal@absl@@YA?AV?$civil_time@Usecond_tag@detail@cctz@time_internal@absl@@@0123@V40123@_J@Z
     ??Kabsl@@YA?AVint128@0@V10@0@Z
     ??Kabsl@@YA?AVuint128@0@V10@0@Z
     ??Labsl@@YA?AVint128@0@V10@0@Z
@@ -958,7 +957,6 @@
     ?max@?$RandenPool@G@random_internal@absl@@SAGXZ
     ?max@?$RandenPool@I@random_internal@absl@@SAIXZ
     ?max@?$RandenPool@_K@random_internal@absl@@SA_KXZ
-    ?max@?$civil_time@Usecond_tag@detail@cctz@time_internal@absl@@@detail@cctz@time_internal@absl@@SA?AV12345@XZ
     ?memcasecmp@strings_internal@absl@@YAHPEBD0_K@Z
     ?memcspn@strings_internal@absl@@YA_KPEBD_K0@Z
     ?memdup@strings_internal@absl@@YAPEADPEBD_K@Z
diff --git a/third_party/abseil-cpp/symbols_x86_rel.def b/third_party/abseil-cpp/symbols_x86_rel.def
index 1ee0b5d..e24357d 100644
--- a/third_party/abseil-cpp/symbols_x86_rel.def
+++ b/third_party/abseil-cpp/symbols_x86_rel.def
@@ -908,7 +908,6 @@
     ?max@?$RandenPool@G@random_internal@absl@@SAGXZ
     ?max@?$RandenPool@I@random_internal@absl@@SAIXZ
     ?max@?$RandenPool@_K@random_internal@absl@@SA_KXZ
-    ?max@?$civil_time@Usecond_tag@detail@cctz@time_internal@absl@@@detail@cctz@time_internal@absl@@SA?AV12345@XZ
     ?memcasecmp@strings_internal@absl@@YAHPBD0I@Z
     ?memcspn@strings_internal@absl@@YAIPBDI0@Z
     ?memdup@strings_internal@absl@@YAPADPBDI@Z
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 6c01c24..b086076 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -87,7 +87,7 @@
 
 // Controls off-thread code cache consumption.
 const base::Feature kConsumeCodeCacheOffThread{
-    "ConsumeCodeCacheOffThread", base::FEATURE_DISABLED_BY_DEFAULT};
+    "ConsumeCodeCacheOffThread", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables user level memory pressure signal generation on Android.
 const base::Feature kUserLevelMemoryPressureSignal{
@@ -744,12 +744,21 @@
 const base::Feature kCompressParkableStrings{"CompressParkableStrings",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enabling this will cause parkable strings to use Snappy for compression iff
+// kCompressParkableStrings is enabled.
+const base::Feature kUseSnappyForParkableStrings{
+    "UseSnappyForParkableStrings", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enabling this will delay the first aging of strings by 60 seconds instead of
 // the default. See comment around the use of the feature for the logic behind
 // the delay.
 const base::Feature kDelayFirstParkingOfStrings{
     "DelayFirstParkingOfStrings", base::FEATURE_DISABLED_BY_DEFAULT};
 
+bool ParkableStringsUseSnappy() {
+  return base::FeatureList::IsEnabled(kUseSnappyForParkableStrings);
+}
+
 bool IsParkableStringsToDiskEnabled() {
   // Always enabled as soon as compression is enabled.
   return base::FeatureList::IsEnabled(kCompressParkableStrings);
diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
index 98391c17..b1f4625 100644
--- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
+++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
@@ -46,9 +46,7 @@
           &out->low_priority_iframes_threshold) ||
       !data.ReadNetworkQualityEstimatorWebHoldback(
           &out->network_quality_estimator_web_holdback) ||
-      !data.ReadWebAppScope(&out->web_app_scope) ||
-      !data.ReadLitepageSubresourceRedirectOrigin(
-          &out->litepage_subresource_redirect_origin)
+      !data.ReadWebAppScope(&out->web_app_scope)
 #if defined(OS_ANDROID)
       || !data.ReadDefaultVideoPosterUrl(&out->default_video_poster_url)
 #endif
diff --git a/third_party/blink/public/blink_resources.grd b/third_party/blink/public/blink_resources.grd
index 5440815..93ce6261a 100644
--- a/third_party/blink/public/blink_resources.grd
+++ b/third_party/blink/public/blink_resources.grd
@@ -22,6 +22,7 @@
       <include name="IDR_UASTYLE_THEME_WIN_QUIRKS_CSS" file="../renderer/core/html/resources/win_quirks.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_THEME_FORCED_COLORS_CSS" file="../renderer/core/html/resources/forced_colors.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_POPUP_CSS" file="../renderer/core/css/popup.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_SELECTMENU_CSS" file="../renderer/core/html/resources/selectmenu.css" flattenhtml="true" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_SVG_CSS" file="../renderer/core/css/svg.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_MARKER_CSS" file="../renderer/core/css/marker.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_MATHML_CSS" file="../renderer/core/css/mathml.css" type="BINDATA" compress="gzip"/>
@@ -31,6 +32,8 @@
       <include name="IDR_UASTYLE_XHTMLMP_CSS" file="../renderer/core/css/xhtmlmp.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_VIEWPORT_ANDROID_CSS" file="../renderer/core/css/viewportAndroid.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_VIEWPORT_TELEVISION_CSS" file="../renderer/core/css/viewportTelevision.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_TRANSITION_CSS" file="../renderer/core/css/transition.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_TRANSITION_ANIMATIONS_CSS" file="../renderer/core/css/transition_animations.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_DOCUMENTXMLTREEVIEWER_CSS" file="../renderer/core/xml/DocumentXMLTreeViewer.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_DOCUMENTXMLTREEVIEWER_JS" file="../renderer/core/xml/DocumentXMLTreeViewer.js" type="BINDATA" compress="gzip"/>
       <include name="IDR_VALIDATION_BUBBLE_ICON" file="../renderer/core/html/forms/resources/input_alert.svg" type="BINDATA" compress="gzip"/>
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 8789651..4999fde 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -292,6 +292,8 @@
     kSkipTouchEventFilterFilteringProcessParamValueBrowserAndRenderer[];
 
 BLINK_COMMON_EXPORT extern const base::Feature kCompressParkableStrings;
+BLINK_COMMON_EXPORT bool ParkableStringsUseSnappy();
+BLINK_COMMON_EXPORT extern const base::Feature kUseSnappyForParkableStrings;
 BLINK_COMMON_EXPORT bool IsParkableStringsToDiskEnabled();
 BLINK_COMMON_EXPORT extern const base::Feature kDelayFirstParkingOfStrings;
 
diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h
index 9eefe05..0924c76 100644
--- a/third_party/blink/public/common/web_preferences/web_preferences.h
+++ b/third_party/blink/public/common/web_preferences/web_preferences.h
@@ -353,10 +353,6 @@
   // change depending on the enterprise policy if the platform supports it.
   bool webxr_immersive_ar_allowed = true;
 
-  // LitePage origin the subresources such as images should be redirected to
-  // when the kSubresourceRedirect feature is enabled.
-  url::Origin litepage_subresource_redirect_origin;
-
   // We try to keep the default values the same as the default values in
   // chrome, except for the cases where it would require lots of extra work for
   // the embedder to use the same default value.
diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
index 5124059..871c4f6 100644
--- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
+++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
@@ -767,11 +767,6 @@
     return r.webxr_immersive_ar_allowed;
   }
 
-  static const url::Origin& litepage_subresource_redirect_origin(
-      const blink::web_pref::WebPreferences& r) {
-    return r.litepage_subresource_redirect_origin;
-  }
-
   static bool Read(blink::mojom::WebPreferencesDataView r,
                    blink::web_pref::WebPreferences* out);
 };
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index ac6b7c5f..a40434c4 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -6713,8 +6713,8 @@
       # True for showing scroll bottleneck rects
       boolean show
 
-  # Requests that backend shows hit-test borders on layers
-  command setShowHitTestBorders
+  # Deprecated, no longer has any effect.
+  deprecated command setShowHitTestBorders
     parameters
       # True for showing hit-test borders
       boolean show
diff --git a/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom b/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom
index 0217233d..f4046a7 100644
--- a/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom
+++ b/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom
@@ -59,4 +59,12 @@
   // response. The JSON `name`, `owner`, `userBiddingSignals` and other unknown
   // fields will be ignored.
   UpdateAdInterestGroups();
+
+  // Gets the true URL from a URN returned from RunAdAuction. This function
+  // will be removed once all FLEDGE auctions switch to using fenced frames.
+  // The uuid_url should have the format format
+  // "urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" as per RFC-4122.
+  // TODO(crbug.com/1253118): Remove this function when we remove support for
+  // showing FLEDGE ads in iframes.
+  DeprecatedGetURLFromURN(url.mojom.Url uuid_url) => (url.mojom.Url? decoded_url);
 };
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 6ab29c0..df061b9 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -1741,8 +1741,8 @@
   kCSSPaintFunction = 2356,
   kWebkitCrossFade = 2357,
   kDisablePictureInPictureAttribute = 2358,
-  kCertificateTransparencyNonCompliantSubresourceInMainFrame = 2359,
-  kCertificateTransparencyNonCompliantResourceInSubframe = 2360,
+  kOBSOLETE_CertificateTransparencyNonCompliantSubresourceInMainFrame = 2359,
+  kOBSOLETE_CertificateTransparencyNonCompliantResourceInSubframe = 2360,
   kV8AbortController_Constructor = 2361,
   kReplaceCharsetInXHRIgnoringCase = 2362,
   kHTMLIFrameElementGestureMedia = 2363,
@@ -3413,6 +3413,7 @@
   kV8UDPSocket_Close_Method = 4104,
   kHTMLInputElementSimulatedClick = 4105,
   kRTCLocalSdpModificationIceUfragPwd = 4106,
+  kV8Navigator_DeprecatedURNToURL_Method = 4107,
 
 
   // Add new features immediately above this line. Don't change assigned
diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
index 1f0ca7565..067bb3b 100644
--- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
+++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
@@ -436,8 +436,4 @@
 
   // Controls whether WebXR's immersive-ar is allowed.
   bool webxr_immersive_ar_allowed;
-
-  // Origin the subresources such as images should be redirected to when the
-  // kSubresourceRedirect feature is enabled.
-  url.mojom.Origin litepage_subresource_redirect_origin;
 };
diff --git a/third_party/blink/public/platform/web_url_response.h b/third_party/blink/public/platform/web_url_response.h
index df040dd..57c21fbd 100644
--- a/third_party/blink/public/platform/web_url_response.h
+++ b/third_party/blink/public/platform/web_url_response.h
@@ -140,7 +140,6 @@
   BLINK_PLATFORM_EXPORT void VisitHttpHeaderFields(WebHTTPHeaderVisitor*) const;
 
   BLINK_PLATFORM_EXPORT void SetHasMajorCertificateErrors(bool);
-  BLINK_PLATFORM_EXPORT void SetCTPolicyCompliance(net::ct::CTPolicyCompliance);
   BLINK_PLATFORM_EXPORT void SetIsLegacyTLSVersion(bool);
   BLINK_PLATFORM_EXPORT void SetHasRangeRequested(bool);
   BLINK_PLATFORM_EXPORT void SetTimingAllowPassed(bool);
diff --git a/third_party/blink/public/strings/blink_accessibility_strings.grd b/third_party/blink/public/strings/blink_accessibility_strings.grd
index 3cf4d6d..7cf9936 100644
--- a/third_party/blink/public/strings/blink_accessibility_strings.grd
+++ b/third_party/blink/public/strings/blink_accessibility_strings.grd
@@ -41,7 +41,9 @@
       <output filename="blink_accessibility_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="blink_accessibility_strings_af.pak" type="data_package" lang="af" />
       <output filename="blink_accessibility_strings_is.pak" type="data_package" lang="is" />
+      <output filename="blink_accessibility_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="blink_accessibility_strings_am.pak" type="data_package" lang="am" />
     <output filename="blink_accessibility_strings_ar.pak" type="data_package" lang="ar" />
diff --git a/third_party/blink/public/strings/blink_strings.grd b/third_party/blink/public/strings/blink_strings.grd
index 2c6ec1c..688ac96 100644
--- a/third_party/blink/public/strings/blink_strings.grd
+++ b/third_party/blink/public/strings/blink_strings.grd
@@ -77,7 +77,9 @@
       <output filename="blink_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="blink_strings_af.pak" type="data_package" lang="af" />
       <output filename="blink_strings_is.pak" type="data_package" lang="is" />
+      <output filename="blink_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="blink_strings_am.pak" type="data_package" lang="am" />
     <output filename="blink_strings_ar.pak" type="data_package" lang="ar" />
diff --git a/third_party/blink/public/web/web_document_loader.h b/third_party/blink/public/web/web_document_loader.h
index 47359fb..23162a37 100644
--- a/third_party/blink/public/web/web_document_loader.h
+++ b/third_party/blink/public/web/web_document_loader.h
@@ -34,7 +34,6 @@
 #include <memory>
 
 #include "services/network/public/mojom/ip_address_space.mojom-shared.h"
-#include "services/network/public/mojom/referrer_policy.mojom-shared.h"
 #include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/mojom/loader/code_cache.mojom.h"
 #include "third_party/blink/public/platform/web_archive_info.h"
@@ -78,9 +77,6 @@
   // Returns the http referrer of the request corresponding to this load.
   virtual WebString Referrer() const = 0;
 
-  // Returns the referrer policy of the request corresponding to this load.
-  virtual network::mojom::ReferrerPolicy GetReferrerPolicy() const = 0;
-
   // Returns the response associated with this datasource.
   virtual const WebURLResponse& GetResponse() const = 0;
 
diff --git a/third_party/blink/public/web/web_history_item.h b/third_party/blink/public/web/web_history_item.h
index 1f47b83..d8ecd17 100644
--- a/third_party/blink/public/web/web_history_item.h
+++ b/third_party/blink/public/web/web_history_item.h
@@ -78,8 +78,8 @@
 
   BLINK_EXPORT WebString GetReferrer() const;
   BLINK_EXPORT network::mojom::ReferrerPolicy GetReferrerPolicy() const;
-  BLINK_EXPORT void SetReferrer(const WebString&,
-                                network::mojom::ReferrerPolicy);
+  BLINK_EXPORT void SetReferrer(const WebString&);
+  BLINK_EXPORT void SetReferrerPolicy(network::mojom::ReferrerPolicy);
 
   BLINK_EXPORT const WebString& Target() const;
   BLINK_EXPORT void SetTarget(const WebString&);
diff --git a/third_party/blink/renderer/bindings/IDLExtendedAttributes.md b/third_party/blink/renderer/bindings/IDLExtendedAttributes.md
index c43b347..f590b6b 100644
--- a/third_party/blink/renderer/bindings/IDLExtendedAttributes.md
+++ b/third_party/blink/renderer/bindings/IDLExtendedAttributes.md
@@ -1452,7 +1452,7 @@
 
 ### [AllowShared] _(p)_
 
-Summary: `[AllowShared]` indicates that a parameter, which must be an ArrayBufferView (or subtype of, e.g. typed arrays), is allowed to be backed by a SharedArrayBuffer.
+Summary: `[AllowShared]` indicates that a parameter, which must be an ArrayBufferView (or subtype of, e.g. typed arrays), is allowed to be backed by a SharedArrayBuffer. It also indicates that an ArrayBuffer parameter allows a SharedArrayBuffer to be passed.
 
 Usage: `[AllowShared]` must be specified on a parameter to a method:
 
@@ -1460,11 +1460,14 @@
 interface Context {
     void bufferData1([AllowShared] ArrayBufferView buffer);
     void bufferData2([AllowShared] Float32Array buffer);
+    void bufferData3([AllowShared] ArrayBuffer buffer);
 }
 ```
 
 A SharedArrayBuffer is a distinct type from an ArrayBuffer, but both types use ArrayBufferViews to view the data in the buffer. Most methods do not permit an ArrayBufferView that is backed by a SharedArrayBuffer, and will throw an exception. This attribute indicates that this method permits a shared ArrayBufferView.
 
+When applied to an ArrayBuffer argument, the underlying C++ method called by the bindings receives a `DOMArrayBufferBase*` instead of `DOMArrayBuffer*`.
+
 ### [PermissiveDictionaryConversion] _(p, d)_
 
 Summary: `[PermissiveDictionaryConversion]` relaxes the rules about what types of values may be passed for an argument of dictionary type.
diff --git a/third_party/blink/renderer/build/scripts/templates/runtime_enabled_features.h.tmpl b/third_party/blink/renderer/build/scripts/templates/runtime_enabled_features.h.tmpl
index 3e7f60ff..0325bd26 100644
--- a/third_party/blink/renderer/build/scripts/templates/runtime_enabled_features.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/templates/runtime_enabled_features.h.tmpl
@@ -88,10 +88,6 @@
 
   {% endfor %}
 
-  // TODO(crbug.com/471333): This is temporary to assist in the removal of
-  // the pre-CullRectUpdate code and will be removed shortly.
-  static bool CullRectUpdateEnabled() { return true; }
-
  protected:
   // See the comment in RuntimeEnabledFeatures for why these are protected.
   {% for feature_set in feature_sets %}
diff --git a/third_party/blink/renderer/core/animation/compositor_animations_test.cc b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
index d289cc1..b36d608 100644
--- a/third_party/blink/renderer/core/animation/compositor_animations_test.cc
+++ b/third_party/blink/renderer/core/animation/compositor_animations_test.cc
@@ -43,6 +43,7 @@
 #include "third_party/blink/public/web/web_settings.h"
 #include "third_party/blink/renderer/core/animation/animation.h"
 #include "third_party/blink/renderer/core/animation/css/compositor_keyframe_double.h"
+#include "third_party/blink/renderer/core/animation/document_animations.h"
 #include "third_party/blink/renderer/core/animation/document_timeline.h"
 #include "third_party/blink/renderer/core/animation/element_animations.h"
 #include "third_party/blink/renderer/core/animation/keyframe_effect.h"
@@ -2235,6 +2236,25 @@
       CompositorAnimations::kNoFailure);
 }
 
+TEST_P(AnimationCompositorAnimationsTest, DetachCompositorTimelinesTest) {
+  LoadTestData("transform-animation.html");
+  Document* document = GetFrame()->GetDocument();
+  cc::AnimationHost* host = document->View()->GetCompositorAnimationHost();
+
+  Element* target = document->getElementById("target");
+  const Animation& animation =
+      *target->GetElementAnimations()->Animations().begin()->key;
+  EXPECT_TRUE(animation.GetCompositorAnimation());
+
+  CompositorAnimationTimeline* compositor_timeline =
+      animation.timeline()->CompositorTimeline();
+  ASSERT_TRUE(compositor_timeline);
+  int id = compositor_timeline->GetAnimationTimeline()->id();
+  ASSERT_TRUE(host->GetTimelineById(id));
+  document->GetDocumentAnimations().DetachCompositorTimelines();
+  ASSERT_FALSE(host->GetTimelineById(id));
+}
+
 TEST_P(AnimationCompositorAnimationsTest,
        CanStartTransformAnimationOnCompositorForSVG) {
   SetBodyInnerHTML(R"HTML(
diff --git a/third_party/blink/renderer/core/animation/document_animations.cc b/third_party/blink/renderer/core/animation/document_animations.cc
index 32cfdff..91c12b1 100644
--- a/third_party/blink/renderer/core/animation/document_animations.cc
+++ b/third_party/blink/renderer/core/animation/document_animations.cc
@@ -33,6 +33,7 @@
 #include <algorithm>
 
 #include "cc/animation/animation_host.h"
+#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/core/animation/animation_clock.h"
 #include "third_party/blink/renderer/core/animation/animation_timeline.h"
 #include "third_party/blink/renderer/core/animation/css/css_scroll_timeline.h"
@@ -45,6 +46,8 @@
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
+#include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/page_animator.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
@@ -186,6 +189,23 @@
   unvalidated_timelines_.clear();
 }
 
+void DocumentAnimations::DetachCompositorTimelines() {
+  if (!Platform::Current()->IsThreadedAnimationEnabled() ||
+      !document_->GetSettings()->GetAcceleratedCompositingEnabled() ||
+      !document_->GetPage())
+    return;
+
+  for (auto& timeline : timelines_) {
+    CompositorAnimationTimeline* compositor_timeline =
+        timeline->CompositorTimeline();
+    if (!compositor_timeline)
+      continue;
+
+    document_->GetPage()->GetChromeClient().DetachCompositorAnimationTimeline(
+        compositor_timeline, document_->GetFrame());
+  }
+}
+
 void DocumentAnimations::Trace(Visitor* visitor) const {
   visitor->Trace(document_);
   visitor->Trace(timelines_);
diff --git a/third_party/blink/renderer/core/animation/document_animations.h b/third_party/blink/renderer/core/animation/document_animations.h
index d7f5b22..efe9754 100644
--- a/third_party/blink/renderer/core/animation/document_animations.h
+++ b/third_party/blink/renderer/core/animation/document_animations.h
@@ -90,6 +90,11 @@
   // https://github.com/w3c/csswg-drafts/issues/5261
   void ValidateTimelines();
 
+  // Detach compositor timelines to prevent further ticking of any animations
+  // associated with the timelines.  Detached timelines may be subsequently
+  // reattached if needed.
+  void DetachCompositorTimelines();
+
   // Add an element to the set of elements with a pending animation update.
   // The elements in the set can be applied later using,
   // ApplyPendingElementUpdates.
diff --git a/third_party/blink/renderer/core/clipboard/data_transfer.cc b/third_party/blink/renderer/core/clipboard/data_transfer.cc
index e95eb4f..1c0d9f2 100644
--- a/third_party/blink/renderer/core/clipboard/data_transfer.cc
+++ b/third_party/blink/renderer/core/clipboard/data_transfer.cc
@@ -130,14 +130,12 @@
         gfx::Vector2dF(layer->GetLayoutObject().FirstFragment().PaintOffset()));
     OverriddenCullRectScope cull_rect_scope(
         *layer, CullRect(gfx::ToEnclosingRect(cull_rect)));
-    PaintLayerPaintingInfo painting_info(layer,
-                                         kGlobalPaintFlattenCompositingLayers);
     auto* builder = MakeGarbageCollected<PaintRecordBuilder>();
 
     dragged_layout_object->GetDocument().Lifecycle().AdvanceTo(
         DocumentLifecycle::kInPaint);
-    PaintLayerPainter(*layer).Paint(builder->Context(), painting_info,
-                                    kPaintLayerNoFlag);
+    PaintLayerPainter(*layer).Paint(builder->Context(),
+                                    PaintFlag::kOmitCompositingInfo);
     dragged_layout_object->GetDocument().Lifecycle().AdvanceTo(
         DocumentLifecycle::kPaintClean);
 
diff --git a/third_party/blink/renderer/core/css/css_default_style_sheets.cc b/third_party/blink/renderer/core/css/css_default_style_sheets.cc
index a3fd439..d927b0f 100644
--- a/third_party/blink/renderer/core/css/css_default_style_sheets.cc
+++ b/third_party/blink/renderer/core/css/css_default_style_sheets.cc
@@ -54,12 +54,6 @@
   return *css_default_style_sheets;
 }
 
-static const MediaQueryEvaluator& ScreenEval() {
-  DEFINE_STATIC_LOCAL(const Persistent<MediaQueryEvaluator>, static_screen_eval,
-                      (MakeGarbageCollected<MediaQueryEvaluator>("screen")));
-  return *static_screen_eval;
-}
-
 static const MediaQueryEvaluator& PrintEval() {
   DEFINE_STATIC_LOCAL(const Persistent<MediaQueryEvaluator>, static_print_eval,
                       (MakeGarbageCollected<MediaQueryEvaluator>("print")));
@@ -88,6 +82,13 @@
   return sheet;
 }
 
+// static
+const MediaQueryEvaluator& CSSDefaultStyleSheets::ScreenEval() {
+  DEFINE_STATIC_LOCAL(const Persistent<MediaQueryEvaluator>, static_screen_eval,
+                      (MakeGarbageCollected<MediaQueryEvaluator>("screen")));
+  return *static_screen_eval;
+}
+
 CSSDefaultStyleSheets::CSSDefaultStyleSheets()
     : media_controls_style_sheet_loader_(nullptr) {
   // Strict-mode rules.
@@ -130,6 +131,7 @@
   forced_colors_style_sheet_.Clear();
   fullscreen_style_sheet_.Clear();
   popup_style_sheet_.Clear();
+  selectmenu_style_sheet_.Clear();
   webxr_overlay_style_sheet_.Clear();
   marker_style_sheet_.Clear();
   // Recreate the default style sheet to clean up possible SVG resources.
@@ -307,6 +309,18 @@
     changed_default_style = true;
   }
 
+  if (!selectmenu_style_sheet_ && IsA<HTMLSelectMenuElement>(element)) {
+    // TODO: We should assert that this sheet only contains rules for
+    // <selectmenu>.
+    String selectmenu_rules =
+        RuntimeEnabledFeatures::HTMLSelectMenuElementEnabled()
+            ? UncompressResourceAsASCIIString(IDR_UASTYLE_SELECTMENU_CSS)
+            : String();
+    selectmenu_style_sheet_ = ParseUASheet(selectmenu_rules);
+    AddRulesToDefaultStyleSheets(selectmenu_style_sheet_, NamespaceType::kHTML);
+    changed_default_style = true;
+  }
+
   DCHECK(!default_html_style_->Features().HasIdsInSelectors());
   return changed_default_style;
 }
@@ -423,6 +437,7 @@
   visitor->Trace(forced_colors_style_sheet_);
   visitor->Trace(fullscreen_style_sheet_);
   visitor->Trace(popup_style_sheet_);
+  visitor->Trace(selectmenu_style_sheet_);
   visitor->Trace(webxr_overlay_style_sheet_);
   visitor->Trace(marker_style_sheet_);
 }
diff --git a/third_party/blink/renderer/core/css/css_default_style_sheets.h b/third_party/blink/renderer/core/css/css_default_style_sheets.h
index c2266d799..faae707 100644
--- a/third_party/blink/renderer/core/css/css_default_style_sheets.h
+++ b/third_party/blink/renderer/core/css/css_default_style_sheets.h
@@ -35,6 +35,7 @@
 
 class Document;
 class Element;
+class MediaQueryEvaluator;
 class RuleFeatureSet;
 class RuleSet;
 class StyleSheetContents;
@@ -45,6 +46,7 @@
   CORE_EXPORT static CSSDefaultStyleSheets& Instance();
 
   static StyleSheetContents* ParseUASheet(const String&);
+  static const MediaQueryEvaluator& ScreenEval();
 
   CSSDefaultStyleSheets();
   CSSDefaultStyleSheets(const CSSDefaultStyleSheets&) = delete;
@@ -79,6 +81,9 @@
   StyleSheetContents* DefaultStyleSheet() { return default_style_sheet_.Get(); }
   StyleSheetContents* QuirksStyleSheet() { return quirks_style_sheet_.Get(); }
   StyleSheetContents* PopupStyleSheet() { return popup_style_sheet_.Get(); }
+  StyleSheetContents* SelectMenuStyleSheet() {
+    return selectmenu_style_sheet_.Get();
+  }
   StyleSheetContents* SvgStyleSheet() { return svg_style_sheet_.Get(); }
   StyleSheetContents* MathmlStyleSheet() { return mathml_style_sheet_.Get(); }
   StyleSheetContents* MediaControlsStyleSheet() {
@@ -147,6 +152,7 @@
   Member<StyleSheetContents> text_track_style_sheet_;
   Member<StyleSheetContents> fullscreen_style_sheet_;
   Member<StyleSheetContents> popup_style_sheet_;
+  Member<StyleSheetContents> selectmenu_style_sheet_;
   Member<StyleSheetContents> webxr_overlay_style_sheet_;
   Member<StyleSheetContents> marker_style_sheet_;
   Member<StyleSheetContents> forced_colors_style_sheet_;
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 4736050..ae202e5a 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -5005,7 +5005,7 @@
       property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal"],
       inherited: true,
       field_group: "*",
-      field_size: 3,
+      field_size: 2,
       field_template: "primitive",
       default_value: "TextEmphasisPosition::kOverRight",
       type_name: "TextEmphasisPosition",
diff --git a/third_party/blink/renderer/core/css/css_selector_list.cc b/third_party/blink/renderer/core/css/css_selector_list.cc
index 1cd3ec4..b421a3b 100644
--- a/third_party/blink/renderer/core/css/css_selector_list.cc
+++ b/third_party/blink/renderer/core/css/css_selector_list.cc
@@ -34,23 +34,11 @@
 
 namespace blink {
 
-namespace {
-// CSSSelector is one of the top types that consume renderer memory,
-// so instead of using the |WTF_HEAP_PROFILER_TYPE_NAME| macro in the
-// allocations below, pass this type name constant to allow profiling
-// in official builds.
-const char kCSSSelectorTypeName[] = "blink::CSSSelector";
-
-}  // namespace
-
 CSSSelectorList CSSSelectorList::Copy() const {
   CSSSelectorList list;
 
   unsigned length = ComputeLength();
-  list.selector_array_ =
-      reinterpret_cast<CSSSelector*>(WTF::Partitions::FastMalloc(
-          WTF::Partitions::ComputeAllocationSize(length, sizeof(CSSSelector)),
-          kCSSSelectorTypeName));
+  list.selector_array_ = std::make_unique<CSSSelector[]>(length);
   for (unsigned i = 0; i < length; ++i)
     new (&list.selector_array_[i]) CSSSelector(selector_array_[i]);
 
@@ -68,10 +56,7 @@
   DCHECK(flattened_size);
 
   CSSSelectorList list;
-  list.selector_array_ = reinterpret_cast<CSSSelector*>(
-      WTF::Partitions::FastMalloc(WTF::Partitions::ComputeAllocationSize(
-                                      flattened_size, sizeof(CSSSelector)),
-                                  kCSSSelectorTypeName));
+  list.selector_array_ = std::make_unique<CSSSelector[]>(flattened_size);
   wtf_size_t array_index = 0;
   for (wtf_size_t i = 0; i < selector_vector.size(); ++i) {
     CSSParserSelector* current = selector_vector[i].get();
@@ -113,22 +98,10 @@
 unsigned CSSSelectorList::ComputeLength() const {
   if (!selector_array_)
     return 0;
-  CSSSelector* current = selector_array_;
+  const CSSSelector* current = First();
   while (!current->IsLastInSelectorList())
     ++current;
-  return static_cast<unsigned>(current - selector_array_) + 1;
-}
-
-void CSSSelectorList::DeleteSelectors() {
-  DCHECK(selector_array_);
-
-  bool finished = false;
-  for (CSSSelector* s = selector_array_; !finished; ++s) {
-    finished = s->IsLastInSelectorList();
-    s->~CSSSelector();
-  }
-
-  WTF::Partitions::FastFree(selector_array_);
+  return SelectorIndex(*current) + 1;
 }
 
 String CSSSelectorList::SelectorsText() const {
diff --git a/third_party/blink/renderer/core/css/css_selector_list.h b/third_party/blink/renderer/core/css/css_selector_list.h
index bbdf6369..2ca94fe 100644
--- a/third_party/blink/renderer/core/css/css_selector_list.h
+++ b/third_party/blink/renderer/core/css/css_selector_list.h
@@ -65,42 +65,37 @@
   USING_FAST_MALLOC(CSSSelectorList);
 
  public:
-  CSSSelectorList() : selector_array_(nullptr) {}
+  CSSSelectorList() = default;
 
-  CSSSelectorList(CSSSelectorList&& o) : selector_array_(o.selector_array_) {
-    o.selector_array_ = nullptr;
-  }
+  CSSSelectorList(CSSSelectorList&& o)
+      : selector_array_(std::move(o.selector_array_)) {}
 
   CSSSelectorList& operator=(CSSSelectorList&& o) {
     DCHECK(this != &o);
-    DeleteSelectorsIfNeeded();
-    selector_array_ = o.selector_array_;
-    o.selector_array_ = nullptr;
+    selector_array_ = std::move(o.selector_array_);
     return *this;
   }
 
-  ~CSSSelectorList() { DeleteSelectorsIfNeeded(); }
+  ~CSSSelectorList() = default;
 
   static CSSSelectorList AdoptSelectorVector(
       Vector<std::unique_ptr<CSSParserSelector>>& selector_vector);
   CSSSelectorList Copy() const;
 
   bool IsValid() const { return !!selector_array_; }
-  const CSSSelector* First() const { return selector_array_; }
+  const CSSSelector* First() const { return &selector_array_[0]; }
   const CSSSelector* FirstForCSSOM() const;
   static const CSSSelector* Next(const CSSSelector&);
   static const CSSSelector* NextInFullList(const CSSSelector&);
 
   // The CSS selector represents a single sequence of simple selectors.
-  bool HasOneSelector() const {
-    return selector_array_ && !Next(*selector_array_);
-  }
+  bool HasOneSelector() const { return selector_array_ && !Next(*First()); }
   const CSSSelector& SelectorAt(wtf_size_t index) const {
     return selector_array_[index];
   }
 
   wtf_size_t SelectorIndex(const CSSSelector& selector) const {
-    return static_cast<wtf_size_t>(&selector - selector_array_);
+    return static_cast<wtf_size_t>(&selector - &selector_array_[0]);
   }
 
   wtf_size_t IndexOfNextSelectorAfter(wtf_size_t index) const {
@@ -118,19 +113,13 @@
   unsigned ComputeLength() const;
 
  private:
-  void DeleteSelectorsIfNeeded() {
-    if (selector_array_)
-      DeleteSelectors();
-  }
-  void DeleteSelectors();
-
   CSSSelectorList(const CSSSelectorList&) = delete;
   CSSSelectorList& operator=(const CSSSelectorList&) = delete;
 
   // End of a multipart selector is indicated by is_last_in_tag_history_ bit in
   // the last item. End of the array is indicated by is_last_in_selector_list_
   // bit in the last item.
-  CSSSelector* selector_array_;
+  std::unique_ptr<CSSSelector[]> selector_array_;
 };
 
 inline const CSSSelector* CSSSelectorList::Next(const CSSSelector& current) {
diff --git a/third_party/blink/renderer/core/css/cssom/paint_worklet_deferred_image.cc b/third_party/blink/renderer/core/css/cssom/paint_worklet_deferred_image.cc
index 53ad989..145e652 100644
--- a/third_party/blink/renderer/core/css/cssom/paint_worklet_deferred_image.cc
+++ b/third_party/blink/renderer/core/css/cssom/paint_worklet_deferred_image.cc
@@ -17,7 +17,7 @@
 
 namespace {
 void DrawInternal(cc::PaintCanvas* canvas,
-                  const PaintFlags& flags,
+                  const cc::PaintFlags& flags,
                   const gfx::RectF& dest_rect,
                   const gfx::RectF& src_rect,
                   const ImageDrawOptions& draw_options,
@@ -30,7 +30,7 @@
 }  // namespace
 
 void PaintWorkletDeferredImage::Draw(cc::PaintCanvas* canvas,
-                                     const PaintFlags& flags,
+                                     const cc::PaintFlags& flags,
                                      const gfx::RectF& dest_rect,
                                      const gfx::RectF& src_rect,
                                      const ImageDrawOptions& draw_options) {
diff --git a/third_party/blink/renderer/core/css/element_rule_collector.h b/third_party/blink/renderer/core/css/element_rule_collector.h
index b615a52..8daf7ae 100644
--- a/third_party/blink/renderer/core/css/element_rule_collector.h
+++ b/third_party/blink/renderer/core/css/element_rule_collector.h
@@ -142,9 +142,10 @@
   }
   void SetIncludeEmptyRules(bool include) { include_empty_rules_ = include; }
   bool IncludeEmptyRules() const { return include_empty_rules_; }
-  bool IsCollectingForPseudoElement() const {
-    return pseudo_style_request_.pseudo_id != kPseudoIdNone;
-  }
+
+  // Return the pseudo id if the style request is for rules associated with a
+  // pseudo element, or kPseudoNone if not.
+  PseudoId GetPseudoId() const { return pseudo_style_request_.pseudo_id; }
 
   void AddMatchedRulesToTracker(StyleRuleUsageTracker*) const;
 
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index 669cfad..ac071ab 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -3990,33 +3990,29 @@
 
 Vector<String> ParseGridTemplateAreasColumnNames(const String& grid_row_names) {
   DCHECK(!grid_row_names.IsEmpty());
-  Vector<String> column_names;
+
   // Using StringImpl to avoid checks and indirection in every call to
   // String::operator[].
   StringImpl& text = *grid_row_names.Impl();
-
   StringBuilder area_name;
+  Vector<String> column_names;
   for (unsigned i = 0; i < text.length(); ++i) {
     if (IsCSSSpace(text[i])) {
-      if (!area_name.IsEmpty()) {
+      if (!area_name.IsEmpty())
         column_names.push_back(area_name.ReleaseString());
-      }
       continue;
     }
     if (text[i] == '.') {
       if (area_name == ".")
         continue;
-      if (!area_name.IsEmpty()) {
+      if (!area_name.IsEmpty())
         column_names.push_back(area_name.ReleaseString());
-      }
     } else {
       if (!IsNameCodePoint(text[i]))
         return Vector<String>();
-      if (area_name == ".") {
+      if (area_name == ".")
         column_names.push_back(area_name.ReleaseString());
-      }
     }
-
     area_name.Append(text[i]);
   }
 
@@ -4286,8 +4282,9 @@
     if (range.Peek().GetType() != kStringToken ||
         !ParseGridTemplateAreasRow(
             range.ConsumeIncludingWhitespace().Value().ToString(),
-            grid_area_map, row_count, column_count))
+            grid_area_map, row_count, column_count)) {
       return false;
+    }
     ++row_count;
 
     // Handle template-rows's track-size.
@@ -4478,8 +4475,9 @@
 
     wtf_size_t look_ahead_column = current_column + 1;
     while (look_ahead_column < column_count &&
-           column_names[look_ahead_column] == grid_area_name)
+           column_names[look_ahead_column] == grid_area_name) {
       look_ahead_column++;
+    }
 
     NamedGridAreaMap::iterator grid_area_it =
         grid_area_map.find(grid_area_name);
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index 7c712e6f..ae70708 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -8242,23 +8242,15 @@
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   switch (style.GetTextEmphasisPosition()) {
-    case blink::TextEmphasisPosition::kOver:
-      list->Append(*CSSIdentifierValue::Create(CSSValueID::kOver));
-      break;
     case blink::TextEmphasisPosition::kOverRight:
       list->Append(*CSSIdentifierValue::Create(CSSValueID::kOver));
-      list->Append(*CSSIdentifierValue::Create(CSSValueID::kRight));
       break;
     case blink::TextEmphasisPosition::kOverLeft:
       list->Append(*CSSIdentifierValue::Create(CSSValueID::kOver));
       list->Append(*CSSIdentifierValue::Create(CSSValueID::kLeft));
       break;
-    case blink::TextEmphasisPosition::kUnder:
-      list->Append(*CSSIdentifierValue::Create(CSSValueID::kUnder));
-      break;
     case blink::TextEmphasisPosition::kUnderRight:
       list->Append(*CSSIdentifierValue::Create(CSSValueID::kUnder));
-      list->Append(*CSSIdentifierValue::Create(CSSValueID::kRight));
       break;
     case blink::TextEmphasisPosition::kUnderLeft:
       list->Append(*CSSIdentifierValue::Create(CSSValueID::kUnder));
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
index 337b64ed..25dda6c 100644
--- a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
@@ -1568,7 +1568,7 @@
     const CSSIdentifierValue& flow_direction) {
   // [ auto-flow && dense? ]
   CSSValue* dense_algorithm = nullptr;
-  if ((css_parsing_utils::ConsumeIdent<CSSValueID::kAutoFlow>(range))) {
+  if (css_parsing_utils::ConsumeIdent<CSSValueID::kAutoFlow>(range)) {
     dense_algorithm =
         css_parsing_utils::ConsumeIdent<CSSValueID::kDense>(range);
   } else {
@@ -1628,16 +1628,16 @@
     css_parsing_utils::AddProperty(
         CSSPropertyID::kGridAutoFlow, CSSPropertyID::kGrid,
         *(To<Longhand>(GetCSSPropertyGridAutoFlow()).InitialValue()), important,
-        css_parsing_utils::IsImplicitProperty::kNotImplicit, properties);
+        css_parsing_utils::IsImplicitProperty::kImplicit, properties);
     css_parsing_utils::AddProperty(
         CSSPropertyID::kGridAutoColumns, CSSPropertyID::kGrid,
         *(To<Longhand>(GetCSSPropertyGridAutoColumns()).InitialValue()),
-        important, css_parsing_utils::IsImplicitProperty::kNotImplicit,
+        important, css_parsing_utils::IsImplicitProperty::kImplicit,
         properties);
     css_parsing_utils::AddProperty(
         CSSPropertyID::kGridAutoRows, CSSPropertyID::kGrid,
         *(To<Longhand>(GetCSSPropertyGridAutoRows()).InitialValue()), important,
-        css_parsing_utils::IsImplicitProperty::kNotImplicit, properties);
+        css_parsing_utils::IsImplicitProperty::kImplicit, properties);
     return true;
   }
 
@@ -1723,16 +1723,14 @@
       properties);
   css_parsing_utils::AddProperty(
       CSSPropertyID::kGridAutoFlow, CSSPropertyID::kGrid, *grid_auto_flow,
-      important, css_parsing_utils::IsImplicitProperty::kNotImplicit,
-      properties);
+      important, css_parsing_utils::IsImplicitProperty::kImplicit, properties);
   css_parsing_utils::AddProperty(
       CSSPropertyID::kGridAutoColumns, CSSPropertyID::kGrid,
       *auto_columns_value, important,
-      css_parsing_utils::IsImplicitProperty::kNotImplicit, properties);
+      css_parsing_utils::IsImplicitProperty::kImplicit, properties);
   css_parsing_utils::AddProperty(
       CSSPropertyID::kGridAutoRows, CSSPropertyID::kGrid, *auto_rows_value,
-      important, css_parsing_utils::IsImplicitProperty::kNotImplicit,
-      properties);
+      important, css_parsing_utils::IsImplicitProperty::kImplicit, properties);
   return true;
 }
 
diff --git a/third_party/blink/renderer/core/css/resolver/matched_properties_cache.cc b/third_party/blink/renderer/core/css/resolver/matched_properties_cache.cc
index c77b9f3..636d99ed 100644
--- a/third_party/blink/renderer/core/css/resolver/matched_properties_cache.cc
+++ b/third_party/blink/renderer/core/css/resolver/matched_properties_cache.cc
@@ -213,9 +213,11 @@
     return false;
   if (style.HasContainerRelativeUnits())
     return false;
-  // Avoiding cache for ::highlight because the style depends on the highlight
-  // names involved and they're not cached.
-  if (style.HasPseudoElementStyle(kPseudoIdHighlight))
+  // Avoiding cache for ::highlight styles, and the originating styles they are
+  // associated with, because the style depends on the highlight names involved
+  // and they're not cached.
+  if (style.HasPseudoElementStyle(kPseudoIdHighlight) ||
+      style.StyleType() == kPseudoIdHighlight)
     return false;
   return true;
 }
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
index ef99edb..19d1bd5 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
@@ -1733,9 +1733,9 @@
   CSSValueID first = To<CSSIdentifierValue>(list.Item(0)).GetValueID();
   if (list.length() < 2) {
     if (first == CSSValueID::kOver)
-      return TextEmphasisPosition::kOver;
+      return TextEmphasisPosition::kOverRight;
     if (first == CSSValueID::kUnder)
-      return TextEmphasisPosition::kUnder;
+      return TextEmphasisPosition::kUnderRight;
     return TextEmphasisPosition::kOverRight;
   }
   CSSValueID second = To<CSSIdentifierValue>(list.Item(1)).GetValueID();
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 0e443a4d..f64e5c6 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -71,6 +71,7 @@
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/css/style_rule_import.h"
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
+#include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/first_letter_pseudo_element.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
@@ -99,6 +100,7 @@
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
+#include "third_party/blink/renderer/core/style/computed_style_constants.h"
 #include "third_party/blink/renderer/core/style/style_initial_data.h"
 #include "third_party/blink/renderer/core/style_property_shorthand.h"
 #include "third_party/blink/renderer/core/svg/svg_element.h"
@@ -268,6 +270,13 @@
                        base_computed_style.TextAutosizingMultiplier());
 }
 
+PseudoId GetPseudoId(const Element& element, ElementRuleCollector* collector) {
+  if (element.IsPseudoElement())
+    return element.GetPseudoId();
+
+  return collector ? collector->GetPseudoId() : kPseudoIdNone;
+}
+
 }  // namespace
 
 static CSSPropertyValueSet* LeftToRightDeclaration() {
@@ -543,7 +552,7 @@
 
   MatchVTTRules(element, collector);
   if (element.IsStyledElement() && element.InlineStyle() &&
-      !collector.IsCollectingForPseudoElement()) {
+      collector.GetPseudoId() == kPseudoIdNone) {
     // Inline style is immutable as long as there is no CSSOM wrapper.
     bool is_inline_style_cacheable = !element.InlineStyle()->IsMutable();
     collector.AddElementStyleProperties(element.InlineStyle(),
@@ -681,11 +690,13 @@
   if (IsForcedColorsModeEnabled())
     func(default_style_sheets.DefaultForcedColorStyle());
 
-  if (element.IsPseudoElement() ||
-      (collector && collector->IsCollectingForPseudoElement())) {
-    if (RuleSet* default_pseudo_style =
-            default_style_sheets.DefaultPseudoElementStyleOrNull()) {
-      func(default_style_sheets.DefaultPseudoElementStyleOrNull());
+  const auto pseudo_id = GetPseudoId(element, collector);
+  if (pseudo_id != kPseudoIdNone) {
+    if (IsTransitionPseudoElement(pseudo_id)) {
+      func(GetDocument().GetStyleEngine().DefaultDocumentTransitionStyle());
+    } else if (auto* rule_set =
+                   default_style_sheets.DefaultPseudoElementStyleOrNull()) {
+      func(rule_set);
     }
   }
 }
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 2973534..bdc8f2e 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -64,6 +64,7 @@
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
 #include "third_party/blink/renderer/core/css/vision_deficiency.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
+#include "third_party/blink/renderer/core/document_transition/document_transition_supplement.h"
 #include "third_party/blink/renderer/core/dom/document_lifecycle.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
@@ -1515,6 +1516,12 @@
 
 void StyleEngine::EnsureUAStyleForPseudoElement(PseudoId pseudo_id) {
   DCHECK(global_rule_set_);
+
+  if (IsTransitionPseudoElement(pseudo_id)) {
+    EnsureUAStyleForTransitionPseudos();
+    return;
+  }
+
   if (CSSDefaultStyleSheets::Instance()
           .EnsureDefaultStyleSheetsForPseudoElement(pseudo_id)) {
     global_rule_set_->MarkDirty();
@@ -1522,6 +1529,23 @@
   }
 }
 
+void StyleEngine::EnsureUAStyleForTransitionPseudos() {
+  if (ua_document_transition_style_)
+    return;
+
+  // Note that we don't need to mark any state dirty for style invalidation
+  // here. This is done externally by the code which invalidates this style
+  // sheet.
+  auto* document_transition =
+      DocumentTransitionSupplement::FromIfExists(GetDocument())
+          ->GetTransition();
+  auto* style_sheet_contents =
+      CSSDefaultStyleSheets::ParseUASheet(document_transition->UAStyleSheet());
+  ua_document_transition_style_ = MakeGarbageCollected<RuleSet>();
+  ua_document_transition_style_->AddRulesFromSheet(
+      style_sheet_contents, CSSDefaultStyleSheets::ScreenEval());
+}
+
 void StyleEngine::EnsureUAStyleForForcedColors() {
   DCHECK(global_rule_set_);
   if (CSSDefaultStyleSheets::Instance()
@@ -1532,6 +1556,15 @@
   }
 }
 
+RuleSet* StyleEngine::DefaultDocumentTransitionStyle() const {
+  DCHECK(ua_document_transition_style_);
+  return ua_document_transition_style_.Get();
+}
+
+void StyleEngine::InvalidateUADocumentTransitionStyle() {
+  ua_document_transition_style_ = nullptr;
+}
+
 bool StyleEngine::HasRulesForId(const AtomicString& id) const {
   DCHECK(global_rule_set_);
   return global_rule_set_->GetRuleFeatureSet().HasSelectorForId(id);
@@ -2895,6 +2928,7 @@
   visitor->Trace(text_tracks_);
   visitor->Trace(vtt_originating_element_);
   visitor->Trace(parent_for_detached_subtree_);
+  visitor->Trace(ua_document_transition_style_);
   FontSelectorClient::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index b22c57c..e255156 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -529,6 +529,9 @@
   void Trace(Visitor*) const override;
   const char* NameInHeapSnapshot() const override { return "StyleEngine"; }
 
+  RuleSet* DefaultDocumentTransitionStyle() const;
+  void InvalidateUADocumentTransitionStyle();
+
  private:
   // FontSelectorClient implementation.
   void FontsNeedUpdate(FontSelector*, FontInvalidationReason) override;
@@ -560,6 +563,7 @@
                                        MediaValueChange);
   void MediaQueryAffectingValueChanged(HeapHashSet<Member<TextTrack>>&,
                                        MediaValueChange);
+  void EnsureUAStyleForTransitionPseudos();
 
   const RuleFeatureSet& GetRuleFeatureSet() const {
     DCHECK(global_rule_set_);
@@ -748,6 +752,12 @@
   Member<MediaQueryEvaluator> media_query_evaluator_;
   Member<CSSGlobalRuleSet> global_rule_set_;
 
+  // This is the default UA generated style sheet for the ::transition* pseudo
+  // elements. This is tracked by StyleEngine as opposed to
+  // CSSDefaultStyleSheets since it is 1:1 with a Document and can be
+  // dynamically updated.
+  Member<RuleSet> ua_document_transition_style_;
+
   PendingInvalidations pending_invalidations_;
 
   StyleInvalidationRoot style_invalidation_root_;
diff --git a/third_party/blink/renderer/core/css/style_property_serializer.cc b/third_party/blink/renderer/core/css/style_property_serializer.cc
index f73f4fd..501ff64 100644
--- a/third_party/blink/renderer/core/css/style_property_serializer.cc
+++ b/third_party/blink/renderer/core/css/style_property_serializer.cc
@@ -29,6 +29,7 @@
 #include "base/memory/values_equivalent.h"
 #include "third_party/blink/renderer/core/animation/css/css_animation_data.h"
 #include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
+#include "third_party/blink/renderer/core/css/css_grid_template_areas_value.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_markup.h"
 #include "third_party/blink/renderer/core/css/css_pending_substitution_value.h"
@@ -503,6 +504,10 @@
       return GetShorthandValue(flexShorthand());
     case CSSPropertyID::kFlexFlow:
       return GetShorthandValue(flexFlowShorthand());
+    case CSSPropertyID::kGrid:
+      return GetShorthandValueForGrid(gridShorthand());
+    case CSSPropertyID::kGridTemplate:
+      return GetShorthandValueForGridTemplate(gridTemplateShorthand());
     case CSSPropertyID::kGridColumn:
       return GetShorthandValue(gridColumnShorthand(), " / ");
     case CSSPropertyID::kGridRow:
@@ -1212,6 +1217,154 @@
   return result.ReleaseString();
 }
 
+namespace {
+
+String NamedGridAreaTextForPosition(const NamedGridAreaMap& grid_area_map,
+                                    wtf_size_t index) {
+  for (const auto& item : grid_area_map) {
+    const GridArea& area = item.value;
+    if (index >= area.rows.StartLine() && index < area.rows.EndLine())
+      return item.key;
+  }
+  return g_empty_string;
+}
+
+}  // namespace
+
+String StylePropertySerializer::GetShorthandValueForGrid(
+    const StylePropertyShorthand& shorthand) const {
+  DCHECK_EQ(shorthand.length(), 6u);
+
+  const CSSValue* auto_flow_values =
+      property_set_.GetPropertyCSSValue(*shorthand.properties()[3]);
+  const CSSValue* auto_row_values =
+      property_set_.GetPropertyCSSValue(*shorthand.properties()[4]);
+  const CSSValue* auto_column_values =
+      property_set_.GetPropertyCSSValue(*shorthand.properties()[5]);
+
+  // 1- <'grid-template'>
+  if (IsA<CSSIdentifierValue>(auto_flow_values) &&
+      To<CSSIdentifierValue>(auto_flow_values)->GetValueID() ==
+          CSSValueID::kRow &&
+      IsA<CSSIdentifierValue>(auto_row_values) &&
+      To<CSSIdentifierValue>(auto_row_values)->GetValueID() ==
+          CSSValueID::kAuto &&
+      IsA<CSSIdentifierValue>(auto_column_values) &&
+      To<CSSIdentifierValue>(auto_column_values)->GetValueID() ==
+          CSSValueID::kAuto) {
+    return GetShorthandValueForGridTemplate(shorthand);
+  }
+  const CSSValue* template_row_values =
+      property_set_.GetPropertyCSSValue(*shorthand.properties()[0]);
+  const CSSValue* template_column_values =
+      property_set_.GetPropertyCSSValue(*shorthand.properties()[1]);
+  const CSSValueList* auto_flow_value_list =
+      DynamicTo<CSSValueList>(auto_flow_values);
+
+  StringBuilder auto_flow_text;
+  auto_flow_text.Append("auto-flow ");
+  if (auto_flow_value_list->HasValue(
+          *CSSIdentifierValue::Create(CSSValueID::kDense))) {
+    auto_flow_text.Append("dense ");
+  }
+
+  // 2- <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>?
+  // | [ auto-flow && dense? ] <'grid-auto-rows'>? / <'grid-template-columns'>
+  StringBuilder result;
+  if (auto_flow_value_list->HasValue(
+          *CSSIdentifierValue::Create(CSSValueID::kColumn))) {
+    result.Append(template_row_values->CssText());
+    result.Append(" / ");
+    result.Append(auto_flow_text);
+    result.Append(auto_column_values->CssText());
+  } else {
+    result.Append(auto_flow_text);
+    result.Append(auto_row_values->CssText());
+    result.Append(" / ");
+    result.Append(template_column_values->CssText());
+  }
+  return result.ReleaseString();
+}
+
+String StylePropertySerializer::GetShorthandValueForGridTemplate(
+    const StylePropertyShorthand& shorthand) const {
+  const CSSValue* template_row_values =
+      property_set_.GetPropertyCSSValue(*shorthand.properties()[0]);
+  const CSSValue* template_column_values =
+      property_set_.GetPropertyCSSValue(*shorthand.properties()[1]);
+  const CSSValue* area_values =
+      property_set_.GetPropertyCSSValue(*shorthand.properties()[2]);
+
+  // 1- 'none' case.
+  if (IsA<CSSIdentifierValue>(template_row_values) &&
+      To<CSSIdentifierValue>(template_row_values)->GetValueID() ==
+          CSSValueID::kNone &&
+      IsA<CSSIdentifierValue>(template_column_values) &&
+      To<CSSIdentifierValue>(template_column_values)->GetValueID() ==
+          CSSValueID::kNone) {
+    return "none";
+  }
+
+  StringBuilder result;
+  // 2- <grid-template-rows> / <grid-template-columns>
+  if (IsA<CSSIdentifierValue>(area_values) &&
+      To<CSSIdentifierValue>(area_values)->GetValueID() == CSSValueID::kNone) {
+    result.Append(template_row_values->CssText());
+    result.Append(" / ");
+    result.Append(template_column_values->CssText());
+    return result.ReleaseString();
+  }
+
+  // 3- [ <line-names>? <string> <track-size>? <line-names>? ]+
+  // [ / <track-list> ]?
+  const auto* template_row_value_list =
+      DynamicTo<CSSValueList>(template_row_values);
+  if (template_row_value_list->length() == 1 &&
+      IsA<CSSIdentifierValue>(template_row_value_list->Item(0)) &&
+      To<CSSIdentifierValue>(template_row_value_list->Item(0)).GetValueID() ==
+          CSSValueID::kAuto) {
+    // If the |template_row_value_list| has only one value and it is 'auto',
+    // then we append the 'grid-template-area' values.
+    result.Append(area_values->CssText());
+  } else {
+    const NamedGridAreaMap& grid_area_map =
+        DynamicTo<cssvalue::CSSGridTemplateAreasValue>(area_values)
+            ->GridAreaMap();
+    wtf_size_t grid_area_index = 0;
+    for (const auto& row_value : *template_row_value_list) {
+      const String row_value_text = row_value->CssText();
+      if (row_value->IsGridLineNamesValue()) {
+        if (!result.IsEmpty())
+          result.Append(' ');
+        result.Append(row_value_text);
+        continue;
+      }
+      const String grid_area_text =
+          NamedGridAreaTextForPosition(grid_area_map, grid_area_index);
+      if (!grid_area_text.IsEmpty()) {
+        if (!result.IsEmpty())
+          result.Append(' ');
+        result.Append('"');
+        result.Append(grid_area_text);
+        result.Append('"');
+        ++grid_area_index;
+      }
+      if (row_value_text != "auto") {
+        if (!result.IsEmpty())
+          result.Append(' ');
+        result.Append(row_value_text);
+      }
+    }
+  }
+  if (!(IsA<CSSIdentifierValue>(template_column_values) &&
+        To<CSSIdentifierValue>(template_column_values)->GetValueID() ==
+            CSSValueID::kNone)) {
+    result.Append(" / ");
+    result.Append(template_column_values->CssText());
+  }
+  return result.ReleaseString();
+}
+
 // only returns a non-null value if all properties have the same, non-null value
 String StylePropertySerializer::GetCommonValue(
     const StylePropertyShorthand& shorthand) const {
diff --git a/third_party/blink/renderer/core/css/style_property_serializer.h b/third_party/blink/renderer/core/css/style_property_serializer.h
index f98db04..f494e6e 100644
--- a/third_party/blink/renderer/core/css/style_property_serializer.h
+++ b/third_party/blink/renderer/core/css/style_property_serializer.h
@@ -56,6 +56,8 @@
   String PageBreakPropertyValue(const StylePropertyShorthand&) const;
   String GetShorthandValue(const StylePropertyShorthand&,
                            String separator = " ") const;
+  String GetShorthandValueForGrid(const StylePropertyShorthand&) const;
+  String GetShorthandValueForGridTemplate(const StylePropertyShorthand&) const;
   String ContainerValue() const;
   String FontValue() const;
   String FontSynthesisValue() const;
diff --git a/third_party/blink/renderer/core/css/threaded/text_renderer_threaded_test.cc b/third_party/blink/renderer/core/css/threaded/text_renderer_threaded_test.cc
index 8939fe9..4792b67 100644
--- a/third_party/blink/renderer/core/css/threaded/text_renderer_threaded_test.cc
+++ b/third_party/blink/renderer/core/css/threaded/text_renderer_threaded_test.cc
@@ -86,7 +86,7 @@
     TextRunPaintInfo text_run_paint_info(text_run);
 
     MockPaintCanvas mpc;
-    PaintFlags flags;
+    cc::PaintFlags flags;
 
     EXPECT_CALL(mpc, getSaveCount()).WillOnce(Return(17));
     EXPECT_CALL(mpc, drawTextBlob(_, 0, 0, _)).Times(1);
diff --git a/third_party/blink/renderer/core/css/transition.css b/third_party/blink/renderer/core/css/transition.css
new file mode 100644
index 0000000..3d9cec99
--- /dev/null
+++ b/third_party/blink/renderer/core/css/transition.css
@@ -0,0 +1,51 @@
+/*
+ * The default style sheet used for document transitions.
+ * See third_party/blink/renderer/core/document_transition/README.md for details
+ *
+ * Copyright 2022 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.
+ */
+
+@namespace "http://www.w3.org/1999/xhtml";
+
+html::transition {
+  position: fixed;
+  top: 0;
+  left: 0;
+  display: block;
+  width: 100vw;
+  height: 100vh;
+}
+
+html::transition-container(*) {
+  position: absolute;
+  top: 0;
+  left: 0;
+  display: block;
+}
+
+html::transition-container(root) {
+  width: 100vw;
+  height: 100vh;
+}
+
+html::transition-old-content(*) {
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  width: 100%;
+  height: 100%;
+  object-fit: fill;
+  display: block;
+}
+
+html::transition-new-content(*) {
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  width: 100%;
+  height: 100%;
+  object-fit: fill;
+  display: block;
+}
diff --git a/third_party/blink/renderer/core/css/transition_animations.css b/third_party/blink/renderer/core/css/transition_animations.css
new file mode 100644
index 0000000..7b6cd42
--- /dev/null
+++ b/third_party/blink/renderer/core/css/transition_animations.css
@@ -0,0 +1,30 @@
+/*
+ * The default animation style sheet used for document transitions.
+ * See third_party/blink/renderer/core/document_transition/README.md for details
+ *
+ * Copyright 2022 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.
+ */
+
+@namespace "http://www.w3.org/1999/xhtml";
+
+@keyframes fade-in {
+  from {
+    opacity: 0;
+  }
+}
+
+html::transition-new-content(*) {
+  animation: fade-in 0.25s;
+}
+
+@keyframes fade-out {
+  to {
+    opacity: 0;
+  }
+}
+
+html::transition-old-content(*) {
+  animation: fade-out 0.25s;
+}
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
index d73fffb..9ee62c8a 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
@@ -681,9 +681,6 @@
 
 bool DisplayLockContext::MarkNeedsCullRectUpdate() {
   DCHECK(ConnectedToView());
-  if (!RuntimeEnabledFeatures::CullRectUpdateEnabled())
-    return false;
-
   if (auto* layout_object = element_->GetLayoutObject()) {
     layout_object->PaintingLayer()->SetForcesChildrenCullRectUpdate();
     return true;
diff --git a/third_party/blink/renderer/core/document_transition/README.md b/third_party/blink/renderer/core/document_transition/README.md
index 1380b61a..0f25c5e 100644
--- a/third_party/blink/renderer/core/document_transition/README.md
+++ b/third_party/blink/renderer/core/document_transition/README.md
@@ -72,6 +72,35 @@
 pseudo elements in UA and developer stylesheets. This string is tracked on the
 PseudoElement class.
 
+## SharedElementResourceId
+SharedElementResourceId is an identifier used to tag the rendered output (called
+a snapshot) of shared elements and the root stacking context generated by the
+compositor. The snapshot provides the content for the ::transition-old-content
+and ::transition-new-content elements referenced above.
+
+The snapshot can be in 2 states :
+
+* Live : This refers to a render pass generated in the current frame. As such
+the snapshot stays in sync with associated DOM element.
+* Cached : This refers to a cached copy of a render pass saved in the Viz
+process.
+
+We generate 2 set of SharedElementResourceIDs for snapshots of elements in the
+old and new DOM as follows :
+
+* At the prepare phase (before the DOM is updated to the new state),
+old_snapshot_id tags elements in the old DOM. This ID refers to a live snapshot
+and the Viz process executes an async operation to save a cached version keyed
+using the same ID. This ID provides the content for ::transition-old-content
+pseudo elements.
+
+* Once the cached version has been saved, the developer can update the DOM to
+the new state called the start phase. At this point new_snapshot_id is created
+to tag elements in the new DOM. This ID always refers to a live snapshot and
+provides the content for ::transition-new-content pseudo elements.
+The old_snapshot_id now refers to the cached version displayed by
+::transition-old-content pseudo elements.
+
 ## Additional Notes
 
 Note that this project is in early stages of design and implementation. To
diff --git a/third_party/blink/renderer/core/document_transition/build.gni b/third_party/blink/renderer/core/document_transition/build.gni
index 828d2ae..173238a 100644
--- a/third_party/blink/renderer/core/document_transition/build.gni
+++ b/third_party/blink/renderer/core/document_transition/build.gni
@@ -5,12 +5,12 @@
 blink_core_sources_document_transition = [
   "document_transition.cc",
   "document_transition.h",
-  "document_transition_container_element.cc",
-  "document_transition_container_element.h",
   "document_transition_content_element.cc",
   "document_transition_content_element.h",
   "document_transition_pseudo_element_base.cc",
   "document_transition_pseudo_element_base.h",
+  "document_transition_style_tracker.cc",
+  "document_transition_style_tracker.h",
   "document_transition_supplement.cc",
   "document_transition_supplement.h",
   "document_transition_utils.h",
diff --git a/third_party/blink/renderer/core/document_transition/document_transition.cc b/third_party/blink/renderer/core/document_transition/document_transition.cc
index 915b555..401fe40 100644
--- a/third_party/blink/renderer/core/document_transition/document_transition.cc
+++ b/third_party/blink/renderer/core/document_transition/document_transition.cc
@@ -14,9 +14,11 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_document_transition_config.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_document_transition_prepare_options.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_document_transition_start_options.h"
+#include "third_party/blink/renderer/core/css/style_change_reason.h"
 #include "third_party/blink/renderer/core/dom/abort_signal.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
+#include "third_party/blink/renderer/core/dom/pseudo_element.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
@@ -100,7 +102,7 @@
   visitor->Trace(start_promise_resolver_);
   visitor->Trace(active_shared_elements_);
   visitor->Trace(signal_);
-  visitor->Trace(transition_elements_);
+  visitor->Trace(style_tracker_);
 
   ScriptWrappable::Trace(visitor);
   ActiveScriptWrappable::Trace(visitor);
@@ -226,15 +228,10 @@
           &DocumentTransition::NotifyPrepareFinished,
           WrapCrossThreadWeakPersistent(this), last_prepare_sequence_id_)));
 
-  // Add DOM elements to render and animate the content of shared elements for
-  // renderer driven transitions.
   if (base::FeatureList::IsEnabled(features::kDocumentTransitionRenderer)) {
-    transition_elements_.resize(prepare_shared_element_count_);
-    for (wtf_size_t i = 0; i < prepare_shared_element_count_; i++) {
-      transition_elements_[i] =
-          MakeGarbageCollected<DocumentTransitionContainerElement>(*document_);
-      transition_elements_[i]->Prepare(active_shared_elements_[i]);
-    }
+    style_tracker_ =
+        MakeGarbageCollected<DocumentTransitionStyleTracker>(*document_);
+    style_tracker_->Prepare(active_shared_elements_);
   }
 
   NotifyHasChangesToCommit();
@@ -279,7 +276,10 @@
     // TODO(khushalsagar) : Viz keeps copy results cached for 5 seconds at this
     // point. We should send an early release. See crbug.com/1266500.
     SetActiveSharedElements({});
-    transition_elements_.clear();
+    if (base::FeatureList::IsEnabled(features::kDocumentTransitionRenderer)) {
+      style_tracker_->Abort();
+      style_tracker_ = nullptr;
+    }
     return ScriptPromise();
   }
 
@@ -289,19 +289,20 @@
       MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   if (base::FeatureList::IsEnabled(features::kDocumentTransitionRenderer)) {
     pending_request_ = Request::CreateAnimateRenderer(document_tag_);
-    for (wtf_size_t i = 0; i < prepare_shared_element_count_; i++) {
-      transition_elements_[i]->Start(active_shared_elements_[i]);
-    }
+    style_tracker_->Start(active_shared_elements_);
 
     // TODO(khushalsagar) : Update this once its clear how transition end should
     // be defined. See crbug.com/1266488.
-    document_->GetTaskRunner(TaskType::kInternalFrameLifecycleControl)
-        ->PostDelayedTask(
-            FROM_HERE,
-            ConvertToBaseOnceCallback(CrossThreadBindOnce(
-                &DocumentTransition::NotifyStartFinished,
-                WrapCrossThreadWeakPersistent(this), last_start_sequence_id_)),
-            kDefaultTransitionDuration);
+    auto task_runner = task_runner_for_testing_
+                           ? task_runner_for_testing_
+                           : document_->GetTaskRunner(
+                                 TaskType::kInternalFrameLifecycleControl);
+    task_runner->PostDelayedTask(
+        FROM_HERE,
+        ConvertToBaseOnceCallback(CrossThreadBindOnce(
+            &DocumentTransition::NotifyStartFinished,
+            WrapCrossThreadWeakPersistent(this), last_start_sequence_id_)),
+        kDefaultTransitionDuration);
   } else {
     pending_request_ = Request::CreateStart(
         document_tag_, prepare_shared_element_count_,
@@ -337,9 +338,8 @@
 
   DCHECK(state_ == State::kPreparing);
   DCHECK(prepare_promise_resolver_);
-  for (wtf_size_t i = 0; i < transition_elements_.size(); i++) {
-    transition_elements_[i]->PrepareResolved();
-  }
+  if (style_tracker_)
+    style_tracker_->PrepareResolved();
 
   // Defer commits before resolving the promise to ensure any updates made in
   // the callback are deferred.
@@ -361,16 +361,14 @@
 
   DCHECK(state_ == State::kStarted);
   DCHECK(start_promise_resolver_);
-
   start_promise_resolver_->Resolve();
   start_promise_resolver_ = nullptr;
   state_ = State::kIdle;
   SetActiveSharedElements({});
 
   if (base::FeatureList::IsEnabled(features::kDocumentTransitionRenderer)) {
-    for (auto& transition_element : transition_elements_)
-      transition_element->StartFinished();
-
+    style_tracker_->StartFinished();
+    style_tracker_ = nullptr;
     pending_request_ = Request::CreateRelease(document_tag_);
     NotifyHasChangesToCommit();
   }
@@ -400,14 +398,10 @@
     // This tags the shared element's content with the resource id used by the
     // first pseudo element. This is okay since in the eventual API we should
     // have a 1:1 mapping between shared elements and pseudo elements.
-    if (!resource_id->IsValid() && !transition_elements_.IsEmpty()) {
-      // The active element is from the old DOM during prepare which maps to the
-      // old content element. And is from the new DOM during start which maps to
-      // the new content element.
-      auto* content_element = state_ == State::kPreparing
-                                  ? transition_elements_[i]->old_content()
-                                  : transition_elements_[i]->new_content();
-      *resource_id = content_element->resource_id();
+    if (base::FeatureList::IsEnabled(features::kDocumentTransitionRenderer)) {
+      if (!resource_id->IsValid()) {
+        *resource_id = style_tracker_->GetLiveSnapshotId(element);
+      }
     }
   }
   DCHECK(shared_element_id->valid());
@@ -449,12 +443,28 @@
   }
 }
 
-void DocumentTransition::UpdateTransforms() {
+void DocumentTransition::RunPostLayoutSteps() {
   DCHECK(document_->Lifecycle().GetState() >=
          DocumentLifecycle::LifecycleState::kLayoutClean);
 
-  for (auto& transition_element : transition_elements_)
-    transition_element->UpdateTransform();
+  if (style_tracker_)
+    style_tracker_->RunPostLayoutSteps();
+}
+
+PseudoElement* DocumentTransition::CreatePseudoElement(
+    Element* parent,
+    PseudoId pseudo_id,
+    const AtomicString& document_transition_tag) {
+  DCHECK(style_tracker_);
+
+  return style_tracker_->CreatePseudoElement(parent, pseudo_id,
+                                             document_transition_tag);
+}
+
+const String& DocumentTransition::UAStyleSheet() const {
+  DCHECK(style_tracker_);
+
+  return style_tracker_->UAStyleSheet();
 }
 
 void DocumentTransition::SetActiveSharedElements(
@@ -533,6 +543,12 @@
         DOMExceptionCode::kAbortError, abort_message));
     prepare_promise_resolver_ = nullptr;
   }
+
+  SetActiveSharedElements({});
+  if (style_tracker_) {
+    style_tracker_->Abort();
+    style_tracker_ = nullptr;
+  }
   StopDeferringCommits();
   state_ = State::kIdle;
   signal_ = nullptr;
diff --git a/third_party/blink/renderer/core/document_transition/document_transition.h b/third_party/blink/renderer/core/document_transition/document_transition.h
index e505023..eec6713b 100644
--- a/third_party/blink/renderer/core/document_transition/document_transition.h
+++ b/third_party/blink/renderer/core/document_transition/document_transition.h
@@ -7,9 +7,11 @@
 
 #include <memory>
 
+#include "base/memory/scoped_refptr.h"
+#include "base/task/single_thread_task_runner.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/document_transition/document_transition_container_element.h"
+#include "third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/graphics/document_transition_shared_element_id.h"
@@ -19,6 +21,10 @@
 class DocumentTransitionRequest;
 }
 
+namespace viz {
+class SharedElementResourceId;
+}
+
 namespace blink {
 
 class AbortSignal;
@@ -27,6 +33,7 @@
 class DocumentTransitionStartOptions;
 class Element;
 class ExceptionState;
+class PseudoElement;
 class ScriptPromise;
 class ScriptPromiseResolver;
 class ScriptState;
@@ -78,10 +85,18 @@
   // https://github.com/vmpstr/shared-element-transitions/issues/17
   void VerifySharedElements();
 
-  // Updates the transform on |transition_elements_| to be consistent with the
-  // post layout transform on shared elements. This must be called with a clean
-  // layout.
-  void UpdateTransforms();
+  // Dispatched during a lifecycle update after clean layout.
+  void RunPostLayoutSteps();
+
+  // Creates a pseudo element for the given |pseudo_id|.
+  PseudoElement* CreatePseudoElement(
+      Element* parent,
+      PseudoId pseudo_id,
+      const AtomicString& document_transition_tag);
+
+  // Returns the UA style sheet for the pseudo element tree generated during a
+  // transition.
+  const String& UAStyleSheet() const;
 
  private:
   friend class DocumentTransitionTest;
@@ -130,7 +145,7 @@
 
   // Created conditionally if renderer based SharedElementTransitions is
   // enabled.
-  HeapVector<Member<DocumentTransitionContainerElement>> transition_elements_;
+  Member<DocumentTransitionStyleTracker> style_tracker_;
 
   std::unique_ptr<Request> pending_request_;
 
@@ -147,6 +162,9 @@
   uint32_t document_tag_ = 0u;
 
   bool deferring_commits_ = false;
+
+  // Set only for tests.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_testing_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_container_element.cc b/third_party/blink/renderer/core/document_transition/document_transition_container_element.cc
deleted file mode 100644
index 2879998..0000000
--- a/third_party/blink/renderer/core/document_transition/document_transition_container_element.cc
+++ /dev/null
@@ -1,181 +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 "third_party/blink/renderer/core/document_transition/document_transition_container_element.h"
-
-#include "third_party/blink/renderer/core/css/css_property_names.h"
-#include "third_party/blink/renderer/core/css/properties/computed_style_utils.h"
-#include "third_party/blink/renderer/core/css_value_keywords.h"
-#include "third_party/blink/renderer/core/layout/layout_object.h"
-#include "third_party/blink/renderer/core/resize_observer/resize_observer.h"
-#include "third_party/blink/renderer/core/resize_observer/resize_observer_entry.h"
-
-namespace blink {
-
-class DocumentTransitionContainerElement::ResizeObserverDelegate final
-    : public ResizeObserver::Delegate {
- public:
-  explicit ResizeObserverDelegate(
-      DocumentTransitionContainerElement* container_element,
-      DocumentTransitionContentElement* content_element)
-      : container_element_(container_element),
-        content_element_(content_element) {
-    DCHECK(container_element);
-    DCHECK(content_element);
-  }
-  ~ResizeObserverDelegate() override = default;
-
-  // Invoked when the target shared element is resized.
-  void OnResize(
-      const HeapVector<Member<ResizeObserverEntry>>& entries) override {
-    DCHECK_EQ(1u, entries.size());
-
-    auto border_box_size = entries[0]->borderBoxSize()[0];
-    auto layout_size = LayoutSize(DoubleSize(border_box_size->inlineSize(),
-                                             border_box_size->blockSize()));
-
-    content_element_->SetIntrinsicSize(layout_size);
-    container_element_->WasResized(layout_size);
-  }
-
-  void Trace(Visitor* visitor) const override {
-    visitor->Trace(container_element_);
-    visitor->Trace(content_element_);
-    ResizeObserver::Delegate::Trace(visitor);
-  }
-
- private:
-  Member<DocumentTransitionContainerElement> container_element_;
-  Member<DocumentTransitionContentElement> content_element_;
-};
-
-DocumentTransitionContainerElement::DocumentTransitionContainerElement(
-    Document& document)
-    : HTMLElement(QualifiedName(g_null_atom,
-                                "transition_container_element",
-                                g_null_atom),
-                  document) {
-  // TODO(khushalsagar) : Move this to a UA style sheet.
-  SetInlineStyleProperty(CSSPropertyID::kPosition, CSSValueID::kFixed);
-  SetInlineStyleProperty(CSSPropertyID::kTop, 0,
-                         CSSPrimitiveValue::UnitType::kPixels);
-  SetInlineStyleProperty(CSSPropertyID::kLeft, 0,
-                         CSSPrimitiveValue::UnitType::kPixels);
-  SetInlineStyleProperty(CSSPropertyID::kBoxSizing, CSSValueID::kBorderBox);
-
-  // Using CSS transition ensures that the animation is retargeted if the state
-  // of the live element changes.
-  SetInlineStyleProperty(CSSPropertyID::kTransition,
-                         "all 0.25s cubic-bezier(0.4, 0.0, 0.2, 1.0)");
-
-  // TODO(khushalsagar) : These pseudo elements will need to be associated with
-  // a persistent DOM element (html or body element). See crbug.com/1265698.
-  document.body()->AppendChild(this);
-}
-
-DocumentTransitionContainerElement::~DocumentTransitionContainerElement() =
-    default;
-
-void DocumentTransitionContainerElement::Prepare(Element* target_element) {
-  DCHECK_EQ(state_, State::kIdle);
-  state_ = State::kPreparing;
-
-  if (target_element) {
-    old_content_ =
-        MakeGarbageCollected<DocumentTransitionContentElement>(GetDocument());
-    AppendChild(old_content_);
-
-    // TODO(khushalsagar) : We need an observer for the element's transform
-    // similar to its size.
-    resize_observer_ = ResizeObserver::Create(
-        GetDocument().domWindow(),
-        MakeGarbageCollected<ResizeObserverDelegate>(this, old_content_));
-    resize_observer_->observe(target_element);
-
-    target_element_ = target_element;
-    UpdateTransform();
-  }
-}
-
-void DocumentTransitionContainerElement::PrepareResolved() {
-  DCHECK_EQ(state_, State::kPreparing);
-  state_ = State::kPrepared;
-
-  if (resize_observer_) {
-    resize_observer_->disconnect();
-    resize_observer_ = nullptr;
-    target_element_ = nullptr;
-  }
-}
-
-void DocumentTransitionContainerElement::Start(Element* target_element) {
-  DCHECK_EQ(state_, State::kPrepared);
-  state_ = State::kStarted;
-
-  if (target_element) {
-    new_content_ =
-        MakeGarbageCollected<DocumentTransitionContentElement>(GetDocument());
-    AppendChild(new_content_);
-
-    resize_observer_ = ResizeObserver::Create(
-        GetDocument().domWindow(),
-        MakeGarbageCollected<ResizeObserverDelegate>(this, new_content_));
-    resize_observer_->observe(target_element);
-
-    target_element_ = target_element;
-    UpdateTransform();
-  }
-
-  DCHECK(old_content_ || new_content_)
-      << "One of old or new content must be provided";
-}
-
-void DocumentTransitionContainerElement::StartFinished() {
-  DCHECK_EQ(state_, State::kStarted);
-  state_ = State::kFinished;
-
-  GetDocument().body()->removeChild(this);
-
-  if (resize_observer_) {
-    resize_observer_->disconnect();
-    resize_observer_ = nullptr;
-  }
-
-  target_element_ = nullptr;
-}
-
-void DocumentTransitionContainerElement::WasResized(
-    const LayoutSize& new_size) {
-  SetInlineStyleProperty(CSSPropertyID::kWidth, new_size.Width(),
-                         CSSPrimitiveValue::UnitType::kPixels);
-  SetInlineStyleProperty(CSSPropertyID::kHeight, new_size.Height(),
-                         CSSPrimitiveValue::UnitType::kPixels);
-}
-
-void DocumentTransitionContainerElement::UpdateTransform() {
-  if (!target_element_)
-    return;
-
-  if (auto* layout_object = target_element_->GetLayoutObject()) {
-    auto transform = layout_object->LocalToAbsoluteTransform();
-    if (transform == container_transform_)
-      return;
-
-    container_transform_ = transform;
-    SetInlineStyleProperty(
-        CSSPropertyID::kTransform,
-        ComputedStyleUtils::ValueForTransformationMatrix(transform, 1, false)
-            ->CssText());
-  }
-}
-
-void DocumentTransitionContainerElement::Trace(Visitor* visitor) const {
-  visitor->Trace(old_content_);
-  visitor->Trace(new_content_);
-  visitor->Trace(resize_observer_);
-  visitor->Trace(target_element_);
-  HTMLElement::Trace(visitor);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_container_element.h b/third_party/blink/renderer/core/document_transition/document_transition_container_element.h
deleted file mode 100644
index f83827fb..0000000
--- a/third_party/blink/renderer/core/document_transition/document_transition_container_element.h
+++ /dev/null
@@ -1,70 +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 THIRD_PARTY_BLINK_RENDERER_CORE_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_CONTAINER_ELEMENT_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_CONTAINER_ELEMENT_H_
-
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/document_transition/document_transition_content_element.h"
-#include "third_party/blink/renderer/core/html/html_element.h"
-#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
-
-namespace blink {
-
-// This class implements the functionality to animate from a static snapshot of
-// an element in previous DOM to a live snapshot of a corresponding element in
-// the new DOM. These snapshots are represented using
-// DocumentTransitionContainerElements.
-//
-// TODO(khushalsagar) : Switch this to a pseudo element.
-class CORE_EXPORT DocumentTransitionContainerElement : public HTMLElement {
- public:
-  explicit DocumentTransitionContainerElement(Document& document);
-  ~DocumentTransitionContainerElement() override;
-
-  void Prepare(Element* target_element);
-  void PrepareResolved();
-  void Start(Element* target_element);
-  void StartFinished();
-
-  const DocumentTransitionContentElement* old_content() const {
-    return old_content_;
-  }
-  const DocumentTransitionContentElement* new_content() const {
-    return new_content_.Get();
-  }
-
-  void Trace(Visitor* visitor) const override;
-
-  void UpdateTransform();
-
- private:
-  class ResizeObserverDelegate;
-
-  // These state transitions are done in a serial order.
-  enum class State { kIdle, kPreparing, kPrepared, kStarted, kFinished };
-
-  // Invoked when the element with the live snapshot is resized.
-  void WasResized(const LayoutSize& new_size);
-
-  State state_ = State::kIdle;
-
-  Member<DocumentTransitionContentElement> old_content_;
-  Member<DocumentTransitionContentElement> new_content_;
-
-  // This is used to subscribe to changes in the size of the element's live
-  // content.
-  Member<ResizeObserver> resize_observer_;
-
-  // The element providing the live content.
-  Member<Element> target_element_;
-
-  // The transform set on the container. This is used to keep the container's
-  // transform in sync with the live element.
-  TransformationMatrix container_transform_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_CONTAINER_ELEMENT_H_
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_content_element.cc b/third_party/blink/renderer/core/document_transition/document_transition_content_element.cc
index 816be24..e2ef2dc 100644
--- a/third_party/blink/renderer/core/document_transition/document_transition_content_element.cc
+++ b/third_party/blink/renderer/core/document_transition/document_transition_content_element.cc
@@ -9,27 +9,15 @@
 namespace blink {
 
 DocumentTransitionContentElement::DocumentTransitionContentElement(
-    Document& document)
-    : HTMLElement(
-          QualifiedName(g_null_atom, "transition_content_element", g_null_atom),
-          document),
-      resource_id_(viz::SharedElementResourceId::Generate()) {
-  // TODO(khushalsagar) : Move this to a UA style sheet.
-  SetInlineStyleProperty(CSSPropertyID::kPosition, CSSValueID::kAbsolute);
-  SetInlineStyleProperty(CSSPropertyID::kTop, 0,
-                         CSSPrimitiveValue::UnitType::kPixels);
-  SetInlineStyleProperty(CSSPropertyID::kLeft, 0,
-                         CSSPrimitiveValue::UnitType::kPixels);
-
-  // This implements the current default behaviour of stretching the snapshot to
-  // match container size.
-  // TODO(khushalsagar) : Change this to a pattern which preserves the aspect
-  // ratio once browser defaults are finalized.
-  SetInlineStyleProperty(CSSPropertyID::kWidth, 100,
-                         CSSPrimitiveValue::UnitType::kPercentage);
-  SetInlineStyleProperty(CSSPropertyID::kHeight, 100,
-                         CSSPrimitiveValue::UnitType::kPercentage);
-  SetInlineStyleProperty(CSSPropertyID::kObjectFit, CSSValueID::kFill);
+    Element* parent,
+    PseudoId pseudo_id,
+    const AtomicString& document_transition_tag,
+    viz::SharedElementResourceId resource_id)
+    : DocumentTransitionPseudoElementBase(parent,
+                                          pseudo_id,
+                                          document_transition_tag),
+      resource_id_(resource_id) {
+  DCHECK(resource_id_.IsValid());
 }
 
 DocumentTransitionContentElement::~DocumentTransitionContentElement() = default;
@@ -49,8 +37,4 @@
   return MakeGarbageCollected<LayoutDocumentTransitionContent>(this);
 }
 
-void DocumentTransitionContentElement::Trace(Visitor* visitor) const {
-  HTMLElement::Trace(visitor);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_content_element.h b/third_party/blink/renderer/core/document_transition/document_transition_content_element.h
index f08cf0f..7cfb24b 100644
--- a/third_party/blink/renderer/core/document_transition/document_transition_content_element.h
+++ b/third_party/blink/renderer/core/document_transition/document_transition_content_element.h
@@ -8,7 +8,8 @@
 #include "base/memory/scoped_refptr.h"
 #include "components/viz/common/shared_element_resource_id.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/html/html_element.h"
+#include "third_party/blink/renderer/core/document_transition/document_transition_pseudo_element_base.h"
+#include "third_party/blink/renderer/core/dom/pseudo_element.h"
 #include "third_party/blink/renderer/platform/geometry/layout_size.h"
 
 namespace blink {
@@ -17,11 +18,14 @@
 // of an element created using content:element(id).
 // The element function is described at
 // https://developer.mozilla.org/en-US/docs/Web/CSS/element().
-//
-// TODO(khushalsagar) : Switch this to a pseudo element.
-class CORE_EXPORT DocumentTransitionContentElement : public HTMLElement {
+class CORE_EXPORT DocumentTransitionContentElement
+    : public DocumentTransitionPseudoElementBase {
  public:
-  explicit DocumentTransitionContentElement(Document& document);
+  explicit DocumentTransitionContentElement(
+      Element* parent,
+      PseudoId,
+      const AtomicString& document_transition_tag,
+      viz::SharedElementResourceId);
   ~DocumentTransitionContentElement() override;
 
   void SetIntrinsicSize(const LayoutSize& intrinsic_size);
@@ -30,8 +34,6 @@
     return resource_id_;
   }
 
-  void Trace(Visitor* visitor) const override;
-
  private:
   LayoutObject* CreateLayoutObject(const ComputedStyle&, LegacyLayout) override;
 
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc
new file mode 100644
index 0000000..df96bf7
--- /dev/null
+++ b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.cc
@@ -0,0 +1,426 @@
+// 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 "third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h"
+
+#include "third_party/blink/public/resources/grit/blink_resources.h"
+#include "third_party/blink/renderer/core/css/properties/computed_style_utils.h"
+#include "third_party/blink/renderer/core/css/style_change_reason.h"
+#include "third_party/blink/renderer/core/css/style_engine.h"
+#include "third_party/blink/renderer/core/document_transition/document_transition_content_element.h"
+#include "third_party/blink/renderer/core/document_transition/document_transition_pseudo_element_base.h"
+#include "third_party/blink/renderer/core/document_transition/document_transition_utils.h"
+#include "third_party/blink/renderer/core/dom/node.h"
+#include "third_party/blink/renderer/core/dom/pseudo_element.h"
+#include "third_party/blink/renderer/core/resize_observer/resize_observer_entry.h"
+#include "third_party/blink/renderer/core/style/computed_style_constants.h"
+#include "third_party/blink/renderer/platform/data_resource_helper.h"
+
+namespace blink {
+namespace {
+
+const AtomicString& RootTag() {
+  DEFINE_STATIC_LOCAL(AtomicString, kRootTag, ("root"));
+  return kRootTag;
+}
+
+const String& StaticUAStyles() {
+  DEFINE_STATIC_LOCAL(
+      String, kStaticUAStyles,
+      (UncompressResourceAsASCIIString(IDR_UASTYLE_TRANSITION_CSS)));
+  return kStaticUAStyles;
+}
+
+const String& AnimationUAStyles() {
+  DEFINE_STATIC_LOCAL(
+      String, kAnimationUAStyles,
+      (UncompressResourceAsASCIIString(IDR_UASTYLE_TRANSITION_ANIMATIONS_CSS)));
+  return kAnimationUAStyles;
+}
+
+AtomicString IdFromIndex(wtf_size_t index) {
+  StringBuilder builder;
+  builder.AppendFormat("shared-%d", index);
+  return builder.ToAtomicString();
+}
+
+}  // namespace
+
+class DocumentTransitionStyleTracker::ContainerPseudoElement
+    : public DocumentTransitionPseudoElementBase {
+ public:
+  ContainerPseudoElement(Element* parent,
+                         PseudoId pseudo_id,
+                         const AtomicString& document_transition_tag,
+                         const DocumentTransitionStyleTracker* style_tracker)
+      : DocumentTransitionPseudoElementBase(parent,
+                                            pseudo_id,
+                                            document_transition_tag),
+        style_tracker_(style_tracker) {}
+
+  ~ContainerPseudoElement() override = default;
+
+  void Trace(Visitor* visitor) const override {
+    PseudoElement::Trace(visitor);
+    visitor->Trace(style_tracker_);
+  }
+
+ private:
+  bool CanGeneratePseudoElement(PseudoId pseudo_id) const override {
+    if (!DocumentTransitionPseudoElementBase::CanGeneratePseudoElement(
+            pseudo_id)) {
+      return false;
+    }
+
+    if (document_transition_tag() == RootTag()) {
+      // TODO(khushalsagar) : Implement live root snapshots. See
+      // crbug.com/1265700.
+      return false;
+    }
+
+    auto& element_data =
+        style_tracker_->element_data_map_.find(document_transition_tag())
+            ->value;
+    auto snapshot_id = pseudo_id == kPseudoIdTransitionOldContent
+                           ? element_data->old_snapshot_id
+                           : element_data->new_snapshot_id;
+    return snapshot_id.IsValid();
+  }
+
+  Member<const DocumentTransitionStyleTracker> style_tracker_;
+};
+
+DocumentTransitionStyleTracker::DocumentTransitionStyleTracker(
+    Document& document)
+    : document_(document) {}
+
+DocumentTransitionStyleTracker::~DocumentTransitionStyleTracker() = default;
+
+void DocumentTransitionStyleTracker::Prepare(
+    const HeapVector<Member<Element>>& old_elements) {
+  DCHECK_EQ(state_, State::kIdle);
+
+  state_ = State::kPreparing;
+
+  // An id for each shared element + root.
+  pseudo_document_transition_tags_.resize(old_elements.size() + 1);
+
+  // The order of IDs in this list defines the DOM order and as a result the
+  // paint order of these elements. This is why root needs to be first in the
+  // list.
+  pseudo_document_transition_tags_[0] = RootTag();
+
+  element_data_map_.ReserveCapacityForSize(old_elements.size());
+  for (wtf_size_t i = 0; i < old_elements.size(); ++i) {
+    auto document_transition_tag = IdFromIndex(i);
+
+    auto* element_data = MakeGarbageCollected<ElementData>();
+    element_data->target_element = old_elements[i];
+    if (old_elements[i])
+      element_data->old_snapshot_id = viz::SharedElementResourceId::Generate();
+    element_data_map_.insert(document_transition_tag, std::move(element_data));
+
+    pseudo_document_transition_tags_[i + 1] =
+        std::move(document_transition_tag);
+  }
+
+  document_->GetStyleEngine().SetDocumentTransitionTags(
+      pseudo_document_transition_tags_);
+
+  // We need a style invalidation to generate the pseudo element tree.
+  InvalidateStyle();
+}
+
+void DocumentTransitionStyleTracker::PrepareResolved() {
+  DCHECK_EQ(state_, State::kPreparing);
+
+  state_ = State::kPrepared;
+
+  for (auto& entry : element_data_map_) {
+    auto& element_data = entry.value;
+    element_data->target_element = nullptr;
+    element_data->cached_border_box_size = element_data->border_box_size;
+    element_data->cached_viewport_matrix = element_data->viewport_matrix;
+  }
+}
+
+void DocumentTransitionStyleTracker::Start(
+    const HeapVector<Member<Element>>& new_elements) {
+  DCHECK_EQ(state_, State::kPrepared);
+  DCHECK_EQ(element_data_map_.size(), new_elements.size());
+
+  state_ = State::kStarted;
+  for (wtf_size_t i = 0; i < new_elements.size(); ++i) {
+    auto document_transition_tag = IdFromIndex(i);
+
+    auto& element_data = element_data_map_.find(document_transition_tag)->value;
+    element_data->target_element = new_elements[i];
+    if (new_elements[i])
+      element_data->new_snapshot_id = viz::SharedElementResourceId::Generate();
+  }
+
+  // We need a style invalidation to generate new content pseudo elements for
+  // new elements in the DOM.
+  InvalidateStyle();
+}
+
+void DocumentTransitionStyleTracker::StartFinished() {
+  DCHECK_EQ(state_, State::kStarted);
+  EndTransition();
+}
+
+void DocumentTransitionStyleTracker::Abort() {
+  DCHECK_NE(state_, State::kStarted);
+  EndTransition();
+}
+
+void DocumentTransitionStyleTracker::EndTransition() {
+  state_ = State::kFinished;
+
+  element_data_map_.clear();
+  pseudo_document_transition_tags_.clear();
+  document_->GetStyleEngine().SetDocumentTransitionTags({});
+
+  // We need a style invalidation to remove the pseudo element tree.
+  InvalidateStyle();
+}
+
+viz::SharedElementResourceId DocumentTransitionStyleTracker::GetLiveSnapshotId(
+    const Element* element) const {
+  DCHECK(element);
+
+  for (const auto& entry : element_data_map_) {
+    if (entry.value->target_element == element) {
+      auto snapshot_id = HasLiveNewContent() ? entry.value->new_snapshot_id
+                                             : entry.value->old_snapshot_id;
+      DCHECK(snapshot_id.IsValid());
+      return snapshot_id;
+    }
+  }
+
+  NOTREACHED();
+  return viz::SharedElementResourceId();
+}
+
+PseudoElement* DocumentTransitionStyleTracker::CreatePseudoElement(
+    Element* parent,
+    PseudoId pseudo_id,
+    const AtomicString& document_transition_tag) {
+  DCHECK(IsTransitionPseudoElement(pseudo_id));
+  DCHECK(pseudo_id == kPseudoIdTransition || document_transition_tag);
+
+  const auto& element_data =
+      document_transition_tag
+          ? element_data_map_.find(document_transition_tag)->value
+          : nullptr;
+
+  switch (pseudo_id) {
+    case kPseudoIdTransition:
+      return MakeGarbageCollected<DocumentTransitionPseudoElementBase>(
+          parent, pseudo_id, document_transition_tag);
+    case kPseudoIdTransitionContainer:
+      return MakeGarbageCollected<ContainerPseudoElement>(
+          parent, pseudo_id, document_transition_tag, this);
+    case kPseudoIdTransitionOldContent: {
+      auto* pseudo_element =
+          MakeGarbageCollected<DocumentTransitionContentElement>(
+              parent, pseudo_id, document_transition_tag,
+              element_data->old_snapshot_id);
+      // If live data is tracking new elements then use the cached size for the
+      // pseudo element displaying snapshot of old element.
+      pseudo_element->SetIntrinsicSize(
+          HasLiveNewContent() ? element_data->cached_border_box_size
+                              : element_data->border_box_size);
+      return pseudo_element;
+    }
+    case kPseudoIdTransitionNewContent: {
+      auto* pseudo_element =
+          MakeGarbageCollected<DocumentTransitionContentElement>(
+              parent, pseudo_id, document_transition_tag,
+              element_data->new_snapshot_id);
+      pseudo_element->SetIntrinsicSize(element_data->border_box_size);
+      return pseudo_element;
+    }
+    default:
+      NOTREACHED();
+  }
+
+  return nullptr;
+}
+
+void DocumentTransitionStyleTracker::RunPostLayoutSteps() {
+  // TODO(khushalsagar) : This callback needs to be switched to PostPrepaint or
+  // PostPaint since we need to paint the pseudo elements in the same order in
+  // which they appear in the DOM. See crbug.com/1275740.
+
+  bool needs_style_invalidation = false;
+
+  for (auto& entry : element_data_map_) {
+    auto& element_data = entry.value;
+    if (!element_data->target_element)
+      continue;
+
+    // TODO(khushalsagar) : Switch paint containment and disallow fragmentation
+    // to implicit constraints. See crbug.com/1277121.
+    auto* layout_object = element_data->target_element->GetLayoutObject();
+    if (!layout_object || !layout_object->ShouldApplyPaintContainment()) {
+      element_data->target_element = nullptr;
+
+      // If we had a valid |target_element| there must be an associated snapshot
+      // ID. Remove it since there is no corresponding DOM element to produce
+      // its snapshot.
+      auto& live_snapshot_id = HasLiveNewContent()
+                                   ? element_data->new_snapshot_id
+                                   : element_data->old_snapshot_id;
+      DCHECK(live_snapshot_id.IsValid());
+      live_snapshot_id = viz::SharedElementResourceId();
+      continue;
+    }
+
+    const auto& viewport_matrix = layout_object->LocalToAbsoluteTransform();
+
+    // ResizeObserverEntry is created to reuse the logic for parsing object size
+    // for different types of LayoutObjects.
+    auto* resize_observer_entry =
+        MakeGarbageCollected<ResizeObserverEntry>(element_data->target_element);
+    auto entry_size = resize_observer_entry->borderBoxSize()[0];
+    auto border_box_size = LayoutSize(
+        DoubleSize(entry_size->inlineSize(), entry_size->blockSize()));
+
+    if (viewport_matrix == element_data->viewport_matrix &&
+        border_box_size == element_data->border_box_size) {
+      continue;
+    }
+
+    element_data->viewport_matrix = viewport_matrix;
+    element_data->border_box_size = border_box_size;
+
+    PseudoId live_content_element = HasLiveNewContent()
+                                        ? kPseudoIdTransitionNewContent
+                                        : kPseudoIdTransitionOldContent;
+    if (auto* pseudo_element =
+            document_->documentElement()->GetNestedPseudoElement(
+                live_content_element, entry.key)) {
+      // A pseudo element of type |tansition*content| must be created using
+      // DocumentTransitionContentElement.
+      static_cast<DocumentTransitionContentElement*>(pseudo_element)
+          ->SetIntrinsicSize(border_box_size);
+    }
+
+    needs_style_invalidation = true;
+  }
+
+  if (needs_style_invalidation)
+    InvalidateStyle();
+}
+
+void DocumentTransitionStyleTracker::InvalidateStyle() {
+  ua_style_sheet_.reset();
+  document_->GetStyleEngine().InvalidateUADocumentTransitionStyle();
+
+  auto* originating_element = document_->documentElement();
+  originating_element->SetNeedsStyleRecalc(
+      kLocalStyleChange, StyleChangeReasonForTracing::Create(
+                             style_change_reason::kDocumentTransition));
+
+  auto invalidate_style = [](PseudoElement* pseudo_element) {
+    pseudo_element->SetNeedsStyleRecalc(
+        kLocalStyleChange, StyleChangeReasonForTracing::Create(
+                               style_change_reason::kDocumentTransition));
+  };
+  DocumentTransitionUtils::ForEachTransitionPseudo(*document_,
+                                                   invalidate_style);
+}
+
+const String& DocumentTransitionStyleTracker::UAStyleSheet() {
+  if (ua_style_sheet_)
+    return *ua_style_sheet_;
+
+  // Animations are added in the start phase of the transition.
+  // Note that the cached ua_style_sheet_ above is invalidated when |state_|
+  // moves to kStarted stage to generate a new stylesheet including styles for
+  // animations.
+  const bool add_animations = state_ == State::kStarted;
+
+  StringBuilder builder;
+  builder.Append(StaticUAStyles());
+  if (add_animations)
+    builder.Append(AnimationUAStyles());
+
+  for (auto& entry : element_data_map_) {
+    auto document_transition_tag = entry.key.GetString().Utf8();
+    auto& element_data = entry.value;
+
+    // ::transition-container styles using computed properties for each element.
+    builder.AppendFormat(
+        R"CSS(
+        html::transition-container(%s) {
+          width: %dpx;
+          height: %dpx;
+          transform:%s;
+        }
+        )CSS",
+        document_transition_tag.c_str(),
+        element_data->border_box_size.Width().ToInt(),
+        element_data->border_box_size.Height().ToInt(),
+        ComputedStyleUtils::ValueForTransformationMatrix(
+            element_data->viewport_matrix, 1, false)
+            ->CssText()
+            .Utf8()
+            .c_str());
+
+    // TODO(khushalsagar) : We'll need to retarget the animation if the final
+    // value changes during the start phase.
+    if (add_animations && element_data->old_snapshot_id.IsValid() &&
+        element_data->new_snapshot_id.IsValid()) {
+      builder.AppendFormat(
+          R"CSS(
+          @keyframes transition-container-anim-%s {
+            from {
+             transform:%s;
+             width:%dpx;
+             height:%dpx;
+            }
+           }
+           )CSS",
+          document_transition_tag.c_str(),
+          ComputedStyleUtils::ValueForTransformationMatrix(
+              element_data->cached_viewport_matrix, 1, false)
+              ->CssText()
+              .Utf8()
+              .c_str(),
+          element_data->cached_border_box_size.Width().ToInt(),
+          element_data->cached_border_box_size.Height().ToInt());
+
+      // TODO(khushalsagar) : The duration/delay in the UA stylesheet will need
+      // to be the duration from TransitionConfig. See crbug.com/1275727.
+      builder.AppendFormat(
+          R"CSS(
+          html::transition-container(%s) {
+            animation: transition-container-anim-%s 0.25s
+          }
+          )CSS",
+          document_transition_tag.c_str(), document_transition_tag.c_str());
+    }
+  }
+
+  ua_style_sheet_ = builder.ReleaseString();
+  return *ua_style_sheet_;
+}
+
+bool DocumentTransitionStyleTracker::HasLiveNewContent() const {
+  return state_ == State::kStarted;
+}
+
+void DocumentTransitionStyleTracker::Trace(Visitor* visitor) const {
+  visitor->Trace(document_);
+  visitor->Trace(element_data_map_);
+}
+
+void DocumentTransitionStyleTracker::ElementData::Trace(
+    Visitor* visitor) const {
+  visitor->Trace(target_element);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h
new file mode 100644
index 0000000..f8c0210
--- /dev/null
+++ b/third_party/blink/renderer/core/document_transition/document_transition_style_tracker.h
@@ -0,0 +1,124 @@
+// Copyright 2022 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_CORE_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_STYLE_TRACKER_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_STYLE_TRACKER_H_
+
+#include "third_party/blink/renderer/core/core_export.h"
+
+#include "components/viz/common/shared_element_resource_id.h"
+#include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/platform/geometry/layout_size.h"
+#include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
+
+namespace blink {
+class PseudoElement;
+
+// This class manages the integration between DocumentTransition and the style
+// system which encompasses the following responsibilities :
+//
+// 1) Triggering style invalidation to change the DOM structure at different
+//    stages during a transition. For example, pseudo elements for new-content
+//    are generated after the new Document has loaded and the transition can be
+//    started.
+//
+// 2) Tracking changes in the state of shared elements that are mirrored in the
+//    style for their corresponding pseudo element. For example, if a shared
+//    element's size or viewport space transform is updated. This data is used
+//    to generate a dynamic UA stylesheet for these pseudo elements.
+//
+// A new instance of this class is created for every transition.
+class DocumentTransitionStyleTracker
+    : public GarbageCollected<DocumentTransitionStyleTracker> {
+ public:
+  explicit DocumentTransitionStyleTracker(Document& document);
+  ~DocumentTransitionStyleTracker();
+
+  // Notifies when the transition is initiated. |elements| is the set of shared
+  // elements in the old DOM.
+  void Prepare(const HeapVector<Member<Element>>& old_elements);
+
+  // Notifies when caching snapshots for elements in the old DOM finishes. This
+  // is dispatched before script is notified to ensure this class releases any
+  // references to elements in the old DOM before it is mutated by script.
+  void PrepareResolved();
+
+  // Notifies when the new DOM has finished loading and a transition can be
+  // started. |elements| is the set of shared elements in the new DOM paired
+  // sequentially with the list of |elements| in the Prepare call.
+  void Start(const HeapVector<Member<Element>>& new_elements);
+
+  // Notifies when the animation setup for the transition during Start have
+  // finished executing.
+  void StartFinished();
+
+  // Dispatched if a transition is aborted. Must be called before "Start" stage
+  // is initiated.
+  void Abort();
+
+  // Returns the resource id that |element| should be tagged with. This
+  // |element| must be a shared element in the current DOM (specified in Prepare
+  // or Start).
+  viz::SharedElementResourceId GetLiveSnapshotId(const Element* element) const;
+
+  // Creates a PseudoElement for the corresponding |pseudo_id| and
+  // |document_transition_tag|. The |pseudo_id| must be a ::transition* element.
+  PseudoElement* CreatePseudoElement(
+      Element* parent,
+      PseudoId pseudo_id,
+      const AtomicString& document_transition_tag);
+
+  // Dispatched after the layout lifecycle stage after each rendering lifecycle
+  // update when a transition is in progress.
+  void RunPostLayoutSteps();
+
+  // Provides a UA stylesheet applied to ::transition* pseudo elements.
+  const String& UAStyleSheet();
+
+  void Trace(Visitor* visitor) const;
+
+ private:
+  class ContainerPseudoElement;
+
+  // These state transitions are executed in a serial order unless the
+  // transition is aborted.
+  enum class State { kIdle, kPreparing, kPrepared, kStarted, kFinished };
+
+  struct ElementData : public GarbageCollected<ElementData> {
+    void Trace(Visitor* visitor) const;
+
+    // The element in the current DOM whose state is being tracked and mirrored
+    // into the corresponding container pseudo element.
+    Member<Element> target_element;
+
+    // Computed info for each element participating in the transition for the
+    // |target_element|. This information is mirrored into the UA stylesheet.
+    LayoutSize border_box_size;
+    TransformationMatrix viewport_matrix;
+
+    // Computed info cached before the DOM switches to the new state.
+    LayoutSize cached_border_box_size;
+    TransformationMatrix cached_viewport_matrix;
+
+    // Valid if there is an element in the old DOM generating a snapshot.
+    viz::SharedElementResourceId old_snapshot_id;
+
+    // Valid if there is an element in the new DOM generating a snapshot.
+    viz::SharedElementResourceId new_snapshot_id;
+  };
+
+  void InvalidateStyle();
+  bool HasLiveNewContent() const;
+  void EndTransition();
+
+  Member<Document> document_;
+  State state_ = State::kIdle;
+  Vector<AtomicString> pseudo_document_transition_tags_;
+  HeapHashMap<AtomicString, Member<ElementData>> element_data_map_;
+  absl::optional<String> ua_style_sheet_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOCUMENT_TRANSITION_DOCUMENT_TRANSITION_STYLE_TRACKER_H_
diff --git a/third_party/blink/renderer/core/document_transition/document_transition_test.cc b/third_party/blink/renderer/core/document_transition/document_transition_test.cc
index 7da774c..1ac1f19 100644
--- a/third_party/blink/renderer/core/document_transition/document_transition_test.cc
+++ b/third_party/blink/renderer/core/document_transition/document_transition_test.cc
@@ -4,8 +4,12 @@
 
 #include "third_party/blink/renderer/core/document_transition/document_transition.h"
 
+#include "base/check_op.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/task/single_thread_task_runner.h"
 #include "base/test/scoped_feature_list.h"
 #include "cc/document_transition/document_transition_request.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/web/web_settings.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
@@ -23,8 +27,10 @@
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
 #include "third_party/blink/renderer/platform/testing/find_cc_layer.h"
 #include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 
 namespace blink {
 
@@ -32,7 +38,9 @@
                                public PaintTestConfigurations,
                                private ScopedDocumentTransitionForTest {
  public:
-  DocumentTransitionTest() : ScopedDocumentTransitionForTest(true) {}
+  DocumentTransitionTest() : ScopedDocumentTransitionForTest(true) {
+    feature_list_.InitWithFeatures({features::kDocumentTransitionRenderer}, {});
+  }
 
   static void ConfigureCompositingWebView(WebSettings* settings) {
     settings->SetPreferCompositingToLCDTextEnabled(true);
@@ -43,6 +51,10 @@
     web_view_helper_->Initialize(nullptr, nullptr,
                                  &ConfigureCompositingWebView);
     web_view_helper_->Resize(gfx::Size(200, 200));
+
+    task_runner_ = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+    DocumentTransitionSupplement::documentTransition(GetDocument())
+        ->task_runner_for_testing_ = task_runner_;
   }
 
   void TearDown() override { web_view_helper_.reset(); }
@@ -103,8 +115,53 @@
     return transition->state_;
   }
 
- private:
+  void ValidatePseudoElementTree(
+      const Vector<WTF::AtomicString>& document_transition_tags,
+      bool has_new_content) {
+    auto* transition_pseudo =
+        GetDocument().documentElement()->GetPseudoElement(kPseudoIdTransition);
+    ASSERT_TRUE(transition_pseudo);
+    EXPECT_TRUE(transition_pseudo->GetComputedStyle());
+    EXPECT_TRUE(transition_pseudo->GetLayoutObject());
+
+    PseudoElement* previous_container = nullptr;
+    for (const auto& document_transition_tag : document_transition_tags) {
+      auto* container_pseudo = transition_pseudo->GetPseudoElement(
+          kPseudoIdTransitionContainer, document_transition_tag);
+      ASSERT_TRUE(container_pseudo);
+      EXPECT_TRUE(container_pseudo->GetComputedStyle());
+      EXPECT_TRUE(container_pseudo->GetLayoutObject());
+
+      if (previous_container) {
+        EXPECT_EQ(LayoutTreeBuilderTraversal::NextSibling(*previous_container),
+                  container_pseudo);
+      }
+      previous_container = container_pseudo;
+
+      auto* old_content = container_pseudo->GetPseudoElement(
+          kPseudoIdTransitionOldContent, document_transition_tag);
+      ASSERT_TRUE(old_content);
+      EXPECT_TRUE(old_content->GetComputedStyle());
+      EXPECT_TRUE(old_content->GetLayoutObject());
+
+      auto* new_content = container_pseudo->GetPseudoElement(
+          kPseudoIdTransitionNewContent, document_transition_tag);
+
+      if (!has_new_content) {
+        ASSERT_FALSE(new_content);
+        continue;
+      }
+
+      ASSERT_TRUE(new_content);
+      EXPECT_TRUE(new_content->GetComputedStyle());
+      EXPECT_TRUE(new_content->GetLayoutObject());
+    }
+  }
+
+ protected:
   std::unique_ptr<frame_test_helpers::WebViewHelper> web_view_helper_;
+  base::test::ScopedFeatureList feature_list_;
+  scoped_refptr<scheduler::FakeTaskRunner> task_runner_;
 };
 
 INSTANTIATE_PAINT_TEST_SUITE_P(DocumentTransitionTest);
@@ -487,6 +544,7 @@
   EXPECT_FALSE(transition->TakePendingRequest());
 
   start_request->TakeFinishedCallback().Run();
+  task_runner_->RunUntilIdle();
   EXPECT_EQ(GetState(transition), State::kIdle);
   start_tester.WaitUntilSettled();
   EXPECT_TRUE(start_tester.IsFulfilled());
@@ -524,6 +582,7 @@
 
   EXPECT_EQ(GetState(transition), State::kStarted);
   UpdateAllLifecyclePhasesAndFinishDirectives();
+  task_runner_->RunUntilIdle();
 
   // Visual updates are restored on start.
   EXPECT_FALSE(LayerTreeHost()->IsDeferringCommits());
@@ -561,76 +620,62 @@
 TEST_P(DocumentTransitionTest, DocumentTransitionPseudoTree) {
   SetHtmlInnerHTML(R"HTML(
     <style>
-      html::transition {
-        display: block;
-      }
-      html::transition-container(*) {
-        display: block;
-      }
-      html::transition-old-content(*) {
-        display: block;
-      }
-      html::transition-new-content(*) {
-        display: block;
-      }
+      div { width: 100px; height: 100px; contain: paint }
     </style>
+
+    <div id=e1></div>
+    <div id=e2></div>
+    <div id=e3></div>
   )HTML");
 
-  Vector<AtomicString> document_transition_tags = {"foo", "bar", "baz"};
-  GetDocument().GetStyleEngine().SetDocumentTransitionTags(
-      document_transition_tags);
-  GetDocument().documentElement()->SetNeedsStyleRecalc(
-      kLocalStyleChange, StyleChangeReasonForTracing::Create("Test"));
-  auto invalidate_style = [](PseudoElement* pseudo_element) {
-    pseudo_element->SetNeedsStyleRecalc(
-        kLocalStyleChange, StyleChangeReasonForTracing::Create("Test"));
-  };
-  DocumentTransitionUtils::ForEachTransitionPseudo(GetDocument(),
-                                                   invalidate_style);
-  GetDocument().UpdateStyleAndLayoutTreeForThisDocument();
+  auto* e1 = GetDocument().getElementById("e1");
+  auto* e2 = GetDocument().getElementById("e2");
+  auto* e3 = GetDocument().getElementById("e3");
 
-  auto* transition_pseudo =
-      GetDocument().documentElement()->GetPseudoElement(kPseudoIdTransition);
-  ASSERT_TRUE(transition_pseudo);
-  EXPECT_TRUE(transition_pseudo->GetComputedStyle());
-  EXPECT_TRUE(transition_pseudo->GetLayoutObject());
+  auto* transition =
+      DocumentTransitionSupplement::documentTransition(GetDocument());
 
-  PseudoElement* previous_container = nullptr;
-  for (const auto& document_transition_tag : document_transition_tags) {
-    auto* container_pseudo = transition_pseudo->GetPseudoElement(
-        kPseudoIdTransitionContainer, document_transition_tag);
-    ASSERT_TRUE(container_pseudo);
-    EXPECT_TRUE(container_pseudo->GetComputedStyle());
-    EXPECT_TRUE(container_pseudo->GetLayoutObject());
+  V8TestingScope v8_scope;
+  ScriptState* script_state = v8_scope.GetScriptState();
+  ExceptionState& exception_state = v8_scope.GetExceptionState();
 
-    if (previous_container) {
-      EXPECT_EQ(LayoutTreeBuilderTraversal::NextSibling(*previous_container),
-                container_pseudo);
-    }
-    previous_container = container_pseudo;
+  DocumentTransitionPrepareOptions options;
+  options.setSharedElements({e1, e2, e3});
+  transition->prepare(script_state, &options, exception_state);
+  ASSERT_FALSE(exception_state.HadException());
+  UpdateAllLifecyclePhasesForTest();
 
-    auto* old_content = container_pseudo->GetPseudoElement(
-        kPseudoIdTransitionOldContent, document_transition_tag);
-    ASSERT_TRUE(old_content);
-    EXPECT_TRUE(old_content->GetComputedStyle());
-    EXPECT_TRUE(old_content->GetLayoutObject());
+  // The prepare phase should generate the pseudo tree.
+  const Vector<AtomicString> document_transition_tags = {"shared-0", "shared-1",
+                                                         "shared-2"};
+  ValidatePseudoElementTree(document_transition_tags, false);
 
-    auto* new_content = container_pseudo->GetPseudoElement(
-        kPseudoIdTransitionNewContent, document_transition_tag);
-    ASSERT_TRUE(new_content);
-    EXPECT_TRUE(new_content->GetComputedStyle());
-    EXPECT_TRUE(new_content->GetLayoutObject());
-  }
+  // Finish the prepare phase, mutate the DOM and start the animation.
+  UpdateAllLifecyclePhasesAndFinishDirectives();
+  SetHtmlInnerHTML(R"HTML(
+    <style>
+      div { width: 200px; height: 200px; contain: paint }
+    </style>
 
-  GetDocument().GetStyleEngine().SetDocumentTransitionTags({});
-  GetDocument().documentElement()->SetNeedsStyleRecalc(
-      kLocalStyleChange, StyleChangeReasonForTracing::Create("Test"));
-  DocumentTransitionUtils::ForEachTransitionPseudo(GetDocument(),
-                                                   invalidate_style);
-  GetDocument().UpdateStyleAndLayoutTreeForThisDocument();
-  transition_pseudo =
-      GetDocument().documentElement()->GetPseudoElement(kPseudoIdTransition);
-  EXPECT_FALSE(transition_pseudo);
+    <div id=e1></div>
+    <div id=e2></div>
+    <div id=e3></div>
+  )HTML");
+  DocumentTransitionStartOptions start_options;
+  start_options.setSharedElements({e1, e2, e3});
+  transition->start(script_state, &start_options, exception_state);
+  ASSERT_FALSE(exception_state.HadException());
+
+  // The start phase should generate pseudo elements for rendering new live
+  // content.
+  UpdateAllLifecyclePhasesAndFinishDirectives();
+  ValidatePseudoElementTree(document_transition_tags, true);
+
+  // Finish the animations which should remove the pseudo element tree.
+  task_runner_->RunUntilIdle();
+  UpdateAllLifecyclePhasesAndFinishDirectives();
+  EXPECT_FALSE(
+      GetDocument().documentElement()->GetPseudoElement(kPseudoIdTransition));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 297b0b9..a1ebce8 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2542,18 +2542,6 @@
                                                                  GetFrame());
 }
 
-void Document::DetachCompositorTimeline(
-    CompositorAnimationTimeline* timeline) const {
-  if (!Platform::Current()->IsThreadedAnimationEnabled() ||
-      !GetSettings()->GetAcceleratedCompositingEnabled())
-    return;
-
-  // During Document::Shutdown() the timeline needs to be unconditionally
-  // detached.
-  GetPage()->GetChromeClient().DetachCompositorAnimationTimeline(timeline,
-                                                                 GetFrame());
-}
-
 void Document::ClearFocusedElementSoon() {
   if (!clear_focused_element_timer_.IsActive())
     clear_focused_element_timer_.StartOneShot(base::TimeDelta(), FROM_HERE);
@@ -2797,7 +2785,7 @@
   CancelPendingJavaScriptUrls();
   http_refresh_scheduler_->Cancel();
 
-  DetachCompositorTimeline(Timeline().CompositorTimeline());
+  GetDocumentAnimations().DetachCompositorTimelines();
 
   if (GetFrame()->IsLocalRoot())
     GetPage()->GetChromeClient().AttachRootLayer(nullptr, GetFrame());
@@ -3500,8 +3488,6 @@
     return;
   }
 
-  fetcher_->ScheduleWarnUnusedPreloads();
-
   if (GetStyleEngine().HaveRenderBlockingStylesheetsLoaded())
     UpdateStyleAndLayout(DocumentUpdateReason::kUnknown);
 
@@ -3580,6 +3566,9 @@
   if (LoadEventStillNeeded())
     ImplicitClose();
 
+  DCHECK(fetcher_);
+  fetcher_->ScheduleWarnUnusedPreloads();
+
   // The readystatechanged or load event may have disconnected this frame.
   if (!GetFrame() || !GetFrame()->IsAttached())
     return false;
@@ -5520,7 +5509,7 @@
 
 const AtomicString& Document::referrer() const {
   if (Loader())
-    return Loader()->GetReferrer().referrer;
+    return Loader()->GetReferrer();
   return g_null_atom;
 }
 
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 26f606f6..bcc2be8 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1429,7 +1429,6 @@
   }
 
   void AttachCompositorTimeline(CompositorAnimationTimeline*) const;
-  void DetachCompositorTimeline(CompositorAnimationTimeline*) const;
 
   void AddToTopLayer(Element*, const Element* before = nullptr);
   void RemoveFromTopLayer(Element*);
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 2fb295d..a8d679d 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -4897,17 +4897,32 @@
   for (legacy_root = this;; legacy_root = parent) {
     parent =
         DynamicTo<Element>(LayoutTreeBuilderTraversal::Parent(*legacy_root));
-    if (legacy_root->ShouldForceLegacyLayoutForChild())
-      return false;
 
+    // Note that even if we also previously forced legacy layout, we may need to
+    // introduce forced legacy layout in the ancestry, e.g. if legacy_root no
+    // longer establishes a new formatting context. It is therefore important
+    // that we first check if we reached the root, and potentially continue the
+    // journey in search of a formatting context root.
     if (!parent ||
         !parent->GetComputedStyle()
              ->InsideFragmentationContextWithNondeterministicEngine())
       break;
+
+    // Otherwise, if this element is always marked for legacy fallback, we can
+    // bail.
+    if (legacy_root->ShouldForceLegacyLayoutForChild())
+      return false;
   }
 
-  legacy_root->SetShouldForceLegacyLayoutForChild(true);
-  legacy_root->SetNeedsReattachLayoutTree();
+  // Only mark for reattachment if needed. Unnecessary reattachments may lead to
+  // over-invalidation and also printing problems; if we re-attach a frameset
+  // when printing, the frames will show up blank.
+  bool needs_reattach = false;
+  if (!legacy_root->ShouldForceLegacyLayoutForChild()) {
+    legacy_root->SetShouldForceLegacyLayoutForChild(true);
+    legacy_root->SetNeedsReattachLayoutTree();
+    needs_reattach = true;
+  }
 
   // When we have found the outermost fragmentation context candidate, we need
   // to make sure to mark for legacy all the way up to the element that we can
@@ -4917,10 +4932,11 @@
   // positive). When this happens, we need to walk all the way up to the
   // ancestor that establishes a formatting context, and this is the subtree
   // that will force legacy layout.
-  legacy_root->ForceLegacyLayoutInFormattingContext(
-      *legacy_root->GetComputedStyle());
+  if (legacy_root->ForceLegacyLayoutInFormattingContext(
+          *legacy_root->GetComputedStyle()))
+    needs_reattach = true;
 
-  return true;
+  return needs_reattach;
 }
 
 bool Element::IsFocusedElementInDocument() const {
diff --git a/third_party/blink/renderer/core/dom/pseudo_element.cc b/third_party/blink/renderer/core/dom/pseudo_element.cc
index 9c08fef..ed0c444 100644
--- a/third_party/blink/renderer/core/dom/pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/pseudo_element.cc
@@ -31,6 +31,7 @@
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
 #include "third_party/blink/renderer/core/document_transition/document_transition_pseudo_element_base.h"
+#include "third_party/blink/renderer/core/document_transition/document_transition_supplement.h"
 #include "third_party/blink/renderer/core/dom/element_rare_data.h"
 #include "third_party/blink/renderer/core/dom/first_letter_pseudo_element.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
@@ -53,8 +54,11 @@
   if (pseudo_id == kPseudoIdFirstLetter) {
     return MakeGarbageCollected<FirstLetterPseudoElement>(parent);
   } else if (IsTransitionPseudoElement(pseudo_id)) {
-    return MakeGarbageCollected<DocumentTransitionPseudoElementBase>(
-        parent, pseudo_id, document_transition_tag);
+    auto* document_transition =
+        DocumentTransitionSupplement::FromIfExists(parent->GetDocument())
+            ->GetTransition();
+    return document_transition->CreatePseudoElement(parent, pseudo_id,
+                                                    document_transition_tag);
   }
   DCHECK(pseudo_id == kPseudoIdAfter || pseudo_id == kPseudoIdBefore ||
          pseudo_id == kPseudoIdBackdrop || pseudo_id == kPseudoIdMarker);
diff --git a/third_party/blink/renderer/core/exported/web_document_loader_impl.cc b/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
index 46d0c702..06fda95 100644
--- a/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
@@ -57,7 +57,7 @@
 }
 
 WebString WebDocumentLoaderImpl::OriginalReferrer() const {
-  return DocumentLoader::OriginalReferrer().referrer;
+  return DocumentLoader::OriginalReferrer();
 }
 
 WebURL WebDocumentLoaderImpl::GetUrl() const {
@@ -69,12 +69,7 @@
 }
 
 WebString WebDocumentLoaderImpl::Referrer() const {
-  return DocumentLoader::GetReferrer().referrer;
-}
-
-network::mojom::ReferrerPolicy WebDocumentLoaderImpl::GetReferrerPolicy()
-    const {
-  return DocumentLoader::GetReferrer().referrer_policy;
+  return DocumentLoader::GetReferrer();
 }
 
 const WebURLResponse& WebDocumentLoaderImpl::GetResponse() const {
diff --git a/third_party/blink/renderer/core/exported/web_document_loader_impl.h b/third_party/blink/renderer/core/exported/web_document_loader_impl.h
index 12daac1..7c19fa0 100644
--- a/third_party/blink/renderer/core/exported/web_document_loader_impl.h
+++ b/third_party/blink/renderer/core/exported/web_document_loader_impl.h
@@ -66,7 +66,6 @@
   WebURL GetUrl() const override;
   WebString HttpMethod() const override;
   WebString Referrer() const override;
-  network::mojom::ReferrerPolicy GetReferrerPolicy() const override;
   const WebURLResponse& GetResponse() const override;
   bool HasUnreachableURL() const override;
   WebURL UnreachableURL() const override;
diff --git a/third_party/blink/renderer/core/exported/web_history_entry.cc b/third_party/blink/renderer/core/exported/web_history_entry.cc
index 1e2baf7..abdda5f 100644
--- a/third_party/blink/renderer/core/exported/web_history_entry.cc
+++ b/third_party/blink/renderer/core/exported/web_history_entry.cc
@@ -115,7 +115,8 @@
   WebHistoryItem item;
   item.Initialize();
   item.SetURLString(WebString::FromUTF16(state.url_string));
-  item.SetReferrer(WebString::FromUTF16(state.referrer), state.referrer_policy);
+  item.SetReferrer(WebString::FromUTF16(state.referrer));
+  item.SetReferrerPolicy(state.referrer_policy);
   item.SetTarget(WebString::FromUTF16(state.target));
   if (state.state_object) {
     item.SetStateObject(WebSerializedScriptValue::FromString(
diff --git a/third_party/blink/renderer/core/exported/web_history_item.cc b/third_party/blink/renderer/core/exported/web_history_item.cc
index ad85862..260df2e 100644
--- a/third_party/blink/renderer/core/exported/web_history_item.cc
+++ b/third_party/blink/renderer/core/exported/web_history_item.cc
@@ -68,17 +68,20 @@
 }
 
 WebString WebHistoryItem::GetReferrer() const {
-  return private_->GetReferrer().referrer;
+  return private_->GetReferrer();
 }
 
 network::mojom::ReferrerPolicy WebHistoryItem::GetReferrerPolicy() const {
-  return private_->GetReferrer().referrer_policy;
+  return private_->GetReferrerPolicy();
 }
 
-void WebHistoryItem::SetReferrer(
-    const WebString& referrer,
+void WebHistoryItem::SetReferrer(const WebString& referrer) {
+  private_->SetReferrer(referrer);
+}
+
+void WebHistoryItem::SetReferrerPolicy(
     network::mojom::ReferrerPolicy referrer_policy) {
-  private_->SetReferrer(Referrer(referrer, referrer_policy));
+  private_->SetReferrerPolicy(referrer_policy);
 }
 
 const WebString& WebHistoryItem::Target() const {
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
index 3834541..978c2dcd 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
@@ -164,7 +164,7 @@
 }
 
 void WebPluginContainerImpl::Paint(GraphicsContext& context,
-                                   const GlobalPaintFlags,
+                                   PaintFlags,
                                    const CullRect& cull_rect,
                                    const gfx::Vector2d& paint_offset) const {
   // Don't paint anything if the plugin doesn't intersect.
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.h b/third_party/blink/renderer/core/exported/web_plugin_container_impl.h
index 5e95d768..78285c47 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.h
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.h
@@ -90,7 +90,7 @@
   // |paint_offset| is used to to paint the contents at the correct location.
   // It should be issued as a transform operation before painting the contents.
   void Paint(GraphicsContext&,
-             const GlobalPaintFlags,
+             PaintFlags,
              const CullRect&,
              const gfx::Vector2d& paint_offset) const override;
   void UpdateGeometry() override;
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_test.cc b/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
index 790cd5e..86dbca25 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_test.cc
@@ -1475,7 +1475,7 @@
   paint_controller->UpdateCurrentPaintChunkProperties(
       PropertyTreeState::Root());
   GraphicsContext graphics_context(*paint_controller);
-  container->Paint(graphics_context, kGlobalPaintNormalPhase,
+  container->Paint(graphics_context, PaintFlag::kNoFlag,
                    CullRect(gfx::Rect(10, 10, 400, 300)), gfx::Vector2d());
   paint_controller->CommitNewDisplayItems();
 
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 7c93a00..95bbd27 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -1395,7 +1395,7 @@
          DocumentLifecycle::kPaintClean);
 
   auto* builder = MakeGarbageCollected<PaintRecordBuilder>();
-  main_view.PaintOutsideOfLifecycle(builder->Context(), kGlobalPaintNormalPhase,
+  main_view.PaintOutsideOfLifecycle(builder->Context(), PaintFlag::kNoFlag,
                                     CullRect(rect));
   // Don't bother to save/restore here as the caller is expecting the canvas
   // to be modified and take care of it.
@@ -1790,8 +1790,6 @@
   settings->SetTouchDragDropEnabled(prefs.touch_drag_drop_enabled);
   settings->SetTouchDragEndContextMenu(prefs.touch_dragend_context_menu);
   settings->SetWebXRImmersiveArAllowed(prefs.webxr_immersive_ar_allowed);
-  settings->SetLitePageSubresourceRedirectOrigin(WebString::FromASCII(
-      prefs.litepage_subresource_redirect_origin.Serialize()));
 
 #if defined(OS_MAC)
   web_view_impl->SetMaximumLegibleScale(
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index 1d3fa14..6e131a26 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -547,12 +547,10 @@
   // would.
   LocalFrameView* view = web_view_helper_.LocalMainFrame()->GetFrameView();
   PaintLayer* root_layer = view->GetLayoutView()->Layer();
-  PaintLayerPaintingInfo painting_info(root_layer, kGlobalPaintNormalPhase);
 
   view->GetLayoutView()->GetDocument().Lifecycle().AdvanceTo(
       DocumentLifecycle::kInPaint);
-  PaintLayerPainter(*root_layer)
-      .PaintLayerContents(builder->Context(), painting_info, kPaintLayerNoFlag);
+  PaintLayerPainter(*root_layer).PaintLayerContents(builder->Context());
   view->GetLayoutView()->GetDocument().Lifecycle().AdvanceTo(
       DocumentLifecycle::kPaintClean);
   builder->EndRecording()->Playback(&canvas);
diff --git a/third_party/blink/renderer/core/frame/dom_window.cc b/third_party/blink/renderer/core/frame/dom_window.cc
index e39d2fda..18974f9 100644
--- a/third_party/blink/renderer/core/frame/dom_window.cc
+++ b/third_party/blink/renderer/core/frame/dom_window.cc
@@ -680,7 +680,7 @@
       options->hasDelegate()) {
     Vector<String> capability_list;
     options->delegate().Split(' ', capability_list);
-    delegate_payment_request = capability_list.Contains("paymentrequest");
+    delegate_payment_request = capability_list.Contains("payment");
   }
 
   PostedMessage* posted_message = MakeGarbageCollected<PostedMessage>();
diff --git a/third_party/blink/renderer/core/frame/embedded_content_view.h b/third_party/blink/renderer/core/frame/embedded_content_view.h
index 999b44ef..847d2ee 100644
--- a/third_party/blink/renderer/core/frame/embedded_content_view.h
+++ b/third_party/blink/renderer/core/frame/embedded_content_view.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_EMBEDDED_CONTENT_VIEW_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/paint/paint_phase.h"
+#include "third_party/blink/renderer/core/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/vector2d.h"
@@ -33,11 +33,13 @@
   virtual LayoutEmbeddedContent* GetLayoutEmbeddedContent() const = 0;
   virtual void AttachToLayout() = 0;
   virtual void DetachFromLayout() = 0;
-  virtual void Paint(
-      GraphicsContext&,
-      const GlobalPaintFlags,
-      const CullRect&,
-      const gfx::Vector2d& paint_offset = gfx::Vector2d()) const = 0;
+  // |cull_rect| is in the same coordinate space as Location() and FrameRect().
+  // |paint_offset| is Location() mapped into the current coordinates space of
+  // the current paint context.
+  virtual void Paint(GraphicsContext&,
+                     PaintFlags,
+                     const CullRect& cull_rect,
+                     const gfx::Vector2d& paint_offset) const = 0;
   // Called when the size of the view changes.  Implementations of
   // EmbeddedContentView should call LayoutEmbeddedContent::UpdateGeometry in
   // addition to any internal logic.
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index f42d6ff..9093dfc 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -2397,10 +2397,9 @@
     if (needs_to_repeat_lifecycle)
       continue;
 
-    // DocumentTransition mirrors post layout transform for shared elements to
-    // UA created elements. If the transform for a shared element was updated,
-    // the style for UA created elements will be dirtied during the notification
-    // below.
+    // DocumentTransition mutates the tree and mirrors post layout transform for
+    // shared elements to UA created elements. This may dirty style/layout
+    // requiring another lifecycle update.
     needs_to_repeat_lifecycle = RunDocumentTransitionSteps(target_state);
     if (needs_to_repeat_lifecycle)
       continue;
@@ -2453,9 +2452,6 @@
     DocumentLifecycle::LifecycleState target_state) {
   DCHECK(frame_ && frame_->GetDocument());
 
-  // This step must be done after layout since it requires the element's
-  // transform computed during layout. But before paint since it can dirty style
-  // and trigger another style/layout update.
   if (target_state != DocumentLifecycle::kPaintClean)
     return false;
 
@@ -2464,11 +2460,7 @@
   if (!document_transition_supplement)
     return false;
 
-  // Update the transforms for elements created for the transition to the
-  // transform on the corresponding shared element. Since this can change
-  // styles on the transition elements, it can trigger another lifecycle
-  // update.
-  document_transition_supplement->GetTransition()->UpdateTransforms();
+  document_transition_supplement->GetTransition()->RunPostLayoutSteps();
   return Lifecycle().GetState() < DocumentLifecycle::kPrePaintClean;
 }
 
@@ -2609,9 +2601,8 @@
                 layout_view->DescendantBlockingWheelEventHandlerChanged()) {
               owner->MarkDescendantBlockingWheelEventHandlerChanged();
             }
-            if (RuntimeEnabledFeatures::CullRectUpdateEnabled() &&
-                (layout_view->Layer()->NeedsCullRectUpdate() ||
-                 layout_view->Layer()->DescendantNeedsCullRectUpdate())) {
+            if (layout_view->Layer()->NeedsCullRectUpdate() ||
+                layout_view->Layer()->DescendantNeedsCullRectUpdate()) {
               layout_view->Layer()
                   ->MarkCompositingContainerChainForNeedsCullRectUpdate();
             }
@@ -2798,8 +2789,7 @@
   auto* layout_view = GetLayoutView();
   DCHECK(layout_view);
 
-  if (RuntimeEnabledFeatures::CullRectUpdateEnabled())
-    CullRectUpdater(*layout_view->Layer()).Update();
+  CullRectUpdater(*layout_view->Layer()).Update();
 
   bool debug_info_newly_enabled =
       UpdateLayerDebugInfoEnabled() && PaintDebugInfoEnabled();
@@ -2852,10 +2842,9 @@
 
     // Draw the overlay layer (video or WebXR DOM overlay) if present.
     if (PaintLayer* full_screen_layer = GetFullScreenOverlayLayer()) {
-      PaintLayerPainter(*full_screen_layer)
-          .Paint(graphics_context, kGlobalPaintNormalPhase, 0);
+      PaintLayerPainter(*full_screen_layer).Paint(graphics_context);
     } else {
-      PaintFrame(graphics_context, kGlobalPaintNormalPhase);
+      PaintFrame(graphics_context);
 
       GetPage()->GetValidationMessageClient().PaintOverlay(graphics_context);
       ForAllNonThrottledLocalFrameViews(
@@ -3918,7 +3907,7 @@
 }
 
 void LocalFrameView::Paint(GraphicsContext& context,
-                           const GlobalPaintFlags global_paint_flags,
+                           PaintFlags paint_flags,
                            const CullRect& cull_rect,
                            const gfx::Vector2d& paint_offset) const {
   const auto* owner_layout_object = GetFrame().OwnerLayoutObject();
@@ -3944,12 +3933,12 @@
 
   // |paint_offset| is not used because paint properties of the contents will
   // ensure the correct location.
-  PaintFrame(context, global_paint_flags);
+  PaintFrame(context, paint_flags);
 }
 
 void LocalFrameView::PaintFrame(GraphicsContext& context,
-                                GlobalPaintFlags global_paint_flags) const {
-  FramePainter(*this).Paint(context, global_paint_flags);
+                                PaintFlags paint_flags) const {
+  FramePainter(*this).Paint(context, paint_flags);
 }
 
 static bool PaintOutsideOfLifecycleIsAllowed(GraphicsContext& context,
@@ -3963,10 +3952,9 @@
   return false;
 }
 
-void LocalFrameView::PaintOutsideOfLifecycle(
-    GraphicsContext& context,
-    const GlobalPaintFlags global_paint_flags,
-    const CullRect& cull_rect) {
+void LocalFrameView::PaintOutsideOfLifecycle(GraphicsContext& context,
+                                             const PaintFlags paint_flags,
+                                             const CullRect& cull_rect) {
   DCHECK(PaintOutsideOfLifecycleIsAllowed(context, *this));
 
   SCOPED_UMA_AND_UKM_TIMER(EnsureUkmAggregator(),
@@ -3980,7 +3968,7 @@
   {
     OverriddenCullRectScope force_cull_rect(*GetLayoutView()->Layer(),
                                             cull_rect);
-    PaintFrame(context, global_paint_flags);
+    PaintFrame(context, paint_flags);
   }
 
   ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
@@ -3996,7 +3984,7 @@
   if (GetLayoutView()->Layer()->SelfOrDescendantNeedsRepaint()) {
     PaintController::CycleScope cycle_scope(paint_controller);
     GraphicsContext graphics_context(paint_controller);
-    PaintFrame(graphics_context, kGlobalPaintNormalPhase);
+    PaintFrame(graphics_context);
     paint_controller.CommitNewDisplayItems();
   }
   Lifecycle().AdvanceTo(DocumentLifecycle::kPaintClean);
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 75ab65c..d9aa7771 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -52,6 +52,7 @@
 #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
 #include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h"
 #include "third_party/blink/renderer/platform/graphics/subtree_paint_property_update_reason.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/timer.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
@@ -551,7 +552,7 @@
   // PaintClean state when these functions are called.
   void PaintOutsideOfLifecycle(
       GraphicsContext&,
-      const GlobalPaintFlags,
+      PaintFlags,
       const CullRect& cull_rect = CullRect::Infinite());
 
   // For testing paint with a custom cull rect.
@@ -841,11 +842,11 @@
 
   // EmbeddedContentView implementation
   void Paint(GraphicsContext&,
-             const GlobalPaintFlags,
+             PaintFlags,
              const CullRect&,
              const gfx::Vector2d&) const final;
 
-  void PaintFrame(GraphicsContext&, GlobalPaintFlags) const;
+  void PaintFrame(GraphicsContext&, PaintFlags = PaintFlag::kNoFlag) const;
 
   LayoutSVGRoot* EmbeddedReplacedContent() const;
 
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.cc b/third_party/blink/renderer/core/frame/remote_frame_view.cc
index 074af47c..1075df9 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.cc
@@ -265,7 +265,7 @@
 }
 
 void RemoteFrameView::Paint(GraphicsContext& context,
-                            const GlobalPaintFlags flags,
+                            PaintFlags flags,
                             const CullRect& rect,
                             const gfx::Vector2d& paint_offset) const {
   if (!rect.Intersects(FrameRect()))
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.h b/third_party/blink/renderer/core/frame/remote_frame_view.h
index 27a4148f..8e52e67 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.h
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.h
@@ -45,7 +45,7 @@
   void SetFrameRect(const gfx::Rect&) override;
   void PropagateFrameRects() override;
   void Paint(GraphicsContext&,
-             const GlobalPaintFlags,
+             PaintFlags,
              const CullRect&,
              const gfx::Vector2d& paint_offset) const override;
   void UpdateGeometry() override;
diff --git a/third_party/blink/renderer/core/frame/web_frame_test.cc b/third_party/blink/renderer/core/frame/web_frame_test.cc
index 94a609a0..9517ed9 100644
--- a/third_party/blink/renderer/core/frame/web_frame_test.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_test.cc
@@ -999,7 +999,7 @@
       "window.frames[0].postMessage('0', {targetOrigin: '*'});");
   String post_message_w_payment_request(
       "window.frames[0].postMessage("
-      "'1', {targetOrigin: '*', delegate: 'paymentrequest'});");
+      "'1', {targetOrigin: '*', delegate: 'payment'});");
 
   // The delegation info is not passed through a postMessage that is sent
   // without either user activation or the delegation option.
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index a63f132..79e835a5 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -431,8 +431,7 @@
     auto* builder = MakeGarbageCollected<PaintRecordBuilder>(context);
     frame_view->PaintOutsideOfLifecycle(
         builder->Context(),
-        kGlobalPaintNormalPhase | kGlobalPaintFlattenCompositingLayers |
-            kGlobalPaintAddUrlMetadata,
+        PaintFlag::kOmitCompositingInfo | PaintFlag::kAddUrlMetadata,
         CullRect(page_rect));
     {
       ScopedPaintChunkProperties scoped_paint_chunk_properties(
@@ -571,10 +570,9 @@
 
     // This calls BeginRecording on |builder| with dimensions specified by the
     // CullRect.
-    GlobalPaintFlags flags =
-        kGlobalPaintNormalPhase | kGlobalPaintFlattenCompositingLayers;
+    PaintFlags flags = PaintFlag::kOmitCompositingInfo;
     if (include_linked_destinations)
-      flags |= kGlobalPaintAddUrlMetadata;
+      flags |= PaintFlag::kAddUrlMetadata;
 
     frame_view->PaintOutsideOfLifecycle(builder->Context(), flags,
                                         CullRect(bounds));
diff --git a/third_party/blink/renderer/core/html/build.gni b/third_party/blink/renderer/core/html/build.gni
index a7216bd..ddd1ba2f 100644
--- a/third_party/blink/renderer/core/html/build.gni
+++ b/third_party/blink/renderer/core/html/build.gni
@@ -728,7 +728,6 @@
   "parser/text_resource_decoder_test.cc",
   "portal/html_portal_element_test.cc",
   "shadow/progress_shadow_element_test.cc",
-  "subresource_redirect_test.cc",
   "time_ranges_test.cc",
   "track/text_track_list_test.cc",
   "track/vtt/buffered_line_reader_test.cc",
diff --git a/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc b/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
index b25fb7d9..7525719 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
@@ -208,27 +208,13 @@
 
   Document& document = GetDocument();
 
-  // TODO(crbug.com/1121840) Where to put the styles for the default elements in
-  // the shadow tree? For now, just set the style attributes with raw inline
-  // strings, but we should be able to do something better than this. Probably
-  // the solution is to have them in the UA styles (html.css).
-
   button_slot_ = MakeGarbageCollected<HTMLSlotElement>(document);
   button_slot_->setAttribute(html_names::kNameAttr, kButtonPartName);
 
   button_part_ = MakeGarbageCollected<HTMLButtonElement>(document);
   button_part_->setAttribute(html_names::kPartAttr, kButtonPartName);
   button_part_->setAttribute(html_names::kBehaviorAttr, kButtonPartName);
-  button_part_->setAttribute(html_names::kStyleAttr,
-                             R"CSS(
-      display: inline-flex;
-      align-items: center;
-      background-color: #ffffff;
-      padding: 0 0 0 3px;
-      border: 1px solid #767676;
-      border-radius: 2px;
-      cursor: default;
-  )CSS");
+  button_part_->SetShadowPseudoId(AtomicString("-internal-selectmenu-button"));
   button_part_listener_ =
       MakeGarbageCollected<HTMLSelectMenuElement::ButtonPartEventListener>(
           this);
@@ -244,26 +230,8 @@
                                      kSelectedValuePartName);
 
   auto* button_icon = MakeGarbageCollected<HTMLDivElement>(document);
-  button_icon->setAttribute(html_names::kStyleAttr,
-                            R"CSS(
-    background-image: url(
-      'data:image/svg+xml,\
-      <svg width="20" height="14" viewBox="0 0 20 16" fill="none" xmlns="http://www.w3.org/2000/svg">\
-        <path d="M4 6 L10 12 L 16 6" stroke="WindowText" stroke-width="3" stroke-linejoin="round"/>\
-      </svg>');
-    background-origin: content-box;
-    background-repeat: no-repeat;
-    background-size: contain;
-    height: 1.0em;
-    margin-inline-start: 4px;
-    opacity: 1;
-    outline: none;
-    padding-bottom: 2px;
-    padding-inline-start: 3px;
-    padding-inline-end: 3px;
-    padding-top: 2px;
-    width: 1.2em;
-    )CSS");
+  button_icon->SetShadowPseudoId(
+      AtomicString("-internal-selectmenu-button-icon"));
 
   listbox_slot_ = MakeGarbageCollected<HTMLSlotElement>(document);
   listbox_slot_->setAttribute(html_names::kNameAttr, kListboxPartName);
@@ -271,15 +239,8 @@
   SetListboxPart(MakeGarbageCollected<HTMLPopupElement>(document));
   listbox_part_->setAttribute(html_names::kPartAttr, kListboxPartName);
   listbox_part_->setAttribute(html_names::kBehaviorAttr, kListboxPartName);
-  listbox_part_->setAttribute(html_names::kStyleAttr,
-                              R"CSS(
-        border: 1px solid rgba(0, 0, 0, 0.15);
-        border-radius: 4px;
-        box-shadow: 0px 12.8px 28.8px rgba(0, 0, 0, 0.13), 0px 0px 9.2px rgba(0, 0, 0, 0.11);
-        box-sizing: border-box;
-        overflow: auto;
-        padding: 4px;
-  )CSS");
+  listbox_part_->SetShadowPseudoId(
+      AtomicString("-internal-selectmenu-listbox"));
 
   auto* options_slot = MakeGarbageCollected<HTMLSlotElement>(document);
 
@@ -772,6 +733,8 @@
        node; node = SelectMenuPartTraversal::Next(*node, this)) {
     if (IsValidOptionPart(node, /*show_warning=*/false)) {
       auto* element = DynamicTo<HTMLOptionElement>(node);
+      if (element->IsDisabledFormControl())
+        continue;
       SetSelectedOption(element);
       element->focus();
       DispatchInputAndChangeEventsIfNeeded();
@@ -785,6 +748,8 @@
        node; node = SelectMenuPartTraversal::Previous(*node, this)) {
     if (IsValidOptionPart(node, /*show_warning=*/false)) {
       auto* element = DynamicTo<HTMLOptionElement>(node);
+      if (element->IsDisabledFormControl())
+        continue;
       SetSelectedOption(element);
       element->focus();
       DispatchInputAndChangeEventsIfNeeded();
@@ -810,7 +775,8 @@
     return;
 
   if (event->type() == event_type_names::kClick &&
-      !select_menu_element_->open()) {
+      !select_menu_element_->open() &&
+      !select_menu_element_->IsDisabledFormControl()) {
     select_menu_element_->OpenListbox();
   } else if (event->type() == event_type_names::kKeydown) {
     bool handled = false;
@@ -820,7 +786,8 @@
     switch (keyboard_event->keyCode()) {
       case VKEY_RETURN:
       case VKEY_SPACE:
-        if (!select_menu_element_->open()) {
+        if (!select_menu_element_->open() &&
+            !select_menu_element_->IsDisabledFormControl()) {
           select_menu_element_->OpenListbox();
         }
         handled = true;
diff --git a/third_party/blink/renderer/core/html/forms/html_select_menu_element.idl b/third_party/blink/renderer/core/html/forms/html_select_menu_element.idl
index bce7a52..5286ed1d 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_menu_element.idl
+++ b/third_party/blink/renderer/core/html/forms/html_select_menu_element.idl
@@ -12,6 +12,7 @@
   // Open question: do we want to replicate the interface of
   // <select> as closely as possible?
   // Or should we try to improve, simplify, or extend it?
+  [CEReactions, Reflect] attribute boolean disabled;
   [ImplementedAs=formOwner] readonly attribute HTMLFormElement? form;
   [CEReactions, Reflect] attribute DOMString name;
   readonly attribute DOMString type;
diff --git a/third_party/blink/renderer/core/html/media/html_media_element_test.cc b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
index bf30b09..4053be9f 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element_test.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
@@ -86,6 +86,7 @@
   MOCK_CONST_METHOD0(GetNetworkState, NetworkState());
   MOCK_CONST_METHOD0(WouldTaintOrigin, bool());
   MOCK_METHOD1(SetLatencyHint, void(double));
+  MOCK_METHOD1(SetWasPlayedWithUserActivation, void(bool));
   MOCK_METHOD1(EnabledAudioTracksChanged, void(const WebVector<TrackId>&));
   MOCK_METHOD1(SelectedVideoTrackChanged, void(TrackId*));
   MOCK_METHOD4(
@@ -1292,4 +1293,39 @@
   EXPECT_NE(old_frame, Media()->LocalFrameForPlayer());
 }
 
+TEST_P(HTMLMediaElementTest, PlayedWithoutUserActivation) {
+  Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp));
+  test::RunPendingTasks();
+
+  SetReadyState(HTMLMediaElement::kHaveEnoughData);
+  test::RunPendingTasks();
+
+  EXPECT_CALL(*MockMediaPlayer(), SetWasPlayedWithUserActivation(false));
+  Media()->Play();
+}
+
+TEST_P(HTMLMediaElementTest, PlayedWithUserActivation) {
+  Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp));
+  test::RunPendingTasks();
+
+  SetReadyState(HTMLMediaElement::kHaveEnoughData);
+  test::RunPendingTasks();
+
+  LocalFrame::NotifyUserActivation(
+      Media()->GetDocument().GetFrame(),
+      mojom::UserActivationNotificationType::kTest);
+
+  EXPECT_CALL(*MockMediaPlayer(), SetWasPlayedWithUserActivation(true));
+  Media()->Play();
+}
+
+TEST_P(HTMLMediaElementTest, PlayedWithUserActivationBeforeLoad) {
+  LocalFrame::NotifyUserActivation(
+      Media()->GetDocument().GetFrame(),
+      mojom::UserActivationNotificationType::kTest);
+
+  EXPECT_CALL(*MockMediaPlayer(), SetWasPlayedWithUserActivation(_)).Times(0);
+  Media()->Play();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/media/html_video_element.cc b/third_party/blink/renderer/core/html/media/html_video_element.cc
index e28e76c..c3b52254 100644
--- a/third_party/blink/renderer/core/html/media/html_video_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_video_element.cc
@@ -381,11 +381,11 @@
 
 void HTMLVideoElement::PaintCurrentFrame(cc::PaintCanvas* canvas,
                                          const gfx::Rect& dest_rect,
-                                         const PaintFlags* flags) const {
+                                         const cc::PaintFlags* flags) const {
   if (!GetWebMediaPlayer())
     return;
 
-  PaintFlags media_flags;
+  cc::PaintFlags media_flags;
   if (flags) {
     media_flags = *flags;
   } else {
diff --git a/third_party/blink/renderer/core/html/media/html_video_element.h b/third_party/blink/renderer/core/html/media/html_video_element.h
index efc71aa..cff0e2c 100644
--- a/third_party/blink/renderer/core/html/media/html_video_element.h
+++ b/third_party/blink/renderer/core/html/media/html_video_element.h
@@ -87,10 +87,10 @@
 
   // Used by canvas to gain raw pixel access
   //
-  // PaintFlags is optional. If unspecified, its blend mode defaults to kSrc.
+  // |paint_flags| is optional. If unspecified, its blend mode defaults to kSrc.
   void PaintCurrentFrame(cc::PaintCanvas*,
                          const gfx::Rect&,
-                         const cc::PaintFlags*) const;
+                         const cc::PaintFlags* paint_flags) const;
 
   bool HasAvailableVideoFrame() const;
 
diff --git a/third_party/blink/renderer/core/html/resources/html.css b/third_party/blink/renderer/core/html/resources/html.css
index 853bd3ea..01d569b 100644
--- a/third_party/blink/renderer/core/html/resources/html.css
+++ b/third_party/blink/renderer/core/html/resources/html.css
@@ -992,27 +992,6 @@
     min-height: 1.2em;
 }
 
-/* selectmenu */
-
-selectmenu {
-    display: inline-block;
-}
-
-selectmenu option:hover {
-    background-color: lightgray;
-    cursor: default;
-    user-select: none;
-}
-
-selectmenu popup {
-  border: 1px solid rgba(0, 0, 0, 0.15);
-  border-radius: 4px;
-  box-shadow: 0px 12.8px 28.8px rgba(0, 0, 0, 0.13), 0px 0px 9.2px rgba(0, 0, 0, 0.11);
-  box-sizing: border-box;
-  overflow: auto;
-  padding: 4px;
-}
-
 output {
     display: inline;
 }
diff --git a/third_party/blink/renderer/core/html/resources/images/selectmenu_button_icon.svg b/third_party/blink/renderer/core/html/resources/images/selectmenu_button_icon.svg
new file mode 100644
index 0000000..1a6c019
--- /dev/null
+++ b/third_party/blink/renderer/core/html/resources/images/selectmenu_button_icon.svg
@@ -0,0 +1,3 @@
+<svg width="20" height="14" viewBox="0 0 20 16" fill="none" xmlns="http://www.w3.org/2000/svg">\
+  <path d="M4 6 L10 12 L 16 6" stroke="WindowText" stroke-width="3" stroke-linejoin="round"/>\
+</svg>
\ No newline at end of file
diff --git a/third_party/blink/renderer/core/html/resources/images/selectmenu_button_icon_white.svg b/third_party/blink/renderer/core/html/resources/images/selectmenu_button_icon_white.svg
new file mode 100644
index 0000000..4355dc8
--- /dev/null
+++ b/third_party/blink/renderer/core/html/resources/images/selectmenu_button_icon_white.svg
@@ -0,0 +1,3 @@
+<svg width="20" height="14" viewBox="0 0 20 16" fill="none" xmlns="http://www.w3.org/2000/svg">\
+  <path d="M4 6 L10 12 L 16 6" stroke="#ffffff" stroke-width="3" stroke-linejoin="round"/>\
+</svg>
\ No newline at end of file
diff --git a/third_party/blink/renderer/core/html/resources/selectmenu.css b/third_party/blink/renderer/core/html/resources/selectmenu.css
new file mode 100644
index 0000000..120b646d
--- /dev/null
+++ b/third_party/blink/renderer/core/html/resources/selectmenu.css
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2022 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.
+ *
+ * The default style sheet used to render <selectmenu> elements
+ * (HTMLSelectMenuElement enabled).
+ */
+
+ @namespace "http://www.w3.org/1999/xhtml";
+
+selectmenu {
+  display: inline-block;
+}
+
+selectmenu::-internal-selectmenu-button {
+  display: inline-flex;
+  align-items: center;
+  background-color: -internal-light-dark(#ffffff, #3B3B3B);
+  padding: 0 0 0 3px;
+  border: 1px solid -internal-light-dark(#767676, #858585);
+  border-radius: 2px;
+  cursor: default;
+  appearance: none;
+}
+
+selectmenu::-internal-selectmenu-button-icon {
+  background-image: -internal-light-dark(-webkit-image-set(url(images/selectmenu_button_icon.svg) 1x), -webkit-image-set(url(images/selectmenu_button_icon_white.svg) 1x));
+  background-origin: content-box;
+  background-repeat: no-repeat;
+  background-size: contain;
+  height: 1.0em;
+  margin-inline-start: 4px;
+  opacity: 1;
+  outline: none;
+  padding-bottom: 2px;
+  padding-inline-start: 3px;
+  padding-inline-end: 3px;
+  padding-top: 2px;
+  width: 1.2em;
+}
+
+selectmenu:disabled::-internal-selectmenu-button {
+  background-color: -internal-light-dark(rgba(239, 239, 239, 0.3), rgba(59, 59, 59, 0.3));
+  border-color: -internal-light-dark(rgba(118, 118, 118, 0.3), rgba(195, 195, 195, 0.3));
+  color: -internal-light-dark(rgba(16, 16, 16, 0.3), rgba(255, 255, 255, 0.3));
+  opacity: 0.7;
+}
+
+selectmenu option:hover {
+  background-color: lightgray;
+  cursor: default;
+  user-select: none;
+}
+
+selectmenu option:disabled {
+  background-color: initial;
+  color: -internal-light-dark(rgba(16, 16, 16, 0.3), rgba(255, 255, 255, 0.3));
+}
+
+selectmenu option:checked:disabled {
+  background-color: -internal-light-dark(rgb(176, 176, 176), rgba(59, 59, 59, 0.3));
+}
+
+selectmenu::-internal-selectmenu-listbox {
+  border: 1px solid rgba(0, 0, 0, 0.15);
+  border-radius: 4px;
+  box-shadow: 0px 12.8px 28.8px rgba(0, 0, 0, 0.13), 0px 0px 9.2px rgba(0, 0, 0, 0.11);
+  box-sizing: border-box;
+  overflow: auto;
+  padding: 4px;
+}
diff --git a/third_party/blink/renderer/core/html/subresource_redirect_test.cc b/third_party/blink/renderer/core/html/subresource_redirect_test.cc
deleted file mode 100644
index 2ff875d..0000000
--- a/third_party/blink/renderer/core/html/subresource_redirect_test.cc
+++ /dev/null
@@ -1,471 +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.
-
-#include <tuple>
-
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "third_party/blink/public/common/features.h"
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/frame/local_frame_view.h"
-#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
-#include "third_party/blink/renderer/core/loader/subresource_redirect_util.h"
-#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
-#include "third_party/blink/renderer/core/style/computed_style.h"
-#include "third_party/blink/renderer/core/style/style_image.h"
-#include "third_party/blink/renderer/core/testing/sim/sim_compositor.h"
-#include "third_party/blink/renderer/core/testing/sim/sim_request.h"
-#include "third_party/blink/renderer/core/testing/sim/sim_test.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
-#include "third_party/blink/renderer/platform/network/network_state_notifier.h"
-#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
-
-namespace blink {
-
-namespace {
-
-Vector<char> ReadTestImage() {
-  return test::ReadFromFile(test::CoreTestDataPath("notifications/500x500.png"))
-      ->CopyAs<Vector<char>>();
-}
-
-class SubresourceRedirectSimTest
-    : public ::testing::WithParamInterface<std::tuple<bool, bool, bool, bool>>,
-      public SimTest {
- protected:
-  SubresourceRedirectSimTest() = default;
-
-  void SetUp() override {
-    SimTest::SetUp();
-
-    scoped_lazy_image_loading_for_test_ =
-        std::make_unique<ScopedLazyImageLoadingForTest>(
-            is_lazyload_image_enabled());
-    scoped_automatic_lazy_image_loading_for_test_ =
-        std::make_unique<ScopedAutomaticLazyImageLoadingForTest>(
-            is_lazyload_image_enabled());
-
-    if (is_subresource_redirect_enabled()) {
-      base::FieldTrialParams params;
-      if (allow_javascript_crossorigin_images())
-        params["allow_javascript_crossorigin_images"] = "true";
-      scoped_feature_list_.InitWithFeaturesAndParameters(
-          {{features::kSubresourceRedirect, params}}, {});
-    }
-    GetNetworkStateNotifier().SetSaveDataEnabledOverride(
-        is_save_data_enabled());
-  }
-
-  void TearDown() override {
-    GetNetworkStateNotifier().ClearOverride();
-    scoped_feature_list_.Reset();
-    scoped_automatic_lazy_image_loading_for_test_.reset();
-    scoped_lazy_image_loading_for_test_.reset();
-
-    SimTest::TearDown();
-  }
-
-  bool is_subresource_redirect_enabled() const {
-    return std::get<0>(GetParam());
-  }
-
-  bool is_lazyload_image_enabled() const { return std::get<1>(GetParam()); }
-
-  bool is_save_data_enabled() const { return std::get<2>(GetParam()); }
-
-  bool allow_javascript_crossorigin_images() const {
-    return std::get<3>(GetParam());
-  }
-
-  void LoadMainResource(const String& html_body) {
-    SimRequest main_resource("https://example.com/", "text/html");
-    LoadURL("https://example.com/");
-
-    main_resource.Complete(html_body);
-    GetDocument().UpdateStyleAndLayoutTree();
-  }
-
-  // Loads the main page resource and then loads the given image in the page.
-  void LoadMainResourceAndImage(const String& html_body,
-                                const String& img_url) {
-    SimRequest image_resource(img_url, "image/png");
-    LoadMainResource(html_body);
-
-    if (!is_lazyload_image_enabled())
-      image_resource.Complete(ReadTestImage());
-
-    Compositor().BeginFrame();
-    test::RunPendingTasks();
-
-    if (is_lazyload_image_enabled()) {
-      // Scroll down until the image is visible.
-      GetDocument().View()->LayoutViewport()->SetScrollOffset(
-          ScrollOffset(0, 10000), mojom::blink::ScrollType::kProgrammatic);
-      if (Compositor().NeedsBeginFrame())
-        Compositor().BeginFrame();
-      test::RunPendingTasks();
-      image_resource.Complete(ReadTestImage());
-    }
-  }
-
-  // Verifies previews state for the fetched request URL.
-  void VerifySubresourceRedirectPreviewsState(
-      const String& url,
-      bool is_subresource_redirect_allowed) {
-    PreviewsState previews_state = GetDocument()
-                                       .Fetcher()
-                                       ->CachedResource(KURL(url))
-                                       ->GetResourceRequest()
-                                       .GetPreviewsState();
-    EXPECT_EQ(is_subresource_redirect_allowed,
-              (previews_state & PreviewsTypes::kSubresourceRedirectOn) != 0);
-  }
-
-  std::unique_ptr<ScopedLazyImageLoadingForTest>
-      scoped_lazy_image_loading_for_test_;
-  std::unique_ptr<ScopedAutomaticLazyImageLoadingForTest>
-      scoped_automatic_lazy_image_loading_for_test_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-  base::HistogramTester histogram_tester_;
-};
-
-// TODO(crbug/1228072): All tests disabled due to high flakiness. The feature
-// experiment has been disabled, all the tests will be re-enabled and deflaked
-// before restarting.
-
-// This test verifies subresource redirect previews state based on different
-// states of SaveData, LazyLoad, SubresourceRedirect features.
-TEST_P(SubresourceRedirectSimTest, DISABLED_CSSBackgroundImage) {
-  LoadMainResourceAndImage(R"HTML(
-        <style>
-        #deferred_image {
-          height:200px;
-          background-image: url('img.png');
-        }
-        </style>
-        <div style='height:10000px;'></div>
-        <div id="deferred_image"></div>
-      )HTML",
-                           "https://example.com/img.png");
-
-  // Subresource redirect previews bit should be set only if SaveData and
-  // SubresourceRedirect feature are enabled.
-  VerifySubresourceRedirectPreviewsState(
-      "https://example.com/img.png",
-      is_save_data_enabled() && is_subresource_redirect_enabled());
-}
-
-TEST_P(SubresourceRedirectSimTest, DISABLED_ImgElement) {
-  LoadMainResourceAndImage(R"HTML(
-        <body>
-          <img src='https://example.com/img.png' loading='lazy'/>
-        </body>
-      )HTML",
-                           "https://example.com/img.png");
-
-  // Subresource redirect previews bit should be set only if SaveData and
-  // SubresourceRedirect feature are enabled.
-  VerifySubresourceRedirectPreviewsState(
-      "https://example.com/img.png",
-      is_save_data_enabled() && is_subresource_redirect_enabled());
-
-  histogram_tester_.ExpectTotalCount("SubresourceRedirect.Blink.Ineligibility",
-                                     0);
-}
-
-TEST_P(SubresourceRedirectSimTest, DISABLED_JavascriptCreatedSameOriginImage) {
-  LoadMainResourceAndImage(R"HTML(
-        <body>
-        <div></div>
-        <script>
-          var img = document.createElement("img");
-          img.loading = 'lazy';
-          img.src = 'https://example.com/img.png';
-          document.getElementsByTagName('div')[0].appendChild(img);
-        </script>
-        </body>
-      )HTML",
-                           "https://example.com/img.png");
-
-  VerifySubresourceRedirectPreviewsState("https://example.com/img.png", false);
-
-  if (is_save_data_enabled() && is_subresource_redirect_enabled()) {
-    histogram_tester_.ExpectUniqueSample(
-        "SubresourceRedirect.Blink.Ineligibility",
-        BlinkSubresourceRedirectIneligibility::kJavascriptCreatedSameOrigin,
-        is_lazyload_image_enabled() ? 2 : 1);
-  } else {
-    histogram_tester_.ExpectTotalCount(
-        "SubresourceRedirect.Blink.Ineligibility", 0);
-  }
-}
-
-TEST_P(SubresourceRedirectSimTest, DISABLED_JavascriptCreatedCrossOriginImage) {
-  LoadMainResourceAndImage(R"HTML(
-        <body>
-        <div></div>
-        <script>
-          var img = document.createElement("img");
-          img.loading = 'lazy';
-          img.src = 'https://differentorigin.com/img.png';
-          document.getElementsByTagName('div')[0].appendChild(img);
-        </script>
-        </body>
-      )HTML",
-                           "https://differentorigin.com/img.png");
-
-  VerifySubresourceRedirectPreviewsState(
-      "https://differentorigin.com/img.png",
-      is_save_data_enabled() && is_subresource_redirect_enabled() &&
-          allow_javascript_crossorigin_images());
-
-  if (is_save_data_enabled() && is_subresource_redirect_enabled() &&
-      !allow_javascript_crossorigin_images()) {
-    histogram_tester_.ExpectUniqueSample(
-        "SubresourceRedirect.Blink.Ineligibility",
-        BlinkSubresourceRedirectIneligibility::kJavascriptCreatedCrossOrigin,
-        is_lazyload_image_enabled() ? 2 : 1);
-  } else {
-    histogram_tester_.ExpectTotalCount(
-        "SubresourceRedirect.Blink.Ineligibility", 0);
-  }
-}
-
-TEST_P(SubresourceRedirectSimTest,
-       DISABLED_ImgElementWithCrossOriginAttribute) {
-  LoadMainResourceAndImage(R"HTML(
-        <body>
-          <img src='https://example.com/img.png' loading='lazy' crossorigin='anonymous'/>
-        </body>
-      )HTML",
-                           "https://example.com/img.png");
-
-  VerifySubresourceRedirectPreviewsState("https://example.com/img.png", false);
-
-  if (is_save_data_enabled() && is_subresource_redirect_enabled()) {
-    histogram_tester_.ExpectUniqueSample(
-        "SubresourceRedirect.Blink.Ineligibility",
-        BlinkSubresourceRedirectIneligibility::kCrossOriginAttributeSet,
-        is_lazyload_image_enabled() ? 2 : 1);
-  } else {
-    histogram_tester_.ExpectTotalCount(
-        "SubresourceRedirect.Blink.Ineligibility", 0);
-  }
-}
-
-TEST_P(SubresourceRedirectSimTest,
-       DISABLED_RestrictedByContentSecurityPolicyDefaultSrc) {
-  LoadMainResourceAndImage(R"HTML(
-        <head>
-          <meta http-equiv="Content-Security-Policy" content="default-src 'self'">
-        </head>
-        <body>
-          <img src='https://example.com/img.png' loading='lazy'/>
-        </body>
-      )HTML",
-                           "https://example.com/img.png");
-
-  VerifySubresourceRedirectPreviewsState("https://example.com/img.png", false);
-
-  if (is_save_data_enabled() && is_subresource_redirect_enabled()) {
-    histogram_tester_.ExpectUniqueSample(
-        "SubresourceRedirect.Blink.Ineligibility",
-        BlinkSubresourceRedirectIneligibility::
-            kContentSecurityPolicyDefaultSrcRestricted,
-        is_lazyload_image_enabled() ? 2 : 1);
-  } else {
-    histogram_tester_.ExpectTotalCount(
-        "SubresourceRedirect.Blink.Ineligibility", 0);
-  }
-}
-
-// TODO(crbug/1223916): Disabled due to high flakiness and build failures.
-TEST_P(SubresourceRedirectSimTest,
-       DISABLED_RestrictedByContentSecurityPolicyImgSrc) {
-  LoadMainResourceAndImage(R"HTML(
-        <head>
-          <meta http-equiv="Content-Security-Policy" content="img-src 'self'">
-        </head>
-        <body>
-          <img src='https://example.com/img.png' loading='lazy'/>
-        </body>
-      )HTML",
-                           "https://example.com/img.png");
-
-  VerifySubresourceRedirectPreviewsState("https://example.com/img.png", false);
-
-  if (is_save_data_enabled() && is_subresource_redirect_enabled()) {
-    histogram_tester_.ExpectUniqueSample(
-        "SubresourceRedirect.Blink.Ineligibility",
-        BlinkSubresourceRedirectIneligibility::
-            kContentSecurityPolicyImgSrcRestricted,
-        is_lazyload_image_enabled() ? 2 : 1);
-  } else {
-    histogram_tester_.ExpectTotalCount(
-        "SubresourceRedirect.Blink.Ineligibility", 0);
-  }
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    SubresourceRedirectSimTest,
-    ::testing::Combine(
-        ::testing::Bool(), /* is_subresource_redirect_enabled */
-        ::testing::Bool(), /* is_lazyload_image_enabled */
-        ::testing::Bool(), /* is_save_data_enabled_*/
-        ::testing::Bool() /* allow_javascript_crossorigin_images */));
-
-class SubresourceRedirectCSPSimTest : public ::testing::WithParamInterface<
-                                          bool /*allow_csp_restricted_images*/>,
-                                      public SimTest {
- protected:
-  SubresourceRedirectCSPSimTest() = default;
-
-  void SetUp() override {
-    SimTest::SetUp();
-
-    base::FieldTrialParams params;
-    params["allow_csp_restricted_images"] =
-        allow_csp_restricted_images() ? "true" : "false";
-    scoped_feature_list_.InitWithFeaturesAndParameters(
-        {{features::kSubresourceRedirect, params}}, {});
-
-    GetNetworkStateNotifier().SetSaveDataEnabledOverride(true);
-    WebView().GetPage()->GetSettings().SetLitePageSubresourceRedirectOrigin(
-        "https://litepages.googlezip.net");
-  }
-
-  void TearDown() override {
-    GetNetworkStateNotifier().ClearOverride();
-    scoped_feature_list_.Reset();
-    SimTest::TearDown();
-  }
-
-  bool allow_csp_restricted_images() const { return GetParam(); }
-
-  void LoadMainResource(const String& html_body) {
-    SimRequest main_resource("https://example.com/", "text/html");
-    LoadURL("https://example.com/");
-
-    main_resource.Complete(html_body);
-    GetDocument().UpdateStyleAndLayoutTree();
-    Compositor().BeginFrame();
-    test::RunPendingTasks();
-  }
-
-  void ScrollDownToLoadImage() {
-    // Scroll down until the image is visible.
-    GetDocument().View()->LayoutViewport()->SetScrollOffset(
-        ScrollOffset(0, 10000), mojom::blink::ScrollType::kProgrammatic);
-    if (Compositor().NeedsBeginFrame())
-      Compositor().BeginFrame();
-    test::RunPendingTasks();
-  }
-
-  // Verifies previews state for the fetched request URL.
-  void VerifySubresourceRedirectPreviewsState(
-      const String& url,
-      bool is_subresource_redirect_allowed) {
-    PreviewsState previews_state = GetDocument()
-                                       .Fetcher()
-                                       ->CachedResource(KURL(url))
-                                       ->GetResourceRequest()
-                                       .GetPreviewsState();
-    EXPECT_EQ(is_subresource_redirect_allowed,
-              (previews_state & PreviewsTypes::kSubresourceRedirectOn) != 0);
-  }
-
-  base::test::ScopedFeatureList scoped_feature_list_;
-  base::HistogramTester histogram_tester_;
-};
-
-// cross-origin image disallowed by CSP default-src directive should not load.
-TEST_P(SubresourceRedirectCSPSimTest, DISABLED_ImageDisallowedByDefaultSrc) {
-  LoadMainResource(R"HTML(
-        <head>
-          <meta http-equiv="Content-Security-Policy" content="default-src 'self'">
-        </head>
-        <body>
-          <img src='https://crossorigin.com/img.png' loading='lazy'/>
-        </body>
-      )HTML");
-  ScrollDownToLoadImage();
-  EXPECT_TRUE(std::any_of(ConsoleMessages().begin(), ConsoleMessages().end(),
-                          [](const auto& console_message) {
-                            return console_message.Contains(
-                                "Refused to load the image");
-                          }));
-}
-
-// cross-origin image disallowed by CSP img-src directive should not load.
-TEST_P(SubresourceRedirectCSPSimTest, DISABLED_ImageDisallowedByImgSrc) {
-  LoadMainResource(R"HTML(
-        <head>
-          <meta http-equiv="Content-Security-Policy" content="img-src 'self'">
-        </head>
-        <body>
-          <img src='https://crossorigin.com/img.png' loading='lazy'/>
-        </body>
-      )HTML");
-  ScrollDownToLoadImage();
-  EXPECT_TRUE(std::any_of(ConsoleMessages().begin(), ConsoleMessages().end(),
-                          [](const auto& console_message) {
-                            return console_message.Contains(
-                                "Refused to load the image");
-                          }));
-}
-
-TEST_P(SubresourceRedirectCSPSimTest, DISABLED_RestrictedByDefaultSrc) {
-  std::unique_ptr<SimSubresourceRequest> redirected_image;
-  WTF::String img_url = "https://example.com/img.png";
-  SimRequestBase::Params params;
-  if (allow_csp_restricted_images()) {
-    // Simulate a redirect to LitePages.
-    img_url = "https://litepages.googlezip.net/example.com/img.png";
-    params.redirect_url = img_url;
-    redirected_image = std::make_unique<SimSubresourceRequest>(
-        "https://example.com/img.png", "image/png", params);
-  }
-
-  SimSubresourceRequest image_resource(img_url, "image/png");
-  LoadMainResource(R"HTML(
-        <head>
-          <meta http-equiv="Content-Security-Policy" content="default-src 'self'">
-        </head>
-        <body>
-          <img src='https://example.com/img.png' loading='lazy'/>
-        </body>
-      )HTML");
-  ScrollDownToLoadImage();
-  image_resource.Complete(ReadTestImage());
-
-  VerifySubresourceRedirectPreviewsState("https://example.com/img.png",
-                                         allow_csp_restricted_images());
-
-  if (allow_csp_restricted_images()) {
-    histogram_tester_.ExpectTotalCount(
-        "SubresourceRedirect.Blink.Ineligibility", 0);
-  } else {
-    EXPECT_LE(1, histogram_tester_.GetBucketCount(
-                     "SubresourceRedirect.Blink.Ineligibility",
-                     BlinkSubresourceRedirectIneligibility::
-                         kContentSecurityPolicyDefaultSrcRestricted));
-  }
-  // No CSP error should be reported.
-  EXPECT_TRUE(std::none_of(ConsoleMessages().begin(), ConsoleMessages().end(),
-                           [](const auto& console_message) {
-                             return console_message.Contains(
-                                 "Refused to load the image");
-                           }));
-}
-
-INSTANTIATE_TEST_SUITE_P(All,
-                         SubresourceRedirectCSPSimTest,
-                         /* allow_csp_restricted_images */ ::testing::Bool());
-
-}  // namespace
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
index f122399..cbc66ae8 100644
--- a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
+++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
@@ -786,6 +786,43 @@
 
 scoped_refptr<StaticBitmapImage> ImageBitmap::Transfer() {
   DCHECK(!IsNeutered());
+  if (!image_->HasOneRef()) {
+    // For it to be safe to transfer a StaticBitmapImage it must not be
+    // referenced by any other object on this thread.
+    // The first step is to attempt to release other references via
+    // NotifyWillTransfer
+    const auto content_id =
+        image_->PaintImageForCurrentFrame().GetContentIdForFrame(0);
+    CanvasResourceProvider::NotifyWillTransfer(content_id);
+
+    // If will still have other references, the last resort is to make a copy
+    // of the bitmap.  This could happen, for example, if another ImageBitmap
+    // or a CanvasPattern object points to the same StaticBitmapImage.
+    // This approach is slow and wateful but it is only to handle extremely
+    // rare edge cases.
+    if (!image_->HasOneRef()) {
+      SkImageInfo info = GetSkImageInfo(image_);
+      if (info.isEmpty())
+        return nullptr;
+      PaintImage paint_image = image_->PaintImageForCurrentFrame();
+      bool use_accelerated = paint_image.IsTextureBacked() &&
+                             info.alphaType() == kPremul_SkAlphaType;
+      auto resource_provider = CreateProvider(
+          use_accelerated ? image_->ContextProviderWrapper() : nullptr, info,
+          image_, true /* fallback_to_software */);
+      if (!resource_provider)
+        return nullptr;
+
+      auto* canvas = resource_provider->Canvas();
+      cc::PaintFlags paint;
+      paint.setBlendMode(SkBlendMode::kSrc);
+      canvas->drawImage(image_->PaintImageForCurrentFrame(), 0, 0,
+                        SkSamplingOptions(), &paint);
+      image_ = resource_provider->Snapshot(image_->CurrentFrameOrientation());
+    }
+  }
+
+  DCHECK(image_->HasOneRef());
   is_neutered_ = true;
   image_->Transfer();
   UpdateImageBitmapMemoryUsage();
diff --git a/third_party/blink/renderer/core/layout/layout_tree_as_text.cc b/third_party/blink/renderer/core/layout/layout_tree_as_text.cc
index b495b8c..50c20e62 100644
--- a/third_party/blink/renderer/core/layout/layout_tree_as_text.cc
+++ b/third_party/blink/renderer/core/layout/layout_tree_as_text.cc
@@ -494,7 +494,7 @@
       ts << " state=(" << fragment->LocalBorderBoxProperties().ToString()
          << ")";
     }
-    if (RuntimeEnabledFeatures::CullRectUpdateEnabled() && o.HasLayer()) {
+    if (o.HasLayer()) {
       ts << " cull_rect=(" << fragment->GetCullRect().ToString()
          << ") contents_cull_rect=("
          << fragment->GetContentsCullRect().ToString() << ")";
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc
index bf0841833..d894de2e 100644
--- a/third_party/blink/renderer/core/layout/layout_view.cc
+++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -663,6 +663,10 @@
   if (!frame)
     RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
 
+  // ClipsContent() is false for the root printing frame, etc.
+  if (!frame->ClipsContent())
+    RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
+
   if (FrameOwner* owner = frame->Owner()) {
     // Setting scrolling="no" on an iframe element disables scrolling.
     if (owner->ScrollbarMode() == mojom::blink::ScrollbarMode::kAlwaysOff)
@@ -676,14 +680,6 @@
       RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
   }
 
-  if (document.IsPrintingOrPaintingPreview()) {
-    // When printing or painting preview, frame-level scrollbars are never
-    // displayed.
-    // TODO(szager): Figure out the right behavior when printing an overflowing
-    // iframe.  https://bugs.chromium.org/p/chromium/issues/detail?id=777528
-    RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
-  }
-
   if (LocalFrameView* frameView = GetFrameView()) {
     // Scrollbars can be disabled by LocalFrameView::setCanHaveScrollbars.
     if (!frameView->CanHaveScrollbars())
diff --git a/third_party/blink/renderer/core/layout/line/inline_flow_box.cc b/third_party/blink/renderer/core/layout/line/inline_flow_box.cc
index 924a7d9..b52826b 100644
--- a/third_party/blink/renderer/core/layout/line/inline_flow_box.cc
+++ b/third_party/blink/renderer/core/layout/line/inline_flow_box.cc
@@ -1030,7 +1030,8 @@
   if (!box_shadow)
     return;
 
-  LayoutRectOutsets outsets(box_shadow->RectOutsetsIncludingOriginal());
+  LayoutRectOutsets outsets =
+      EnclosingLayoutRectOutsets(box_shadow->RectOutsetsIncludingOriginal());
   // Similar to how glyph overflow works, if our lines are flipped, then it's
   // actually the opposite shadow that applies, since the line is "upside down"
   // in terms of block coordinates.
@@ -1101,11 +1102,14 @@
   if (it != text_box_data_map.end()) {
     const GlyphOverflow& glyph_overflow = it->value.second;
     bool is_flipped_line = style.IsFlippedLinesWritingMode();
-    visual_rect_outsets = EnclosingLayoutRectOutsets(FloatRectOutsets(
-        is_flipped_line ? glyph_overflow.bottom : glyph_overflow.top,
-        glyph_overflow.right,
-        is_flipped_line ? glyph_overflow.top : glyph_overflow.bottom,
-        glyph_overflow.left));
+    visual_rect_outsets = EnclosingLayoutRectOutsets(
+        gfx::OutsetsF()
+            .set_left(glyph_overflow.left)
+            .set_right(glyph_overflow.right)
+            .set_top(is_flipped_line ? glyph_overflow.bottom
+                                     : glyph_overflow.top)
+            .set_bottom(is_flipped_line ? glyph_overflow.top
+                                        : glyph_overflow.bottom));
   }
 
   if (float stroke_width = style.TextStrokeWidth()) {
@@ -1129,7 +1133,8 @@
   if (ShadowList* text_shadow = style.TextShadow()) {
     LayoutRectOutsets text_shadow_logical_outsets =
         LineOrientationLayoutRectOutsets(
-            LayoutRectOutsets(text_shadow->RectOutsetsIncludingOriginal()),
+            EnclosingLayoutRectOutsets(
+                text_shadow->RectOutsetsIncludingOriginal()),
             style.GetWritingMode());
     text_shadow_logical_outsets.ClampNegativeToZero();
     visual_rect_outsets += text_shadow_logical_outsets;
diff --git a/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.h b/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.h
index 08e8d3a..b0f67ea 100644
--- a/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.h
+++ b/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.h
@@ -56,6 +56,12 @@
  private:
   // https://drafts.css-houdini.org/css-layout-api/#layout-definitions
   typedef HeapHashMap<String, Member<CSSLayoutDefinition>> DefinitionMap;
+
+  // TODO(crbug.com/1286244): Return a proper destination for LayoutWorklet.
+  network::mojom::RequestDestination GetDestination() const override {
+    return network::mojom::RequestDestination::kScript;
+  }
+
   DefinitionMap layout_definitions_;
   Member<PendingLayoutRegistry> pending_layout_registry_;
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc b/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc
index 96877ef..484d796 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc
@@ -458,7 +458,8 @@
   if (ShadowList* text_shadow = style.TextShadow()) {
     LayoutRectOutsets text_shadow_logical_outsets =
         LineOrientationLayoutRectOutsets(
-            LayoutRectOutsets(text_shadow->RectOutsetsIncludingOriginal()),
+            EnclosingLayoutRectOutsets(
+                text_shadow->RectOutsetsIncludingOriginal()),
             writing_mode);
     text_shadow_logical_outsets.ClampNegativeToZero();
     ink_overflow.Expand(text_shadow_logical_outsets);
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h
index a456c55..9153845 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h
@@ -87,20 +87,19 @@
 
   // Data needed to layout a single cell.
   struct Cell {
-    Cell(NGBoxStrut border_box_borders,
+    Cell(NGBoxStrut borders,
          LayoutUnit block_size,
          wtf_size_t start_column,
          bool has_grown,
          bool is_constrained)
-        : border_box_borders(border_box_borders),
+        : borders(borders),
           block_size(block_size),
           start_column(start_column),
           has_grown(has_grown),
           is_constrained(is_constrained) {}
 
     bool operator==(const Cell& other) const {
-      return border_box_borders == other.border_box_borders &&
-             block_size == other.block_size &&
+      return borders == other.borders && block_size == other.block_size &&
              start_column == other.start_column &&
              has_grown == other.has_grown &&
              is_constrained == other.is_constrained;
@@ -108,7 +107,7 @@
     bool operator!=(const Cell& other) const { return !(*this == other); }
 
     // Size of borders drawn on the inside of the border box.
-    const NGBoxStrut border_box_borders;
+    const NGBoxStrut borders;
     // Size of the cell. Need this for cells that span multiple rows.
     const LayoutUnit block_size;
     const wtf_size_t start_column;
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
index f930a60..57813d5 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
@@ -280,8 +280,8 @@
           }
         }
         data->cells.emplace_back(
-            cell_block_constraints[cell_index].border_box_borders,
-            cell_block_size, cell_block_constraints[cell_index].column_index,
+            cell_block_constraints[cell_index].borders, cell_block_size,
+            cell_block_constraints[cell_index].column_index,
             /* has_grown */ cell_block_size >
                 cell_block_constraints[cell_index].min_block_size,
             cell_block_constraints[cell_index].is_constrained);
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
index cbadd82..12631a5 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
@@ -1055,11 +1055,10 @@
     LayoutUnit border_block_spacing,
     NGTableTypes::Rows* rows) {
   DCHECK_GE(rowspan_cell.span, 0u);
-  DistributeExcessBlockSizeToRows(
-      rowspan_cell.start_row, rowspan_cell.span,
-      rowspan_cell.cell_block_constraint.min_block_size,
-      /* desired_block_size_is_rowspan */ true, border_block_spacing,
-      kIndefiniteSize, rows);
+  DistributeExcessBlockSizeToRows(rowspan_cell.start_row, rowspan_cell.span,
+                                  rowspan_cell.min_block_size,
+                                  /* desired_block_size_is_rowspan */ true,
+                                  border_block_spacing, kIndefiniteSize, rows);
 }
 
 // Legacy code ignores section block size.
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc
index 0f75e77..944028de 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc
@@ -241,10 +241,7 @@
 }
 
 TEST_F(NGTableAlgorithmHelpersTest, DistributeRowspanCellToRows) {
-  NGTableTypes::CellBlockConstraint cell_block_constraint{
-      LayoutUnit(300), NGBoxStrut(), 0, 0, 3, true};
-  NGTableTypes::RowspanCell rowspan_cell = NGTableTypes::CreateRowspanCell(
-      0, 3, &cell_block_constraint, absl::nullopt);
+  NGTableTypes::RowspanCell rowspan_cell = {0, 3, LayoutUnit(300)};
   NGTableTypes::Rows rows;
 
   // Distribute to regular rows, rows grow in proportion to size.
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
index bb46edd..a40d44d 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
@@ -234,31 +234,6 @@
                  /* needs_redistribution */ false};
 }
 
-NGTableTypes::CellBlockConstraint NGTableTypes::CreateCellBlockConstraint(
-    const NGLayoutInputNode& node,
-    LayoutUnit computed_block_size,
-    const NGBoxStrut& border_box_borders,
-    wtf_size_t row_index,
-    wtf_size_t column_index,
-    wtf_size_t rowspan) {
-  bool is_constrained = node.Style().LogicalHeight().IsFixed();
-  return CellBlockConstraint{
-      computed_block_size, border_box_borders, row_index, column_index, rowspan,
-      is_constrained};
-}
-
-NGTableTypes::RowspanCell NGTableTypes::CreateRowspanCell(
-    wtf_size_t row_index,
-    wtf_size_t rowspan,
-    CellBlockConstraint* cell_block_constraint,
-    absl::optional<LayoutUnit> css_cell_block_size) {
-  if (css_cell_block_size) {
-    cell_block_constraint->min_block_size =
-        std::max(cell_block_constraint->min_block_size, *css_cell_block_size);
-  }
-  return RowspanCell{row_index, rowspan, *cell_block_constraint};
-}
-
 void NGTableTypes::CellInlineConstraint::Encompass(
     const NGTableTypes::CellInlineConstraint& other) {
   // Standard says:
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
index 9a0e843f..5ffd15c 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
@@ -118,20 +118,17 @@
   struct CellBlockConstraint {
     DISALLOW_NEW();
     LayoutUnit min_block_size;
-    NGBoxStrut border_box_borders;
-    wtf_size_t row_index;
+    NGBoxStrut borders;
     wtf_size_t column_index;
     wtf_size_t rowspan;
     bool is_constrained;  // True if this cell has a specified block-size.
     CellBlockConstraint(LayoutUnit min_block_size,
-                        NGBoxStrut border_box_borders,
-                        wtf_size_t row_index,
+                        NGBoxStrut borders,
                         wtf_size_t column_index,
                         wtf_size_t rowspan,
                         bool is_constrained)
         : min_block_size(min_block_size),
-          border_box_borders(border_box_borders),
-          row_index(row_index),
+          borders(borders),
           column_index(column_index),
           rowspan(rowspan),
           is_constrained(is_constrained) {}
@@ -140,15 +137,13 @@
   // RowspanCells span multiple rows.
   struct RowspanCell {
     DISALLOW_NEW();
-    CellBlockConstraint cell_block_constraint;
     wtf_size_t start_row;
     wtf_size_t span;
+    LayoutUnit min_block_size;
     RowspanCell(wtf_size_t start_row,
                 wtf_size_t span,
-                const CellBlockConstraint& cell_block_constraint)
-        : cell_block_constraint(cell_block_constraint),
-          start_row(start_row),
-          span(span) {}
+                LayoutUnit min_block_size)
+        : start_row(start_row), span(span), min_block_size(min_block_size) {}
 
     // Original Legacy sorting criteria from
     // CompareRowspanCellsInHeightDistributionOrder
@@ -161,17 +156,16 @@
                (c1.start_row + c1.span) <= (c2.start_row + c2.span);
       };
 
-      // If cells span the same rows, bigger cell is distributed first.
-      if (start_row == rhs.start_row && span == rhs.span) {
-        return cell_block_constraint.min_block_size >
-               rhs.cell_block_constraint.min_block_size;
-      }
-      // If one cell is fully enclosed by another, inner cell wins.
+      // If cells span the same rows, the bigger cell is distributed first.
+      if (start_row == rhs.start_row && span == rhs.span)
+        return min_block_size > rhs.min_block_size;
+
+      // If one cell is fully enclosed by another, the inner cell wins.
       if (IsEnclosed(*this, rhs))
         return true;
       if (IsEnclosed(rhs, *this))
         return false;
-      // Lower rows wins.
+      // Lowest row wins.
       return start_row < rhs.start_row;
     }
   };
@@ -224,20 +218,6 @@
                                LayoutUnit block_size,
                                bool treat_as_tbody);
 
-  static CellBlockConstraint CreateCellBlockConstraint(
-      const NGLayoutInputNode&,
-      LayoutUnit computed_block_size,
-      const NGBoxStrut& border_box_borders,
-      wtf_size_t row_index,
-      wtf_size_t column_index,
-      wtf_size_t rowspan);
-
-  static RowspanCell CreateRowspanCell(
-      wtf_size_t row_index,
-      wtf_size_t rowspan,
-      CellBlockConstraint*,
-      absl::optional<LayoutUnit> css_block_size);
-
   // Columns are cached by LayoutNGTable, and need to be RefCounted.
   typedef base::RefCountedData<WTF::Vector<Column>> Columns;
   // Inline constraints are optional because we need to distinguish between an
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
index 470027b..91fc871 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
@@ -30,13 +30,11 @@
     wtf_size_t start_column_index,
     wtf_size_t span,
     NGTableTypes::Columns* column_constraints) {
-  if (span == 0)
-    return;
   DCHECK_LT(start_column_index, column_constraints->data.size());
+  DCHECK_GT(span, 1u);
+
   wtf_size_t effective_span =
       std::min(span, column_constraints->data.size() - start_column_index);
-  if (effective_span == 0)
-    return;
   NGTableTypes::Column* start_column =
       &column_constraints->data[start_column_index];
   NGTableTypes::Column* end_column = start_column + effective_span;
@@ -229,28 +227,28 @@
     const NGBoxFragment fragment(
         table_writing_direction,
         To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment()));
-    bool is_parallel =
+    const Length& cell_specified_block_length =
         IsParallelWritingMode(table_writing_direction.GetWritingMode(),
-                              cell.Style().GetWritingMode());
-
+                              cell_style.GetWritingMode())
+            ? cell_style.LogicalHeight()
+            : cell_style.LogicalWidth();
     const wtf_size_t rowspan = cell.TableCellRowspan();
-    NGTableTypes::CellBlockConstraint cell_block_constraint =
-        NGTableTypes::CreateCellBlockConstraint(
-            cell, fragment.BlockSize(), cell_borders, row_index,
-            colspan_cell_tabulator->CurrentColumn(), rowspan);
+
+    NGTableTypes::CellBlockConstraint cell_block_constraint = {
+        fragment.BlockSize(), cell_borders,
+        colspan_cell_tabulator->CurrentColumn(), rowspan,
+        cell_specified_block_length.IsFixed()};
     colspan_cell_tabulator->ProcessCell(cell);
     cell_block_constraints->push_back(cell_block_constraint);
     is_constrained |= cell_block_constraint.is_constrained && rowspan == 1;
     row_baseline_tabulator.ProcessCell(
         fragment, NGTableAlgorithmUtils::IsBaseline(cell_style.VerticalAlign()),
-        is_parallel, rowspan > 1,
+        rowspan > 1,
         layout_result->HasDescendantThatDependsOnPercentageBlockSize());
 
     // Compute cell's css block size.
     absl::optional<LayoutUnit> cell_css_block_size;
     absl::optional<float> cell_css_percent;
-    const Length& cell_specified_block_length =
-        is_parallel ? cell_style.LogicalHeight() : cell_style.LogicalWidth();
 
     // TODO(1105272) Handle cell_specified_block_length.IsCalculated()
     if (cell_specified_block_length.IsPercent()) {
@@ -284,8 +282,11 @@
                     cell_css_block_size.value_or(LayoutUnit())});
     } else {
       has_rowspan_start = true;
-      rowspan_cells->push_back(NGTableTypes::CreateRowspanCell(
-          row_index, rowspan, &cell_block_constraint, cell_css_block_size));
+      LayoutUnit min_block_size = cell_block_constraint.min_block_size;
+      if (cell_css_block_size)
+        min_block_size = std::max(min_block_size, *cell_css_block_size);
+      rowspan_cells->push_back(
+          NGTableTypes::RowspanCell{row_index, rowspan, min_block_size});
     }
   }
 
@@ -612,9 +613,6 @@
   // Redistribute rowspanned cell block sizes.
   std::stable_sort(rowspan_cells.begin(), rowspan_cells.end());
   for (NGTableTypes::RowspanCell& rowspan_cell : rowspan_cells) {
-    // Spec: rowspan of 0 means all remaining rows.
-    if (rowspan_cell.span == 0)
-      rowspan_cell.span = current_row - rowspan_cell.start_row;
     // Truncate rows that are too long.
     rowspan_cell.span =
         std::min(current_row - rowspan_cell.start_row, rowspan_cell.span);
@@ -682,11 +680,10 @@
 void NGRowBaselineTabulator::ProcessCell(
     const NGBoxFragment& fragment,
     const bool is_baseline_aligned,
-    const bool is_parallel,
     const bool is_rowspanned,
     const bool descendant_depends_on_percentage_block_size) {
-  if (is_parallel && is_baseline_aligned &&
-      fragment.HasDescendantsForTablePart() && fragment.FirstBaseline()) {
+  if (is_baseline_aligned && fragment.HasDescendantsForTablePart() &&
+      fragment.FirstBaseline()) {
     max_cell_baseline_depends_on_percentage_block_descendant_ |=
         descendant_depends_on_percentage_block_size;
     const LayoutUnit cell_baseline = *fragment.FirstBaseline();
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
index 16af3604..e16ef709 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
@@ -117,7 +117,6 @@
  public:
   void ProcessCell(const NGBoxFragment& fragment,
                    bool is_baseline_aligned,
-                   bool is_parallel,
                    bool is_rowspanned,
                    bool descendant_depends_on_percentage_block_size);
 
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc
index 15494bd..514dc132 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc
@@ -75,7 +75,7 @@
         cell_location_start_column == cell_location_end_column;
 
     return NGTableAlgorithmUtils::CreateTableCellConstraintSpace(
-        table_data.table_writing_direction, cell, cell_data.border_box_borders,
+        table_data.table_writing_direction, cell, cell_data.borders,
         {cell_inline_size, cell_block_size}, container_builder_.InlineSize(),
         row_baseline, start_column, !is_initial_block_size_definite,
         table_data.is_table_block_size_specified, is_hidden_for_paint,
@@ -86,15 +86,12 @@
   // differs from its intrinsic size. This might cause row baseline to move, if
   // cell was baseline-aligned.
   // To compute correct baseline, we need to do an initial layout pass.
-  WritingMode table_writing_mode = ConstraintSpace().GetWritingMode();
   LayoutUnit row_baseline = row.baseline;
   wtf_size_t cell_index = row.start_cell_index;
   if (row.has_baseline_aligned_percentage_block_size_descendants) {
     NGRowBaselineTabulator row_baseline_tabulator;
     for (NGBlockNode cell = To<NGBlockNode>(Node().FirstChild()); cell;
          cell = To<NGBlockNode>(cell.NextSibling()), ++cell_index) {
-      bool is_parallel = IsParallelWritingMode(table_writing_mode,
-                                               cell.Style().GetWritingMode());
       NGConstraintSpace cell_constraint_space =
           CreateCellConstraintSpace(cell, cell_index, absl::nullopt);
       scoped_refptr<const NGLayoutResult> layout_result =
@@ -105,7 +102,7 @@
       row_baseline_tabulator.ProcessCell(
           fragment,
           NGTableAlgorithmUtils::IsBaseline(cell.Style().VerticalAlign()),
-          is_parallel, cell.TableCellRowspan() > 1,
+          cell.TableCellRowspan() > 1,
           layout_result->HasDescendantThatDependsOnPercentageBlockSize());
     }
     row_baseline = row_baseline_tabulator.ComputeBaseline(row.block_size);
@@ -128,12 +125,10 @@
     NGBoxFragment fragment(
         table_data.table_writing_direction,
         To<NGPhysicalBoxFragment>(cell_result->PhysicalFragment()));
-    bool is_parallel = IsParallelWritingMode(table_writing_mode,
-                                             cell.Style().GetWritingMode());
     row_baseline_tabulator.ProcessCell(
         fragment,
         NGTableAlgorithmUtils::IsBaseline(cell.Style().VerticalAlign()),
-        is_parallel, cell.TableCellRowspan() > 1,
+        cell.TableCellRowspan() > 1,
         cell_result->HasDescendantThatDependsOnPercentageBlockSize());
   }
   container_builder_.SetFragmentBlockSize(row.block_size);
diff --git a/third_party/blink/renderer/core/layout/shapes/shape.cc b/third_party/blink/renderer/core/layout/shapes/shape.cc
index ff3d323..c345dd2 100644
--- a/third_party/blink/renderer/core/layout/shapes/shape.cc
+++ b/third_party/blink/renderer/core/layout/shapes/shape.cc
@@ -33,6 +33,7 @@
 #include <memory>
 #include <utility>
 
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/core/css/basic_shape_functions.h"
 #include "third_party/blink/renderer/core/layout/shapes/box_shape.h"
@@ -48,7 +49,6 @@
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
@@ -247,7 +247,7 @@
   // that loads SVG Images during paint invalidations to mark layoutObjects
   // for layout, which is not allowed. See https://crbug.com/429346
   ImageObserverDisabler disabler(image);
-  PaintFlags flags;
+  cc::PaintFlags flags;
   gfx::RectF image_source_rect(gfx::SizeF(image->Size()));
   gfx::Rect image_dest_rect(image_size);
   SkiaPaintCanvas canvas(surface->getCanvas());
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
index d7bde9f..ce5c322 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
@@ -191,10 +191,9 @@
   // - masker/filter not applied when laying out the children
   // - fill is set to the initial fill paint server (solid, black)
   // - stroke is set to the initial stroke paint server (none)
-  PaintInfo info(builder->Context(), CullRect::Infinite(),
-                 PaintPhase::kForeground, kGlobalPaintNormalPhase,
-                 kPaintLayerPaintingRenderingClipPathAsMask |
-                     kPaintLayerPaintingRenderingResourceSubtree);
+  PaintInfo info(
+      builder->Context(), CullRect::Infinite(), PaintPhase::kForeground,
+      PaintFlag::kPaintingClipPathAsMask | PaintFlag::kPaintingResourceSubtree);
 
   for (const SVGElement& child_element :
        Traversal<SVGElement>::ChildrenOf(*GetElement())) {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc
index 35afada..3ae5a4c 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc
@@ -109,7 +109,7 @@
     const gfx::RectF& reference_box,
     const AffineTransform* additional_transform,
     const AutoDarkMode& auto_dark_mode,
-    PaintFlags& flags) {
+    cc::PaintFlags& flags) {
   NOT_DESTROYED();
   ClearInvalidationMask();
 
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h
index 1548ce5..69398b6 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h
@@ -44,7 +44,7 @@
                    const gfx::RectF& reference_box,
                    const AffineTransform* additional_transform,
                    const AutoDarkMode& auto_dark_mode,
-                   PaintFlags&) final;
+                   cc::PaintFlags&) final;
 
   bool IsChildAllowed(LayoutObject* child, const ComputedStyle&) const final;
 
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h
index 6baabc1c5..a50422c 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h
@@ -20,8 +20,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_LAYOUT_SVG_RESOURCE_PAINT_SERVER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_LAYOUT_SVG_RESOURCE_PAINT_SERVER_H_
 
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 
 namespace blink {
 
@@ -36,7 +36,7 @@
                            const gfx::RectF& reference_box,
                            const AffineTransform* additional_transform,
                            const AutoDarkMode& auto_dark_mode,
-                           PaintFlags&) = 0;
+                           cc::PaintFlags&) = 0;
 };
 
 template <>
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc
index 61df7da..984a7f1 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc
@@ -180,7 +180,7 @@
     const gfx::RectF& reference_box,
     const AffineTransform* additional_transform,
     const AutoDarkMode&,
-    PaintFlags& flags) {
+    cc::PaintFlags& flags) {
   NOT_DESTROYED();
   ClearInvalidationMask();
 
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h
index db2290b..334feb1 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h
@@ -52,7 +52,7 @@
                    const gfx::RectF& reference_box,
                    const AffineTransform* additional_transform,
                    const AutoDarkMode&,
-                   PaintFlags&) override;
+                   cc::PaintFlags&) override;
 
   static const LayoutSVGResourceType kResourceType = kPatternResourceType;
   LayoutSVGResourceType ResourceType() const override {
diff --git a/third_party/blink/renderer/core/loader/base_fetch_context.cc b/third_party/blink/renderer/core/loader/base_fetch_context.cc
index a5890a1..539bc71 100644
--- a/third_party/blink/renderer/core/loader/base_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/base_fetch_context.cc
@@ -21,7 +21,6 @@
 #include "third_party/blink/renderer/core/inspector/identifiers_factory.h"
 #include "third_party/blink/renderer/core/loader/frame_client_hints_preferences_context.h"
 #include "third_party/blink/renderer/core/loader/subresource_filter.h"
-#include "third_party/blink/renderer/core/loader/subresource_redirect_util.h"
 #include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/loader/cors/cors.h"
@@ -520,12 +519,6 @@
     return absl::nullopt;
   }
 
-  if (ShouldDisableCSPCheckForLitePageSubresourceRedirectOrigin(
-          GetResourceFetcherProperties().GetLitePageSubresourceRedirectOrigin(),
-          request_context, redirect_status, url)) {
-    return absl::nullopt;
-  }
-
   ContentSecurityPolicy* csp =
       GetContentSecurityPolicyForWorld(options.world_for_csp.get());
   if (csp &&
diff --git a/third_party/blink/renderer/core/loader/build.gni b/third_party/blink/renderer/core/loader/build.gni
index 4b3a87b1..9b495fc 100644
--- a/third_party/blink/renderer/core/loader/build.gni
+++ b/third_party/blink/renderer/core/loader/build.gni
@@ -42,8 +42,6 @@
   "http_refresh_scheduler.h",
   "idleness_detector.cc",
   "idleness_detector.h",
-  "subresource_redirect_util.cc",
-  "subresource_redirect_util.h",
   "image_loader.cc",
   "image_loader.h",
   "importance_attribute.cc",
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 9fd095e5..e3fab42 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -238,7 +238,7 @@
   std::unique_ptr<PolicyContainer> policy_container;
   KURL url;
   AtomicString http_method;
-  Referrer referrer;
+  AtomicString referrer;
   scoped_refptr<EncodedFormData> http_body;
   AtomicString http_content_type;
   PreviewsState previews_state;
@@ -254,7 +254,7 @@
   Member<HistoryItem> history_item;
   Member<DocumentParser> parser;
   Member<SubresourceFilter> subresource_filter;
-  Referrer original_referrer;
+  AtomicString original_referrer;
   ResourceResponse response;
   WebFrameLoadType load_type;
   bool is_client_redirect;
@@ -334,10 +334,7 @@
       policy_container_(std::move(policy_container)),
       url_(params_->url),
       http_method_(static_cast<String>(params_->http_method)),
-      referrer_(Referrer(params_->referrer.IsEmpty()
-                             ? Referrer::NoReferrer()
-                             : static_cast<String>(params_->referrer),
-                         params_->referrer_policy)),
+      referrer_(static_cast<String>(params_->referrer)),
       http_body_(params_->http_body),
       http_content_type_(static_cast<String>(params_->http_content_type)),
       origin_policy_(params_->origin_policy),
@@ -501,7 +498,7 @@
   LocalDOMWindow* window = frame_->DomWindow();
   params->url = window->Url();
   params->unreachable_url = unreachable_url_;
-  params->referrer = referrer_.referrer;
+  params->referrer = referrer_;
   // All the security properties of the document must be preserved. Note that
   // sandbox flags and various policies are copied separately during commit in
   // CommitNavigation() and CalculateSandboxFlags().
@@ -594,7 +591,7 @@
   return navigation_timing_info_.get();
 }
 
-const Referrer& DocumentLoader::OriginalReferrer() const {
+const AtomicString& DocumentLoader::OriginalReferrer() const {
   return original_referrer_;
 }
 
@@ -611,7 +608,7 @@
   return http_method_;
 }
 
-const Referrer& DocumentLoader::GetReferrer() const {
+const AtomicString& DocumentLoader::GetReferrer() const {
   return referrer_;
 }
 
@@ -831,8 +828,7 @@
     history_item_ = MakeGarbageCollected<HistoryItem>();
 
   history_item_->SetURL(UrlForHistory());
-  history_item_->SetReferrer(SecurityPolicy::GenerateReferrer(
-      referrer_.referrer_policy, history_item_->Url(), referrer_.referrer));
+  history_item_->SetReferrer(referrer_.GetString());
   if (EqualIgnoringASCIICase(http_method_, "POST")) {
     // FIXME: Eventually we have to make this smart enough to handle the case
     // where we have a stream for the body to handle the "data interspersed with
@@ -1094,11 +1090,7 @@
     http_method_ = new_http_method;
   }
 
-  if (redirect.new_referrer.IsEmpty()) {
-    referrer_ = Referrer(Referrer::NoReferrer(), redirect.new_referrer_policy);
-  } else {
-    referrer_ = Referrer(redirect.new_referrer, redirect.new_referrer_policy);
-  }
+  referrer_ = redirect.new_referrer;
 
   // TODO(dgozman): check whether clearing origin policy is intended behavior.
   origin_policy_ = absl::nullopt;
@@ -2553,12 +2545,6 @@
   // (provisional document loader) instead of frame_'s document loader.
   if (response_.DidServiceWorkerNavigationPreload())
     CountUse(WebFeature::kServiceWorkerNavigationPreload);
-  if (!frame_->IsMainFrame() && response_.GetCTPolicyCompliance() ==
-                                    ResourceResponse::kCTPolicyDoesNotComply) {
-    // Exclude main-frame navigations; those are tracked elsewhere.
-    CountUse(
-        WebFeature::kCertificateTransparencyNonCompliantResourceInSubframe);
-  }
   if (frame_->DomWindow()->IsFeatureEnabled(
           mojom::blink::DocumentPolicyFeature::kForceLoadAtTop)) {
     CountUse(WebFeature::kForceLoadAtTop);
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index c18a794..6b630b1 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -140,7 +140,7 @@
 
   const AtomicString& MimeType() const;
 
-  const Referrer& OriginalReferrer() const;
+  const AtomicString& OriginalReferrer() const;
 
   MHTMLArchive* Archive() const { return archive_.Get(); }
 
@@ -156,7 +156,7 @@
 
   const KURL& UrlForHistory() const;
   const AtomicString& HttpMethod() const;
-  const Referrer& GetReferrer() const;
+  const AtomicString& GetReferrer() const;
   const SecurityOrigin* GetRequestorOrigin() const;
   const KURL& UnreachableURL() const;
   const absl::optional<blink::mojom::FetchCacheMode>& ForceFetchCacheMode()
@@ -501,7 +501,8 @@
   // These fields are copied from WebNavigationParams, see there for definition.
   KURL url_;
   AtomicString http_method_;
-  Referrer referrer_;
+  // The referrer on the final request for this document.
+  AtomicString referrer_;
   scoped_refptr<EncodedFormData> http_body_;
   AtomicString http_content_type_;
   PreviewsState previews_state_;
@@ -525,7 +526,7 @@
 
   Member<SubresourceFilter> subresource_filter_;
 
-  const Referrer original_referrer_;
+  const AtomicString original_referrer_;
 
   ResourceResponse response_;
 
diff --git a/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties.cc b/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties.cc
index 3af9c06..c7184a2 100644
--- a/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties.cc
+++ b/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties.cc
@@ -148,10 +148,4 @@
   return IsMainFrame() ? main_frame_limit : sub_frame_limit;
 }
 
-scoped_refptr<SecurityOrigin>
-FrameResourceFetcherProperties::GetLitePageSubresourceRedirectOrigin() const {
-  return SecurityOrigin::CreateFromString(
-      document_->GetSettings()->GetLitePageSubresourceRedirectOrigin());
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties.h b/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties.h
index 30c73ad..85ef085be 100644
--- a/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties.h
+++ b/third_party/blink/renderer/core/loader/frame_resource_fetcher_properties.h
@@ -44,8 +44,6 @@
   scheduler::FrameStatus GetFrameStatus() const override;
   const KURL& WebBundlePhysicalUrl() const override;
   int GetOutstandingThrottledLimit() const override;
-  scoped_refptr<SecurityOrigin> GetLitePageSubresourceRedirectOrigin()
-      const override;
 
  private:
   const Member<DocumentLoader> document_loader_;
diff --git a/third_party/blink/renderer/core/loader/history_item.cc b/third_party/blink/renderer/core/loader/history_item.cc
index 11178fb..ff06aac 100644
--- a/third_party/blink/renderer/core/loader/history_item.cc
+++ b/third_party/blink/renderer/core/loader/history_item.cc
@@ -61,10 +61,14 @@
   return KURL(url_string_);
 }
 
-const Referrer& HistoryItem::GetReferrer() const {
+const String& HistoryItem::GetReferrer() const {
   return referrer_;
 }
 
+network::mojom::ReferrerPolicy HistoryItem::GetReferrerPolicy() const {
+  return referrer_policy_;
+}
+
 void HistoryItem::SetURLString(const String& url_string) {
   if (url_string_ != url_string)
     url_string_ = url_string;
@@ -74,10 +78,12 @@
   SetURLString(url.GetString());
 }
 
-void HistoryItem::SetReferrer(const Referrer& referrer) {
-  // This should be a CHECK.
-  referrer_ = SecurityPolicy::GenerateReferrer(referrer.referrer_policy, Url(),
-                                               referrer.referrer);
+void HistoryItem::SetReferrer(const String& referrer) {
+  referrer_ = referrer;
+}
+
+void HistoryItem::SetReferrerPolicy(network::mojom::ReferrerPolicy policy) {
+  referrer_policy_ = policy;
 }
 
 void HistoryItem::SetVisualViewportScrollOffset(const ScrollOffset& offset) {
@@ -157,8 +163,8 @@
 ResourceRequest HistoryItem::GenerateResourceRequest(
     mojom::FetchCacheMode cache_mode) {
   ResourceRequest request(url_string_);
-  request.SetReferrerString(referrer_.referrer);
-  request.SetReferrerPolicy(referrer_.referrer_policy);
+  request.SetReferrerString(referrer_);
+  request.SetReferrerPolicy(referrer_policy_);
   request.SetCacheMode(cache_mode);
   if (form_data_) {
     request.SetHttpMethod(http_names::kPOST);
diff --git a/third_party/blink/renderer/core/loader/history_item.h b/third_party/blink/renderer/core/loader/history_item.h
index 25c2ef1..05a0024 100644
--- a/third_party/blink/renderer/core/loader/history_item.h
+++ b/third_party/blink/renderer/core/loader/history_item.h
@@ -57,7 +57,8 @@
   const String& UrlString() const;
   KURL Url() const;
 
-  const Referrer& GetReferrer() const;
+  const String& GetReferrer() const;
+  network::mojom::ReferrerPolicy GetReferrerPolicy() const;
 
   EncodedFormData* FormData();
   const AtomicString& FormContentType() const;
@@ -94,7 +95,8 @@
 
   void SetURL(const KURL&);
   void SetURLString(const String&);
-  void SetReferrer(const Referrer&);
+  void SetReferrer(const String&);
+  void SetReferrerPolicy(network::mojom::ReferrerPolicy);
 
   void SetStateObject(scoped_refptr<SerializedScriptValue>);
   SerializedScriptValue* StateObject() const { return state_object_.get(); }
@@ -136,7 +138,13 @@
 
  private:
   String url_string_;
-  Referrer referrer_;
+
+  // The referrer provided when this item was originally requested.
+  String referrer_;
+
+  // The referrer policy of the document this item represents.
+  network::mojom::ReferrerPolicy referrer_policy_ =
+      network::mojom::ReferrerPolicy::kDefault;
 
   Vector<String> document_state_vector_;
   Member<DocumentState> document_state_;
diff --git a/third_party/blink/renderer/core/loader/image_loader.cc b/third_party/blink/renderer/core/loader/image_loader.cc
index e3abd40..0c3446d 100644
--- a/third_party/blink/renderer/core/loader/image_loader.cc
+++ b/third_party/blink/renderer/core/loader/image_loader.cc
@@ -51,7 +51,6 @@
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_image.h"
 #include "third_party/blink/renderer/core/loader/importance_attribute.h"
 #include "third_party/blink/renderer/core/loader/lazy_image_helper.h"
-#include "third_party/blink/renderer/core/loader/subresource_redirect_util.h"
 #include "third_party/blink/renderer/core/probe/async_task_context.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/core/svg/graphics/svg_image.h"
@@ -554,14 +553,6 @@
       params.SetLazyImageNonBlocking();
     }
 
-    if (ShouldEnableSubresourceRedirect(
-            DynamicTo<HTMLImageElement>(GetElement()), params.Url())) {
-      auto& subresource_request = params.MutableResourceRequest();
-      subresource_request.SetPreviewsState(
-          subresource_request.GetPreviewsState() |
-          PreviewsTypes::kSubresourceRedirectOn);
-    }
-
     new_image_content = ImageResourceContent::Fetch(params, document.Fetcher());
 
     // If this load is starting while navigating away, treat it as an auditing
diff --git a/third_party/blink/renderer/core/loader/interactive_detector.cc b/third_party/blink/renderer/core/loader/interactive_detector.cc
index 6890c946..7f5c28f 100644
--- a/third_party/blink/renderer/core/loader/interactive_detector.cc
+++ b/third_party/blink/renderer/core/loader/interactive_detector.cc
@@ -38,7 +38,7 @@
 
 // A fix for FID computation.
 const base::Feature kFixFirstInputDelayForDesktop{
-    "FixFirstInputDelayForDesktop", base::FEATURE_DISABLED_BY_DEFAULT};
+    "FixFirstInputDelayForDesktop", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Required length of main thread and network quiet window for determining
 // Time to Interactive.
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
index 209d6986..1da42e2 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
@@ -169,11 +169,15 @@
     DCHECK(it != finished_observers_.end());
     fully_erased = finished_observers_.erase(it);
   }
-  info_->DidRemoveClientOrObserver();
+  DidRemoveObserver();
   if (fully_erased)
     observer->NotifyImageFullyRemoved(this);
 }
 
+void ImageResourceContent::DidRemoveObserver() {
+  info_->DidRemoveClientOrObserver();
+}
+
 static void PriorityFromObserver(const ImageResourceObserver* observer,
                                  ResourcePriority& priority) {
   ResourcePriority next_priority = observer->ComputeResourcePriority();
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.h b/third_party/blink/renderer/core/loader/resource/image_resource_content.h
index c2722c8..315c0c4 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_content.h
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.h
@@ -79,6 +79,7 @@
 
   void AddObserver(ImageResourceObserver*);
   void RemoveObserver(ImageResourceObserver*);
+  void DidRemoveObserver();
 
   // The device pixel ratio we got from the server for this image, or 1.0.
   float DevicePixelRatioHeaderValue() const;
diff --git a/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc b/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc
index d844f38..71cabb2 100644
--- a/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc
+++ b/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc
@@ -182,16 +182,6 @@
     subresource_filter->ReportAdRequestId(response.RequestId());
 
   DCHECK(frame_client);
-  if (response.GetCTPolicyCompliance() ==
-      ResourceResponse::kCTPolicyDoesNotComply) {
-    CountUsage(
-        frame->IsMainFrame()
-            ? WebFeature::
-                  kCertificateTransparencyNonCompliantSubresourceInMainFrame
-            : WebFeature::
-                  kCertificateTransparencyNonCompliantResourceInSubframe);
-  }
-
   if (response_source == ResponseSource::kFromMemoryCache) {
     ResourceRequest resource_request(resource->GetResourceRequest());
 
diff --git a/third_party/blink/renderer/core/loader/subresource_redirect_util.cc b/third_party/blink/renderer/core/loader/subresource_redirect_util.cc
deleted file mode 100644
index 92170db..0000000
--- a/third_party/blink/renderer/core/loader/subresource_redirect_util.cc
+++ /dev/null
@@ -1,164 +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 "third_party/blink/renderer/core/loader/subresource_redirect_util.h"
-
-#include "base/metrics/histogram_functions.h"
-#include "third_party/blink/public/common/features.h"
-#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
-#include "third_party/blink/renderer/core/frame/csp/csp_directive_list.h"
-#include "third_party/blink/renderer/core/frame/settings.h"
-#include "third_party/blink/renderer/core/html/cross_origin_attribute.h"
-#include "third_party/blink/renderer/platform/network/network_state_notifier.h"
-#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
-
-namespace blink {
-
-namespace {
-
-// Records the ineligibility metrics.
-void RecordSubresourceRedirectIneligibility(
-    BlinkSubresourceRedirectIneligibility reason) {
-  base::UmaHistogramEnumeration("SubresourceRedirect.Blink.Ineligibility",
-                                reason);
-}
-
-// Returns whether the URL scheme is http or https.
-bool IsSchemeHttpOrHttps(const KURL& url) {
-  return url.Protocol() == url::kHttpsScheme ||
-         url.Protocol() == url::kHttpScheme;
-}
-
-// Returns whether CSP restricted subresource redirect images should be allowed
-// for subresource redirect compression.
-bool ShouldAllowCspRestrictedImages() {
-  return base::GetFieldTrialParamByFeatureAsBool(
-      blink::features::kSubresourceRedirect, "allow_csp_restricted_images",
-      false);
-}
-
-}  // namespace
-
-bool ShouldEnableSubresourceRedirect(HTMLImageElement* image_element,
-                                     const KURL& url) {
-  if (!image_element)
-    return false;
-
-  // Allow redirection only when DataSaver is enabled and subresource redirect
-  // feature is enabled which allows redirecting to better optimized versions.
-  if (!base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect) ||
-      !GetNetworkStateNotifier().SaveDataEnabled()) {
-    return false;
-  }
-
-  // Allow redirection only for http(s) subresources on http(s) documents.
-  if (!IsSchemeHttpOrHttps(url) ||
-      !IsSchemeHttpOrHttps(image_element->GetDocument().Url())) {
-    return false;
-  }
-
-  // Allow subresource redirect only when cross-origin attribute is not set,
-  // which indicates CORS validation is not triggered for the image.
-  if (GetCrossOriginAttributeValue(image_element->FastGetAttribute(
-          html_names::kCrossoriginAttr)) != kCrossOriginAttributeNotSet) {
-    RecordSubresourceRedirectIneligibility(
-        BlinkSubresourceRedirectIneligibility::kCrossOriginAttributeSet);
-    return false;
-  }
-
-  // Enable subresource redirect for <img> elements created by parser, or for
-  // crossorigin <img> elements without cross-origin attribute when not created
-  // by parser. Images created from javascript, fetched via XHR/Fetch API should
-  // not be subresource redirected when they are sameorigin, since the redirect
-  // brings in cross-origin issues. Such images having cross-origin attribute
-  // should not be subresource redirected too due to the additional CORB/CORS
-  // handling needed for them.
-  if (!image_element->ElementCreatedByParser()) {
-    bool is_sameorigin =
-        SecurityOrigin::AreSameOrigin(url, image_element->GetDocument().Url());
-    bool allow_javascript_crossorigin_images =
-        base::GetFieldTrialParamByFeatureAsBool(
-            blink::features::kSubresourceRedirect,
-            "allow_javascript_crossorigin_images", false);
-    if (!allow_javascript_crossorigin_images) {
-      RecordSubresourceRedirectIneligibility(
-          is_sameorigin ? BlinkSubresourceRedirectIneligibility::
-                              kJavascriptCreatedSameOrigin
-                        : BlinkSubresourceRedirectIneligibility::
-                              kJavascriptCreatedCrossOrigin);
-      return false;
-    }
-    if (is_sameorigin) {
-      RecordSubresourceRedirectIneligibility(
-          BlinkSubresourceRedirectIneligibility::kJavascriptCreatedSameOrigin);
-      return false;
-    }
-  }
-
-  if (ShouldAllowCspRestrictedImages())
-    return true;
-
-  // Check the actual subresource redirect URL constructed from the subresource
-  // redirect origin is restricted by CSP.
-  auto litepage_subresource_redirect_origin = SecurityOrigin::CreateFromString(
-      image_element->GetDocument()
-          .GetSettings()
-          ->GetLitePageSubresourceRedirectOrigin());
-  KURL subresource_redirect_url = url;
-  subresource_redirect_url.SetProtocol(
-      litepage_subresource_redirect_origin->Protocol());
-  subresource_redirect_url.SetHost(
-      litepage_subresource_redirect_origin->Host());
-  subresource_redirect_url.SetPort(
-      litepage_subresource_redirect_origin->Port());
-  auto* content_security_policy =
-      image_element->GetExecutionContext()->GetContentSecurityPolicy();
-  if (content_security_policy &&
-      !content_security_policy->AllowImageFromSource(
-          subresource_redirect_url, subresource_redirect_url,
-          RedirectStatus::kNoRedirect,
-          ReportingDisposition::kSuppressReporting)) {
-    // When any of the CSP policy has img-src directive, then treat it as the
-    // image was disallowed by img-src directive.
-    bool disabled_by_img_src = false;
-    for (const auto& policy : content_security_policy->GetParsedPolicies()) {
-      if (CSPDirectiveListOperativeDirective(*policy, CSPDirectiveName::ImgSrc)
-              .type == CSPDirectiveName::ImgSrc) {
-        disabled_by_img_src = true;
-      }
-    }
-    RecordSubresourceRedirectIneligibility(
-        disabled_by_img_src ? BlinkSubresourceRedirectIneligibility::
-                                  kContentSecurityPolicyImgSrcRestricted
-                            : BlinkSubresourceRedirectIneligibility::
-                                  kContentSecurityPolicyDefaultSrcRestricted);
-    return false;
-  }
-  return true;
-}
-
-bool ShouldDisableCSPCheckForLitePageSubresourceRedirectOrigin(
-    scoped_refptr<SecurityOrigin> litepage_subresource_redirect_origin,
-    mojom::blink::RequestContextType request_context,
-    ResourceRequest::RedirectStatus redirect_status,
-    const KURL& url) {
-  if (!base::FeatureList::IsEnabled(blink::features::kSubresourceRedirect))
-    return false;
-  if (request_context != mojom::blink::RequestContextType::IMAGE &&
-      request_context != mojom::blink::RequestContextType::IMAGE_SET) {
-    return false;
-  }
-  if (redirect_status != ResourceRequest::RedirectStatus::kFollowedRedirect)
-    return false;
-  if (!ShouldAllowCspRestrictedImages())
-    return false;
-
-  if (litepage_subresource_redirect_origin->IsOpaque())
-    return false;
-
-  return litepage_subresource_redirect_origin->IsSameOriginWith(
-      SecurityOrigin::Create(url).get());
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/subresource_redirect_util.h b/third_party/blink/renderer/core/loader/subresource_redirect_util.h
deleted file mode 100644
index c27d28f1..0000000
--- a/third_party/blink/renderer/core/loader/subresource_redirect_util.h
+++ /dev/null
@@ -1,54 +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 THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_SUBRESOURCE_REDIRECT_UTIL_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_SUBRESOURCE_REDIRECT_UTIL_H_
-
-#include "third_party/blink/renderer/core/html/html_image_element.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl.h"
-
-namespace blink {
-
-// Enumerates the different ineligibility reasons for why a subresource
-// redirection was disabled in blink. This is recorded in metrics and should not
-// be reordered or removed. Should be in sync with the same name in enums.xml
-enum class BlinkSubresourceRedirectIneligibility {
-  // Created by javascript and is same-origin with the document.
-  kJavascriptCreatedSameOrigin = 0,
-
-  // Created by javascript and is cross-origin with the document.
-  kJavascriptCreatedCrossOrigin = 1,
-
-  // Restricted by Content-Security-Policy default-src directive.
-  kContentSecurityPolicyDefaultSrcRestricted = 2,
-
-  // Restricted by Content-Security-Policy img-src directive.
-  kContentSecurityPolicyImgSrcRestricted = 3,
-
-  // Crossorigin attribute was set which indicates CORS validation will happen
-  // for the subresource.
-  kCrossOriginAttributeSet = 4,
-
-  // New enum entries should be added here.
-
-  kMaxValue = kCrossOriginAttributeSet
-};
-
-// Returns whether subresource redirect can be attempted for fetching the image
-// with |url|. Redirect to other origins could be disabled due to CSP or CORS
-// restrictions.
-bool ShouldEnableSubresourceRedirect(HTMLImageElement* image_element,
-                                     const KURL& url);
-
-// Returns whether CSP check should be disabled for this resource request for
-// |url| with the given |request_context| and |redirect_status|.
-bool ShouldDisableCSPCheckForLitePageSubresourceRedirectOrigin(
-    scoped_refptr<SecurityOrigin> litepage_subresource_redirect_origin,
-    mojom::blink::RequestContextType request_context,
-    ResourceRequest::RedirectStatus redirect_status,
-    const KURL& url);
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_SUBRESOURCE_REDIRECT_UTIL_H_
diff --git a/third_party/blink/renderer/core/loader/worker_resource_fetcher_properties.h b/third_party/blink/renderer/core/loader/worker_resource_fetcher_properties.h
index 5c9d9a0..cde789c 100644
--- a/third_party/blink/renderer/core/loader/worker_resource_fetcher_properties.h
+++ b/third_party/blink/renderer/core/loader/worker_resource_fetcher_properties.h
@@ -52,10 +52,6 @@
   }
   const KURL& WebBundlePhysicalUrl() const override;
   int GetOutstandingThrottledLimit() const override;
-  scoped_refptr<SecurityOrigin> GetLitePageSubresourceRedirectOrigin()
-      const override {
-    return nullptr;
-  }
 
  private:
   const Member<WorkerOrWorkletGlobalScope> global_scope_;
diff --git a/third_party/blink/renderer/core/page/drag_controller.cc b/third_party/blink/renderer/core/page/drag_controller.cc
index 80fb9bd..da03bc3 100644
--- a/third_party/blink/renderer/core/page/drag_controller.cc
+++ b/third_party/blink/renderer/core/page/drag_controller.cc
@@ -1171,8 +1171,8 @@
   DCHECK(frame.GetDocument()->IsActive());
 
   gfx::RectF painting_rect = ClippedSelection(frame);
-  GlobalPaintFlags paint_flags =
-      kGlobalPaintSelectionDragImageOnly | kGlobalPaintFlattenCompositingLayers;
+  PaintFlags paint_flags =
+      PaintFlag::kSelectionDragImageOnly | PaintFlag::kOmitCompositingInfo;
 
   auto* builder = MakeGarbageCollected<PaintRecordBuilder>();
   frame.View()->PaintOutsideOfLifecycle(
diff --git a/third_party/blink/renderer/core/page/drag_image.cc b/third_party/blink/renderer/core/page/drag_image.cc
index f3b40a67..d8d40b6 100644
--- a/third_party/blink/renderer/core/page/drag_image.cc
+++ b/third_party/blink/renderer/core/page/drag_image.cc
@@ -215,7 +215,7 @@
   const float kDragLabelRadius = 5;
 
   gfx::Rect rect(image_size);
-  PaintFlags background_paint;
+  cc::PaintFlags background_paint;
   background_paint.setColor(SkColorSetRGB(140, 140, 140));
   background_paint.setAntiAlias(true);
   SkRRect rrect;
@@ -224,7 +224,7 @@
   resource_provider->Canvas()->drawRRect(rrect, background_paint);
 
   // Draw the text
-  PaintFlags text_paint;
+  cc::PaintFlags text_paint;
   if (draw_url_string) {
     if (clip_url_string)
       url_string = StringTruncator::CenterTruncate(
diff --git a/third_party/blink/renderer/core/page/print_context_test.cc b/third_party/blink/renderer/core/page/print_context_test.cc
index 0ee11ee..59d2b87 100644
--- a/third_party/blink/renderer/core/page/print_context_test.cc
+++ b/third_party/blink/renderer/core/page/print_context_test.cc
@@ -174,7 +174,7 @@
     GraphicsContext& context = builder->Context();
     context.SetPrinting(true);
     GetDocument().View()->PaintOutsideOfLifecycle(
-        context, kGlobalPaintAddUrlMetadata, CullRect(page_rect));
+        context, PaintFlag::kAddUrlMetadata, CullRect(page_rect));
     {
       DrawingRecorder recorder(
           context, *GetDocument().GetLayoutView(),
diff --git a/third_party/blink/renderer/core/paint/applied_decoration_painter.cc b/third_party/blink/renderer/core/paint/applied_decoration_painter.cc
index 4cac1c2f..83b87662 100644
--- a/third_party/blink/renderer/core/paint/applied_decoration_painter.cc
+++ b/third_party/blink/renderer/core/paint/applied_decoration_painter.cc
@@ -10,7 +10,7 @@
 
 namespace blink {
 
-void AppliedDecorationPainter::Paint(const PaintFlags* flags) {
+void AppliedDecorationPainter::Paint(const cc::PaintFlags* flags) {
   context_.SetStrokeStyle(decoration_info_.StrokeStyle());
   context_.SetStrokeColor(decoration_info_.LineColor());
 
@@ -38,7 +38,7 @@
 }
 
 void AppliedDecorationPainter::StrokeWavyTextDecoration(
-    const PaintFlags* flags) {
+    const cc::PaintFlags* flags) {
   // We need this because of the clipping we're doing below, as we paint both
   // overlines and underlines here. That clip would hide the overlines, when
   // painting the underlines.
diff --git a/third_party/blink/renderer/core/paint/applied_decoration_painter.h b/third_party/blink/renderer/core/paint/applied_decoration_painter.h
index c663b75..e7256e27 100644
--- a/third_party/blink/renderer/core/paint/applied_decoration_painter.h
+++ b/third_party/blink/renderer/core/paint/applied_decoration_painter.h
@@ -5,8 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_APPLIED_DECORATION_PAINTER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_APPLIED_DECORATION_PAINTER_H_
 
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/core/paint/text_decoration_info.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
@@ -23,10 +23,10 @@
                            const TextDecorationInfo& decoration_info)
       : context_(context), decoration_info_(decoration_info) {}
 
-  void Paint(const PaintFlags* flags = nullptr);
+  void Paint(const cc::PaintFlags* flags = nullptr);
 
  private:
-  void StrokeWavyTextDecoration(const PaintFlags* flags);
+  void StrokeWavyTextDecoration(const cc::PaintFlags* flags);
 
   GraphicsContext& context_;
   const TextDecorationInfo& decoration_info_;
diff --git a/third_party/blink/renderer/core/paint/box_border_painter.cc b/third_party/blink/renderer/core/paint/box_border_painter.cc
index 668c8f2d..7cd70e9 100644
--- a/third_party/blink/renderer/core/paint/box_border_painter.cc
+++ b/third_party/blink/renderer/core/paint/box_border_painter.cc
@@ -282,9 +282,9 @@
       path.addRRect(SkRRect(inner));
       path.setFillType(SkPathFillType::kInverseWinding);
 
-      PaintFlags flags;
+      cc::PaintFlags flags;
       flags.setColor(color.Rgb());
-      flags.setStyle(PaintFlags::kFill_Style);
+      flags.setStyle(cc::PaintFlags::kFill_Style);
       flags.setAntiAlias(true);
       context.DrawPath(path, flags, auto_dark_mode);
 
@@ -620,7 +620,7 @@
   path.lineTo(gfx::PointFToSkPoint(quad[1]));
   path.lineTo(gfx::PointFToSkPoint(quad[2]));
   path.lineTo(gfx::PointFToSkPoint(quad[3]));
-  PaintFlags flags(context.FillFlags());
+  cc::PaintFlags flags(context.FillFlags());
   flags.setAntiAlias(antialias);
   flags.setColor(color.Rgb());
 
diff --git a/third_party/blink/renderer/core/paint/box_model_object_painter.cc b/third_party/blink/renderer/core/paint/box_model_object_painter.cc
index 5da76243..743e2a9 100644
--- a/third_party/blink/renderer/core/paint/box_model_object_painter.cc
+++ b/third_party/blink/renderer/core/paint/box_model_object_painter.cc
@@ -63,7 +63,7 @@
     const PhysicalOffset& paint_offset,
     bool object_has_multiple_boxes) {
   PaintInfo mask_paint_info(paint_info.context, CullRect(mask_rect),
-                            PaintPhase::kTextClip, kGlobalPaintNormalPhase, 0);
+                            PaintPhase::kTextClip);
   mask_paint_info.SetFragmentID(paint_info.FragmentID());
   if (flow_box_) {
     LayoutSize local_offset = ToLayoutSize(flow_box_->Location());
diff --git a/third_party/blink/renderer/core/paint/box_painter.cc b/third_party/blink/renderer/core/paint/box_painter.cc
index 8797784..f658871 100644
--- a/third_party/blink/renderer/core/paint/box_painter.cc
+++ b/third_party/blink/renderer/core/paint/box_painter.cc
@@ -263,7 +263,7 @@
 
   // Hit test data are only needed for compositing. This flag is used for for
   // printing and drag images which do not need hit testing.
-  if (paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers)
+  if (paint_info.ShouldOmitCompositingInfo())
     return;
 
   // If an object is not visible, it does not participate in hit testing.
@@ -298,7 +298,7 @@
     const DisplayItemClient& background_client) {
   // Scroll hit test data are only needed for compositing. This flag is used for
   // printing and drag images which do not need hit testing.
-  if (paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers)
+  if (paint_info.ShouldOmitCompositingInfo())
     return;
 
   // If an object is not visible, it does not scroll.
diff --git a/third_party/blink/renderer/core/paint/box_painter_base.cc b/third_party/blink/renderer/core/paint/box_painter_base.cc
index ceb3c4b..73a93fd 100644
--- a/third_party/blink/renderer/core/paint/box_painter_base.cc
+++ b/third_party/blink/renderer/core/paint/box_painter_base.cc
@@ -784,22 +784,18 @@
     }
   }
 
-  FloatRectOutsets insets(
-      -fractional_inset *
-          edges[static_cast<unsigned>(BoxSide::kTop)].UsedWidth(),
-      -fractional_inset *
-          edges[static_cast<unsigned>(BoxSide::kRight)].UsedWidth(),
-      -fractional_inset *
-          edges[static_cast<unsigned>(BoxSide::kBottom)].UsedWidth(),
-      -fractional_inset *
-          edges[static_cast<unsigned>(BoxSide::kLeft)].UsedWidth());
-
+  auto insets =
+      gfx::InsetsF()
+          .set_left(edges[static_cast<unsigned>(BoxSide::kLeft)].UsedWidth())
+          .set_right(edges[static_cast<unsigned>(BoxSide::kRight)].UsedWidth())
+          .set_top(edges[static_cast<unsigned>(BoxSide::kTop)].UsedWidth())
+          .set_bottom(
+              edges[static_cast<unsigned>(BoxSide::kBottom)].UsedWidth());
+  insets.Scale(fractional_inset);
   gfx::RectF inset_rect = background_rounded_rect.Rect();
-  inset_rect.Outset(insets.Left(), insets.Top(), insets.Right(),
-                    insets.Bottom());
+  inset_rect.Inset(insets);
   FloatRoundedRect::Radii inset_radii(background_rounded_rect.GetRadii());
-  inset_radii.Shrink(-insets.Top(), -insets.Bottom(), -insets.Left(),
-                     -insets.Right());
+  inset_radii.Shrink(insets);
   return FloatRoundedRect(inset_rect, inset_radii);
 }
 
diff --git a/third_party/blink/renderer/core/paint/build.gni b/third_party/blink/renderer/core/paint/build.gni
index 3fe0c5b6..b2693c6 100644
--- a/third_party/blink/renderer/core/paint/build.gni
+++ b/third_party/blink/renderer/core/paint/build.gni
@@ -155,13 +155,13 @@
   "paint_layer_paint_order_iterator.h",
   "paint_layer_painter.cc",
   "paint_layer_painter.h",
-  "paint_layer_painting_info.h",
   "paint_layer_resource_info.cc",
   "paint_layer_resource_info.h",
   "paint_layer_scrollable_area.cc",
   "paint_layer_scrollable_area.h",
   "paint_layer_stacking_node.cc",
   "paint_layer_stacking_node.h",
+  "paint_flags.h",
   "paint_phase.cc",
   "paint_phase.h",
   "paint_property_tree_builder.cc",
diff --git a/third_party/blink/renderer/core/paint/cull_rect_updater.cc b/third_party/blink/renderer/core/paint/cull_rect_updater.cc
index e160232..9392cd7d 100644
--- a/third_party/blink/renderer/core/paint/cull_rect_updater.cc
+++ b/third_party/blink/renderer/core/paint/cull_rect_updater.cc
@@ -61,7 +61,6 @@
 }
 
 void CullRectUpdater::UpdateInternal(const CullRect& input_cull_rect) {
-  DCHECK(RuntimeEnabledFeatures::CullRectUpdateEnabled());
   const auto& object = starting_layer_.GetLayoutObject();
   if (object.GetFrameView()->ShouldThrottleRendering())
     return;
@@ -376,9 +375,6 @@
 OverriddenCullRectScope::OverriddenCullRectScope(PaintLayer& starting_layer,
                                                  const CullRect& cull_rect)
     : starting_layer_(starting_layer) {
-  if (!RuntimeEnabledFeatures::CullRectUpdateEnabled())
-    return;
-
   if (starting_layer.GetLayoutObject().GetFrame()->IsLocalRoot() &&
       !starting_layer.NeedsCullRectUpdate() &&
       !starting_layer.DescendantNeedsCullRectUpdate() &&
@@ -394,7 +390,7 @@
 }
 
 OverriddenCullRectScope::~OverriddenCullRectScope() {
-  if (RuntimeEnabledFeatures::CullRectUpdateEnabled() && updated_)
+  if (updated_)
     starting_layer_.SetNeedsCullRectUpdate();
 }
 
diff --git a/third_party/blink/renderer/core/paint/custom_scrollbar_theme.cc b/third_party/blink/renderer/core/paint/custom_scrollbar_theme.cc
index 0e63a6e..afef4809 100644
--- a/third_party/blink/renderer/core/paint/custom_scrollbar_theme.cc
+++ b/third_party/blink/renderer/core/paint/custom_scrollbar_theme.cc
@@ -209,8 +209,7 @@
     GraphicsContext& graphics_context,
     const PhysicalRect& rect) {
   PaintInfo paint_info(graphics_context, CullRect(ToPixelSnappedRect(rect)),
-                       PaintPhase::kForeground, kGlobalPaintNormalPhase,
-                       kPaintLayerNoFlag);
+                       PaintPhase::kForeground);
   ObjectPainter(layout_custom_scrollbar_part)
       .PaintAllPhasesAtomically(paint_info);
 }
diff --git a/third_party/blink/renderer/core/paint/document_marker_painter.cc b/third_party/blink/renderer/core/paint/document_marker_painter.cc
index 0f131e5..cbec247 100644
--- a/third_party/blink/renderer/core/paint/document_marker_painter.cc
+++ b/third_party/blink/renderer/core/paint/document_marker_painter.cc
@@ -49,10 +49,10 @@
                kMarkerWidth * 7 / 8, kMarkerHeight * 1 / 4,
                kMarkerWidth * 9 / 8, kMarkerHeight * 1 / 4);
 
-  PaintFlags flags;
+  cc::PaintFlags flags;
   flags.setAntiAlias(true);
   flags.setColor(color);
-  flags.setStyle(PaintFlags::kStroke_Style);
+  flags.setStyle(cc::PaintFlags::kStroke_Style);
   flags.setStrokeWidth(kMarkerHeight * 1 / 2);
 
   PaintRecorder recorder;
@@ -76,7 +76,7 @@
   // Match the artwork used by the Mac.
   static const float kR = 1.5f;
 
-  PaintFlags flags;
+  cc::PaintFlags flags;
   flags.setAntiAlias(true);
   flags.setColor(color);
   PaintRecorder recorder;
@@ -106,7 +106,7 @@
   const auto rect = SkRect::MakeWH(width, kMarkerHeight * zoom);
   const auto local_matrix = SkMatrix::Scale(zoom, zoom);
 
-  PaintFlags flags;
+  cc::PaintFlags flags;
   flags.setAntiAlias(true);
   flags.setShader(PaintShader::MakePaintRecord(
       sk_ref_sp(marker), SkRect::MakeWH(kMarkerWidth, kMarkerHeight),
diff --git a/third_party/blink/renderer/core/paint/embedded_content_painter.cc b/third_party/blink/renderer/core/paint/embedded_content_painter.cc
index 6241a11..3414716 100644
--- a/third_party/blink/renderer/core/paint/embedded_content_painter.cc
+++ b/third_party/blink/renderer/core/paint/embedded_content_painter.cc
@@ -33,8 +33,7 @@
       paint_location - embedded_content_view->FrameRect().origin();
   CullRect adjusted_cull_rect = paint_info.GetCullRect();
   adjusted_cull_rect.Move(-view_paint_offset);
-  embedded_content_view->Paint(paint_info.context,
-                               paint_info.GetGlobalPaintFlags(),
+  embedded_content_view->Paint(paint_info.context, paint_info.GetPaintFlags(),
                                adjusted_cull_rect, view_paint_offset);
 }
 
diff --git a/third_party/blink/renderer/core/paint/filter_effect_builder.cc b/third_party/blink/renderer/core/paint/filter_effect_builder.cc
index 24eaa58b..26c21713 100644
--- a/third_party/blink/renderer/core/paint/filter_effect_builder.cc
+++ b/third_party/blink/renderer/core/paint/filter_effect_builder.cc
@@ -125,8 +125,8 @@
 
 FilterEffectBuilder::FilterEffectBuilder(const gfx::RectF& reference_box,
                                          float zoom,
-                                         const PaintFlags* fill_flags,
-                                         const PaintFlags* stroke_flags,
+                                         const cc::PaintFlags* fill_flags,
+                                         const cc::PaintFlags* stroke_flags,
                                          SkTileMode blur_tile_mode)
     : reference_box_(reference_box),
       zoom_(zoom),
diff --git a/third_party/blink/renderer/core/paint/filter_effect_builder.h b/third_party/blink/renderer/core/paint/filter_effect_builder.h
index b479097..701613c6 100644
--- a/third_party/blink/renderer/core/paint/filter_effect_builder.h
+++ b/third_party/blink/renderer/core/paint/filter_effect_builder.h
@@ -26,8 +26,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_FILTER_EFFECT_BUILDER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_FILTER_EFFECT_BUILDER_H_
 
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/skia/include/core/SkTileMode.h"
@@ -48,8 +48,8 @@
  public:
   FilterEffectBuilder(const gfx::RectF& reference_box,
                       float zoom,
-                      const PaintFlags* fill_flags = nullptr,
-                      const PaintFlags* stroke_flags = nullptr,
+                      const cc::PaintFlags* fill_flags = nullptr,
+                      const cc::PaintFlags* stroke_flags = nullptr,
                       SkTileMode blur_tile_mode = SkTileMode::kDecal);
 
   Filter* BuildReferenceFilter(const ReferenceFilterOperation&,
@@ -69,8 +69,8 @@
   const gfx::RectF reference_box_;
   const float zoom_;
   float shorthand_scale_;  // Scale factor for shorthand filter functions.
-  const PaintFlags* fill_flags_;
-  const PaintFlags* stroke_flags_;
+  const cc::PaintFlags* fill_flags_;
+  const cc::PaintFlags* stroke_flags_;
   const SkTileMode blur_tile_mode_;
 };
 
diff --git a/third_party/blink/renderer/core/paint/frame_painter.cc b/third_party/blink/renderer/core/paint/frame_painter.cc
index d91905e4..bd67c7c 100644
--- a/third_party/blink/renderer/core/paint/frame_painter.cc
+++ b/third_party/blink/renderer/core/paint/frame_painter.cc
@@ -36,8 +36,7 @@
 
 bool FramePainter::in_paint_contents_ = false;
 
-void FramePainter::Paint(GraphicsContext& context,
-                         const GlobalPaintFlags global_paint_flags) {
+void FramePainter::Paint(GraphicsContext& context, PaintFlags paint_flags) {
   Document* document = GetFrameView().GetFrame().GetDocument();
 
   if (GetFrameView().ShouldThrottleRendering() || !document->IsActive())
@@ -75,11 +74,6 @@
 
   FontCachePurgePreventer font_cache_purge_preventer;
 
-  PaintLayerFlags root_layer_paint_flags = 0;
-  // This will prevent clipping the root PaintLayer to its visible content rect.
-  if (document->IsPrintingOrPaintingPreview())
-    root_layer_paint_flags = kPaintLayerPaintingOverflowContents;
-
   PaintLayer* root_layer = layout_view->Layer();
 
 #if DCHECK_IS_ON()
@@ -94,7 +88,7 @@
       root_layer->GetLayoutObject().GetFrame());
   context.SetDeviceScaleFactor(device_scale_factor);
 
-  layer_painter.Paint(context, global_paint_flags, root_layer_paint_flags);
+  layer_painter.Paint(context, paint_flags);
 
   // Regions may have changed as a result of the visibility/z-index of element
   // changing.
diff --git a/third_party/blink/renderer/core/paint/frame_painter.h b/third_party/blink/renderer/core/paint/frame_painter.h
index 6f6dd028..e23f70d 100644
--- a/third_party/blink/renderer/core/paint/frame_painter.h
+++ b/third_party/blink/renderer/core/paint/frame_painter.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_FRAME_PAINTER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_FRAME_PAINTER_H_
 
-#include "third_party/blink/renderer/core/paint/paint_phase.h"
+#include "third_party/blink/renderer/core/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 
 namespace blink {
@@ -22,7 +22,7 @@
   FramePainter(const FramePainter&) = delete;
   FramePainter& operator=(const FramePainter&) = delete;
 
-  void Paint(GraphicsContext&, const GlobalPaintFlags);
+  void Paint(GraphicsContext&, PaintFlags);
 
  private:
   const LocalFrameView& GetFrameView();
diff --git a/third_party/blink/renderer/core/paint/highlight_painting_utils.cc b/third_party/blink/renderer/core/paint/highlight_painting_utils.cc
index d0059ec..e6e10a1 100644
--- a/third_party/blink/renderer/core/paint/highlight_painting_utils.cc
+++ b/third_party/blink/renderer/core/paint/highlight_painting_utils.cc
@@ -189,13 +189,13 @@
                      Node* node,
                      PseudoId pseudo,
                      const CSSProperty& color_property,
-                     const GlobalPaintFlags global_paint_flags,
+                     PaintFlags paint_flags,
                      const AtomicString& pseudo_argument = g_null_atom) {
   if (pseudo == kPseudoIdSelection) {
     // If the element is unselectable, or we are only painting the selection,
     // don't override the foreground color with the selection foreground color.
     if ((node && !style.IsSelectable()) ||
-        (global_paint_flags & kGlobalPaintSelectionDragImageOnly)) {
+        (paint_flags & PaintFlag::kSelectionDragImageOnly)) {
       return style.VisitedDependentColor(color_property);
     }
   }
@@ -322,10 +322,10 @@
     const ComputedStyle& style,
     Node* node,
     PseudoId pseudo,
-    const GlobalPaintFlags global_paint_flags,
+    PaintFlags paint_flags,
     const AtomicString& pseudo_argument) {
   return HighlightColor(document, style, node, pseudo,
-                        GetCSSPropertyWebkitTextFillColor(), global_paint_flags,
+                        GetCSSPropertyWebkitTextFillColor(), paint_flags,
                         pseudo_argument);
 }
 
@@ -334,9 +334,11 @@
     const ComputedStyle& style,
     Node* node,
     PseudoId pseudo,
-    const GlobalPaintFlags global_paint_flags) {
+    PaintFlags paint_flags,
+    const AtomicString& pseudo_argument) {
   return HighlightColor(document, style, node, pseudo,
-                        GetCSSPropertyTextEmphasisColor(), global_paint_flags);
+                        GetCSSPropertyTextEmphasisColor(), paint_flags,
+                        pseudo_argument);
 }
 
 TextPaintStyle HighlightPaintingUtils::HighlightPaintingStyle(
@@ -349,7 +351,7 @@
     const AtomicString& pseudo_argument) {
   TextPaintStyle highlight_style = text_style;
   bool uses_text_as_clip = paint_info.phase == PaintPhase::kTextClip;
-  const GlobalPaintFlags global_paint_flags = paint_info.GetGlobalPaintFlags();
+  const PaintFlags paint_flags = paint_info.GetPaintFlags();
 
   // Each highlight overlay’s shadows are completely independent of any shadows
   // specified on the originating element (or the other highlight overlays).
@@ -357,9 +359,9 @@
 
   if (!uses_text_as_clip) {
     highlight_style.fill_color = HighlightForegroundColor(
-        document, style, node, pseudo, global_paint_flags, pseudo_argument);
+        document, style, node, pseudo, paint_flags, pseudo_argument);
     highlight_style.emphasis_mark_color = HighlightEmphasisMarkColor(
-        document, style, node, pseudo, global_paint_flags);
+        document, style, node, pseudo, paint_flags, pseudo_argument);
   }
 
   if (scoped_refptr<const ComputedStyle> pseudo_style =
diff --git a/third_party/blink/renderer/core/paint/highlight_painting_utils.h b/third_party/blink/renderer/core/paint/highlight_painting_utils.h
index e428ea7..e77b8bf6 100644
--- a/third_party/blink/renderer/core/paint/highlight_painting_utils.h
+++ b/third_party/blink/renderer/core/paint/highlight_painting_utils.h
@@ -8,7 +8,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/paint/paint_phase.h"
+#include "third_party/blink/renderer/core/paint/paint_flags.h"
 #include "third_party/blink/renderer/core/style/applied_text_decoration.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
@@ -40,13 +40,15 @@
       const ComputedStyle&,
       Node*,
       PseudoId,
-      const GlobalPaintFlags,
+      PaintFlags,
       const AtomicString& pseudo_argument = g_null_atom);
-  static Color HighlightEmphasisMarkColor(const Document&,
-                                          const ComputedStyle&,
-                                          Node*,
-                                          PseudoId,
-                                          const GlobalPaintFlags);
+  static Color HighlightEmphasisMarkColor(
+      const Document&,
+      const ComputedStyle&,
+      Node*,
+      PseudoId,
+      PaintFlags,
+      const AtomicString& pseudo_argument = g_null_atom);
   static TextPaintStyle HighlightPaintingStyle(
       const Document&,
       const ComputedStyle&,
diff --git a/third_party/blink/renderer/core/paint/highlight_painting_utils_test.cc b/third_party/blink/renderer/core/paint/highlight_painting_utils_test.cc
index 6b5c0aa0..bbdccda 100644
--- a/third_party/blink/renderer/core/paint/highlight_painting_utils_test.cc
+++ b/third_party/blink/renderer/core/paint/highlight_painting_utils_test.cc
@@ -40,7 +40,7 @@
 
   auto* body = GetDocument().body();
   auto* text_node = body->firstChild();
-  GlobalPaintFlags flags{0};
+  PaintFlags flags = PaintFlag::kNoFlag;
 
   Compositor().BeginFrame();
 
@@ -99,7 +99,7 @@
 
   auto* body = GetDocument().body();
   auto* text_node = body->firstChild();
-  GlobalPaintFlags flags{0};
+  PaintFlags flags = PaintFlag::kNoFlag;
 
   Compositor().BeginFrame();
 
@@ -170,8 +170,7 @@
   std::unique_ptr<PaintController> controller{
       std::make_unique<PaintController>()};
   GraphicsContext context(*controller);
-  PaintInfo paint_info(context, CullRect(), PaintPhase::kForeground,
-                       kGlobalPaintNormalPhase, 0 /* paint_flags */);
+  PaintInfo paint_info(context, CullRect(), PaintPhase::kForeground);
   TextPaintStyle paint_style;
 
   paint_style = HighlightPaintingUtils::HighlightPaintingStyle(
diff --git a/third_party/blink/renderer/core/paint/html_canvas_painter.cc b/third_party/blink/renderer/core/paint/html_canvas_painter.cc
index 05954847..b812c87 100644
--- a/third_party/blink/renderer/core/paint/html_canvas_painter.cc
+++ b/third_party/blink/renderer/core/paint/html_canvas_painter.cc
@@ -44,9 +44,6 @@
         .MarkFirstContentfulPaint();
   }
 
-  bool flatten_composited_layers =
-      paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers;
-
   if (auto* layer = canvas->ContentsCcLayer()) {
     // TODO(crbug.com/705019): For a texture layer canvas, setting the layer
     // background color to an opaque color will cause the layer to be treated as
@@ -59,7 +56,7 @@
     }
     // We do not take the foreign layer code path when printing because it
     // prevents painting canvas content as vector graphics.
-    if (!flatten_composited_layers && !canvas->IsPrinting()) {
+    if (!paint_info.ShouldOmitCompositingInfo() && !canvas->IsPrinting()) {
       gfx::Rect pixel_snapped_rect = ToPixelSnappedRect(paint_rect);
       layer->SetBounds(pixel_snapped_rect.size());
       layer->SetIsDrawable(true);
@@ -79,7 +76,7 @@
                               paint_offset);
   ScopedInterpolationQuality interpolation_quality_scope(
       context, InterpolationQualityForCanvas(layout_html_canvas_.StyleRef()));
-  canvas->Paint(context, paint_rect, flatten_composited_layers);
+  canvas->Paint(context, paint_rect, paint_info.ShouldOmitCompositingInfo());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/inline_text_box_painter.cc b/third_party/blink/renderer/core/paint/inline_text_box_painter.cc
index 7429cfd..0f61f58 100644
--- a/third_party/blink/renderer/core/paint/inline_text_box_painter.cc
+++ b/third_party/blink/renderer/core/paint/inline_text_box_painter.cc
@@ -35,6 +35,7 @@
 #include "third_party/blink/renderer/platform/graphics/paint/paint_shader.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/skia/include/effects/SkGradientShader.h"
+#include "ui/gfx/geometry/outsets_f.h"
 
 namespace blink {
 
@@ -868,13 +869,13 @@
 
 void InlineTextBoxPainter::ExpandToIncludeNewlineForSelection(
     PhysicalRect& rect) {
-  FloatRectOutsets outsets = FloatRectOutsets();
+  gfx::OutsetsF outsets;
   float space_width = inline_text_box_.NewlineSpaceWidth();
   if (inline_text_box_.IsLeftToRightDirection())
-    outsets.SetRight(space_width);
+    outsets.set_right(space_width);
   else
-    outsets.SetLeft(space_width);
-  rect.Expand(outsets);
+    outsets.set_left(space_width);
+  rect.Expand(EnclosingLayoutRectOutsets(outsets));
 }
 
 void InlineTextBoxPainter::PaintStyleableMarkerUnderline(
diff --git a/third_party/blink/renderer/core/paint/link_highlight_impl.cc b/third_party/blink/renderer/core/paint/link_highlight_impl.cc
index 572a327f..f001aa2 100644
--- a/third_party/blink/renderer/core/paint/link_highlight_impl.cc
+++ b/third_party/blink/renderer/core/paint/link_highlight_impl.cc
@@ -164,8 +164,8 @@
   cc::PaintCanvas* canvas =
       recorder.beginRecording(record_bounds.width(), record_bounds.height());
 
-  PaintFlags flags;
-  flags.setStyle(PaintFlags::kFill_Style);
+  cc::PaintFlags flags;
+  flags.setStyle(cc::PaintFlags::kFill_Style);
   flags.setAntiAlias(true);
   flags.setColor(color_.Rgb());
   canvas->drawPath(path_.GetSkPath(), flags);
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
index 0bbe0f8..a5c6b795 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -521,7 +521,7 @@
 
   // Hit test data are only needed for compositing. This flag is used for for
   // printing and drag images which do not need hit testing.
-  if (paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers)
+  if (paint_info.ShouldOmitCompositingInfo())
     return false;
 
   // If an object is not visible, it does not participate in hit testing.
@@ -1750,7 +1750,7 @@
                                              const PhysicalOffset& paint_offset,
                                              bool object_has_multiple_boxes) {
   PaintInfo mask_paint_info(paint_info.context, CullRect(mask_rect),
-                            PaintPhase::kTextClip, kGlobalPaintNormalPhase, 0);
+                            PaintPhase::kTextClip);
   mask_paint_info.SetFragmentID(paint_info.FragmentID());
   if (!object_has_multiple_boxes) {
     PaintObject(mask_paint_info, paint_offset);
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter_test.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter_test.cc
index 9206e50..5c2fa8f0 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter_test.cc
@@ -121,8 +121,7 @@
 
   GetDocument().View()->PaintOutsideOfLifecycle(
       builder->Context(),
-      kGlobalPaintNormalPhase | kGlobalPaintAddUrlMetadata |
-          kGlobalPaintFlattenCompositingLayers,
+      PaintFlag::kAddUrlMetadata | PaintFlag::kOmitCompositingInfo,
       CullRect::Infinite());
 
   auto record = builder->EndRecording();
@@ -180,7 +179,7 @@
   auto* builder = MakeGarbageCollected<PaintRecordBuilder>();
   GetDocument().View()->PaintOutsideOfLifecycle(
       builder->Context(),
-      kGlobalPaintSelectionDragImageOnly | kGlobalPaintFlattenCompositingLayers,
+      PaintFlag::kSelectionDragImageOnly | PaintFlag::kOmitCompositingInfo,
       CullRect::Infinite());
 
   auto record = builder->EndRecording();
diff --git a/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc
index 56333fe5..6f896eb 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_text_painter.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/paint/ng/ng_text_painter.h"
 
 #include "base/stl_util.h"
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/layout/layout_object_inlines.h"
@@ -27,7 +28,6 @@
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context_state_saver.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/wtf/text/character_names.h"
 
 namespace blink {
@@ -116,7 +116,7 @@
                           const ComputedStyle& style,
                           SvgPaintMode svg_paint_mode,
                           LayoutSVGResourceMode resource_mode,
-                          PaintFlags& flags) {
+                          cc::PaintFlags& flags) {
   const LayoutObject& layout_parent = svg_paint_mode == SvgPaintMode::kText
                                           ? *state.InlineText().Parent()
                                           : state.TextDecorationObject();
@@ -393,11 +393,11 @@
                                          const AutoDarkMode& auto_dark_mode) {
   const NGTextPainter::SvgTextPaintState& state = svg_text_paint_state_.value();
   if (state.IsPaintingTextMatch()) {
-    PaintFlags fill_flags;
+    cc::PaintFlags fill_flags;
     fill_flags.setColor(state.TextMatchColor().Rgb());
     fill_flags.setAntiAlias(true);
 
-    PaintFlags stroke_flags;
+    cc::PaintFlags stroke_flags;
     bool should_paint_stroke = false;
     if (SetupPaintForSvgText(state, graphics_context_, state.Style(),
                              SvgPaintMode::kText, kApplyToStrokeMode,
@@ -445,7 +445,7 @@
     }
 
     if (resource_mode) {
-      PaintFlags flags;
+      cc::PaintFlags flags;
       if (SetupPaintForSvgText(state, graphics_context_, style_to_paint,
                                SvgPaintMode::kText, *resource_mode, flags)) {
         graphics_context_.DrawText(font_, fragment_paint_info_,
@@ -492,7 +492,7 @@
     }
 
     if (resource_mode) {
-      PaintFlags flags;
+      cc::PaintFlags flags;
       if (SetupPaintForSvgText(state, graphics_context_, style_to_paint,
                                SvgPaintMode::kTextDecoration, *resource_mode,
                                flags)) {
@@ -538,7 +538,7 @@
     }
 
     if (resource_mode) {
-      PaintFlags flags;
+      cc::PaintFlags flags;
       if (SetupPaintForSvgText(state, graphics_context_, style_to_paint,
                                SvgPaintMode::kTextDecoration, *resource_mode,
                                flags)) {
diff --git a/third_party/blink/renderer/core/paint/nine_piece_image_grid.cc b/third_party/blink/renderer/core/paint/nine_piece_image_grid.cc
index 7107c4f..71865b0b 100644
--- a/third_party/blink/renderer/core/paint/nine_piece_image_grid.cc
+++ b/third_party/blink/renderer/core/paint/nine_piece_image_grid.cc
@@ -5,8 +5,8 @@
 #include "third_party/blink/renderer/core/paint/nine_piece_image_grid.h"
 
 #include "base/numerics/clamped_math.h"
-#include "third_party/blink/renderer/platform/geometry/int_rect_outsets.h"
 #include "third_party/blink/renderer/platform/geometry/length_functions.h"
+#include "ui/gfx/geometry/outsets.h"
 
 namespace blink {
 
@@ -60,7 +60,7 @@
                                        const gfx::Vector2dF& slice_scale,
                                        float zoom,
                                        const gfx::Rect& border_image_area,
-                                       const IntRectOutsets& border_widths,
+                                       const gfx::Outsets& border_widths,
                                        PhysicalBoxSides sides_to_include)
     : border_image_area_(border_image_area),
       image_size_(image_size),
@@ -87,25 +87,25 @@
                                              zoom / slice_scale.y());
   const BorderImageLengthBox& border_slices = nine_piece_image.BorderSlices();
   top_.width = sides_to_include.top
-                   ? ComputeEdgeWidth(border_slices.Top(), border_widths.Top(),
+                   ? ComputeEdgeWidth(border_slices.Top(), border_widths.top(),
                                       top_.slice * auto_slice_adjustment.y(),
                                       border_image_area.height())
                    : 0;
   right_.width =
       sides_to_include.right
-          ? ComputeEdgeWidth(border_slices.Right(), border_widths.Right(),
+          ? ComputeEdgeWidth(border_slices.Right(), border_widths.right(),
                              right_.slice * auto_slice_adjustment.x(),
                              border_image_area.width())
           : 0;
   bottom_.width =
       sides_to_include.bottom
-          ? ComputeEdgeWidth(border_slices.Bottom(), border_widths.Bottom(),
+          ? ComputeEdgeWidth(border_slices.Bottom(), border_widths.bottom(),
                              bottom_.slice * auto_slice_adjustment.y(),
                              border_image_area.height())
           : 0;
   left_.width =
       sides_to_include.left
-          ? ComputeEdgeWidth(border_slices.Left(), border_widths.Left(),
+          ? ComputeEdgeWidth(border_slices.Left(), border_widths.left(),
                              left_.slice * auto_slice_adjustment.x(),
                              border_image_area.width())
           : 0;
diff --git a/third_party/blink/renderer/core/paint/nine_piece_image_grid.h b/third_party/blink/renderer/core/paint/nine_piece_image_grid.h
index 9f119c2..f2e15c5 100644
--- a/third_party/blink/renderer/core/paint/nine_piece_image_grid.h
+++ b/third_party/blink/renderer/core/paint/nine_piece_image_grid.h
@@ -14,9 +14,11 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/size_f.h"
 
-namespace blink {
+namespace gfx {
+class Outsets;
+}
 
-class IntRectOutsets;
+namespace blink {
 
 enum NinePiece {
   kMinPiece = 0,
@@ -67,7 +69,7 @@
                      const gfx::Vector2dF& slice_scale,
                      float zoom,
                      const gfx::Rect& border_image_area,
-                     const IntRectOutsets& border_widths,
+                     const gfx::Outsets& border_widths,
                      PhysicalBoxSides sides_to_include = PhysicalBoxSides());
 
   struct CORE_EXPORT NinePieceDrawInfo {
diff --git a/third_party/blink/renderer/core/paint/nine_piece_image_grid_test.cc b/third_party/blink/renderer/core/paint/nine_piece_image_grid_test.cc
index e26da4d..3bc33cc5 100644
--- a/third_party/blink/renderer/core/paint/nine_piece_image_grid_test.cc
+++ b/third_party/blink/renderer/core/paint/nine_piece_image_grid_test.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/style/nine_piece_image.h"
 #include "third_party/blink/renderer/core/style/style_generated_image.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
+#include "ui/gfx/geometry/outsets.h"
 
 namespace blink {
 namespace {
@@ -32,7 +33,7 @@
 
   gfx::SizeF image_size(100, 100);
   gfx::Rect border_image_area(0, 0, 100, 100);
-  IntRectOutsets border_widths(0, 0, 0, 0);
+  gfx::Outsets border_widths(0);
 
   NinePieceImageGrid grid =
       NinePieceImageGrid(nine_piece, image_size, gfx::Vector2dF(1, 1), 1,
@@ -52,7 +53,7 @@
 
   gfx::SizeF image_size(100, 100);
   gfx::Rect border_image_area(0, 0, 100, 100);
-  IntRectOutsets border_widths(10, 10, 10, 10);
+  gfx::Outsets border_widths(10);
 
   NinePieceImageGrid grid =
       NinePieceImageGrid(nine_piece, image_size, gfx::Vector2dF(1, 1), 1,
@@ -72,7 +73,7 @@
 
   gfx::SizeF image_size(100, 100);
   gfx::Rect border_image_area(0, 0, 100, 100);
-  IntRectOutsets border_widths(10, 10, 10, 10);
+  gfx::Outsets border_widths(10);
 
   NinePieceImageGrid grid =
       NinePieceImageGrid(nine_piece, image_size, gfx::Vector2dF(1, 1), 1,
@@ -96,7 +97,7 @@
 
   gfx::SizeF image_size(6, 6);
   gfx::Rect border_image_area(0, 0, 6, 6);
-  IntRectOutsets border_widths(3, 3, 3, 3);
+  gfx::Outsets border_widths(3);
 
   NinePieceImageGrid grid(nine_piece, image_size, gfx::Vector2dF(1, 1), 1,
                           border_image_area, border_widths);
@@ -119,13 +120,13 @@
   gfx::Rect border_image_area(0, 0, 100, 100);
 
   const struct {
-    IntRectOutsets border_widths;
+    gfx::Outsets border_widths;
     bool expected_is_drawable;
   } test_cases[] = {
-      {IntRectOutsets(0, 0, 0, 0), false},
-      {IntRectOutsets(10, 0, 0, 0), false},
-      {IntRectOutsets(0, 0, 0, 10), false},
-      {IntRectOutsets(10, 0, 0, 10), true},
+      {gfx::Outsets(), false},
+      {gfx::Outsets().set_top(10), false},
+      {gfx::Outsets().set_left(10), false},
+      {gfx::Outsets().set_top(10).set_left(10), true},
   };
 
   for (const auto& test_case : test_cases) {
@@ -148,7 +149,7 @@
 
   gfx::SizeF image_size(100, 100);
   gfx::Rect border_image_area(0, 0, 100, 100);
-  IntRectOutsets border_widths(10, 10, 10, 10);
+  gfx::Outsets border_widths(10);
 
   // Set border slices wide enough so that the widths are scaled
   // down and corner pieces cover the entire border image area.
@@ -210,7 +211,7 @@
   const struct {
     gfx::SizeF image_size;
     gfx::Rect border_image_area;
-    IntRectOutsets border_widths;
+    gfx::Outsets border_widths;
     bool fill;
     LengthBox image_slices;
     ENinePieceImageRule horizontal_rule;
@@ -229,7 +230,7 @@
       {// Empty border and slices but with fill
        gfx::SizeF(100, 100),
        gfx::Rect(0, 0, 100, 100),
-       IntRectOutsets(0, 0, 0, 0),
+       gfx::Outsets(0),
        true,
        LengthBox(Length::Fixed(0), Length::Fixed(0), Length::Fixed(0),
                  Length::Fixed(0)),
@@ -258,7 +259,7 @@
       {// Single border and fill
        gfx::SizeF(100, 100),
        gfx::Rect(0, 0, 100, 100),
-       IntRectOutsets(0, 0, 10, 0),
+       gfx::Outsets().set_bottom(10),
        true,
        LengthBox(Length::Percent(20), Length::Percent(20), Length::Percent(20),
                  Length::Percent(20)),
@@ -287,7 +288,7 @@
       {// All borders, no fill
        gfx::SizeF(100, 100),
        gfx::Rect(0, 0, 100, 100),
-       IntRectOutsets(10, 10, 10, 10),
+       gfx::Outsets(10),
        false,
        LengthBox(Length::Percent(20), Length::Percent(20), Length::Percent(20),
                  Length::Percent(20)),
@@ -316,7 +317,7 @@
       {// Single border, no fill
        gfx::SizeF(100, 100),
        gfx::Rect(0, 0, 100, 100),
-       IntRectOutsets(0, 0, 0, 10),
+       gfx::Outsets().set_left(10),
        false,
        LengthBox(Length::Percent(20), Length::Percent(20), Length::Percent(20),
                  Length::Percent(20)),
@@ -346,7 +347,7 @@
        // vertically)
        gfx::SizeF(100, 100),
        gfx::Rect(0, 0, 100, 100),
-       IntRectOutsets(10, 10, 10, 10),
+       gfx::Outsets(10),
        true,
        LengthBox(Length::Fixed(0), Length::Fixed(0), Length::Fixed(0),
                  Length::Fixed(0)),
@@ -432,7 +433,7 @@
 
   gfx::SizeF image_size(50, 50);
   gfx::Rect border_image_area(0, 0, 200, 200);
-  IntRectOutsets border_widths(20, 20, 20, 20);
+  gfx::Outsets border_widths(20);
 
   NinePieceImageGrid grid(nine_piece, image_size, gfx::Vector2dF(2, 2), 2,
                           border_image_area, border_widths);
@@ -495,7 +496,7 @@
   constexpr float zoom = 2.2f;
   gfx::SizeF image_size(3 * zoom, 3 * zoom);
   gfx::Rect border_image_area(0, 0, 220, 220);
-  IntRectOutsets border_widths(33, 33, 33, 33);
+  gfx::Outsets border_widths(33);
 
   NinePieceImageGrid grid(nine_piece, image_size, gfx::Vector2dF(zoom, zoom),
                           zoom, border_image_area, border_widths);
diff --git a/third_party/blink/renderer/core/paint/nine_piece_image_painter.cc b/third_party/blink/renderer/core/paint/nine_piece_image_painter.cc
index 06956f8..e300e129b 100644
--- a/third_party/blink/renderer/core/paint/nine_piece_image_painter.cc
+++ b/third_party/blink/renderer/core/paint/nine_piece_image_painter.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/style/nine_piece_image.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/scoped_interpolation_quality.h"
+#include "ui/gfx/geometry/outsets.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace blink {
@@ -105,9 +106,11 @@
       image_size.width() / unzoomed_image_size.width(),
       image_size.height() / unzoomed_image_size.height());
 
-  IntRectOutsets border_widths(
-      style.BorderTopWidth().ToInt(), style.BorderRightWidth().ToInt(),
-      style.BorderBottomWidth().ToInt(), style.BorderLeftWidth().ToInt());
+  auto border_widths = gfx::Outsets()
+                           .set_left_right(style.BorderLeftWidth().ToInt(),
+                                           style.BorderRightWidth().ToInt())
+                           .set_top_bottom(style.BorderTopWidth().ToInt(),
+                                           style.BorderBottomWidth().ToInt());
   NinePieceImageGrid grid(
       nine_piece_image, image_size, slice_scale, style.EffectiveZoom(),
       ToPixelSnappedRect(border_image_rect), border_widths, sides_to_include);
diff --git a/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc b/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
index 9aeb9e8..66eeb8a 100644
--- a/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
@@ -831,10 +831,6 @@
 }
 
 TEST_P(PaintAndRasterInvalidationTest, PaintPropertyChange) {
-  // TODO(wangxianzhu): See the TODO in CullRectUpdater::SetFragmentCullRects().
-  if (RuntimeEnabledFeatures::CullRectUpdateEnabled())
-    return;
-
   SetUpHTML(*this);
   Element* target = GetDocument().getElementById("target");
   auto* object = target->GetLayoutObject();
diff --git a/third_party/blink/renderer/core/paint/paint_controller_paint_test.cc b/third_party/blink/renderer/core/paint/paint_controller_paint_test.cc
index 7e71d18..693e99a 100644
--- a/third_party/blink/renderer/core/paint/paint_controller_paint_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_controller_paint_test.cc
@@ -215,10 +215,8 @@
   auto& div3 = *GetLayoutObjectByElementId("div3");
   auto& div4 = *GetLayoutObjectByElementId("div4");
 
-  if (RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
-    EXPECT_EQ(gfx::Rect(0, 0, 4200, 4200),
-              container.FirstFragment().GetContentsCullRect().Rect());
-  }
+  EXPECT_EQ(gfx::Rect(0, 0, 4200, 4200),
+            container.FirstFragment().GetContentsCullRect().Rect());
   EXPECT_THAT(ContentDisplayItems(),
               ElementsAre(VIEW_SCROLLING_BACKGROUND_DISPLAY_ITEM,
                           IsSameId(div1.Id(), kBackgroundType),
@@ -249,10 +247,8 @@
       ScrollOffset(5000, 5000), mojom::blink::ScrollType::kProgrammatic);
   UpdateAllLifecyclePhasesForTest();
 
-  if (RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
-    EXPECT_EQ(gfx::Rect(1000, 1000, 8100, 8100),
-              container.FirstFragment().GetContentsCullRect().Rect());
-  }
+  EXPECT_EQ(gfx::Rect(1000, 1000, 8100, 8100),
+            container.FirstFragment().GetContentsCullRect().Rect());
   EXPECT_THAT(ContentDisplayItems(),
               ElementsAre(VIEW_SCROLLING_BACKGROUND_DISPLAY_ITEM,
                           IsSameId(div2.Id(), kBackgroundType),
diff --git a/third_party/blink/renderer/core/paint/paint_controller_paint_test.h b/third_party/blink/renderer/core/paint/paint_controller_paint_test.h
index 81e9dec..be0066775 100644
--- a/third_party/blink/renderer/core/paint/paint_controller_paint_test.h
+++ b/third_party/blink/renderer/core/paint/paint_controller_paint_test.h
@@ -47,8 +47,7 @@
         DocumentUpdateReason::kTest);
     // Run CullRectUpdater to ease testing of cull rects and repaint flags of
     // PaintLayers on cull rect change.
-    if (RuntimeEnabledFeatures::CullRectUpdateEnabled())
-      CullRectUpdater(*GetLayoutView().Layer()).Update();
+    CullRectUpdater(*GetLayoutView().Layer()).Update();
   }
 
   void PaintContents(const gfx::Rect& interest_rect) {
diff --git a/third_party/blink/renderer/core/paint/paint_flags.h b/third_party/blink/renderer/core/paint/paint_flags.h
new file mode 100644
index 0000000..971ff8fb
--- /dev/null
+++ b/third_party/blink/renderer/core/paint/paint_flags.h
@@ -0,0 +1,42 @@
+// Copyright 2022 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_CORE_PAINT_PAINT_FLAGS_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_FLAGS_H_
+
+namespace blink {
+
+// Using an anonymous enum under a namespace instead of an enum class to allow
+// bitwise operations with unsigned PaintFlags.
+namespace PaintFlag {
+enum : unsigned {
+  kNoFlag = 0,
+
+  // Used when painting selection as part of a drag-image. This flag disables
+  // a lot of the painting code and specifically triggers a
+  // PaintPhase::kSelectionDragImage.
+  kSelectionDragImageOnly = 1 << 0,
+
+  // Used when painting a drag-image, printing, etc. when we won't use the
+  // information for creating compositing layers.
+  kOmitCompositingInfo = 1 << 1,
+
+  // Used when printing or painting a preview to in order to add URL
+  // metadata for links.
+  kAddUrlMetadata = 1 << 2,
+
+  // Used to paint a mask-based clip-path.
+  kPaintingClipPathAsMask = 1 << 3,
+
+  // Used to paint SVG resource subtree for masks, filter images, etc.
+  kPaintingResourceSubtree = 1 << 4,
+};
+}  // namespace PaintFlag
+
+// Combination of bits under PaintFlag.
+using PaintFlags = unsigned;
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_FLAGS_H_
diff --git a/third_party/blink/renderer/core/paint/paint_info.h b/third_party/blink/renderer/core/paint/paint_info.h
index c804ee9..290f1ec5e 100644
--- a/third_party/blink/renderer/core/paint/paint_info.h
+++ b/third_party/blink/renderer/core/paint/paint_info.h
@@ -30,20 +30,13 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
-// TODO(jchaffraix): Once we unify PaintBehavior and PaintLayerFlags, we should
-// move PaintLayerFlags to PaintPhase and rename it. Thus removing the need for
-// this #include
-// "third_party/blink/renderer/core/paint/paint_layer_painting_info.h"
-#include "third_party/blink/renderer/core/paint/paint_layer_painting_info.h"
+#include "third_party/blink/renderer/core/paint/paint_flags.h"
 #include "third_party/blink/renderer/core/paint/paint_phase.h"
 #include "third_party/blink/renderer/platform/geometry/layout_rect.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
-#include "third_party/blink/renderer/platform/graphics/image.h"
 #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
 #include "third_party/blink/renderer/platform/graphics/paint/display_item.h"
-#include "third_party/blink/renderer/platform/transforms/affine_transform.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-#include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace blink {
@@ -55,13 +48,11 @@
   PaintInfo(GraphicsContext& context,
             const CullRect& cull_rect,
             PaintPhase phase,
-            GlobalPaintFlags global_paint_flags,
-            PaintLayerFlags paint_flags)
+            PaintFlags paint_flags = PaintFlag::kNoFlag)
       : context(context),
         phase(phase),
         cull_rect_(cull_rect),
-        paint_flags_(paint_flags),
-        global_paint_flags_(global_paint_flags) {}
+        paint_flags_(paint_flags) {}
 
   PaintInfo(GraphicsContext& new_context,
             const PaintInfo& copy_other_fields_from)
@@ -69,10 +60,10 @@
         phase(copy_other_fields_from.phase),
         cull_rect_(copy_other_fields_from.cull_rect_),
         fragment_id_(copy_other_fields_from.fragment_id_),
-        paint_flags_(copy_other_fields_from.paint_flags_),
-        global_paint_flags_(copy_other_fields_from.global_paint_flags_) {
-    // We should never pass the flag to other PaintInfo.
+        paint_flags_(copy_other_fields_from.paint_flags_) {
+    // We should never pass these flags to other PaintInfo.
     DCHECK(!copy_other_fields_from.is_painting_background_in_contents_space);
+    DCHECK(!copy_other_fields_from.skips_background_);
   }
 
   // Creates a PaintInfo for painting descendants. See comments about the paint
@@ -90,34 +81,29 @@
     return result;
   }
 
+  bool ShouldOmitCompositingInfo() const {
+    return paint_flags_ & PaintFlag::kOmitCompositingInfo;
+  }
+
   bool IsRenderingClipPathAsMaskImage() const {
-    return paint_flags_ & kPaintLayerPaintingRenderingClipPathAsMask;
+    return paint_flags_ & PaintFlag::kPaintingClipPathAsMask;
   }
   bool IsRenderingResourceSubtree() const {
-    return paint_flags_ & kPaintLayerPaintingRenderingResourceSubtree;
+    return paint_flags_ & PaintFlag::kPaintingResourceSubtree;
   }
 
-  bool ShouldSkipBackground() const {
-    return paint_flags_ & kPaintLayerPaintingSkipBackground;
-  }
-  void SetSkipsBackground(bool b) {
-    if (b)
-      paint_flags_ |= kPaintLayerPaintingSkipBackground;
-    else
-      paint_flags_ &= ~kPaintLayerPaintingSkipBackground;
-  }
+  bool ShouldSkipBackground() const { return skips_background_; }
+  void SetSkipsBackground(bool b) { skips_background_ = b; }
 
   bool ShouldAddUrlMetadata() const {
-    return global_paint_flags_ & kGlobalPaintAddUrlMetadata;
+    return paint_flags_ & PaintFlag::kAddUrlMetadata;
   }
 
   DisplayItem::Type DisplayItemTypeForClipping() const {
     return DisplayItem::PaintPhaseToClipType(phase);
   }
 
-  GlobalPaintFlags GetGlobalPaintFlags() const { return global_paint_flags_; }
-
-  PaintLayerFlags PaintFlags() const { return paint_flags_; }
+  PaintFlags GetPaintFlags() const { return paint_flags_; }
 
   const CullRect& GetCullRect() const { return cull_rect_; }
   void SetCullRect(const CullRect& cull_rect) { cull_rect_ = cull_rect; }
@@ -196,8 +182,6 @@
     descendant_painting_blocked_ = blocked;
   }
 
-  // FIXME: Introduce setters/getters at some point. Requires a lot of changes
-  // throughout paint/.
   GraphicsContext& context;
   PaintPhase phase;
 
@@ -212,18 +196,15 @@
   // NGPhysicalFragment to FragmentData in such cases).
   wtf_size_t fragment_id_ = WTF::kNotFound;
 
-  PaintLayerFlags paint_flags_;
-  const GlobalPaintFlags global_paint_flags_;
+  const PaintFlags paint_flags_;
 
-  // For CAP only.
   bool is_painting_background_in_contents_space = false;
+  bool skips_background_ = false;
 
   // Used by display-locking.
   bool descendant_painting_blocked_ = false;
 };
 
-Image::ImageDecodingMode GetImageDecodingMode(Node*);
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_INFO_H_
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 4d44b67..010aa3de 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -2783,8 +2783,6 @@
 }
 
 void PaintLayer::SetNeedsCullRectUpdate() {
-  DCHECK(RuntimeEnabledFeatures::CullRectUpdateEnabled());
-
   if (needs_cull_rect_update_)
     return;
   needs_cull_rect_update_ = true;
@@ -2792,8 +2790,6 @@
 }
 
 void PaintLayer::SetForcesChildrenCullRectUpdate() {
-  DCHECK(RuntimeEnabledFeatures::CullRectUpdateEnabled());
-
   if (forces_children_cull_rect_update_)
     return;
   forces_children_cull_rect_update_ = true;
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h
index 17672e9..29a7a4f 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.h
+++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -586,23 +586,17 @@
   void SetDescendantNeedsRepaint();
   void ClearNeedsRepaintRecursively();
 
-  bool NeedsCullRectUpdate() const {
-    DCHECK(RuntimeEnabledFeatures::CullRectUpdateEnabled());
-    return needs_cull_rect_update_;
-  }
+  bool NeedsCullRectUpdate() const { return needs_cull_rect_update_; }
   bool ForcesChildrenCullRectUpdate() const {
-    DCHECK(RuntimeEnabledFeatures::CullRectUpdateEnabled());
     return forces_children_cull_rect_update_;
   }
   bool DescendantNeedsCullRectUpdate() const {
-    DCHECK(RuntimeEnabledFeatures::CullRectUpdateEnabled());
     return descendant_needs_cull_rect_update_;
   }
   void SetNeedsCullRectUpdate();
   void SetForcesChildrenCullRectUpdate();
   void MarkCompositingContainerChainForNeedsCullRectUpdate();
   void ClearNeedsCullRectUpdate() {
-    DCHECK(RuntimeEnabledFeatures::CullRectUpdateEnabled());
     needs_cull_rect_update_ = false;
     forces_children_cull_rect_update_ = false;
     descendant_needs_cull_rect_update_ = false;
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter.cc b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
index 441d81af..cd25934 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
@@ -30,13 +30,6 @@
 
 namespace blink {
 
-void PaintLayerPainter::Paint(GraphicsContext& context,
-                              const GlobalPaintFlags global_paint_flags,
-                              PaintLayerFlags paint_flags) {
-  PaintLayerPaintingInfo painting_info(&paint_layer_, global_paint_flags);
-  Paint(context, painting_info, paint_flags);
-}
-
 bool PaintLayerPainter::PaintedOutputInvisible(const ComputedStyle& style) {
   if (style.HasNonInitialBackdropFilter())
     return false;
@@ -80,10 +73,8 @@
   return contents_visual_rect;
 }
 
-PaintResult PaintLayerPainter::Paint(
-    GraphicsContext& context,
-    const PaintLayerPaintingInfo& painting_info,
-    PaintLayerFlags paint_flags) {
+PaintResult PaintLayerPainter::Paint(GraphicsContext& context,
+                                     PaintFlags paint_flags) {
   const LayoutObject& layout_object = paint_layer_.GetLayoutObject();
   if (UNLIKELY(layout_object.NeedsLayout() &&
                !layout_object.ChildLayoutBlockedByDisplayLock())) {
@@ -115,13 +106,12 @@
     return kFullyPainted;
   }
 
-  return PaintLayerContents(context, painting_info, paint_flags);
+  return PaintLayerContents(context, paint_flags);
 }
 
-static bool ShouldCreateSubsequence(
-    const PaintLayer& paint_layer,
-    const GraphicsContext& context,
-    const PaintLayerPaintingInfo& painting_info) {
+static bool ShouldCreateSubsequence(const PaintLayer& paint_layer,
+                                    const GraphicsContext& context,
+                                    PaintFlags paint_flags) {
   // Caching is not needed during printing or painting previews.
   if (paint_layer.GetLayoutObject().GetDocument().IsPrintingOrPaintingPreview())
     return false;
@@ -134,17 +124,13 @@
 
   // Don't create subsequence during special painting to avoid cache conflict
   // with normal painting.
-  if (painting_info.GetGlobalPaintFlags() &
-      kGlobalPaintFlattenCompositingLayers)
+  if (paint_flags & PaintFlag::kOmitCompositingInfo)
     return false;
 
   return true;
 }
 
 static bool IsUnclippedLayoutView(const PaintLayer& layer) {
-  // If MainFrameClipsContent is false which means that WebPreferences::
-  // record_whole_document is true, we should not cull the scrolling contents
-  // of the main frame.
   if (IsA<LayoutView>(layer.GetLayoutObject())) {
     const auto* frame = layer.GetLayoutObject().GetFrame();
     if (frame && !frame->ClipsContent())
@@ -154,12 +140,10 @@
 }
 
 bool PaintLayerPainter::ShouldUseInfiniteCullRect() {
-  return ShouldUseInfiniteCullRectInternal(kGlobalPaintNormalPhase,
-                                           /*for_cull_rect_update*/ true);
+  return ShouldUseInfiniteCullRectInternal(/*for_cull_rect_update*/ true);
 }
 
 bool PaintLayerPainter::ShouldUseInfiniteCullRectInternal(
-    GlobalPaintFlags global_flags,
     bool for_cull_rect_update) {
   bool is_printing = paint_layer_.GetLayoutObject().GetDocument().Printing();
   if (IsUnclippedLayoutView(paint_layer_) && !is_printing)
@@ -235,50 +219,6 @@
   return false;
 }
 
-void PaintLayerPainter::AdjustForPaintProperties(
-    const GraphicsContext& context,
-    PaintLayerPaintingInfo& painting_info,
-    PaintLayerFlags& paint_flags) {
-  const auto& first_fragment = paint_layer_.GetLayoutObject().FirstFragment();
-
-  bool should_use_infinite_cull_rect =
-      ShouldUseInfiniteCullRectInternal(painting_info.GetGlobalPaintFlags(),
-                                        /*for_cull_rect_update*/ false);
-  if (should_use_infinite_cull_rect) {
-    // Avoid clipping during CollectFragments.
-    if (IsUnclippedLayoutView(paint_layer_))
-      paint_flags |= kPaintLayerPaintingOverflowContents;
-  }
-
-  if (painting_info.root_layer == &paint_layer_)
-    return;
-
-  if (!should_use_infinite_cull_rect) {
-    // painting_info.cull_rect is currently in |painting_info.root_layer|'s
-    // pixel-snapped border box space. We need to adjust it into
-    // |paint_layer_|'s space. This handles the following cases:
-    // - The current layer has PaintOffsetTranslation;
-    // - The current layer's transform state escapes the root layers contents
-    //   transform, e.g. a fixed-position layer;
-    // - Scroll offsets.
-    const auto& first_root_fragment =
-        painting_info.root_layer->GetLayoutObject().FirstFragment();
-    const auto* source_transform =
-        &first_root_fragment.LocalBorderBoxProperties().Transform();
-    const auto& destination_transform =
-        first_fragment.LocalBorderBoxProperties().Transform();
-    if (source_transform == &destination_transform)
-      return;
-  }
-
-  // We reach here if the layer requires infinite cull rect or has different
-  // transform space from the current root layer. Use the current layer as
-  // the new root layer.
-  painting_info.root_layer = &paint_layer_;
-  // This flag no longer applies for the new root layer.
-  paint_flags &= ~kPaintLayerPaintingOverflowContents;
-}
-
 static gfx::Rect FirstFragmentVisualRect(const LayoutBoxModelObject& object) {
   // We don't want to include overflowing contents.
   PhysicalRect overflow_rect =
@@ -288,10 +228,8 @@
   return ToEnclosingRect(overflow_rect);
 }
 
-PaintResult PaintLayerPainter::PaintLayerContents(
-    GraphicsContext& context,
-    const PaintLayerPaintingInfo& painting_info_arg,
-    PaintLayerFlags paint_flags_arg) {
+PaintResult PaintLayerPainter::PaintLayerContents(GraphicsContext& context,
+                                                  PaintFlags paint_flags) {
   DCHECK(paint_layer_.IsSelfPaintingLayer() ||
          paint_layer_.HasSelfPaintingLayerDescendant());
 
@@ -309,8 +247,8 @@
     return kMayBeClippedByCullRect;
   }
 
-  bool selection_drag_image_only = painting_info_arg.GetGlobalPaintFlags() &
-                                   kGlobalPaintSelectionDragImageOnly;
+  bool selection_drag_image_only =
+      paint_flags & PaintFlag::kSelectionDragImageOnly;
   if (selection_drag_image_only && !object.IsSelected())
     return result;
 
@@ -330,26 +268,17 @@
   IgnorePaintTimingScope::SetIsDocumentElementInvisible(
       is_document_element_invisible);
 
-  PaintLayerFlags paint_flags = paint_flags_arg;
-  PaintLayerPaintingInfo painting_info = painting_info_arg;
-  AdjustForPaintProperties(context, painting_info, paint_flags);
-
   bool is_self_painting_layer = paint_layer_.IsSelfPaintingLayer();
-  bool is_painting_overlay_overflow_controls =
-      paint_flags & kPaintLayerPaintingOverlayOverflowControls;
-  bool is_painting_overflow_contents =
-      paint_flags & kPaintLayerPaintingOverflowContents;
+  bool is_unclipped_layout_view = IsUnclippedLayoutView(paint_layer_);
 
   bool should_paint_content =
       paint_layer_.HasVisibleContent() &&
       // Content under a LayoutSVGHiddenContainer is auxiliary resources for
       // painting. Foreign content should never paint in this situation, as it
       // is primary, not auxiliary.
-      !paint_layer_.IsUnderSVGHiddenContainer() && is_self_painting_layer &&
-      !is_painting_overlay_overflow_controls;
+      !paint_layer_.IsUnderSVGHiddenContainer() && is_self_painting_layer;
 
-  if (object.FirstFragment().NextFragment() ||
-      IsUnclippedLayoutView(paint_layer_)) {
+  if (object.FirstFragment().NextFragment() || is_unclipped_layout_view) {
     result = kMayBeClippedByCullRect;
   } else {
     gfx::Rect visual_rect = FirstFragmentVisualRect(object);
@@ -373,7 +302,7 @@
     }
 
     if (!cull_rect_intersects_self && !cull_rect_intersects_contents) {
-      if (!is_painting_overflow_contents && paint_layer_.KnownToClipSubtree()) {
+      if (!is_unclipped_layout_view && paint_layer_.KnownToClipSubtree()) {
         paint_layer_.SetPreviousPaintResult(kMayBeClippedByCullRect);
         return kMayBeClippedByCullRect;
       }
@@ -384,11 +313,9 @@
     // Will update in ScopedBoxContentsPaintState.
   }
 
-  PaintLayerPaintingInfo local_painting_info(painting_info);
-
   bool should_create_subsequence =
       should_paint_content &&
-      ShouldCreateSubsequence(paint_layer_, context, painting_info);
+      ShouldCreateSubsequence(paint_layer_, context, paint_flags);
   absl::optional<SubsequenceRecorder> subsequence_recorder;
   if (should_create_subsequence) {
     if (!paint_layer_.SelfOrDescendantNeedsRepaint() &&
@@ -417,16 +344,12 @@
   bool should_paint_background =
       should_paint_content && !selection_drag_image_only;
   if (should_paint_background) {
-    PaintWithPhase(PaintPhase::kSelfBlockBackgroundOnly, context,
-                   local_painting_info, paint_flags);
+    PaintWithPhase(PaintPhase::kSelfBlockBackgroundOnly, context, paint_flags);
   }
 
-  bool should_paint_children = !is_painting_overlay_overflow_controls;
-  if (should_paint_children) {
-    if (PaintChildren(kNegativeZOrderChildren, context, painting_info,
-                      paint_flags) == kMayBeClippedByCullRect)
-      result = kMayBeClippedByCullRect;
-  }
+  if (PaintChildren(kNegativeZOrderChildren, context, paint_flags) ==
+      kMayBeClippedByCullRect)
+    result = kMayBeClippedByCullRect;
 
   if (should_paint_content) {
     // If the negative-z-order children created paint chunks, this gives the
@@ -437,51 +360,43 @@
         DisplayItem::kLayerChunkForeground);
 
     if (selection_drag_image_only) {
-      PaintWithPhase(PaintPhase::kSelectionDragImage, context,
-                     local_painting_info, paint_flags);
+      PaintWithPhase(PaintPhase::kSelectionDragImage, context, paint_flags);
     } else {
-      PaintForegroundPhases(context, local_painting_info, paint_flags);
+      PaintForegroundPhases(context, paint_flags);
     }
   }
 
   // Outline always needs to be painted even if we have no visible content.
-  bool should_paint_self_outline = is_self_painting_layer &&
-                                   !is_painting_overlay_overflow_controls &&
-                                   object.StyleRef().HasOutline();
+  bool should_paint_self_outline =
+      is_self_painting_layer && object.StyleRef().HasOutline();
 
   bool is_video = IsA<LayoutVideo>(object);
-  if (!is_video && should_paint_self_outline) {
-    PaintWithPhase(PaintPhase::kSelfOutlineOnly, context, local_painting_info,
-                   paint_flags);
-  }
+  if (!is_video && should_paint_self_outline)
+    PaintWithPhase(PaintPhase::kSelfOutlineOnly, context, paint_flags);
 
-  if (should_paint_children) {
-    if (PaintChildren(kNormalFlowAndPositiveZOrderChildren, context,
-                      painting_info, paint_flags) == kMayBeClippedByCullRect)
-      result = kMayBeClippedByCullRect;
-  }
+  if (PaintChildren(kNormalFlowAndPositiveZOrderChildren, context,
+                    paint_flags) == kMayBeClippedByCullRect)
+    result = kMayBeClippedByCullRect;
 
   if (paint_layer_.GetScrollableArea() &&
       paint_layer_.GetScrollableArea()
           ->ShouldOverflowControlsPaintAsOverlay()) {
-    if (is_painting_overlay_overflow_controls ||
-        !paint_layer_.NeedsReorderOverlayOverflowControls()) {
-      PaintOverlayOverflowControls(context, local_painting_info, paint_flags);
-    }
+    if (!paint_layer_.NeedsReorderOverlayOverflowControls())
+      PaintOverlayOverflowControls(context, paint_flags);
+    // Otherwise the overlay overflow controls will be painted after scrolling
+    // children in PaintChildren().
   }
 
   if (is_video && should_paint_self_outline) {
     // We paint outlines for video later so that they aren't obscured by the
     // video controls.
-    PaintWithPhase(PaintPhase::kSelfOutlineOnly, context, local_painting_info,
-                   paint_flags);
+    PaintWithPhase(PaintPhase::kSelfOutlineOnly, context, paint_flags);
   }
 
   if (should_paint_content && !selection_drag_image_only) {
     if (const auto* properties = object.FirstFragment().PaintProperties()) {
       if (properties->Mask()) {
-        PaintWithPhase(PaintPhase::kMask, context, local_painting_info,
-                       paint_flags);
+        PaintWithPhase(PaintPhase::kMask, context, paint_flags);
       }
       if (properties->ClipPathMask())
         ClipPathClipper::PaintClipPathAsMaskImage(context, object, object);
@@ -495,8 +410,7 @@
 PaintResult PaintLayerPainter::PaintChildren(
     PaintLayerIteration children_to_visit,
     GraphicsContext& context,
-    const PaintLayerPaintingInfo& painting_info,
-    PaintLayerFlags paint_flags) {
+    PaintFlags paint_flags) {
   PaintResult result = kFullyPainted;
   if (!paint_layer_.HasSelfPaintingLayerDescendant())
     return result;
@@ -509,7 +423,7 @@
     if (child->IsReplacedNormalFlowStacking())
       continue;
 
-    if (PaintLayerPainter(*child).Paint(context, painting_info, paint_flags) ==
+    if (PaintLayerPainter(*child).Paint(context, paint_flags) ==
         kMayBeClippedByCullRect)
       result = kMayBeClippedByCullRect;
 
@@ -519,11 +433,12 @@
            *layers_painting_overlay_overflow_controls_after) {
         DCHECK(reparent_overflow_controls_layer
                    ->NeedsReorderOverlayOverflowControls());
-        if (PaintLayerPainter(*reparent_overflow_controls_layer)
-                .Paint(context, painting_info,
-                       kPaintLayerPaintingOverlayOverflowControls) ==
-            kMayBeClippedByCullRect)
+        PaintLayerPainter(*reparent_overflow_controls_layer)
+            .PaintOverlayOverflowControls(context, paint_flags);
+        if (reparent_overflow_controls_layer->PreviousPaintResult() ==
+            kMayBeClippedByCullRect) {
           result = kMayBeClippedByCullRect;
+        }
       }
     }
   }
@@ -531,22 +446,12 @@
   return result;
 }
 
-void PaintLayerPainter::PaintOverlayOverflowControls(
-    GraphicsContext& context,
-    const PaintLayerPaintingInfo& painting_info,
-    PaintLayerFlags paint_flags) {
+void PaintLayerPainter::PaintOverlayOverflowControls(GraphicsContext& context,
+                                                     PaintFlags paint_flags) {
+  DCHECK(paint_layer_.GetScrollableArea());
   DCHECK(
-      paint_layer_.GetScrollableArea() &&
       paint_layer_.GetScrollableArea()->ShouldOverflowControlsPaintAsOverlay());
-
-  // We don't need to paint composited overflow controls.
-  if (paint_layer_.GetScrollableArea()->HasLayerForHorizontalScrollbar() ||
-      paint_layer_.GetScrollableArea()->HasLayerForVerticalScrollbar() ||
-      paint_layer_.GetScrollableArea()->HasLayerForScrollCorner())
-    return;
-
-  PaintWithPhase(PaintPhase::kOverlayOverflowControls, context, painting_info,
-                 paint_flags);
+  PaintWithPhase(PaintPhase::kOverlayOverflowControls, context, paint_flags);
 }
 
 void PaintLayerPainter::PaintFragmentWithPhase(
@@ -554,8 +459,7 @@
     const FragmentData& fragment_data,
     const NGPhysicalBoxFragment* physical_fragment,
     GraphicsContext& context,
-    const PaintLayerPaintingInfo& painting_info,
-    PaintLayerFlags paint_flags) {
+    PaintFlags paint_flags) {
   DCHECK(paint_layer_.IsSelfPaintingLayer());
 
   CullRect cull_rect = fragment_data.GetCullRect();
@@ -573,8 +477,7 @@
       context.GetPaintController(), chunk_properties, paint_layer_,
       DisplayItem::PaintPhaseToDrawingType(phase));
 
-  PaintInfo paint_info(context, cull_rect, phase,
-                       painting_info.GetGlobalPaintFlags(), paint_flags);
+  PaintInfo paint_info(context, cull_rect, phase, paint_flags);
   if (paint_layer_.GetLayoutObject().ChildPaintBlockedByDisplayLock())
     paint_info.SetDescendantPaintingBlocked(true);
 
@@ -586,11 +489,9 @@
   }
 }
 
-void PaintLayerPainter::PaintWithPhase(
-    PaintPhase phase,
-    GraphicsContext& context,
-    const PaintLayerPaintingInfo& local_painting_info,
-    PaintLayerFlags paint_flags) {
+void PaintLayerPainter::PaintWithPhase(PaintPhase phase,
+                                       GraphicsContext& context,
+                                       PaintFlags paint_flags) {
   const auto* layout_box_with_fragments =
       paint_layer_.GetLayoutBoxWithBlockFragments();
   wtf_size_t fragment_idx = 0u;
@@ -608,35 +509,30 @@
       scoped_display_item_fragment.emplace(context, fragment_idx);
 
     PaintFragmentWithPhase(phase, *fragment, physical_fragment, context,
-                           local_painting_info, paint_flags);
+                           paint_flags);
   }
 }
 
-void PaintLayerPainter::PaintForegroundPhases(
-    GraphicsContext& context,
-    const PaintLayerPaintingInfo& local_painting_info,
-    PaintLayerFlags paint_flags) {
+void PaintLayerPainter::PaintForegroundPhases(GraphicsContext& context,
+                                              PaintFlags paint_flags) {
   PaintWithPhase(PaintPhase::kDescendantBlockBackgroundsOnly, context,
-                 local_painting_info, paint_flags);
+                 paint_flags);
 
   if (paint_layer_.GetLayoutObject().GetDocument().InForcedColorsMode()) {
     PaintWithPhase(PaintPhase::kForcedColorsModeBackplate, context,
-                   local_painting_info, paint_flags);
+                   paint_flags);
   }
 
   if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() ||
       paint_layer_.NeedsPaintPhaseFloat()) {
-    PaintWithPhase(PaintPhase::kFloat, context, local_painting_info,
-                   paint_flags);
+    PaintWithPhase(PaintPhase::kFloat, context, paint_flags);
   }
 
-  PaintWithPhase(PaintPhase::kForeground, context, local_painting_info,
-                 paint_flags);
+  PaintWithPhase(PaintPhase::kForeground, context, paint_flags);
 
   if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() ||
       paint_layer_.NeedsPaintPhaseDescendantOutlines()) {
-    PaintWithPhase(PaintPhase::kDescendantOutlinesOnly, context,
-                   local_painting_info, paint_flags);
+    PaintWithPhase(PaintPhase::kDescendantOutlinesOnly, context, paint_flags);
   }
 }
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter.h b/third_party/blink/renderer/core/paint/paint_layer_painter.h
index a16f0f0..615f1ad 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter.h
@@ -6,9 +6,9 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_PAINTER_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/paint/paint_flags.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_fragment.h"
-#include "third_party/blink/renderer/core/paint/paint_layer_painting_info.h"
 #include "third_party/blink/renderer/core/paint/paint_result.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
@@ -31,19 +31,11 @@
 
   // Paints the layers from back to front. It assumes that the caller will
   // clip to the bounds of damage rect if necessary.
-  void Paint(GraphicsContext&,
-             const GlobalPaintFlags = kGlobalPaintNormalPhase,
-             PaintLayerFlags = kPaintLayerNoFlag);
-  // Paint() assumes that the caller will clip to the bounds of the painting
-  // dirty if necessary.
-  PaintResult Paint(GraphicsContext&,
-                    const PaintLayerPaintingInfo&,
-                    PaintLayerFlags);
+  PaintResult Paint(GraphicsContext&, PaintFlags = PaintFlag::kNoFlag);
   // PaintLayerContents() assumes that the caller will clip to the bounds of the
   // painting dirty rect if necessary.
   PaintResult PaintLayerContents(GraphicsContext&,
-                                 const PaintLayerPaintingInfo&,
-                                 PaintLayerFlags);
+                                 PaintFlags = PaintFlag::kNoFlag);
 
   // Returns true if the painted output of this PaintLayer and its children is
   // invisible and therefore can't impact painted output.
@@ -53,7 +45,6 @@
   // contents.
   static PhysicalRect ContentsVisualRect(const FragmentData&, const LayoutBox&);
 
-  // For CullRectUpdate.
   bool ShouldUseInfiniteCullRect();
 
  private:
@@ -61,30 +52,17 @@
 
   PaintResult PaintChildren(PaintLayerIteration children_to_visit,
                             GraphicsContext&,
-                            const PaintLayerPaintingInfo&,
-                            PaintLayerFlags);
+                            PaintFlags);
   void PaintFragmentWithPhase(PaintPhase,
                               const FragmentData&,
                               const NGPhysicalBoxFragment*,
                               GraphicsContext&,
-                              const PaintLayerPaintingInfo&,
-                              PaintLayerFlags);
-  void PaintWithPhase(PaintPhase,
-                      GraphicsContext&,
-                      const PaintLayerPaintingInfo&,
-                      PaintLayerFlags);
-  void PaintForegroundPhases(GraphicsContext&,
-                             const PaintLayerPaintingInfo&,
-                             PaintLayerFlags);
-  void PaintOverlayOverflowControls(GraphicsContext&,
-                                    const PaintLayerPaintingInfo&,
-                                    PaintLayerFlags);
+                              PaintFlags);
+  void PaintWithPhase(PaintPhase, GraphicsContext&, PaintFlags);
+  void PaintForegroundPhases(GraphicsContext&, PaintFlags);
+  void PaintOverlayOverflowControls(GraphicsContext&, PaintFlags);
 
-  bool ShouldUseInfiniteCullRectInternal(GlobalPaintFlags,
-                                         bool for_cull_rect_update);
-  void AdjustForPaintProperties(const GraphicsContext&,
-                                PaintLayerPaintingInfo&,
-                                PaintLayerFlags&);
+  bool ShouldUseInfiniteCullRectInternal(bool for_cull_rect_update);
 
   PaintLayer& paint_layer_;
 };
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc b/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc
index 8ffd7ce..4520bbd 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc
@@ -468,14 +468,8 @@
   GetLayoutView().GetScrollableArea()->SetScrollOffset(
       ScrollOffset(0, 3000), mojom::blink::ScrollType::kProgrammatic);
   UpdateAllLifecyclePhasesExceptPaint();
-  if (RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
-    // The layer needs repaint when its contents cull rect changes.
-    EXPECT_TRUE(target_layer->SelfNeedsRepaint());
-  } else {
-    // Scrolling doesn't set SelfNeedsRepaint flag. Change of paint dirty rect
-    // of a partially painted layer will trigger repaint.
-    EXPECT_FALSE(target_layer->SelfNeedsRepaint());
-  }
+  // The layer needs repaint when its contents cull rect changes.
+  EXPECT_TRUE(target_layer->SelfNeedsRepaint());
 
   counter.Reset();
   UpdateAllLifecyclePhasesForTest();
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painting_info.h b/third_party/blink/renderer/core/paint/paint_layer_painting_info.h
deleted file mode 100644
index a2916eb..0000000
--- a/third_party/blink/renderer/core/paint/paint_layer_painting_info.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2003, 2009, 2012 Apple Inc. All rights reserved.
- * Copyright (C) 2013 Intel Corporation. All rights reserved.
- *
- * Portions are Copyright (C) 1998 Netscape Communications Corporation.
- *
- * Other contributors:
- *   Robert O'Callahan <roc+@cs.cmu.edu>
- *   David Baron <dbaron@dbaron.org>
- *   Christian Biesinger <cbiesinger@web.de>
- *   Randall Jesup <rjesup@wgate.com>
- *   Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
- *   Josh Soref <timeless@mac.com>
- *   Boris Zbarsky <bzbarsky@mit.edu>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
- *
- * Alternatively, the contents of this file may be used under the terms
- * of either the Mozilla Public License Version 1.1, found at
- * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
- * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
- * (the "GPL"), in which case the provisions of the MPL or the GPL are
- * applicable instead of those above.  If you wish to allow use of your
- * version of this file only under the terms of one of those two
- * licenses (the MPL or the GPL) and not to allow others to use your
- * version of this file under the LGPL, indicate your decision by
- * deletingthe provisions above and replace them with the notice and
- * other provisions required by the MPL or the GPL, as the case may be.
- * If you do not delete the provisions above, a recipient may use your
- * version of this file under any of the LGPL, the MPL or the GPL.
- */
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_PAINTING_INFO_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_PAINTING_INFO_H_
-
-#include "base/dcheck_is_on.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-#if DCHECK_IS_ON()
-#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-#endif
-
-namespace blink {
-
-class PaintLayer;
-
-enum PaintLayerFlag {
-  kPaintLayerNoFlag = 0,
-  kPaintLayerPaintingOverlayOverflowControls = 1 << 0,
-  kPaintLayerPaintingOverflowContents = 1 << 1,
-  kPaintLayerPaintingSkipBackground = 1 << 2,
-  kPaintLayerPaintingRenderingClipPathAsMask = 1 << 3,
-  kPaintLayerPaintingRenderingResourceSubtree = 1 << 4,
-};
-
-typedef unsigned PaintLayerFlags;
-
-struct PaintLayerPaintingInfo {
-  STACK_ALLOCATED();
-
- public:
-  PaintLayerPaintingInfo(PaintLayer* root_layer,
-                         GlobalPaintFlags global_paint_flags)
-      : root_layer(root_layer), global_paint_flags_(global_paint_flags) {}
-
-  GlobalPaintFlags GetGlobalPaintFlags() const { return global_paint_flags_; }
-
-  // TODO(jchaffraix): We should encapsulate all these fields.
-  const PaintLayer* root_layer;
-
- private:
-  const GlobalPaintFlags global_paint_flags_;
-};
-
-#if DCHECK_IS_ON()
-inline String PaintLayerFlagsToDebugString(PaintLayerFlags flags) {
-  if (flags == 0)
-    return "(kPaintLayerNoFlag)";
-
-  StringBuilder builder;
-  builder.Append("(");
-  bool need_separator = false;
-  auto append = [&builder, &need_separator](const char* str) {
-    if (need_separator)
-      builder.Append("|");
-    builder.Append(str);
-    need_separator = true;
-  };
-
-  if (flags & kPaintLayerPaintingOverlayOverflowControls)
-    append("kPaintLayerPaintingOverlayOverflowControls");
-  if (flags & kPaintLayerPaintingOverflowContents)
-    append("kPaintLayerPaintingOverflowContents");
-  if (flags & kPaintLayerPaintingSkipBackground)
-    append("kPaintLayerPaintingSkipBackground");
-  if (flags & kPaintLayerPaintingRenderingClipPathAsMask)
-    append("kPaintLayerPaintingRenderingClipPathAsMask");
-  if (flags & kPaintLayerPaintingRenderingResourceSubtree)
-    append("kPaintLayerPaintingRenderingResourceSubtree");
-
-  builder.Append(")");
-  return builder.ToString();
-}
-#endif  // DCHECK_IS_ON()
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_PAINTING_INFO_H_
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index 66fba60..0771ed2 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -594,14 +594,6 @@
       box->SetBackgroundNeedsFullPaintInvalidation();
     }
   }
-
-  // If any scrolling content might have been clipped by a cull rect, then
-  // that cull rect could be affected by scroll offset. For composited
-  // scrollers, this will be taken care of by the interest rect computation
-  // in CompositedLayerMapping.
-  if (!RuntimeEnabledFeatures::CullRectUpdateEnabled() &&
-      !UsesCompositedScrolling())
-    Layer()->SetNeedsRepaint();
 }
 
 gfx::Vector2d PaintLayerScrollableArea::ScrollOffsetInt() const {
@@ -1228,10 +1220,7 @@
 }
 
 // This function returns true if the given box requires overflow scrollbars (as
-// opposed to the 'viewport' scrollbars managed by the PaintLayerCompositor).
-// FIXME: we should use the same scrolling machinery for both the viewport and
-// overflow. Currently, we need to avoid producing scrollbars here if they'll be
-// handled externally in the RLC.
+// opposed to the viewport scrollbars managed by VisualViewport).
 static bool CanHaveOverflowScrollbars(const LayoutBox& box) {
   return box.GetDocument().ViewportDefiningElement() != box.GetNode();
 }
diff --git a/third_party/blink/renderer/core/paint/paint_layer_test.cc b/third_party/blink/renderer/core/paint/paint_layer_test.cc
index c649bca..dffe039d 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_test.cc
@@ -196,16 +196,10 @@
       gfx::Vector2d(1000, 1000),
       content_layer->ContainingLayer()->PixelSnappedScrolledContentOffset());
 
-  if (RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
-    EXPECT_FALSE(scroll_layer->SelfNeedsRepaint());
-    // The content layer needs repaint because its cull rect changed.
-    EXPECT_TRUE(content_layer->SelfNeedsRepaint());
-  } else {
-    EXPECT_TRUE(scroll_layer->SelfNeedsRepaint());
-    // We don't set the layer needing repaint, but will repaint the layer when
-    // we find that the cull rect changes during paint.
-    EXPECT_FALSE(content_layer->SelfNeedsRepaint());
-  }
+  EXPECT_FALSE(scroll_layer->SelfNeedsRepaint());
+  // The content layer needs repaint because its cull rect changed.
+  EXPECT_TRUE(content_layer->SelfNeedsRepaint());
+
   UpdateAllLifecyclePhasesForTest();
 }
 
diff --git a/third_party/blink/renderer/core/paint/paint_phase.h b/third_party/blink/renderer/core/paint/paint_phase.h
index 99ba489..728ce657 100644
--- a/third_party/blink/renderer/core/paint/paint_phase.h
+++ b/third_party/blink/renderer/core/paint/paint_phase.h
@@ -121,25 +121,6 @@
          phase == PaintPhase::kDescendantOutlinesOnly;
 }
 
-// Those flags are meant as global tree operations. This means
-// that they should be constant for a paint phase.
-enum GlobalPaintFlag {
-  kGlobalPaintNormalPhase = 0,
-  // Used when painting selection as part of a drag-image. This
-  // flag disables a lot of the painting code and specifically
-  // triggers a PaintPhaseSelection.
-  kGlobalPaintSelectionDragImageOnly = 1 << 0,
-  // Used when painting a drag-image or printing in order to
-  // ignore the hardware layers and paint the whole tree
-  // into the topmost layer.
-  kGlobalPaintFlattenCompositingLayers = 1 << 1,
-  // Used when printing or painting a preview to in order to add URL
-  // metadata for links.
-  kGlobalPaintAddUrlMetadata = 1 << 2
-};
-
-typedef unsigned GlobalPaintFlags;
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_PHASE_H_
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 75c25697..fe3f281 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
@@ -107,7 +107,6 @@
 
 PaintPropertyTreeBuilderContext::PaintPropertyTreeBuilderContext()
     : force_subtree_update_reasons(0u),
-      clip_changed(false),
       is_repeating_fixed_position(false),
       has_svg_hidden_container_ancestor(false),
       supports_composited_raster_invalidation(true),
@@ -313,12 +312,6 @@
   void OnUpdate(PaintPropertyChangeType change) {
     property_changed_ = std::max(property_changed_, change);
   }
-  // Like |OnUpdate| but sets |clip_changed| if the clip values change.
-  void OnUpdateClip(PaintPropertyChangeType change) {
-    OnUpdate(change);
-    full_context_.clip_changed |=
-        change >= PaintPropertyChangeType::kChangedOnlySimpleValues;
-  }
   // Like |OnUpdate| but forces a piercing subtree update if the scroll tree
   // hierarchy changes because the scroll tree does not have isolation nodes
   // and non-piercing updates can fail to update scroll descendants.
@@ -342,10 +335,6 @@
           PaintPropertyTreeBuilderContext::kSubtreeUpdateIsolationPiercing;
     }
   }
-  void OnClearClip(bool cleared) {
-    OnClear(cleared);
-    full_context_.clip_changed |= cleared;
-  }
 
   CompositorElementId GetCompositorElementId(
       CompositorElementIdNamespace namespace_id) const {
@@ -1231,14 +1220,14 @@
         // TODO(crbug.com/1248598): We use pixel-snapped mask clip and clip path
         // clip as the layout clip rect, which may cause wrong result when
         // mapping in layout coordinates.
-        OnUpdateClip(properties_->UpdateMaskClip(
+        OnUpdate(properties_->UpdateMaskClip(
             *context_.current.clip,
             ClipPaintPropertyNode::State(context_.current.transform,
                                          gfx::RectF(combined_clip),
                                          FloatRoundedRect(combined_clip))));
         output_clip = properties_->MaskClip();
       } else {
-        OnClearClip(properties_->ClearMaskClip());
+        OnClear(properties_->ClearMaskClip());
       }
 
       CompositorElementId mask_compositor_element_id;
@@ -1385,7 +1374,7 @@
       OnClear(properties_->ClearEffect());
       OnClear(properties_->ClearMask());
       OnClear(properties_->ClearClipPathMask());
-      OnClearClip(properties_->ClearMaskClip());
+      OnClear(properties_->ClearMaskClip());
     }
   }
 
@@ -1586,13 +1575,13 @@
   if (NeedsPaintPropertyUpdate()) {
     if (context_.fragment_clip) {
       const auto& clip_rect = *context_.fragment_clip;
-      OnUpdateClip(properties_->UpdateFragmentClip(
+      OnUpdate(properties_->UpdateFragmentClip(
           *context_.current.clip,
           ClipPaintPropertyNode::State(context_.current.transform,
                                        gfx::RectF(clip_rect),
                                        ToSnappedClipRect(clip_rect))));
     } else {
-      OnClearClip(properties_->ClearFragmentClip());
+      OnClear(properties_->ClearFragmentClip());
     }
   }
 
@@ -1620,13 +1609,13 @@
       DCHECK(object_.CanContainAbsolutePositionObjects());
       const auto& clip_rect =
           To<LayoutBox>(object_).ClipRect(context_.current.paint_offset);
-      OnUpdateClip(properties_->UpdateCssClip(
+      OnUpdate(properties_->UpdateCssClip(
           *context_.current.clip,
           ClipPaintPropertyNode::State(context_.current.transform,
                                        gfx::RectF(clip_rect),
                                        ToSnappedClipRect(clip_rect))));
     } else {
-      OnClearClip(properties_->ClearCssClip());
+      OnClear(properties_->ClearCssClip());
     }
   }
 
@@ -1637,7 +1626,7 @@
 void FragmentPaintPropertyTreeBuilder::UpdateClipPathClip() {
   if (NeedsPaintPropertyUpdate()) {
     if (!NeedsClipPathClip(object_, fragment_data_)) {
-      OnClearClip(properties_->ClearClipPathClip());
+      OnClear(properties_->ClearClipPathClip());
     } else {
       // TODO(crbug.com/1248598): We use pixel-snapped clip path clip as the
       // layout clip rect, which may cause wrong result when mapping in layout
@@ -1647,8 +1636,8 @@
           gfx::RectF(*fragment_data_.ClipPathBoundingBox()),
           FloatRoundedRect(*fragment_data_.ClipPathBoundingBox()));
       state.clip_path = fragment_data_.ClipPathPath();
-      OnUpdateClip(properties_->UpdateClipPathClip(*context_.current.clip,
-                                                   std::move(state)));
+      OnUpdate(properties_->UpdateClipPathClip(*context_.current.clip,
+                                               std::move(state)));
     }
   }
 
@@ -1786,10 +1775,10 @@
     return;
 
   if (NeedsOverflowControlsClip()) {
-    // Clip overflow controls to the border box rect. Not wrapped with
-    // OnUpdateClip() because this clip doesn't affect descendants. Wrap with
-    // OnUpdate() to let PrePaintTreeWalk see the change. This may cause
-    // unnecessary subtree update, but is not a big deal because it is rare.
+    // Clip overflow controls to the border box rect. Though this clip doesn't
+    // affect descendants, wrap with OnUpdate() to let PrePaintTreeWalk see the
+    // change. This may cause unnecessary subtree update, but is not a big deal
+    // because it is rare.
     const auto& clip_rect = PhysicalRect(context_.current.paint_offset,
                                          To<LayoutBox>(object_).Size());
     OnUpdate(properties_->UpdateOverflowControlsClip(
@@ -1834,10 +1823,10 @@
                                              paint_clip_rect);
       ClipPaintPropertyNode::State state(context_.current.transform,
                                          layout_clip_rect, paint_clip_rect);
-      OnUpdateClip(properties_->UpdateInnerBorderRadiusClip(
-          *context_.current.clip, std::move(state)));
+      OnUpdate(properties_->UpdateInnerBorderRadiusClip(*context_.current.clip,
+                                                        std::move(state)));
     } else {
-      OnClearClip(properties_->ClearInnerBorderRadiusClip());
+      OnClear(properties_->ClearInnerBorderRadiusClip());
     }
   }
 
@@ -1912,10 +1901,10 @@
         // the first parameter?
         state.SetClipRect(clip_rect, FloatRoundedRect(clip_rect));
       }
-      OnUpdateClip(properties_->UpdateOverflowClip(*context_.current.clip,
-                                                   std::move(state)));
+      OnUpdate(properties_->UpdateOverflowClip(*context_.current.clip,
+                                               std::move(state)));
     } else {
-      OnClearClip(properties_->ClearOverflowClip());
+      OnClear(properties_->ClearOverflowClip());
     }
   }
 
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.h b/third_party/blink/renderer/core/paint/paint_property_tree_builder.h
index 2bc0f53d..55649c11 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.h
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.h
@@ -230,13 +230,6 @@
   // Note that the next four bitfields are conceptually bool, but are declared
   // as unsigned in order to be packed in the same word as the above bitfield.
 
-  // Whether a clip paint property node appeared, disappeared, or changed
-  // its clip since this variable was last set to false. This is used
-  // to find out whether a clip changed since the last transform update.
-  // Code outside of this class resets clip_changed to false when transforms
-  // change. Used only when CullRectUpdate is not enabled.
-  unsigned clip_changed : 1;
-
   // When printing, fixed-position objects and their descendants need to repeat
   // in each page.
   unsigned is_repeating_fixed_position : 1;
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
index f2740ea..76edbde 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -76,16 +76,8 @@
   paint_invalidator_.ProcessPendingDelayedPaintInvalidations();
 
 #if DCHECK_IS_ON()
-  if (needs_tree_builder_context_update) {
-    if (!RuntimeEnabledFeatures::CullRectUpdateEnabled() && VLOG_IS_ON(2) &&
-        root_frame_view.GetLayoutView()) {
-      VLOG(2) << "PrePaintTreeWalk::Walk(root_frame_view=" << &root_frame_view
-              << ")\nPaintLayer tree:";
-      ShowLayerTree(root_frame_view.GetLayoutView()->Layer());
-    }
-    if (VLOG_IS_ON(1))
-      ShowAllPropertyTrees(root_frame_view);
-  }
+  if (needs_tree_builder_context_update && VLOG_IS_ON(1))
+    ShowAllPropertyTrees(root_frame_view);
 #endif
 
   // If the frame is invalidated, we need to inform the frame's chrome client
@@ -314,7 +306,7 @@
     const PrePaintTreeWalkContext& context) {
   return context.paint_invalidator_context.NeedsSubtreeWalk() ||
          context.effective_allowed_touch_action_changed ||
-         context.blocking_wheel_event_handler_changed || context.clip_changed;
+         context.blocking_wheel_event_handler_changed;
 }
 
 bool PrePaintTreeWalk::ObjectRequiresTreeBuilderContext(
@@ -512,13 +504,6 @@
     property_changed =
         std::max(property_changed, property_tree_builder->UpdateForChildren());
 
-    if (!RuntimeEnabledFeatures::CullRectUpdateEnabled() &&
-        context.tree_builder_context->clip_changed) {
-      // Save clip_changed flag in |context| so that all descendants will see it
-      // even if we don't create tree_builder_context.
-      context.clip_changed = true;
-    }
-
     if (property_changed != PaintPropertyChangeType::kUnchanged) {
       if (property_changed >
           PaintPropertyChangeType::kChangedOnlyCompositedValues) {
@@ -533,15 +518,9 @@
     }
   }
 
-  if (RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
-    if (property_changed != PaintPropertyChangeType::kUnchanged) {
-      CullRectUpdater::PaintPropertiesChanged(
-          object, *context.paint_invalidator_context.painting_layer);
-    }
-  } else if (context.clip_changed && object.HasLayer()) {
-    // When this or ancestor clip changed, the layer needs repaint because it
-    // may paint more or less results according to the changed clip.
-    To<LayoutBoxModelObject>(object).Layer()->SetNeedsRepaint();
+  if (property_changed != PaintPropertyChangeType::kUnchanged) {
+    CullRectUpdater::PaintPropertiesChanged(
+        object, *context.paint_invalidator_context.painting_layer);
   }
 }
 
@@ -964,7 +943,7 @@
             containing_fragment_info.fragmentation_nesting_level) {
           // Only walk OOFs once if they aren't contained within the current
           // fragmentation context.
-          if (!parent_fragment->IsFirstForNode())
+          if (!context.is_parent_first_for_node)
             continue;
         }
 
@@ -1137,13 +1116,6 @@
   PrePaintTreeWalkContext context(parent_context,
                                   needs_tree_builder_context_update);
 
-  if (object.HasTransform()) {
-    // Ignore clip changes from ancestor across transform boundaries.
-    context.clip_changed = false;
-    if (context.tree_builder_context)
-      context.tree_builder_context->clip_changed = false;
-  }
-
   WalkInternal(object, context, pre_paint_info);
 
   bool child_walk_blocked = object.ChildPrePaintBlockedByDisplayLock();
@@ -1155,15 +1127,16 @@
     // Note that |effective_allowed_touch_action_changed| and
     // |blocking_wheel_event_handler_changed| are special in that they requires
     // us to specifically recalculate this value on each subtree element. Other
-    // flags simply need a subtree walk. Some consideration needs to be given to
-    // |clip_changed| which ensures that we repaint every layer, but for the
-    // purposes of PrePaint, this flag is just forcing a subtree walk.
+    // flags simply need a subtree walk.
     object.GetDisplayLockContext()->SetNeedsPrePaintSubtreeWalk(
         context.effective_allowed_touch_action_changed,
         context.blocking_wheel_event_handler_changed);
   }
 
   if (!child_walk_blocked) {
+    if (pre_paint_info)
+      context.is_parent_first_for_node = pre_paint_info->is_first_for_node;
+
     WalkChildren(object, physical_fragment, context, is_inside_fragment_child);
 
     if (const auto* layout_embedded_content =
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.h b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.h
index bab9dc7..7449594 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.h
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.h
@@ -72,12 +72,6 @@
     // subtree may need to update.
     bool blocking_wheel_event_handler_changed = false;
 
-    // This is set to true once we see tree_builder_context->clip_changed is
-    // true. It will be propagated to descendant contexts even if we don't
-    // create tree_builder_context. Used only when CullRectUpdate is not
-    // enabled.
-    bool clip_changed = false;
-
     // True if we're fragment-traversing an object whose fragment wasn't found
     // and walked when walking the layout object tree. This may happen for
     // out-of-flow positioned and floated fragments inside block fragmentation,
@@ -85,6 +79,11 @@
     // fragmentainer even if the OOF / float is there.
     bool is_inside_orphaned_object = false;
 
+    // True if we're visiting the parent for the first time, i.e. when we're in
+    // the first fragmentainer where the parent occurs (or if we're not
+    // fragmented at all).
+    bool is_parent_first_for_node = true;
+
     ContainingFragment current_fragmentainer;
     ContainingFragment absolute_positioned_container;
     ContainingFragment fixed_positioned_container;
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk_test.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk_test.cc
index 013ee5b..4e30332 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk_test.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk_test.cc
@@ -451,9 +451,6 @@
 }
 
 TEST_P(PrePaintTreeWalkTest, CullRectUpdateOnSVGTransformChange) {
-  if (!RuntimeEnabledFeatures::CullRectUpdateEnabled())
-    return;
-
   SetBodyInnerHTML(R"HTML(
     <svg style="width: 200px; height: 200px">
       <rect id="rect"/>
diff --git a/third_party/blink/renderer/core/paint/rounded_border_geometry.cc b/third_party/blink/renderer/core/paint/rounded_border_geometry.cc
index 9d3e3c1..0987963 100644
--- a/third_party/blink/renderer/core/paint/rounded_border_geometry.cc
+++ b/third_party/blink/renderer/core/paint/rounded_border_geometry.cc
@@ -93,9 +93,7 @@
   if (style.HasBorderRadius()) {
     FloatRoundedRect::Radii radii =
         RoundedBorder(style, border_rect).GetRadii();
-    // Insets use negative values.
-    radii.Shrink(-insets.Top().ToFloat(), -insets.Bottom().ToFloat(),
-                 -insets.Left().ToFloat(), -insets.Right().ToFloat());
+    radii.Shrink(gfx::InsetsF(insets));
     float_inner_rect.SetRadii(radii);
   }
   return float_inner_rect;
@@ -145,12 +143,10 @@
             .GetRadii();
     if (outsets.Top() <= 0 && outsets.Bottom() <= 0 && outsets.Left() <= 0 &&
         outsets.Right() <= 0) {
-      radii.Shrink(-outsets.Top().ToFloat(), -outsets.Bottom().ToFloat(),
-                   -outsets.Left().ToFloat(), -outsets.Right().ToFloat());
+      radii.Shrink(gfx::InsetsF(outsets));
     } else {
       // radii.Expand() will DCHECK if all values are >= 0.
-      radii.Expand(outsets.Top().ToFloat(), outsets.Bottom().ToFloat(),
-                   outsets.Left().ToFloat(), outsets.Right().ToFloat());
+      radii.Expand(gfx::OutsetsF(outsets));
     }
     ExcludeSides(sides_to_include, &radii);
     rounded_rect.SetRadii(radii);
diff --git a/third_party/blink/renderer/core/paint/scrollable_area_painter.cc b/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
index 75b7356..977800c3 100644
--- a/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
+++ b/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
@@ -118,8 +118,8 @@
                   resizer_corner_rect.height() * 3 / 4);
   points[3].set_y(points[1].y());
 
-  PaintFlags paint_flags;
-  paint_flags.setStyle(PaintFlags::kStroke_Style);
+  cc::PaintFlags paint_flags;
+  paint_flags.setStyle(cc::PaintFlags::kStroke_Style);
   paint_flags.setStrokeWidth(std::ceil(paint_scale));
 
   SkPathBuilder line_path;
diff --git a/third_party/blink/renderer/core/paint/svg_foreign_object_painter.cc b/third_party/blink/renderer/core/paint/svg_foreign_object_painter.cc
index ddc40194..694804d 100644
--- a/third_party/blink/renderer/core/paint/svg_foreign_object_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_foreign_object_painter.cc
@@ -37,10 +37,8 @@
 
   // <foreignObject> is a replaced normal-flow stacking element.
   // See IsReplacedNormalFlowStacking in paint_layer_painter.cc.
-  PaintLayerPaintingInfo layer_painting_info(layout_svg_foreign_object_.Layer(),
-                                             paint_info.GetGlobalPaintFlags());
   PaintLayerPainter(*layout_svg_foreign_object_.Layer())
-      .Paint(paint_info.context, layer_painting_info, paint_info.PaintFlags());
+      .Paint(paint_info.context, paint_info.GetPaintFlags());
 }
 
 void SVGForeignObjectPainter::Paint(const PaintInfo& paint_info) {
diff --git a/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc b/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc
index 94839a2..bfa41176c 100644
--- a/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc
@@ -385,7 +385,7 @@
     switch (decoration_style.PaintOrderType(i)) {
       case PT_FILL:
         if (decoration_style.HasFill()) {
-          PaintFlags fill_flags;
+          cc::PaintFlags fill_flags;
           if (!SVGObjectPainter(*decoration_layout_object)
                    .PreparePaint(paint_info.context,
                                  paint_info.IsRenderingClipPathAsMaskImage(),
@@ -400,7 +400,7 @@
         break;
       case PT_STROKE:
         if (decoration_style.HasVisibleStroke()) {
-          PaintFlags stroke_flags;
+          cc::PaintFlags stroke_flags;
           if (!SVGObjectPainter(*decoration_layout_object)
                    .PreparePaint(paint_info.context,
                                  paint_info.IsRenderingClipPathAsMaskImage(),
@@ -437,7 +437,7 @@
     const PaintInfo& paint_info,
     const ComputedStyle& style,
     LayoutSVGResourceMode resource_mode,
-    PaintFlags& flags,
+    cc::PaintFlags& flags,
     const AffineTransform* shader_transform) {
   LayoutSVGInlineText& text_layout_object = InlineText();
 
@@ -496,7 +496,7 @@
                                         const SVGTextFragment& fragment,
                                         int start_position,
                                         int end_position,
-                                        const PaintFlags& flags) {
+                                        const cc::PaintFlags& flags) {
   LayoutSVGInlineText& text_layout_object = InlineText();
   const Font& scaled_font = text_layout_object.ScaledFont();
 
@@ -596,7 +596,7 @@
   // the regular style.
   TextRun text_run = svg_inline_text_box_.ConstructTextRun(style, fragment);
   if (!should_paint_selection || start_position >= end_position) {
-    PaintFlags flags;
+    cc::PaintFlags flags;
     if (SetupTextPaint(paint_info, style, resource_mode, flags,
                        shader_transform))
       PaintText(paint_info, text_run, fragment, 0, fragment.length, flags);
@@ -608,7 +608,7 @@
   bool paint_selected_text_only =
       paint_info.phase == PaintPhase::kSelectionDragImage;
   if (start_position > 0 && !paint_selected_text_only) {
-    PaintFlags flags;
+    cc::PaintFlags flags;
     if (SetupTextPaint(paint_info, style, resource_mode, flags,
                        shader_transform))
       PaintText(paint_info, text_run, fragment, 0, start_position, flags);
@@ -619,7 +619,7 @@
   {
     SelectionStyleScope scope(ParentInlineLayoutObject(), style,
                               selection_style);
-    PaintFlags flags;
+    cc::PaintFlags flags;
     if (SetupTextPaint(paint_info, selection_style, resource_mode, flags,
                        shader_transform)) {
       PaintText(paint_info, text_run, fragment, start_position, end_position,
@@ -631,7 +631,7 @@
   // selection to the end of the current chunk part.
   if (end_position < static_cast<int>(fragment.length) &&
       !paint_selected_text_only) {
-    PaintFlags flags;
+    cc::PaintFlags flags;
     if (SetupTextPaint(paint_info, style, resource_mode, flags,
                        shader_transform)) {
       PaintText(paint_info, text_run, fragment, end_position, fragment.length,
@@ -703,11 +703,11 @@
           : false,
       style.UsedColorScheme());
 
-  PaintFlags fill_flags;
+  cc::PaintFlags fill_flags;
   fill_flags.setColor(text_color.Rgb());
   fill_flags.setAntiAlias(true);
 
-  PaintFlags stroke_flags;
+  cc::PaintFlags stroke_flags;
   bool should_paint_stroke = false;
   if (SetupTextPaint(paint_info, style, kApplyToStrokeMode, stroke_flags,
                      nullptr)) {
diff --git a/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.h b/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.h
index 6f32a38..2fd35902 100644
--- a/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.h
+++ b/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.h
@@ -64,14 +64,14 @@
   bool SetupTextPaint(const PaintInfo&,
                       const ComputedStyle&,
                       LayoutSVGResourceMode,
-                      PaintFlags&,
+                      cc::PaintFlags&,
                       const AffineTransform*);
   void PaintText(const PaintInfo&,
                  TextRun&,
                  const SVGTextFragment&,
                  int start_position,
                  int end_position,
-                 const PaintFlags&);
+                 const cc::PaintFlags&);
   void PaintText(const PaintInfo&,
                  const ComputedStyle&,
                  const ComputedStyle& selection_style,
diff --git a/third_party/blink/renderer/core/paint/svg_model_object_painter.cc b/third_party/blink/renderer/core/paint/svg_model_object_painter.cc
index dc18d4c8..f005fea 100644
--- a/third_party/blink/renderer/core/paint/svg_model_object_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_model_object_painter.cc
@@ -32,7 +32,7 @@
   DCHECK(paint_info.phase == PaintPhase::kForeground);
   // Hit test display items are only needed for compositing. This flag is used
   // for for printing and drag images which do not need hit testing.
-  if (paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers)
+  if (paint_info.ShouldOmitCompositingInfo())
     return;
 
   paint_info.context.GetPaintController().RecordHitTestData(
diff --git a/third_party/blink/renderer/core/paint/svg_object_painter.cc b/third_party/blink/renderer/core/paint/svg_object_painter.cc
index 31e18d1b9..7a943c6 100644
--- a/third_party/blink/renderer/core/paint/svg_object_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_object_painter.cc
@@ -16,7 +16,7 @@
 namespace {
 
 void CopyStateFromGraphicsContext(const GraphicsContext& context,
-                                  PaintFlags& flags) {
+                                  cc::PaintFlags& flags) {
   // TODO(fs): The color filter can be set when generating a picture for a mask
   // due to color-interpolation. We could also just apply the
   // color-interpolation property from the the shape itself (which could mean
@@ -35,16 +35,16 @@
 void SVGObjectPainter::PaintResourceSubtree(GraphicsContext& context) {
   DCHECK(!layout_object_.SelfNeedsLayout());
 
-  PaintInfo info(context, CullRect::Infinite(), PaintPhase::kForeground,
-                 kGlobalPaintNormalPhase | kGlobalPaintFlattenCompositingLayers,
-                 kPaintLayerPaintingRenderingResourceSubtree);
+  PaintInfo info(
+      context, CullRect::Infinite(), PaintPhase::kForeground,
+      PaintFlag::kOmitCompositingInfo | PaintFlag::kPaintingResourceSubtree);
   layout_object_.Paint(info);
 }
 
 bool SVGObjectPainter::ApplyPaintResource(
     const SVGPaint& paint,
     const AffineTransform* additional_paint_server_transform,
-    PaintFlags& flags) {
+    cc::PaintFlags& flags) {
   SVGElementResourceClient* client = SVGResources::GetClient(layout_object_);
   if (!client)
     return false;
@@ -67,7 +67,7 @@
     bool is_rendering_clip_path_as_mask_image,
     const ComputedStyle& style,
     LayoutSVGResourceMode resource_mode,
-    PaintFlags& flags,
+    cc::PaintFlags& flags,
     const AffineTransform* additional_paint_server_transform) {
   if (is_rendering_clip_path_as_mask_image) {
     if (resource_mode == kApplyToStrokeMode)
diff --git a/third_party/blink/renderer/core/paint/svg_object_painter.h b/third_party/blink/renderer/core/paint/svg_object_painter.h
index a45b590..4bfcf5dd 100644
--- a/third_party/blink/renderer/core/paint/svg_object_painter.h
+++ b/third_party/blink/renderer/core/paint/svg_object_painter.h
@@ -5,8 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SVG_OBJECT_PAINTER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SVG_OBJECT_PAINTER_H_
 
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
@@ -37,7 +37,7 @@
       bool is_rendering_clip_path_as_mask_image,
       const ComputedStyle&,
       LayoutSVGResourceMode,
-      PaintFlags& paint_flags,
+      cc::PaintFlags& paint_flags,
       const AffineTransform* additional_paint_server_transform = nullptr);
 
   void PaintResourceSubtree(GraphicsContext&);
@@ -46,7 +46,7 @@
   bool ApplyPaintResource(
       const SVGPaint& paint,
       const AffineTransform* additional_paint_server_transform,
-      PaintFlags& flags);
+      cc::PaintFlags& flags);
 
   const LayoutObject& layout_object_;
 };
diff --git a/third_party/blink/renderer/core/paint/svg_shape_painter.cc b/third_party/blink/renderer/core/paint/svg_shape_painter.cc
index 1886d6c..893d655 100644
--- a/third_party/blink/renderer/core/paint/svg_shape_painter.cc
+++ b/third_party/blink/renderer/core/paint/svg_shape_painter.cc
@@ -77,7 +77,7 @@
       for (int i = 0; i < 3; i++) {
         switch (style.PaintOrderType(i)) {
           case PT_FILL: {
-            PaintFlags fill_flags;
+            cc::PaintFlags fill_flags;
             if (!SVGObjectPainter(layout_svg_shape_)
                      .PreparePaint(paint_info.context,
                                    paint_info.IsRenderingClipPathAsMaskImage(),
@@ -103,7 +103,7 @@
                   return;
               }
 
-              PaintFlags stroke_flags;
+              cc::PaintFlags stroke_flags;
               if (!SVGObjectPainter(layout_svg_shape_)
                        .PreparePaint(
                            paint_info.context,
@@ -154,7 +154,7 @@
 };
 
 void SVGShapePainter::FillShape(GraphicsContext& context,
-                                const PaintFlags& flags,
+                                const cc::PaintFlags& flags,
                                 SkPathFillType fill_type) {
   AutoDarkMode auto_dark_mode(PaintAutoDarkMode(
       layout_svg_shape_.StyleRef(), DarkModeFilter::ElementRole::kSVG));
@@ -180,7 +180,7 @@
 }
 
 void SVGShapePainter::StrokeShape(GraphicsContext& context,
-                                  const PaintFlags& flags) {
+                                  const cc::PaintFlags& flags) {
   DCHECK(layout_svg_shape_.StyleRef().HasVisibleStroke());
 
   AutoDarkMode auto_dark_mode(PaintAutoDarkMode(
diff --git a/third_party/blink/renderer/core/paint/svg_shape_painter.h b/third_party/blink/renderer/core/paint/svg_shape_painter.h
index 8c991f0..5d6efe7 100644
--- a/third_party/blink/renderer/core/paint/svg_shape_painter.h
+++ b/third_party/blink/renderer/core/paint/svg_shape_painter.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SVG_SHAPE_PAINTER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SVG_SHAPE_PAINTER_H_
 
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/skia/include/core/SkPath.h"
 
@@ -27,8 +27,8 @@
   void Paint(const PaintInfo&);
 
  private:
-  void FillShape(GraphicsContext&, const PaintFlags&, SkPathFillType);
-  void StrokeShape(GraphicsContext&, const PaintFlags&);
+  void FillShape(GraphicsContext&, const cc::PaintFlags&, SkPathFillType);
+  void StrokeShape(GraphicsContext&, const cc::PaintFlags&);
 
   void PaintMarkers(const PaintInfo&);
   void PaintMarker(const PaintInfo&,
diff --git a/third_party/blink/renderer/core/paint/text_painter_base.cc b/third_party/blink/renderer/core/paint/text_painter_base.cc
index 9f4c827..6946c84 100644
--- a/third_party/blink/renderer/core/paint/text_painter_base.cc
+++ b/third_party/blink/renderer/core/paint/text_painter_base.cc
@@ -224,7 +224,7 @@
     const Vector<AppliedTextDecoration>& decorations,
     const TextPaintStyle& text_style,
     bool* has_line_through_decoration,
-    const PaintFlags* flags) {
+    const cc::PaintFlags* flags) {
   GraphicsContext& context = paint_info.context;
   GraphicsContextStateSaver state_saver(context);
   UpdateGraphicsContext(context, text_style, state_saver);
@@ -323,7 +323,7 @@
     const PaintInfo& paint_info,
     const Vector<AppliedTextDecoration>& decorations,
     const TextPaintStyle& text_style,
-    const PaintFlags* flags) {
+    const cc::PaintFlags* flags) {
   GraphicsContext& context = paint_info.context;
   GraphicsContextStateSaver state_saver(context);
   UpdateGraphicsContext(context, text_style, state_saver);
@@ -359,7 +359,7 @@
     GraphicsContext& context,
     TextDecorationInfo& decoration_info,
     TextDecorationLine line,
-    const PaintFlags* flags) {
+    const cc::PaintFlags* flags) {
   AppliedDecorationPainter decoration_painter(context, decoration_info);
   if (decoration_info.Style().TextDecorationSkipInk() ==
       ETextDecorationSkipInk::kAuto) {
diff --git a/third_party/blink/renderer/core/paint/text_painter_base.h b/third_party/blink/renderer/core/paint/text_painter_base.h
index 8e88608..1d577212 100644
--- a/third_party/blink/renderer/core/paint/text_painter_base.h
+++ b/third_party/blink/renderer/core/paint/text_painter_base.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_TEXT_PAINTER_BASE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_TEXT_PAINTER_BASE_H_
 
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
 #include "third_party/blink/renderer/core/paint/text_decoration_info.h"
@@ -14,7 +15,6 @@
 #include "third_party/blink/renderer/platform/fonts/font.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/graphics/draw_looper_builder.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/transforms/affine_transform.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
@@ -66,7 +66,7 @@
   void PaintDecorationUnderOrOverLine(GraphicsContext&,
                                       TextDecorationInfo&,
                                       TextDecorationLine line,
-                                      const PaintFlags* flags = nullptr);
+                                      const cc::PaintFlags* flags = nullptr);
 
   static Color TextColorForWhiteBackground(Color);
   static TextPaintStyle TextPaintingStyle(const Document&,
@@ -106,12 +106,12 @@
                                          const Vector<AppliedTextDecoration>&,
                                          const TextPaintStyle& text_style,
                                          bool* has_line_through_decoration,
-                                         const PaintFlags* flags = nullptr);
+                                         const cc::PaintFlags* flags = nullptr);
   void PaintDecorationsOnlyLineThrough(TextDecorationInfo&,
                                        const PaintInfo&,
                                        const Vector<AppliedTextDecoration>&,
                                        const TextPaintStyle&,
-                                       const PaintFlags* flags = nullptr);
+                                       const cc::PaintFlags* flags = nullptr);
 
   // Paints emphasis mark as for ideographic full stop character. Callers of
   // this function should rotate canvas to paint emphasis mark at left/right
diff --git a/third_party/blink/renderer/core/paint/text_painter_test.cc b/third_party/blink/renderer/core/paint/text_painter_test.cc
index db8cfeb..53ff369 100644
--- a/third_party/blink/renderer/core/paint/text_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/text_painter_test.cc
@@ -31,13 +31,12 @@
   LineLayoutText GetLineLayoutText() { return LineLayoutText(layout_text_); }
 
   PaintInfo CreatePaintInfoForBackground() {
-    return PaintInfo(context_, CullRect(), PaintPhase::kSelfBlockBackgroundOnly,
-                     kGlobalPaintNormalPhase, 0);
+    return PaintInfo(context_, CullRect(),
+                     PaintPhase::kSelfBlockBackgroundOnly);
   }
 
   PaintInfo CreatePaintInfoForTextClip() {
-    return PaintInfo(context_, CullRect(), PaintPhase::kTextClip,
-                     kGlobalPaintNormalPhase, 0);
+    return PaintInfo(context_, CullRect(), PaintPhase::kTextClip);
   }
 
  protected:
diff --git a/third_party/blink/renderer/core/paint/video_painter.cc b/third_party/blink/renderer/core/paint/video_painter.cc
index 60fe242..dff78d3 100644
--- a/third_party/blink/renderer/core/paint/video_painter.cc
+++ b/third_party/blink/renderer/core/paint/video_painter.cc
@@ -71,8 +71,7 @@
   // Video frames are only painted in software for printing or capturing node
   // images via web APIs.
   bool force_software_video_paint =
-      paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers &&
-      !force_video_poster;
+      paint_info.ShouldOmitCompositingInfo() && !force_video_poster;
 
   bool paint_with_foreign_layer = paint_info.phase == PaintPhase::kForeground &&
                                   !should_display_poster &&
@@ -98,7 +97,7 @@
     ImagePainter(layout_video_)
         .PaintIntoRect(context, replaced_rect, content_box_rect);
   } else {
-    PaintFlags video_flags = context.FillFlags();
+    cc::PaintFlags video_flags = context.FillFlags();
     video_flags.setColor(SK_ColorBLACK);
     layout_video_.VideoElement()->PaintCurrentFrame(
         context.Canvas(), snapped_replaced_rect, &video_flags);
diff --git a/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc b/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
index b7ad0144..d01d1442 100644
--- a/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
+++ b/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
@@ -1854,9 +1854,6 @@
 }
 
 TEST_P(FrameThrottlingTest, CullRectUpdate) {
-  if (!RuntimeEnabledFeatures::CullRectUpdateEnabled())
-    return;
-
   SimRequest main_resource("https://example.com/", "text/html");
   SimRequest frame_resource("https://example.com/iframe.html", "text/html");
 
diff --git a/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.cc b/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.cc
index 420d0c7..5b203b7 100644
--- a/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.cc
+++ b/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.cc
@@ -7,7 +7,6 @@
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/public/platform/web_vector.h"
-#include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_css_style_sheet_init.h"
 #include "third_party/blink/renderer/core/css/css_style_sheet.h"
@@ -65,16 +64,7 @@
         v8::Local<v8::Value>(), settings_object, params.SourceURL(), KURL(),
         ScriptFetchOptions(), error);
   }
-  v8::Local<v8::Value> v8_value_stylesheet;
-  if (!ToV8Traits<CSSStyleSheet>::ToV8(script_state, style_sheet)
-           .ToLocal(&v8_value_stylesheet)) {
-    v8::Local<v8::Value> error = exception_state.GetException();
-    exception_state.ClearException();
-    return ValueWrapperSyntheticModuleScript::CreateWithError(
-        v8::Local<v8::Value>(), settings_object, params.SourceURL(), KURL(),
-        ScriptFetchOptions(), error);
-  };
-
+  v8::Local<v8::Value> v8_value_stylesheet = ToV8(style_sheet, script_state);
   return ValueWrapperSyntheticModuleScript::CreateWithDefaultExport(
       v8_value_stylesheet, settings_object, params.SourceURL(), KURL(),
       ScriptFetchOptions());
diff --git a/third_party/blink/renderer/core/scroll/scrollbar_theme_aura.cc b/third_party/blink/renderer/core/scroll/scrollbar_theme_aura.cc
index 4c98c2e0..8e37d3e 100644
--- a/third_party/blink/renderer/core/scroll/scrollbar_theme_aura.cc
+++ b/third_party/blink/renderer/core/scroll/scrollbar_theme_aura.cc
@@ -40,7 +40,6 @@
 #include "third_party/blink/renderer/core/scroll/scrollbar.h"
 #include "third_party/blink/renderer/core/scroll/scrollbar_theme_overlay.h"
 #include "third_party/blink/renderer/core/style/computed_style_base_constants.h"
-#include "third_party/blink/renderer/platform/geometry/int_rect_outsets.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
 #include "third_party/blink/renderer/platform/web_test_support.h"
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index dddfb36..3e7213a 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -590,6 +590,12 @@
   DCHECK(pseudo);
   DCHECK_GT(pseudo->StyleType(), kPseudoIdNone);
 
+  // The pseudo style cache assumes that only one entry will be added for any
+  // any given (PseudoId,argument). Adding more than one entry is a bug, even
+  // if the styles being cached are equal.
+  DCHECK(!GetCachedPseudoElementStyle(pseudo->StyleType(),
+                                      pseudo->PseudoArgument()));
+
   const ComputedStyle* result = pseudo.get();
 
   EnsurePseudoElementStyleCache().push_back(std::move(pseudo));
@@ -2476,8 +2482,10 @@
   DCHECK(HasVisualOverflowingEffect());
   LayoutRectOutsets outsets;
 
-  if (const ShadowList* box_shadow = BoxShadow())
-    outsets = LayoutRectOutsets(box_shadow->RectOutsetsIncludingOriginal());
+  if (const ShadowList* box_shadow = BoxShadow()) {
+    outsets =
+        EnclosingLayoutRectOutsets(box_shadow->RectOutsetsIncludingOriginal());
+  }
 
   if (HasBorderImageOutsets())
     outsets.Unite(BorderImageOutsets());
diff --git a/third_party/blink/renderer/core/style/computed_style_constants.h b/third_party/blink/renderer/core/style/computed_style_constants.h
index 5bce0b2..84feb83 100644
--- a/third_party/blink/renderer/core/style/computed_style_constants.h
+++ b/third_party/blink/renderer/core/style/computed_style_constants.h
@@ -317,24 +317,19 @@
 };
 
 enum class TextEmphasisPosition : unsigned {
-  kOver,  // Same as kOverRight
   kOverRight,
   kOverLeft,
-  kUnder,  // Same as kUnderRight
   kUnderRight,
   kUnderLeft,
 };
 
 inline bool IsOver(TextEmphasisPosition position) {
-  return position == TextEmphasisPosition::kOver ||
-         position == TextEmphasisPosition::kOverRight ||
+  return position == TextEmphasisPosition::kOverRight ||
          position == TextEmphasisPosition::kOverLeft;
 }
 
 inline bool IsRight(TextEmphasisPosition position) {
-  return position == TextEmphasisPosition::kOver ||
-         position == TextEmphasisPosition::kOverRight ||
-         position == TextEmphasisPosition::kUnder ||
+  return position == TextEmphasisPosition::kOverRight ||
          position == TextEmphasisPosition::kUnderRight;
 }
 
diff --git a/third_party/blink/renderer/core/style/shadow_data.cc b/third_party/blink/renderer/core/style/shadow_data.cc
index a848356..d9f4d0c 100644
--- a/third_party/blink/renderer/core/style/shadow_data.cc
+++ b/third_party/blink/renderer/core/style/shadow_data.cc
@@ -37,15 +37,17 @@
                     StyleColor(Color::kTransparent));
 }
 
-FloatRectOutsets ShadowData::RectOutsets() const {
+gfx::OutsetsF ShadowData::RectOutsets() const {
   // 3 * sigma is how Skia computes the box blur extent.
   // See also https://crbug.com/624175.
   // TODO(fmalita): since the blur extent must reflect rasterization bounds,
   // its value should be queried from Skia (pending API availability).
   float blur_and_spread = ceil(3 * BlurRadiusToStdDev(Blur())) + Spread();
-  return FloatRectOutsets(
-      blur_and_spread - Y() /* top */, blur_and_spread + X() /* right */,
-      blur_and_spread + Y() /* bottom */, blur_and_spread - X() /* left */);
+  return gfx::OutsetsF()
+      .set_left(blur_and_spread - X())
+      .set_right(blur_and_spread + X())
+      .set_top(blur_and_spread - Y())
+      .set_bottom(blur_and_spread + Y());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/style/shadow_data.h b/third_party/blink/renderer/core/style/shadow_data.h
index e04ebee7..40b7c28d 100644
--- a/third_party/blink/renderer/core/style/shadow_data.h
+++ b/third_party/blink/renderer/core/style/shadow_data.h
@@ -28,7 +28,7 @@
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/style_color.h"
-#include "third_party/blink/renderer/platform/geometry/float_rect_outsets.h"
+#include "ui/gfx/geometry/outsets_f.h"
 #include "ui/gfx/geometry/point_f.h"
 
 namespace blink {
@@ -69,7 +69,7 @@
 
   // Outsets needed to adjust a source rectangle to the one cast by this
   // shadow.
-  FloatRectOutsets RectOutsets() const;
+  gfx::OutsetsF RectOutsets() const;
 
  private:
   gfx::PointF location_;
diff --git a/third_party/blink/renderer/core/style/shadow_list.cc b/third_party/blink/renderer/core/style/shadow_list.cc
index 863bb20..c7265f4 100644
--- a/third_party/blink/renderer/core/style/shadow_list.cc
+++ b/third_party/blink/renderer/core/style/shadow_list.cc
@@ -31,23 +31,23 @@
 #include "third_party/blink/renderer/core/style/shadow_list.h"
 
 #include <memory>
+#include "ui/gfx/geometry/outsets_f.h"
 #include "ui/gfx/geometry/rect_f.h"
 
 namespace blink {
 
-FloatRectOutsets ShadowList::RectOutsetsIncludingOriginal() const {
-  FloatRectOutsets outsets;
+gfx::OutsetsF ShadowList::RectOutsetsIncludingOriginal() const {
+  gfx::OutsetsF outsets;
   for (const ShadowData& shadow : Shadows()) {
     if (shadow.Style() == ShadowStyle::kInset)
       continue;
-    outsets.Unite(shadow.RectOutsets());
+    outsets.SetToMax(shadow.RectOutsets());
   }
   return outsets;
 }
 
 void ShadowList::AdjustRectForShadow(gfx::RectF& rect) const {
-  auto outset = RectOutsetsIncludingOriginal();
-  rect.Outset(outset.Left(), outset.Top(), outset.Right(), outset.Bottom());
+  rect.Outset(RectOutsetsIncludingOriginal());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/style/shadow_list.h b/third_party/blink/renderer/core/style/shadow_list.h
index ccb7820..02eea73c 100644
--- a/third_party/blink/renderer/core/style/shadow_list.h
+++ b/third_party/blink/renderer/core/style/shadow_list.h
@@ -32,13 +32,13 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_SHADOW_LIST_H_
 
 #include "third_party/blink/renderer/core/style/shadow_data.h"
-#include "third_party/blink/renderer/platform/geometry/float_rect_outsets.h"
 #include "third_party/blink/renderer/platform/graphics/draw_looper_builder.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace gfx {
+class OutsetsF;
 class RectF;
 }
 
@@ -62,7 +62,7 @@
 
   // Outsets needed to include all shadows in this list, as well as the
   // source (i.e. no outsets will be negative).
-  FloatRectOutsets RectOutsetsIncludingOriginal() const;
+  gfx::OutsetsF RectOutsetsIncludingOriginal() const;
 
   void AdjustRectForShadow(gfx::RectF&) const;
 
diff --git a/third_party/blink/renderer/core/style/style_fetched_image.cc b/third_party/blink/renderer/core/style/style_fetched_image.cc
index 3a3912c..4209b3b 100644
--- a/third_party/blink/renderer/core/style/style_fetched_image.cc
+++ b/third_party/blink/renderer/core/style/style_fetched_image.cc
@@ -61,6 +61,11 @@
 
 StyleFetchedImage::~StyleFetchedImage() = default;
 
+void StyleFetchedImage::Prefinalize() {
+  image_->DidRemoveObserver();
+  image_ = nullptr;
+}
+
 bool StyleFetchedImage::IsEqual(const StyleImage& other) const {
   if (!other.IsImageResource())
     return false;
diff --git a/third_party/blink/renderer/core/style/style_fetched_image.h b/third_party/blink/renderer/core/style/style_fetched_image.h
index 20547c2..6046e50 100644
--- a/third_party/blink/renderer/core/style/style_fetched_image.h
+++ b/third_party/blink/renderer/core/style/style_fetched_image.h
@@ -26,6 +26,7 @@
 
 #include "third_party/blink/renderer/core/loader/resource/image_resource_observer.h"
 #include "third_party/blink/renderer/core/style/style_image.h"
+#include "third_party/blink/renderer/platform/heap/prefinalizer.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 
@@ -37,6 +38,7 @@
 // url(...) function.)
 class StyleFetchedImage final : public StyleImage,
                                 public ImageResourceObserver {
+  USING_PRE_FINALIZER(StyleFetchedImage, Prefinalize);
 
  public:
   StyleFetchedImage(ImageResourceContent* image,
@@ -81,6 +83,7 @@
 
  private:
   bool IsEqual(const StyleImage&) const override;
+  void Prefinalize();
 
   // ImageResourceObserver overrides
   void ImageNotifyFinished(ImageResourceContent*) override;
diff --git a/third_party/blink/renderer/core/style/style_fetched_image_set.cc b/third_party/blink/renderer/core/style/style_fetched_image_set.cc
index f2b0373..0aa58f4 100644
--- a/third_party/blink/renderer/core/style/style_fetched_image_set.cc
+++ b/third_party/blink/renderer/core/style/style_fetched_image_set.cc
@@ -48,6 +48,11 @@
 
 StyleFetchedImageSet::~StyleFetchedImageSet() = default;
 
+void StyleFetchedImageSet::Prefinalize() {
+  best_fit_image_->DidRemoveObserver();
+  best_fit_image_ = nullptr;
+}
+
 bool StyleFetchedImageSet::IsEqual(const StyleImage& other) const {
   if (!other.IsImageResourceSet())
     return false;
diff --git a/third_party/blink/renderer/core/style/style_fetched_image_set.h b/third_party/blink/renderer/core/style/style_fetched_image_set.h
index 7f3921d0..b985476 100644
--- a/third_party/blink/renderer/core/style/style_fetched_image_set.h
+++ b/third_party/blink/renderer/core/style/style_fetched_image_set.h
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/loader/resource/image_resource_observer.h"
 #include "third_party/blink/renderer/core/style/style_image.h"
 #include "third_party/blink/renderer/platform/geometry/layout_size.h"
+#include "third_party/blink/renderer/platform/heap/prefinalizer.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 
@@ -44,6 +45,7 @@
 // alternatives via the referenced CSSImageSetValue.
 class StyleFetchedImageSet final : public StyleImage,
                                    public ImageResourceObserver {
+  USING_PRE_FINALIZER(StyleFetchedImageSet, Prefinalize);
 
  public:
   StyleFetchedImageSet(ImageResourceContent*,
@@ -87,6 +89,7 @@
 
  private:
   bool IsEqual(const StyleImage& other) const override;
+  void Prefinalize();
 
   // ImageResourceObserver overrides
   String DebugName() const override { return "StyleFetchedImageSet"; }
diff --git a/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.cc b/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.cc
index 4836d1b..6b94014 100644
--- a/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.cc
+++ b/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.cc
@@ -105,8 +105,8 @@
 
 SVGFilterBuilder::SVGFilterBuilder(FilterEffect* source_graphic,
                                    SVGFilterGraphNodeMap* node_map,
-                                   const PaintFlags* fill_flags,
-                                   const PaintFlags* stroke_flags)
+                                   const cc::PaintFlags* fill_flags,
+                                   const cc::PaintFlags* stroke_flags)
     : node_map_(node_map) {
   builtin_effects_.insert(FilterInputKeywords::GetSourceGraphic(),
                           source_graphic);
diff --git a/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.h b/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.h
index d191f6a..a432434 100644
--- a/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.h
+++ b/third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.h
@@ -21,9 +21,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_SVG_GRAPHICS_FILTERS_SVG_FILTER_BUILDER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SVG_GRAPHICS_FILTERS_SVG_FILTER_BUILDER_H_
 
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
 #include "third_party/blink/renderer/platform/graphics/interpolation_space.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
@@ -89,8 +89,8 @@
  public:
   SVGFilterBuilder(FilterEffect* source_graphic,
                    SVGFilterGraphNodeMap* = nullptr,
-                   const PaintFlags* fill_flags = nullptr,
-                   const PaintFlags* stroke_flags = nullptr);
+                   const cc::PaintFlags* fill_flags = nullptr,
+                   const cc::PaintFlags* stroke_flags = nullptr);
 
   void BuildGraph(Filter*, SVGFilterElement&, const gfx::RectF&);
 
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image.cc b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
index 0e867d34..bb7eb1d 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image.cc
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
@@ -389,7 +389,7 @@
 
 void SVGImage::DrawForContainer(const DrawInfo& draw_info,
                                 cc::PaintCanvas* canvas,
-                                const PaintFlags& flags,
+                                const cc::PaintFlags& flags,
                                 const gfx::RectF& dst_rect,
                                 const gfx::RectF& src_rect) {
   gfx::RectF unzoomed_src = src_rect;
@@ -434,8 +434,8 @@
     // spacing area.
     if (!tiling_info.spacing.IsZero())
       builder->Context().Clip(tile);
-    DrawForContainer(draw_info, builder->Context().Canvas(), PaintFlags(), tile,
-                     tiling_info.image_rect);
+    DrawForContainer(draw_info, builder->Context().Canvas(), cc::PaintFlags(),
+                     tile, tiling_info.image_rect);
   }
 
   sk_sp<PaintShader> tile_shader = PaintShader::MakePaintRecord(
@@ -445,7 +445,7 @@
   // If the shader could not be instantiated (e.g. non-invertible matrix),
   // draw transparent.
   // Note: we can't simply bail, because of arbitrary blend mode.
-  PaintFlags flags = base_flags;
+  cc::PaintFlags flags = base_flags;
   flags.setColor(tile_shader ? SK_ColorBLACK : SK_ColorTRANSPARENT);
   flags.setShader(std::move(tile_shader));
   // Reset filter quality.
@@ -467,7 +467,7 @@
   const gfx::Rect dest_rect(gfx::ToRoundedSize(size));
   cc::PaintCanvas* canvas =
       recorder.beginRecording(gfx::RectToSkRect(dest_rect));
-  DrawForContainer(draw_info, canvas, PaintFlags(), gfx::RectF(dest_rect),
+  DrawForContainer(draw_info, canvas, cc::PaintFlags(), gfx::RectF(dest_rect),
                    gfx::RectF(size));
   builder.set_paint_record(recorder.finishRecordingAsPicture(), dest_rect,
                            PaintImage::GetNextContentId());
@@ -479,7 +479,7 @@
 }
 
 bool SVGImage::ApplyShaderInternal(const DrawInfo& draw_info,
-                                   PaintFlags& flags,
+                                   cc::PaintFlags& flags,
                                    const SkMatrix& local_matrix) {
   if (draw_info.ContainerSize().IsEmpty())
     return false;
@@ -499,7 +499,7 @@
   return true;
 }
 
-bool SVGImage::ApplyShader(PaintFlags& flags,
+bool SVGImage::ApplyShader(cc::PaintFlags& flags,
                            const SkMatrix& local_matrix,
                            const gfx::RectF& dst_rect,
                            const gfx::RectF& src_rect,
@@ -510,7 +510,7 @@
 }
 
 bool SVGImage::ApplyShaderForContainer(const DrawInfo& draw_info,
-                                       PaintFlags& flags,
+                                       cc::PaintFlags& flags,
                                        const SkMatrix& local_matrix) {
   // Compensate for the container size rounding.
   gfx::SizeF residual_scale =
@@ -522,7 +522,7 @@
 }
 
 void SVGImage::Draw(cc::PaintCanvas* canvas,
-                    const PaintFlags& flags,
+                    const cc::PaintFlags& flags,
                     const gfx::RectF& dst_rect,
                     const gfx::RectF& src_rect,
                     const ImageDrawOptions& draw_options) {
@@ -563,7 +563,7 @@
   return view->GetPaintRecord();
 }
 
-static bool DrawNeedsLayer(const PaintFlags& flags) {
+static bool DrawNeedsLayer(const cc::PaintFlags& flags) {
   if (SkColorGetA(flags.getColor()) < 255)
     return true;
 
@@ -577,7 +577,7 @@
 
 void SVGImage::DrawInternal(const DrawInfo& draw_info,
                             cc::PaintCanvas* canvas,
-                            const PaintFlags& flags,
+                            const cc::PaintFlags& flags,
                             const gfx::RectF& dst_rect,
                             const gfx::RectF& unzoomed_src_rect) {
   sk_sp<PaintRecord> record = PaintRecordForCurrentFrame(draw_info);
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image_test.cc b/third_party/blink/renderer/core/svg/graphics/svg_image_test.cc
index 2b65de7..9c395b6 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image_test.cc
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image_test.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
+#include "cc/paint/paint_flags.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
@@ -23,7 +24,6 @@
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/timer.h"
 #include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
@@ -59,7 +59,7 @@
     Image* image = image_.get();
     std::unique_ptr<SkCanvas> null_canvas = SkMakeNullCanvas();
     SkiaPaintCanvas canvas(null_canvas.get());
-    PaintFlags flags;
+    cc::PaintFlags flags;
     gfx::RectF dummy_rect(0, 0, 100, 100);
     image->Draw(&canvas, flags, dummy_rect, dummy_rect, ImageDrawOptions());
   }
diff --git a/third_party/blink/renderer/core/svg/svg_element_test.cc b/third_party/blink/renderer/core/svg/svg_element_test.cc
index 083ec256..951800a 100644
--- a/third_party/blink/renderer/core/svg/svg_element_test.cc
+++ b/third_party/blink/renderer/core/svg/svg_element_test.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/svg/svg_element.h"
 
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
+#include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/svg/svg_element_rare_data.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 
diff --git a/third_party/blink/renderer/core/testing/sim/sim_compositor.cc b/third_party/blink/renderer/core/testing/sim/sim_compositor.cc
index c29169fe..18b2833 100644
--- a/third_party/blink/renderer/core/testing/sim/sim_compositor.cc
+++ b/third_party/blink/renderer/core/testing/sim/sim_compositor.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
+#include "third_party/blink/renderer/core/paint/paint_flags.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
@@ -60,7 +61,7 @@
   auto* frame = web_view_->MainFrameImpl()->GetFrame();
   auto* builder = MakeGarbageCollected<PaintRecordBuilder>();
   frame->View()->PaintOutsideOfLifecycle(builder->Context(),
-                                         kGlobalPaintFlattenCompositingLayers);
+                                         PaintFlag::kOmitCompositingInfo);
 
   auto infinite_rect = LayoutRect::InfiniteIntRect();
   SimCanvas canvas(infinite_rect.width(), infinite_rect.height());
diff --git a/third_party/blink/renderer/core/timing/event_timing.cc b/third_party/blink/renderer/core/timing/event_timing.cc
index b5a5be6..6e98839 100644
--- a/third_party/blink/renderer/core/timing/event_timing.cc
+++ b/third_party/blink/renderer/core/timing/event_timing.cc
@@ -46,7 +46,7 @@
 
 // Record FID even when there's no event listener.
 const base::Feature kFirstInputDelayWithoutEventListener{
-    "FirstInputDelayWithoutEventListener", base::FEATURE_DISABLED_BY_DEFAULT};
+    "FirstInputDelayWithoutEventListener", base::FEATURE_ENABLED_BY_DEFAULT};
 
 EventTiming::EventTiming(base::TimeTicks processing_start,
                          WindowPerformance* performance,
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
index c02c65e..6172da2 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
@@ -163,7 +163,8 @@
         back_forward_cache_controller_host)
     : WorkerGlobalScope(std::move(parsed_creation_params.creation_params),
                         thread,
-                        time_origin),
+                        time_origin,
+                        false),
       token_(thread->WorkerObjectProxy().token()),
       parent_token_(parsed_creation_params.parent_context_token),
       cross_origin_isolated_capability_(Agent::IsCrossOriginIsolated()),
diff --git a/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc b/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
index 693bd9c..95f1474e 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
@@ -58,7 +58,7 @@
     SharedWorkerThread* thread,
     base::TimeTicks time_origin,
     const SharedWorkerToken& token)
-    : WorkerGlobalScope(std::move(creation_params), thread, time_origin),
+    : WorkerGlobalScope(std::move(creation_params), thread, time_origin, false),
       token_(token) {}
 
 SharedWorkerGlobalScope::~SharedWorkerGlobalScope() = default;
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.cc b/third_party/blink/renderer/core/workers/worker_global_scope.cc
index 6716c08..853af40 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -94,7 +94,7 @@
 
 scoped_refptr<SecurityOrigin> CreateSecurityOrigin(
     GlobalScopeCreationParams* creation_params,
-    ExecutionContext* execution_context) {
+    bool is_service_worker_global_scope) {
   // A worker environment settings object's origin must be set as follows:
   //
   // - DedicatedWorkers and SharedWorkers
@@ -119,7 +119,7 @@
   // https://w3c.github.io/ServiceWorker/#start-register
   // Step 3: If scriptURL’s scheme is not one of "http" and "https", reject
   // promise with a TypeError and abort these steps. [spec text]
-  DCHECK(!execution_context->IsServiceWorkerGlobalScope() ||
+  DCHECK(!is_service_worker_global_scope ||
          !KURL(creation_params->script_url).ProtocolIsData());
 
   // TODO(https://crbug.com/1058305) Inherit |agent_cluster_id_| for dedicated
@@ -537,10 +537,12 @@
 WorkerGlobalScope::WorkerGlobalScope(
     std::unique_ptr<GlobalScopeCreationParams> creation_params,
     WorkerThread* thread,
-    base::TimeTicks time_origin)
+    base::TimeTicks time_origin,
+    bool is_service_worker_global_scope)
     : WorkerOrWorkletGlobalScope(
           thread->GetIsolate(),
-          CreateSecurityOrigin(creation_params.get(), GetExecutionContext()),
+          CreateSecurityOrigin(creation_params.get(),
+                               is_service_worker_global_scope),
           creation_params->starter_secure_context,
           MakeGarbageCollected<Agent>(
               thread->GetIsolate(),
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.h b/third_party/blink/renderer/core/workers/worker_global_scope.h
index 56ad0ae..c6add2fa 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.h
@@ -235,7 +235,8 @@
  protected:
   WorkerGlobalScope(std::unique_ptr<GlobalScopeCreationParams>,
                     WorkerThread*,
-                    base::TimeTicks time_origin);
+                    base::TimeTicks time_origin,
+                    bool is_service_worker_global_scope);
 
   // ExecutionContext
   void ExceptionThrown(ErrorEvent*) override;
diff --git a/third_party/blink/renderer/core/workers/worker_thread_test_helper.h b/third_party/blink/renderer/core/workers/worker_thread_test_helper.h
index 480fad3..d0db12d 100644
--- a/third_party/blink/renderer/core/workers/worker_thread_test_helper.h
+++ b/third_party/blink/renderer/core/workers/worker_thread_test_helper.h
@@ -49,7 +49,8 @@
       WorkerThread* thread)
       : WorkerGlobalScope(std::move(creation_params),
                           thread,
-                          base::TimeTicks::Now()) {
+                          base::TimeTicks::Now(),
+                          false) {
     ReadyToRunWorkerScript();
   }
 
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
index 92a8c50..65c38a8 100644
--- a/third_party/blink/renderer/core/workers/worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worklet_global_scope.cc
@@ -286,14 +286,10 @@
       ScriptController()->GetScriptState(),
       std::move(outside_settings_task_runner), pending_tasks);
 
-  // TODO(nhiroki): Pass an appropriate destination defined in each worklet
-  // spec (e.g., "paint worklet", "audio worklet") (https://crbug.com/843980,
-  // https://crbug.com/843982)
-  auto destination = mojom::blink::RequestContextType::SCRIPT;
+  auto request_context_type = mojom::blink::RequestContextType::SCRIPT;
   FetchModuleScript(module_url_record, outside_settings_object,
-                    outside_resource_timing_notifier, destination,
-                    network::mojom::RequestDestination::kScript,
-                    credentials_mode,
+                    outside_resource_timing_notifier, request_context_type,
+                    GetDestination(), credentials_mode,
                     ModuleScriptCustomFetchType::kWorkletAddModule, client);
 }
 
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope.h b/third_party/blink/renderer/core/workers/worklet_global_scope.h
index 59efa9d8..a239f9e9 100644
--- a/third_party/blink/renderer/core/workers/worklet_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worklet_global_scope.h
@@ -161,6 +161,10 @@
                      WorkerThread*,
                      bool create_microtask_queue);
 
+  // Returns a destination used for fetching worklet scripts.
+  // https://html.spec.whatwg.org/C/#worklet-destination-type
+  virtual network::mojom::RequestDestination GetDestination() const = 0;
+
   EventTarget* ErrorEventTarget() final { return nullptr; }
 
   // The |url_| and |user_agent_| are inherited from the parent Document.
diff --git a/third_party/blink/renderer/core/workers/worklet_global_scope_test_helper.h b/third_party/blink/renderer/core/workers/worklet_global_scope_test_helper.h
index 401378fc8..91360a2 100644
--- a/third_party/blink/renderer/core/workers/worklet_global_scope_test_helper.h
+++ b/third_party/blink/renderer/core/workers/worklet_global_scope_test_helper.h
@@ -20,6 +20,10 @@
   }
 
  private:
+  network::mojom::RequestDestination GetDestination() const override {
+    return network::mojom::RequestDestination::kScript;
+  }
+
   // A fake token identifying this worker. This is default constructed to a
   // valid token.
   const AnimationWorkletToken token_;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_enums.h b/third_party/blink/renderer/modules/accessibility/ax_enums.h
index a1f4b543..6fbe0be 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_enums.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_enums.h
@@ -104,6 +104,7 @@
 };
 
 enum AXIgnoredReason {
+  kAXActiveFullscreenElement,
   kAXActiveModalDialog,
   kAXAriaModalDialog,
   kAXAriaHiddenElement,
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 1cd48ae..cd08e3a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -922,7 +922,7 @@
     HTMLSelectMenuElement::PartType part_type =
         owner_select_menu->AssignedPartType(GetNode());
     if (part_type == HTMLSelectMenuElement::PartType::kButton) {
-      return ax::mojom::blink::Role::kPopUpButton;
+      return ax::mojom::blink::Role::kComboBoxMenuButton;
     } else if (part_type == HTMLSelectMenuElement::PartType::kListBox) {
       return ax::mojom::blink::Role::kListBox;
     } else if (part_type == HTMLSelectMenuElement::PartType::kOption) {
@@ -2914,7 +2914,7 @@
   }
 
   // An ARIA combobox can get value from inner contents.
-  if (AriaRoleAttribute() == ax::mojom::blink::Role::kComboBoxMenuButton) {
+  if (RoleValue() == ax::mojom::blink::Role::kComboBoxMenuButton) {
     AXObjectSet visited;
     return TextFromDescendants(visited, nullptr, false);
   }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 68083aa..afff75f2 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -50,6 +50,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
+#include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
 #include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
 #include "third_party/blink/renderer/core/html/custom/element_internals.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
@@ -119,6 +120,8 @@
 // inspector_type_builder_helper.cc.
 String IgnoredReasonName(AXIgnoredReason reason) {
   switch (reason) {
+    case kAXActiveFullscreenElement:
+      return "activeFullscreenElement";
     case kAXActiveModalDialog:
       return "activeModalDialog";
     case kAXAriaModalDialog:
@@ -463,10 +466,6 @@
   return role_name_vector;
 }
 
-HTMLDialogElement* GetActiveDialogElement(Node* node) {
-  return node->GetDocument().ActiveModalDialog();
-}
-
 void AddIntListAttributeFromObjects(ax::mojom::blink::IntListAttribute attr,
                                     const AXObject::AXObjectVector& objects,
                                     ui::AXNodeData* node_data) {
@@ -2435,14 +2434,16 @@
   if (IsMissingParent())
     RepairMissingParent();
 
-  cached_is_hidden_via_style = ComputeIsHiddenViaStyle();
+  const ComputedStyle* style = GetComputedStyle();
+
+  cached_is_hidden_via_style = ComputeIsHiddenViaStyle(style);
 
   // Decisions in what subtree descendants are included (each descendant's
   // cached children_) depends on the ARIA hidden state. When it changes,
   // the entire subtree needs to recompute descendants.
   // In addition, the below computations for is_ignored_but_included_in_tree is
   // dependent on having the correct new cached value.
-  bool is_inert = ComputeIsInert();
+  bool is_inert = ComputeIsInertViaStyle(style);
   bool is_aria_hidden = ComputeIsAriaHidden();
   if (cached_is_inert_ != is_inert ||
       cached_is_aria_hidden_ != is_aria_hidden) {
@@ -2554,20 +2555,15 @@
   return cached_is_inert_;
 }
 
-bool AXObject::ComputeIsInert(IgnoredReasons* ignored_reasons) const {
-  if (GetNode()) {
-    if (GetNode()->IsInert()) {
+bool AXObject::ComputeIsInertViaStyle(const ComputedStyle* style,
+                                      IgnoredReasons* ignored_reasons) const {
+  if (style) {
+    if (style->IsInert()) {
       if (ignored_reasons) {
-        HTMLDialogElement* dialog = GetActiveDialogElement(GetNode());
-        if (dialog) {
-          AXObject* dialog_object = AXObjectCache().GetOrCreate(dialog);
-          if (dialog_object) {
-            ignored_reasons->push_back(
-                IgnoredReason(kAXActiveModalDialog, dialog_object));
-          } else {
-            ignored_reasons->push_back(IgnoredReason(kAXInertElement));
-          }
-        } else {
+        // The 'inert' attribute sets forced inertness, which cannot be escaped
+        // by descendants (see details in computed_style_extra_fields.json5).
+        // So we only need to check InertRoot() if inertness is forced.
+        if (style->IsForcedInert()) {
           const AXObject* inert_root_el = InertRoot();
           if (inert_root_el == this) {
             ignored_reasons->push_back(IgnoredReason(kAXInertElement));
@@ -2575,13 +2571,35 @@
             ignored_reasons->push_back(
                 IgnoredReason(kAXInertSubtree, inert_root_el));
           }
+          return true;
         }
+        // If the inertness is overridable, it must have been set by a modal
+        // dialog or a fullscreen element (see AdjustStyleForInert).
+        Document& document = GetNode()->GetDocument();
+        if (HTMLDialogElement* dialog = document.ActiveModalDialog()) {
+          if (AXObject* dialog_object = AXObjectCache().GetOrCreate(dialog)) {
+            ignored_reasons->push_back(
+                IgnoredReason(kAXActiveModalDialog, dialog_object));
+            return true;
+          }
+        } else if (Element* fullscreen =
+                       Fullscreen::FullscreenElementFrom(document)) {
+          if (AXObject* fullscreen_object =
+                  AXObjectCache().GetOrCreate(fullscreen)) {
+            ignored_reasons->push_back(
+                IgnoredReason(kAXActiveFullscreenElement, fullscreen_object));
+            return true;
+          }
+        }
+        ignored_reasons->push_back(IgnoredReason(kAXInertElement));
       }
       return true;
     } else if (IsBlockedByAriaModalDialog(ignored_reasons)) {
       return true;
     }
   } else {
+    // Either GetNode() is null, or it's locked by content-visibility, or we
+    // failed to obtain a ComputedStyle. Make a guess iterating the ancestors.
     AXObject* parent = ParentObject();
     if (parent && parent->IsInert()) {
       if (ignored_reasons)
@@ -2592,6 +2610,10 @@
   return false;
 }
 
+bool AXObject::ComputeIsInert(IgnoredReasons* ignored_reasons) const {
+  return ComputeIsInertViaStyle(GetComputedStyle(), ignored_reasons);
+}
+
 bool AXObject::IsAriaHidden() const {
   UpdateCachedAttributeValuesIfNeeded();
   return cached_is_aria_hidden_;
@@ -3481,6 +3503,23 @@
                                 name_from, nullptr, nullptr);
 }
 
+const ComputedStyle* AXObject::GetComputedStyle() const {
+  Node* node = GetNode();
+  if (!node)
+    return nullptr;
+
+  // content-visibility:hidden or content-visibility: auto.
+  if (DisplayLockUtilities::IsDisplayLockedPreventingPaint(node))
+    return nullptr;
+
+  // For elements with layout objects we can get their style directly.
+  if (GetLayoutObject())
+    return GetLayoutObject()->Style();
+
+  // No layout object: must ensure computed style.
+  return node->EnsureComputedStyle();
+}
+
 // There are 4 ways to use CSS to hide something:
 // * "display: none" is "destroy rendering state and don't do anything in the
 //   subtree"
@@ -3490,7 +3529,18 @@
 //   work, but don't destroy the work that was already there"
 // * "content-visibility: auto" is "paint when it's scrolled into the viewport,
 //   but its layout information is not updated when it isn't"
-bool AXObject::ComputeIsHiddenViaStyle() const {
+bool AXObject::ComputeIsHiddenViaStyle(const ComputedStyle* style) const {
+  if (style) {
+    if (GetLayoutObject())
+      return style->Visibility() != EVisibility::kVisible;
+
+    // TODO(crbug.com/1286465): It's not consistent to only check
+    // IsEnsuredInDisplayNone() on layoutless elements.
+    return GetNode()->IsElementNode() &&
+           (style->IsEnsuredInDisplayNone() ||
+            style->Visibility() != EVisibility::kVisible);
+  }
+
   Node* node = GetNode();
   if (!node)
     return false;
@@ -3513,17 +3563,7 @@
         *node, DisplayLockActivationReason::kAccessibility);
   }
 
-  // For elements with layout objects we can get their style directly.
-  if (GetLayoutObject())
-    return GetLayoutObject()->Style()->Visibility() != EVisibility::kVisible;
-
-  // No layout object: must ensure computed style.
-  if (Element* element = DynamicTo<Element>(node)) {
-    const ComputedStyle* style = element->EnsureComputedStyle();
-    return !style || style->IsEnsuredInDisplayNone() ||
-           style->Visibility() != EVisibility::kVisible;
-  }
-  return false;
+  return node->IsElementNode();
 }
 
 bool AXObject::IsHiddenViaStyle() const {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index 0e4851a..1582192 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -1459,7 +1459,10 @@
   ax::mojom::blink::Role RemapAriaRoleDueToParent(ax::mojom::blink::Role) const;
   unsigned ComputeAriaColumnIndex() const;
   unsigned ComputeAriaRowIndex() const;
-  bool ComputeIsHiddenViaStyle() const;
+  const ComputedStyle* GetComputedStyle() const;
+  bool ComputeIsHiddenViaStyle(const ComputedStyle*) const;
+  bool ComputeIsInertViaStyle(const ComputedStyle*,
+                              IgnoredReasons* = nullptr) const;
 
   // This returns true if the element associated with this AXObject is has
   // focusable style, meaning that it is visible. Note that we prefer to rely on
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
index be85bf5..9adfde96 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
@@ -9,6 +9,8 @@
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
+#include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
+#include "third_party/blink/renderer/core/html/html_dialog_element.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
 #include "third_party/blink/renderer/modules/accessibility/testing/accessibility_test.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
@@ -1215,5 +1217,231 @@
   EXPECT_GT(bounds1.X(), bounds2.X());
 }
 
+TEST_F(AccessibilityTest, ComputeIsInertReason) {
+  ScopedInertAttributeForTest enabled_scope(true);
+  NonThrowableExceptionState exception_state;
+  SetBodyInnerHTML(R"HTML(
+    <div id="div1" inert>inert</div>
+    <div id="div2" hidden>
+      <span id="span" inert>non-rendered inert</span>
+    </div>
+    <dialog id="dialog1">dialog</dialog>
+    <dialog id="dialog2" inert>inert dialog</dialog>
+    <p id="p1">fullscreen</p>
+    <p id="p2" inert>inert fullscreen</p>
+  )HTML");
+
+  Document& document = GetDocument();
+  Element* body = document.body();
+  Element* div1 = GetElementById("div1");
+  Node* div1_text = div1->firstChild();
+  Element* div2 = GetElementById("div2");
+  Element* span = GetElementById("span");
+  Node* span_text = span->firstChild();
+  auto* dialog1 = To<HTMLDialogElement>(GetElementById("dialog1"));
+  Node* dialog1_text = dialog1->firstChild();
+  auto* dialog2 = To<HTMLDialogElement>(GetElementById("dialog2"));
+  Node* dialog2_text = dialog2->firstChild();
+  Element* p1 = GetElementById("p1");
+  Node* p1_text = p1->firstChild();
+  Element* p2 = GetElementById("p2");
+  Node* p2_text = p2->firstChild();
+
+  auto AssertInertReasons = [&](Node* node, AXIgnoredReason expectation) {
+    AXObject* object = GetAXObjectCache().GetOrCreate(node);
+    ASSERT_NE(object, nullptr);
+    AXObject::IgnoredReasons reasons;
+    ASSERT_TRUE(object->ComputeIsInert(&reasons));
+    ASSERT_EQ(reasons.size(), 1u);
+    ASSERT_EQ(reasons[0].reason, expectation);
+  };
+  auto AssertNotInert = [&](Node* node) {
+    AXObject* object = GetAXObjectCache().GetOrCreate(node);
+    ASSERT_NE(object, nullptr);
+    AXObject::IgnoredReasons reasons;
+    ASSERT_FALSE(object->ComputeIsInert(&reasons));
+    ASSERT_EQ(reasons.size(), 0u);
+  };
+  auto EnterFullscreen = [&](Element* element) {
+    LocalFrame::NotifyUserActivation(
+        document.GetFrame(), mojom::UserActivationNotificationType::kTest);
+    Fullscreen::RequestFullscreen(*element);
+    Fullscreen::DidResolveEnterFullscreenRequest(document, /*granted*/ true);
+  };
+  auto ExitFullscreen = [&]() {
+    Fullscreen::FullyExitFullscreen(document);
+    Fullscreen::DidExitFullscreen(document);
+  };
+
+  AssertNotInert(body);
+  AssertInertReasons(div1, kAXInertElement);
+  AssertInertReasons(div1_text, kAXInertSubtree);
+  AssertNotInert(div2);
+  AssertInertReasons(span, kAXInertElement);
+  AssertInertReasons(span_text, kAXInertSubtree);
+  AssertNotInert(dialog1);
+  AssertNotInert(dialog1_text);
+  AssertInertReasons(dialog2, kAXInertElement);
+  AssertInertReasons(dialog2_text, kAXInertSubtree);
+  AssertNotInert(p1);
+  AssertNotInert(p1_text);
+  AssertInertReasons(p2, kAXInertElement);
+  AssertInertReasons(p2_text, kAXInertSubtree);
+
+  dialog1->showModal(exception_state);
+
+  AssertInertReasons(body, kAXActiveModalDialog);
+  AssertInertReasons(div1, kAXInertElement);
+  AssertInertReasons(div1_text, kAXInertSubtree);
+  AssertInertReasons(div2, kAXActiveModalDialog);
+  AssertInertReasons(span, kAXInertElement);
+  AssertInertReasons(span_text, kAXInertSubtree);
+  AssertNotInert(dialog1);
+  AssertNotInert(dialog1_text);
+  AssertInertReasons(dialog2, kAXInertElement);
+  AssertInertReasons(dialog2_text, kAXInertSubtree);
+  AssertInertReasons(p1, kAXActiveModalDialog);
+  AssertInertReasons(p1_text, kAXActiveModalDialog);
+  AssertInertReasons(p2, kAXInertElement);
+  AssertInertReasons(p2_text, kAXInertSubtree);
+
+  dialog2->showModal(exception_state);
+
+  AssertInertReasons(body, kAXActiveModalDialog);
+  AssertInertReasons(div1, kAXInertElement);
+  AssertInertReasons(div1_text, kAXInertSubtree);
+  AssertInertReasons(div2, kAXActiveModalDialog);
+  AssertInertReasons(span, kAXInertElement);
+  AssertInertReasons(span_text, kAXInertSubtree);
+  AssertInertReasons(dialog1, kAXActiveModalDialog);
+  AssertInertReasons(dialog1_text, kAXActiveModalDialog);
+  AssertInertReasons(dialog2, kAXInertElement);
+  AssertInertReasons(dialog2_text, kAXInertSubtree);
+  AssertInertReasons(p1, kAXActiveModalDialog);
+  AssertInertReasons(p1_text, kAXActiveModalDialog);
+  AssertInertReasons(p2, kAXInertElement);
+  AssertInertReasons(p2_text, kAXInertSubtree);
+
+  EnterFullscreen(p1);
+
+  AssertInertReasons(body, kAXActiveModalDialog);
+  AssertInertReasons(div1, kAXInertElement);
+  AssertInertReasons(div1_text, kAXInertSubtree);
+  AssertInertReasons(div2, kAXActiveModalDialog);
+  AssertInertReasons(span, kAXInertElement);
+  AssertInertReasons(span_text, kAXInertSubtree);
+  AssertInertReasons(dialog1, kAXActiveModalDialog);
+  AssertInertReasons(dialog1_text, kAXActiveModalDialog);
+  AssertInertReasons(dialog2, kAXInertElement);
+  AssertInertReasons(dialog2_text, kAXInertSubtree);
+  AssertInertReasons(p1, kAXActiveModalDialog);
+  AssertInertReasons(p1_text, kAXActiveModalDialog);
+  AssertInertReasons(p2, kAXInertElement);
+  AssertInertReasons(p2_text, kAXInertSubtree);
+
+  dialog1->close();
+  dialog2->close();
+
+  AssertInertReasons(body, kAXActiveFullscreenElement);
+  AssertInertReasons(div1, kAXInertElement);
+  AssertInertReasons(div1_text, kAXInertSubtree);
+  AssertInertReasons(div2, kAXActiveFullscreenElement);
+  AssertInertReasons(span, kAXInertElement);
+  AssertInertReasons(span_text, kAXInertSubtree);
+  AssertInertReasons(dialog1, kAXActiveFullscreenElement);
+  AssertInertReasons(dialog1_text, kAXActiveFullscreenElement);
+  AssertInertReasons(dialog2, kAXInertElement);
+  AssertInertReasons(dialog2_text, kAXInertSubtree);
+  AssertNotInert(p1);
+  AssertNotInert(p1_text);
+  AssertInertReasons(p2, kAXInertElement);
+  AssertInertReasons(p2_text, kAXInertSubtree);
+
+  ExitFullscreen();
+  EnterFullscreen(p2);
+
+  AssertInertReasons(body, kAXActiveFullscreenElement);
+  AssertInertReasons(div1, kAXInertElement);
+  AssertInertReasons(div1_text, kAXInertSubtree);
+  AssertInertReasons(div2, kAXActiveFullscreenElement);
+  AssertInertReasons(span, kAXInertElement);
+  AssertInertReasons(span_text, kAXInertSubtree);
+  AssertInertReasons(dialog1, kAXActiveFullscreenElement);
+  AssertInertReasons(dialog1_text, kAXActiveFullscreenElement);
+  AssertInertReasons(dialog2, kAXInertElement);
+  AssertInertReasons(dialog2_text, kAXInertSubtree);
+  AssertInertReasons(p1, kAXActiveFullscreenElement);
+  AssertInertReasons(p1_text, kAXActiveFullscreenElement);
+  AssertInertReasons(p2, kAXInertElement);
+  AssertInertReasons(p2_text, kAXInertSubtree);
+
+  ExitFullscreen();
+
+  AssertNotInert(body);
+  AssertInertReasons(div1, kAXInertElement);
+  AssertInertReasons(div1_text, kAXInertSubtree);
+  AssertNotInert(div2);
+  AssertInertReasons(span, kAXInertElement);
+  AssertInertReasons(span_text, kAXInertSubtree);
+  AssertNotInert(dialog1);
+  AssertNotInert(dialog1_text);
+  AssertInertReasons(dialog2, kAXInertElement);
+  AssertInertReasons(dialog2_text, kAXInertSubtree);
+  AssertNotInert(p1);
+  AssertNotInert(p1_text);
+  AssertInertReasons(p2, kAXInertElement);
+  AssertInertReasons(p2_text, kAXInertSubtree);
+}
+
+TEST_F(AccessibilityTest, IsInertInDisplayNone) {
+  const Document& document = GetDocument();
+  ScopedInertAttributeForTest enabled_scope(true);
+  NonThrowableExceptionState exception_state;
+  SetBodyInnerHTML(R"HTML(
+    <div hidden>
+      foo
+      <p inert>
+        bar
+        <span>baz</span>
+      </p>
+    </div>
+  )HTML");
+
+  Element* body = document.body();
+  AXObject* ax_body = GetAXObjectCache().GetOrCreate(body);
+  ASSERT_NE(ax_body, nullptr);
+  ASSERT_FALSE(ax_body->IsInert());
+
+  Element* div = body->QuerySelector("div");
+  AXObject* ax_div = GetAXObjectCache().GetOrCreate(div);
+  ASSERT_NE(ax_div, nullptr);
+  ASSERT_FALSE(ax_div->IsInert());
+
+  Node* div_text = div->firstChild();
+  AXObject* ax_div_text = GetAXObjectCache().GetOrCreate(div_text);
+  ASSERT_NE(ax_div_text, nullptr);
+  ASSERT_FALSE(ax_div_text->IsInert());
+
+  Element* p = div->QuerySelector("p");
+  AXObject* ax_p = GetAXObjectCache().GetOrCreate(p);
+  ASSERT_NE(ax_p, nullptr);
+  ASSERT_TRUE(ax_p->IsInert());
+
+  Node* p_text = p->firstChild();
+  AXObject* ax_p_text = GetAXObjectCache().GetOrCreate(p_text);
+  ASSERT_NE(ax_p_text, nullptr);
+  ASSERT_TRUE(ax_p_text->IsInert());
+
+  Element* span = p->QuerySelector("span");
+  AXObject* ax_span = GetAXObjectCache().GetOrCreate(span);
+  ASSERT_NE(ax_span, nullptr);
+  ASSERT_TRUE(ax_span->IsInert());
+
+  Node* span_text = span->firstChild();
+  AXObject* ax_span_text = GetAXObjectCache().GetOrCreate(span_text);
+  ASSERT_NE(ax_span_text, nullptr);
+  ASSERT_TRUE(ax_span_text->IsInert());
+}
+
 }  // namespace test
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/inspector_type_builder_helper.cc b/third_party/blink/renderer/modules/accessibility/inspector_type_builder_helper.cc
index 659b582..515c6b7b 100644
--- a/third_party/blink/renderer/modules/accessibility/inspector_type_builder_helper.cc
+++ b/third_party/blink/renderer/modules/accessibility/inspector_type_builder_helper.cc
@@ -20,6 +20,8 @@
 
 String IgnoredReasonName(AXIgnoredReason reason) {
   switch (reason) {
+    case kAXActiveFullscreenElement:
+      return "activeFullscreenElement";
     case kAXActiveModalDialog:
       return "activeModalDialog";
     case kAXAriaModalDialog:
diff --git a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
index 9caa50f..fb0c8106 100644
--- a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
+++ b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
@@ -928,6 +928,35 @@
   return out;
 }
 
+ScriptPromise NavigatorAuction::deprecatedURNToURL(
+    ScriptState* script_state,
+    const String& uuid_url_string,
+    ExceptionState& exception_state) {
+  if (!uuid_url_string.StartsWithIgnoringCase("urn:uuid:")) {
+    exception_state.ThrowTypeError(
+        String::Format("Passed URL must start with 'urn:uuid:'."));
+    return ScriptPromise();
+  }
+
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  ScriptPromise promise = resolver->Promise();
+  KURL uuid_url(uuid_url_string);
+  ad_auction_service_->DeprecatedGetURLFromURN(
+      std::move(uuid_url),
+      WTF::Bind(&NavigatorAuction::GetURLFromURNComplete, WrapPersistent(this),
+                WrapPersistent(resolver)));
+  return promise;
+}
+
+ScriptPromise NavigatorAuction::deprecatedURNToURL(
+    ScriptState* script_state,
+    Navigator& navigator,
+    const String& uuid_url,
+    ExceptionState& exception_state) {
+  return From(ExecutionContext::From(script_state), navigator)
+      .deprecatedURNToURL(script_state, uuid_url, exception_state);
+}
+
 ScriptPromise NavigatorAuction::createAdRequest(
     ScriptState* script_state,
     const AdRequestConfig* config,
@@ -1063,4 +1092,17 @@
   }
 }
 
+void NavigatorAuction::GetURLFromURNComplete(
+    ScriptPromiseResolver* resolver,
+    const absl::optional<KURL>& decoded_url) {
+  if (!resolver->GetExecutionContext() ||
+      resolver->GetExecutionContext()->IsContextDestroyed())
+    return;
+  if (decoded_url) {
+    resolver->Resolve(*decoded_url);
+  } else {
+    resolver->Resolve(v8::Null(resolver->GetScriptState()->GetIsolate()));
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/ad_auction/navigator_auction.h b/third_party/blink/renderer/modules/ad_auction/navigator_auction.h
index 097a9d9..9872e46 100644
--- a/third_party/blink/renderer/modules/ad_auction/navigator_auction.h
+++ b/third_party/blink/renderer/modules/ad_auction/navigator_auction.h
@@ -80,6 +80,15 @@
                                             uint16_t num_ad_components,
                                             ExceptionState& exception_state);
 
+  ScriptPromise deprecatedURNToURL(ScriptState* script_state,
+                                   const String& uuid_url_string,
+                                   ExceptionState& exception_state);
+
+  static ScriptPromise deprecatedURNToURL(ScriptState* script_state,
+                                          Navigator& navigator,
+                                          const String& uuid_url_string,
+                                          ExceptionState& exception_state);
+
   ScriptPromise createAdRequest(ScriptState*,
                                 const AdRequestConfig*,
                                 ExceptionState&);
@@ -111,6 +120,9 @@
                           const absl::optional<KURL>& creative_url);
   // Completion callback for Mojo call made by runAdAuction().
   void AuctionComplete(ScriptPromiseResolver*, const absl::optional<KURL>&);
+  // Completion callback for Mojo call made by deprecatedURNToURL().
+  void GetURLFromURNComplete(ScriptPromiseResolver*,
+                             const absl::optional<KURL>&);
 
   HeapMojoRemote<mojom::blink::AdAuctionService> ad_auction_service_;
 };
diff --git a/third_party/blink/renderer/modules/ad_auction/navigator_auction.idl b/third_party/blink/renderer/modules/ad_auction/navigator_auction.idl
index 5e1749b5b..0853b9f7 100644
--- a/third_party/blink/renderer/modules/ad_auction/navigator_auction.idl
+++ b/third_party/blink/renderer/modules/ad_auction/navigator_auction.idl
@@ -29,6 +29,9 @@
   [RuntimeEnabled=Fledge, CallWith=ScriptState, Measure, RaisesException]
   sequence<USVString> adAuctionComponents([Clamp] unsigned short numComponents);
 
+  [RuntimeEnable=AllowURLsinIframes, CallWith=ScriptState, Measure, RaisesException]
+  Promise<USVString> deprecatedURNToURL(USVString uuid_url);
+
   [RuntimeEnabled=Parakeet, CallWith=ScriptState, Measure, RaisesException]
   Promise<Ads> createAdRequest(AdRequestConfig config);
 
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
index dfa168f..534af7f 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope.h
@@ -73,6 +73,12 @@
 
  private:
   void RegisterWithProxyClientIfNeeded();
+
+  // TODO(crbug.com/1286242): Return a proper destination for AnimationWorklet.
+  network::mojom::RequestDestination GetDestination() const override {
+    return network::mojom::RequestDestination::kScript;
+  }
+
   Animator* CreateInstance(
       const String& name,
       WorkletAnimationOptions options,
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 0520ec9..b840ecc9 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
@@ -181,7 +181,7 @@
     // two layers, so the shadow and filter can properly interact with alpha.
     // We also need to flip how and where the shadows and filter are applied
     // if there are shadows.
-    PaintFlags flags;
+    cc::PaintFlags flags;
     GetState().FillStyle()->ApplyToFlags(flags);
     flags.setColor(GetState().FillStyle()->PaintColor());
     flags.setBlendMode(GetState().GlobalComposite());
@@ -195,7 +195,7 @@
         GetState(), CanvasRenderingContext2DState::kDontCopyClipList,
         CanvasRenderingContext2DState::SaveType::kInternalLayer));
 
-    PaintFlags extra_flags;
+    cc::PaintFlags extra_flags;
     GetState().FillStyle()->ApplyToFlags(extra_flags);
     extra_flags.setColor(GetState().FillStyle()->PaintColor());
     extra_flags.setAlpha(globalAlpha() * 255);
@@ -203,7 +203,7 @@
       extra_flags.setImageFilter(StateGetFilter());
     canvas->saveLayer(nullptr, &extra_flags);
   } else {
-    PaintFlags flags;
+    cc::PaintFlags flags;
     GetState().FillStyle()->ApplyToFlags(flags);
     flags.setColor(GetState().FillStyle()->PaintColor());
     flags.setBlendMode(GetState().GlobalComposite());
@@ -1013,7 +1013,7 @@
 
   Draw<OverdrawOp::kNone>(
       [sk_path, use_paint_cache](cc::PaintCanvas* c,
-                                 const PaintFlags* flags)  // draw lambda
+                                 const cc::PaintFlags* flags)  // draw lambda
       { c->drawPath(sk_path, *flags, use_paint_cache); },
       [](const SkIRect& rect)  // overdraw test lambda
       { return false; },
@@ -1112,7 +1112,7 @@
 
   SkRect rect = SkRect::MakeXYWH(fx, fy, fwidth, fheight);
   Draw<OverdrawOp::kNone>(
-      [rect](cc::PaintCanvas* c, const PaintFlags* flags)  // draw lambda
+      [rect](cc::PaintCanvas* c, const cc::PaintFlags* flags)  // draw lambda
       { c->drawRect(rect, *flags); },
       [rect, this](const SkIRect& clip_bounds)  // overdraw test lambda
       {
@@ -1128,8 +1128,8 @@
 
 static void StrokeRectOnCanvas(const gfx::RectF& rect,
                                cc::PaintCanvas* canvas,
-                               const PaintFlags* flags) {
-  DCHECK_EQ(flags->getStyle(), PaintFlags::kStroke_Style);
+                               const cc::PaintFlags* flags) {
+  DCHECK_EQ(flags->getStyle(), cc::PaintFlags::kStroke_Style);
   if ((rect.width() > 0) != (rect.height() > 0)) {
     // When stroking, we must skip the zero-dimension segments
     SkPath path;
@@ -1172,7 +1172,7 @@
     return;
 
   Draw<OverdrawOp::kNone>(
-      [rect](cc::PaintCanvas* c, const PaintFlags* flags)  // draw lambda
+      [rect](cc::PaintCanvas* c, const cc::PaintFlags* flags)  // draw lambda
       { StrokeRectOnCanvas(rect, c, flags); },
       kNoOverdraw, gfx::RectFToSkRect(bounds),
       CanvasRenderingContext2DState::kStrokePaintType,
@@ -1313,9 +1313,9 @@
                                                 width, height);
   }
 
-  PaintFlags clear_flags;
+  cc::PaintFlags clear_flags;
   clear_flags.setBlendMode(SkBlendMode::kClear);
-  clear_flags.setStyle(PaintFlags::kFill_Style);
+  clear_flags.setStyle(cc::PaintFlags::kFill_Style);
 
   // clamp to float to avoid float cast overflow when used as SkScalar
   AdjustRectForCanvas(x, y, width, height);
@@ -1507,11 +1507,11 @@
     const gfx::RectF& src_rect,
     const gfx::RectF& dst_rect,
     const SkSamplingOptions& sampling,
-    const PaintFlags* flags) {
+    const cc::PaintFlags* flags) {
   cc::RecordPaintCanvas::DisableFlushCheckScope disable_flush_check_scope(
       static_cast<cc::RecordPaintCanvas*>(c));
   int initial_save_count = c->getSaveCount();
-  PaintFlags image_flags = *flags;
+  cc::PaintFlags image_flags = *flags;
 
   if (flags->getImageFilter()) {
     SkMatrix ctm = c->getTotalMatrix();
@@ -1536,7 +1536,7 @@
     c->save();
     c->concat(inv_ctm);
 
-    PaintFlags layer_flags;
+    cc::PaintFlags layer_flags;
     layer_flags.setBlendMode(flags->getBlendMode());
     layer_flags.setImageFilter(flags->getImageFilter());
 
@@ -1683,10 +1683,10 @@
 
   Draw<OverdrawOp::kDrawImage>(
       [this, image_source, image, src_rect, dst_rect](
-          cc::PaintCanvas* c, const PaintFlags* flags)  // draw lambda
+          cc::PaintCanvas* c, const cc::PaintFlags* flags)  // draw lambda
       {
         SkSamplingOptions sampling =
-            PaintFlags::FilterQualityToSkSamplingOptions(
+            cc::PaintFlags::FilterQualityToSkSamplingOptions(
                 flags ? flags->getFilterQuality()
                       : cc::PaintFlags::FilterQuality::kNone);
         DrawImageInternal(c, image_source, image.get(), src_rect, dst_rect,
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 467b2ad..925fcd8 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
@@ -439,7 +439,7 @@
   }
 
   void CheckOverdraw(const SkRect&,
-                     const PaintFlags*,
+                     const cc::PaintFlags*,
                      CanvasRenderingContext2DState::ImageType,
                      BaseRenderingContext2D::OverdrawOp overdraw_op);
 
@@ -542,7 +542,7 @@
                          const gfx::RectF& src_rect,
                          const gfx::RectF& dst_rect,
                          const SkSamplingOptions&,
-                         const PaintFlags*);
+                         const cc::PaintFlags*);
   void ClipInternal(const Path&,
                     const String& winding_rule_string,
                     UsePaintCache);
@@ -597,7 +597,7 @@
 
 ALWAYS_INLINE void BaseRenderingContext2D::CheckOverdraw(
     const SkRect& rect,
-    const PaintFlags* flags,
+    const cc::PaintFlags* flags,
     CanvasRenderingContext2DState::ImageType image_type,
     BaseRenderingContext2D::OverdrawOp overdraw_op) {
   // Note on performance: because this method is inlined, all conditional
@@ -657,14 +657,14 @@
                    paint_type, image_type);
   } else if (global_composite == SkBlendMode::kSrc) {
     ClearCanvasForSrcCompositeOp();  // Takes care of CheckOverdraw()
-    const PaintFlags* flags =
+    const cc::PaintFlags* flags =
         state.GetFlags(paint_type, kDrawForegroundOnly, image_type);
     draw_func(GetPaintCanvasForDraw(clip_bounds, draw_type), flags);
   } else {
     SkIRect dirty_rect;
     if (ComputeDirtyRect(gfx::SkRectToRectF(bounds), clip_bounds,
                          &dirty_rect)) {
-      const PaintFlags* flags =
+      const cc::PaintFlags* flags =
           state.GetFlags(paint_type, kDrawShadowAndForeground, image_type);
       if (paint_type != CanvasRenderingContext2DState::kStrokePaintType &&
           draw_covers_clip_bounds(clip_bounds)) {
@@ -744,17 +744,17 @@
           ShouldUseDropShadowPaintFilter(paint_type, image_type)));
   SkM44 ctm = c->getLocalToDevice();
   c->setMatrix(SkM44());
-  PaintFlags composite_flags;
+  cc::PaintFlags composite_flags;
   composite_flags.setBlendMode(state.GlobalComposite());
   if (state.ShouldDrawShadows()) {
     // unroll into two independently composited passes if drawing shadows
-    PaintFlags shadow_flags =
+    cc::PaintFlags shadow_flags =
         *state.GetFlags(paint_type, kDrawShadowOnly, image_type);
     int save_count = c->getSaveCount();
     c->save();
     if (canvas_filter ||
         ShouldUseDropShadowPaintFilter(paint_type, image_type)) {
-      PaintFlags foreground_flags =
+      cc::PaintFlags foreground_flags =
           *state.GetFlags(paint_type, kDrawForegroundOnly, image_type);
       shadow_flags.setImageFilter(sk_make_sp<ComposePaintFilter>(
           sk_make_sp<ComposePaintFilter>(foreground_flags.getImageFilter(),
@@ -780,7 +780,7 @@
 
   composite_flags.setImageFilter(std::move(canvas_filter));
   c->saveLayer(nullptr, &composite_flags);
-  PaintFlags foreground_flags =
+  cc::PaintFlags foreground_flags =
       *state.GetFlags(paint_type, kDrawForegroundOnly, image_type);
   foreground_flags.setBlendMode(SkBlendMode::kSrcOver);
   c->setMatrix(ctm);
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.cc
index 9ba47362..df8071f9 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.cc
@@ -167,7 +167,7 @@
   bounds = gfx::RectF(block->PhysicalVisualOverflowRect());
   auto* paint_record_builder = MakeGarbageCollected<PaintRecordBuilder>();
   PaintInfo paint_info(paint_record_builder->Context(), CullRect::Infinite(),
-                       PaintPhase::kForeground, kGlobalPaintNormalPhase, 0);
+                       PaintPhase::kForeground);
   NGBoxFragmentPainter(fragment).PaintObject(
       paint_info, PhysicalOffset(LayoutUnit(x), LayoutUnit(y)));
   return paint_record_builder->EndRecording();
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
index 8ad03ad0..79340a55 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -35,6 +35,7 @@
 
 #include "base/metrics/histogram_functions.h"
 #include "base/rand_util.h"
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
 #include "third_party/blink/public/common/privacy_budget/identifiability_metrics.h"
@@ -69,7 +70,6 @@
 #include "third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/stroke_data.h"
@@ -944,7 +944,8 @@
       canvas()->GetDocument(), GetState().GetFontDescription(), x, y,
       wrap_width, bounds);
   Draw<OverdrawOp::kNone>(
-      [recording](cc::PaintCanvas* c, const PaintFlags* flags)  // draw lambda
+      [recording](cc::PaintCanvas* c,
+                  const cc::PaintFlags* flags)  // draw lambda
       { c->drawPicture(recording); },
       [](const SkIRect& rect) { return false; }, gfx::RectFToSkRect(bounds),
       CanvasRenderingContext2DState::PaintType::kFillPaintType,
@@ -1052,7 +1053,7 @@
 
   Draw<OverdrawOp::kNone>(
       [this, text = std::move(text), direction, bidi_override, location](
-          cc::PaintCanvas* c, const PaintFlags* flags)  // draw lambda
+          cc::PaintCanvas* c, const cc::PaintFlags* flags)  // draw lambda
       {
         TextRun text_run(text, 0, 0, TextRun::kAllowTrailingExpansion,
                          direction, bidi_override);
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
index a020d1e..a61c2fe9 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
@@ -30,7 +30,6 @@
 #include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
 #include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/skia/include/effects/SkDashPathEffect.h"
@@ -85,15 +84,15 @@
       stroke_style_dirty_(true),
       line_dash_dirty_(false),
       image_smoothing_quality_(cc::PaintFlags::FilterQuality::kLow) {
-  fill_flags_.setStyle(PaintFlags::kFill_Style);
+  fill_flags_.setStyle(cc::PaintFlags::kFill_Style);
   fill_flags_.setAntiAlias(true);
-  image_flags_.setStyle(PaintFlags::kFill_Style);
+  image_flags_.setStyle(cc::PaintFlags::kFill_Style);
   image_flags_.setAntiAlias(true);
-  stroke_flags_.setStyle(PaintFlags::kStroke_Style);
+  stroke_flags_.setStyle(cc::PaintFlags::kStroke_Style);
   stroke_flags_.setStrokeWidth(1);
-  stroke_flags_.setStrokeCap(PaintFlags::kButt_Cap);
+  stroke_flags_.setStrokeCap(cc::PaintFlags::kButt_Cap);
   stroke_flags_.setStrokeMiter(10);
-  stroke_flags_.setStrokeJoin(PaintFlags::kMiter_Join);
+  stroke_flags_.setStrokeJoin(cc::PaintFlags::kMiter_Join);
   stroke_flags_.setAntiAlias(true);
   SetImageSmoothingEnabled(true);
 }
@@ -426,10 +425,10 @@
 
   // We can't reuse m_fillFlags and m_strokeFlags for the filter, since these
   // incorporate the global alpha, which isn't applicable here.
-  PaintFlags fill_flags_for_filter;
+  cc::PaintFlags fill_flags_for_filter;
   fill_style_->ApplyToFlags(fill_flags_for_filter);
   fill_flags_for_filter.setColor(fill_style_->PaintColor());
-  PaintFlags stroke_flags_for_filter;
+  cc::PaintFlags stroke_flags_for_filter;
   stroke_style_->ApplyToFlags(stroke_flags_for_filter);
   stroke_flags_for_filter.setColor(stroke_style_->PaintColor());
 
@@ -498,10 +497,10 @@
 
   // We can't reuse m_fillFlags and m_strokeFlags for the filter, since these
   // incorporate the global alpha, which isn't applicable here.
-  PaintFlags fill_flags_for_filter;
+  cc::PaintFlags fill_flags_for_filter;
   fill_style_->ApplyToFlags(fill_flags_for_filter);
   fill_flags_for_filter.setColor(fill_style_->PaintColor());
-  PaintFlags stroke_flags_for_filter;
+  cc::PaintFlags stroke_flags_for_filter;
   stroke_style_->ApplyToFlags(stroke_flags_for_filter);
   stroke_flags_for_filter.setColor(stroke_style_->PaintColor());
 
@@ -696,11 +695,11 @@
   image_flags_.setFilterQuality(filter_quality);
 }
 
-const PaintFlags* CanvasRenderingContext2DState::GetFlags(
+const cc::PaintFlags* CanvasRenderingContext2DState::GetFlags(
     PaintType paint_type,
     ShadowMode shadow_mode,
     ImageType image_type) const {
-  PaintFlags* flags;
+  cc::PaintFlags* flags;
   switch (paint_type) {
     case kStrokePaintType:
       UpdateLineDash();
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h
index 9326e7c..03f0c54 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h
@@ -13,7 +13,6 @@
 #include "third_party/blink/renderer/platform/fonts/font_selector_client.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_filter.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -188,14 +187,14 @@
   double LineWidth() const { return stroke_flags_.getStrokeWidth(); }
 
   void SetLineCap(LineCap line_cap) {
-    stroke_flags_.setStrokeCap(static_cast<PaintFlags::Cap>(line_cap));
+    stroke_flags_.setStrokeCap(static_cast<cc::PaintFlags::Cap>(line_cap));
   }
   LineCap GetLineCap() const {
     return static_cast<LineCap>(stroke_flags_.getStrokeCap());
   }
 
   void SetLineJoin(LineJoin line_join) {
-    stroke_flags_.setStrokeJoin(static_cast<PaintFlags::Join>(line_join));
+    stroke_flags_.setStrokeJoin(static_cast<cc::PaintFlags::Join>(line_join));
   }
   LineJoin GetLineJoin() const {
     return static_cast<LineJoin>(stroke_flags_.getStrokeJoin());
@@ -241,7 +240,9 @@
 
   // If paint will not be used for painting a bitmap, set bitmapOpacity to
   // Opaque.
-  const PaintFlags* GetFlags(PaintType, ShadowMode, ImageType = kNoImage) const;
+  const cc::PaintFlags* GetFlags(PaintType,
+                                 ShadowMode,
+                                 ImageType = kNoImage) const;
 
   SaveType GetSaveType() const { return save_type_; }
 
@@ -264,9 +265,9 @@
   Member<CanvasStyle> stroke_style_;
   Member<CanvasStyle> fill_style_;
 
-  mutable PaintFlags stroke_flags_;
-  mutable PaintFlags fill_flags_;
-  mutable PaintFlags image_flags_;
+  mutable cc::PaintFlags stroke_flags_;
+  mutable cc::PaintFlags fill_flags_;
+  mutable cc::PaintFlags image_flags_;
 
   gfx::Vector2dF shadow_offset_;
   double shadow_blur_;
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc
index bf585256..dff686e 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.cc
@@ -39,7 +39,6 @@
 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_gradient.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_pattern.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
 #include "third_party/skia/include/core/SkShader.h"
 
@@ -111,7 +110,7 @@
 CanvasStyle::CanvasStyle(CanvasPattern* pattern)
     : type_(kImagePattern), pattern_(pattern) {}
 
-void CanvasStyle::ApplyToFlags(PaintFlags& flags) const {
+void CanvasStyle::ApplyToFlags(cc::PaintFlags& flags) const {
   ImageDrawOptions draw_options;
   switch (type_) {
     case kColorRGBA:
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.h
index 96468d9..a2d04d9 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.h
@@ -27,8 +27,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_CANVAS2D_CANVAS_STYLE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_CANVAS2D_CANVAS_STYLE_H_
 
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -52,7 +52,7 @@
   CanvasGradient* GetCanvasGradient() const { return gradient_.Get(); }
   CanvasPattern* GetCanvasPattern() const { return pattern_; }
 
-  void ApplyToFlags(PaintFlags&) const;
+  void ApplyToFlags(cc::PaintFlags&) const;
   RGBA32 PaintColor() const;
 
   bool IsEquivalentRGBA(RGBA32 rgba) const {
diff --git a/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context_base.h b/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context_base.h
index 7e38191..c496a98a 100644
--- a/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/canvas/imagebitmap/image_bitmap_rendering_context_base.h
@@ -30,13 +30,6 @@
 
   void Trace(Visitor*) const override;
 
-  // TODO(juanmihd): Remove this method crbug.com/941579
-  HTMLCanvasElement* canvas() const {
-    if (Host()->IsOffscreenCanvas())
-      return nullptr;
-    return static_cast<HTMLCanvasElement*>(Host());
-  }
-
   bool CanCreateCanvas2dResourceProvider() const;
   V8UnionHTMLCanvasElementOrOffscreenCanvas* getHTMLOrOffscreenCanvas() const;
 
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
index d8e256a..b03dca4d 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
@@ -107,6 +107,7 @@
 
   ExecutionContext* execution_context = canvas->GetTopExecutionContext();
   if (auto* window = DynamicTo<LocalDOMWindow>(execution_context)) {
+    DCHECK(window->GetFrame());
     if (window->GetFrame()->GetSettings()->GetDisableReadingFromCanvas())
       canvas->SetDisableReadingFromCanvasTrue();
     return;
@@ -723,7 +724,7 @@
   Draw<OverdrawOp::kNone>(
       [this, text = std::move(text), direction, location](
           cc::PaintCanvas* paint_canvas,
-          const PaintFlags* flags) /* draw lambda */ {
+          const cc::PaintFlags* flags) /* draw lambda */ {
         TextRun text_run(text, 0, 0, TextRun::kAllowTrailingExpansion,
                          direction, false);
         text_run.SetNormalizeSpace(true);
diff --git a/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition.cc b/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition.cc
index 81f1e95..e659a35 100644
--- a/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition.cc
+++ b/third_party/blink/renderer/modules/csspaint/nativepaint/clip_path_paint_definition.cc
@@ -275,7 +275,7 @@
   auto* rendering_context = MakeGarbageCollected<PaintRenderingContext2D>(
       gfx::ToRoundedSize(container_size), context_settings, 1, 1);
 
-  PaintFlags flags;
+  cc::PaintFlags flags;
   flags.setAntiAlias(true);
   rendering_context->GetPaintCanvas()->drawPath(path.GetSkPath(), flags);
 
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.h b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.h
index 06dbdc1..50589a5 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.h
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.h
@@ -63,6 +63,10 @@
   }
 
  private:
+  network::mojom::RequestDestination GetDestination() const override {
+    return network::mojom::RequestDestination::kPaintWorklet;
+  }
+
   // Registers the global scope with a proxy client, if not already done. Only
   // used for worklet-thread bound PaintWorkletGlobalScopes.
   void RegisterWithProxyClientIfNeeded();
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
index 54e55b40..f4f2ca4f 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -236,7 +236,7 @@
     mojo::PendingRemote<mojom::blink::CacheStorage> cache_storage_remote,
     base::TimeTicks time_origin,
     const ServiceWorkerToken& service_worker_token)
-    : WorkerGlobalScope(std::move(creation_params), thread, time_origin),
+    : WorkerGlobalScope(std::move(creation_params), thread, time_origin, true),
       installed_scripts_manager_(std::move(installed_scripts_manager)),
       cache_storage_remote_(std::move(cache_storage_remote)),
       token_(service_worker_token) {
diff --git a/third_party/blink/renderer/modules/video_rvfc/video_frame_callback_requester_impl_test.cc b/third_party/blink/renderer/modules/video_rvfc/video_frame_callback_requester_impl_test.cc
index 0f2b6673..7756ddd4 100644
--- a/third_party/blink/renderer/modules/video_rvfc/video_frame_callback_requester_impl_test.cc
+++ b/third_party/blink/renderer/modules/video_rvfc/video_frame_callback_requester_impl_test.cc
@@ -38,20 +38,11 @@
                std::unique_ptr<VideoFramePresentationMetadata>());
 };
 
-class MockFunction : public ScriptFunction {
+class MockFunction : public NewScriptFunction::Callable {
  public:
-  static testing::StrictMock<MockFunction>* Create(ScriptState* script_state) {
-    return MakeGarbageCollected<testing::StrictMock<MockFunction>>(
-        script_state);
-  }
+  MockFunction() = default;
 
-  v8::Local<v8::Function> Bind() { return BindToV8Function(); }
-
-  MOCK_METHOD1(Call, ScriptValue(ScriptValue));
-
- protected:
-  explicit MockFunction(ScriptState* script_state)
-      : ScriptFunction(script_state) {}
+  MOCK_METHOD2(Call, ScriptValue(ScriptState*, ScriptValue));
 };
 
 // Helper class to wrap a VideoFramePresentationData, which can't have a copy
@@ -220,8 +211,11 @@
         now);
   }
 
-  V8VideoFrameRequestCallback* GetCallback(MockFunction* function) {
-    return V8VideoFrameRequestCallback::Create(function->Bind());
+  V8VideoFrameRequestCallback* GetCallback(ScriptState* script_state,
+                                           MockFunction* function) {
+    return V8VideoFrameRequestCallback::Create(
+        MakeGarbageCollected<NewScriptFunction>(script_state, function)
+            ->V8Function());
   }
 
   void RegisterCallbackDirectly(
@@ -251,16 +245,17 @@
 TEST_F(VideoFrameCallbackRequesterImplTest, VerifyRequestVideoFrameCallback) {
   V8TestingScope scope;
 
-  auto* function = MockFunction::Create(scope.GetScriptState());
+  auto* function = MakeGarbageCollected<MockFunction>();
 
   // Queuing up a video.rVFC call should propagate to the WebMediaPlayer.
   EXPECT_CALL(*media_player(), RequestVideoFrameCallback()).Times(1);
-  vfc_requester().requestVideoFrameCallback(GetCallback(function));
+  vfc_requester().requestVideoFrameCallback(
+      GetCallback(scope.GetScriptState(), function));
 
   testing::Mock::VerifyAndClear(media_player());
 
   // Callbacks should not be run immediately when a frame is presented.
-  EXPECT_CALL(*function, Call(_)).Times(0);
+  EXPECT_CALL(*function, Call(_, _)).Times(0);
   SimulateFramePresented();
 
   testing::Mock::VerifyAndClear(function);
@@ -269,7 +264,7 @@
   auto metadata = std::make_unique<VideoFramePresentationMetadata>();
   metadata->presented_frames = 1;
 
-  EXPECT_CALL(*function, Call(_)).Times(1);
+  EXPECT_CALL(*function, Call(_, _)).Times(1);
   EXPECT_CALL(*media_player(), GetVideoFramePresentationMetadata())
       .WillOnce(Return(ByMove(std::move(metadata))));
   SimulateVideoFrameCallback(base::TimeTicks::Now());
@@ -281,14 +276,14 @@
        VerifyCancelVideoFrameCallback_BeforePresentedFrame) {
   V8TestingScope scope;
 
-  auto* function = MockFunction::Create(scope.GetScriptState());
+  auto* function = MakeGarbageCollected<MockFunction>();
 
   // Queue and cancel a request before a frame is presented.
-  int callback_id =
-      vfc_requester().requestVideoFrameCallback(GetCallback(function));
+  int callback_id = vfc_requester().requestVideoFrameCallback(
+      GetCallback(scope.GetScriptState(), function));
   vfc_requester().cancelVideoFrameCallback(callback_id);
 
-  EXPECT_CALL(*function, Call(_)).Times(0);
+  EXPECT_CALL(*function, Call(_, _)).Times(0);
   SimulateFramePresented();
   SimulateVideoFrameCallback(base::TimeTicks::Now());
 
@@ -299,15 +294,15 @@
        VerifyCancelVideoFrameCallback_AfterPresentedFrame) {
   V8TestingScope scope;
 
-  auto* function = MockFunction::Create(scope.GetScriptState());
+  auto* function = MakeGarbageCollected<MockFunction>();
 
   // Queue a request.
-  int callback_id =
-      vfc_requester().requestVideoFrameCallback(GetCallback(function));
+  int callback_id = vfc_requester().requestVideoFrameCallback(
+      GetCallback(scope.GetScriptState(), function));
   SimulateFramePresented();
 
   // The callback should be scheduled for execution, but not yet run.
-  EXPECT_CALL(*function, Call(_)).Times(0);
+  EXPECT_CALL(*function, Call(_, _)).Times(0);
   vfc_requester().cancelVideoFrameCallback(callback_id);
   SimulateVideoFrameCallback(base::TimeTicks::Now());
 
@@ -318,14 +313,15 @@
        VerifyClearedMediaPlayerCancelsPendingExecution) {
   V8TestingScope scope;
 
-  auto* function = MockFunction::Create(scope.GetScriptState());
+  auto* function = MakeGarbageCollected<MockFunction>();
 
   // Queue a request.
-  vfc_requester().requestVideoFrameCallback(GetCallback(function));
+  vfc_requester().requestVideoFrameCallback(
+      GetCallback(scope.GetScriptState(), function));
   SimulateFramePresented();
 
   // The callback should be scheduled for execution, but not yet run.
-  EXPECT_CALL(*function, Call(_)).Times(0);
+  EXPECT_CALL(*function, Call(_, _)).Times(0);
 
   // Simulate the HTMLVideoElement getting changing its WebMediaPlayer.
   vfc_requester().OnWebMediaPlayerCleared();
@@ -374,8 +370,9 @@
 
   testing::Mock::VerifyAndClear(media_player());
 
-  auto* function = MockFunction::Create(scope.GetScriptState());
-  vfc_requester().requestVideoFrameCallback(GetCallback(function));
+  auto* function = MakeGarbageCollected<MockFunction>();
+  vfc_requester().requestVideoFrameCallback(
+      GetCallback(scope.GetScriptState(), function));
 
   // Immersive frames should trigger video frame updates when there are pending
   // callbacks.
@@ -389,9 +386,10 @@
 TEST_F(VideoFrameCallbackRequesterImplNullMediaPlayerTest, VerifyNoCrash) {
   V8TestingScope scope;
 
-  auto* function = MockFunction::Create(scope.GetScriptState());
+  auto* function = MakeGarbageCollected<MockFunction>();
 
-  vfc_requester().requestVideoFrameCallback(GetCallback(function));
+  vfc_requester().requestVideoFrameCallback(
+      GetCallback(scope.GetScriptState(), function));
 
   SimulateFramePresented();
   SimulateVideoFrameCallback(base::TimeTicks::Now());
diff --git a/third_party/blink/renderer/modules/webaudio/analyser_node.cc b/third_party/blink/renderer/modules/webaudio/analyser_node.cc
index 7d717a83..7e4099b1 100644
--- a/third_party/blink/renderer/modules/webaudio/analyser_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/analyser_node.cc
@@ -78,8 +78,9 @@
   // audio data through unchanged if the channel count matches from input to
   // output (resulting in inputBus == outputBus). Otherwise, do an up-mix to
   // stereo.
-  if (input_bus != output_bus)
+  if (input_bus != output_bus) {
     output_bus->CopyFrom(*input_bus);
+  }
 }
 
 void AnalyserHandler::SetFftSize(unsigned size,
@@ -211,8 +212,9 @@
 
   AnalyserNode* node = Create(*context, exception_state);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
   node->HandleChannelOptions(options, exception_state);
 
diff --git a/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.cc b/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.cc
index 61d1d62..3d1ec0b 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.cc
@@ -49,8 +49,9 @@
 }
 
 void AudioBasicProcessorHandler::Initialize() {
-  if (IsInitialized())
+  if (IsInitialized()) {
     return;
+  }
 
   DCHECK(Processor());
   Processor()->Initialize();
@@ -59,8 +60,9 @@
 }
 
 void AudioBasicProcessorHandler::Uninitialize() {
-  if (!IsInitialized())
+  if (!IsInitialized()) {
     return;
+  }
 
   DCHECK(Processor());
   Processor()->Uninitialize();
@@ -79,8 +81,9 @@
 
     // FIXME: if we take "tail time" into account, then we can avoid calling
     // processor()->process() once the tail dies down.
-    if (!Input(0).IsConnected())
+    if (!Input(0).IsConnected()) {
       source_bus->Zero();
+    }
 
     Processor()->Process(source_bus.get(), destination_bus, frames_to_process);
   }
@@ -88,8 +91,9 @@
 
 void AudioBasicProcessorHandler::ProcessOnlyAudioParams(
     uint32_t frames_to_process) {
-  if (!IsInitialized() || !Processor())
+  if (!IsInitialized() || !Processor()) {
     return;
+  }
 
   Processor()->ProcessOnlyAudioParams(frames_to_process);
 }
diff --git a/third_party/blink/renderer/modules/webaudio/audio_buffer.cc b/third_party/blink/renderer/modules/webaudio/audio_buffer.cc
index 111fad79..f652c37 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_buffer.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_buffer.cc
@@ -44,14 +44,16 @@
                                  float sample_rate) {
   if (!audio_utilities::IsValidAudioBufferSampleRate(sample_rate) ||
       number_of_channels > BaseAudioContext::MaxNumberOfChannels() ||
-      !number_of_channels || !number_of_frames)
+      !number_of_channels || !number_of_frames) {
     return nullptr;
+  }
 
   AudioBuffer* buffer = MakeGarbageCollected<AudioBuffer>(
       number_of_channels, number_of_frames, sample_rate);
 
-  if (!buffer->CreatedSuccessfully(number_of_channels))
+  if (!buffer->CreatedSuccessfully(number_of_channels)) {
     return nullptr;
+  }
   return buffer;
 }
 
@@ -116,23 +118,27 @@
                                               float sample_rate) {
   if (!audio_utilities::IsValidAudioBufferSampleRate(sample_rate) ||
       number_of_channels > BaseAudioContext::MaxNumberOfChannels() ||
-      !number_of_channels || !number_of_frames)
+      !number_of_channels || !number_of_frames) {
     return nullptr;
+  }
 
   AudioBuffer* buffer = MakeGarbageCollected<AudioBuffer>(
       number_of_channels, number_of_frames, sample_rate, kDontInitialize);
 
-  if (!buffer->CreatedSuccessfully(number_of_channels))
+  if (!buffer->CreatedSuccessfully(number_of_channels)) {
     return nullptr;
+  }
   return buffer;
 }
 
 AudioBuffer* AudioBuffer::CreateFromAudioBus(AudioBus* bus) {
-  if (!bus)
+  if (!bus) {
     return nullptr;
+  }
   AudioBuffer* buffer = MakeGarbageCollected<AudioBuffer>(bus);
-  if (buffer->CreatedSuccessfully(bus->NumberOfChannels()))
+  if (buffer->CreatedSuccessfully(bus->NumberOfChannels())) {
     return buffer;
+  }
   return nullptr;
 }
 
@@ -161,8 +167,9 @@
         CreateFloat32ArrayOrNull(length_, policy);
     // If the channel data array could not be created, just return. The caller
     // will need to check that the desired number of channels were created.
-    if (!channel_data_array)
+    if (!channel_data_array) {
       return;
+    }
 
     channels_.push_back(channel_data_array);
   }
@@ -178,8 +185,9 @@
         CreateFloat32ArrayOrNull(length_, kDontInitialize);
     // If the channel data array could not be created, just return. The caller
     // will need to check that the desired number of channels were created.
-    if (!channel_data_array)
+    if (!channel_data_array) {
       return;
+    }
 
     const float* src = bus->Channel(i)->Data();
     float* dst = channel_data_array->Data();
@@ -204,8 +212,9 @@
 }
 
 NotShared<DOMFloat32Array> AudioBuffer::getChannelData(unsigned channel_index) {
-  if (channel_index >= channels_.size())
+  if (channel_index >= channels_.size()) {
     return NotShared<DOMFloat32Array>(nullptr);
+  }
 
   return NotShared<DOMFloat32Array>(channels_[channel_index].Get());
 }
diff --git a/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.cc b/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.cc
index b6ab78c..8a84934 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.cc
@@ -130,8 +130,9 @@
       return;
     }
 
-    for (unsigned i = 0; i < output_bus->NumberOfChannels(); ++i)
+    for (unsigned i = 0; i < output_bus->NumberOfChannels(); ++i) {
       destination_channels_[i] = output_bus->Channel(i)->MutableData();
+    }
 
     // Render by reading directly from the buffer.
     if (!RenderFromBuffer(output_bus, quantum_frame_offset,
@@ -160,9 +161,10 @@
       // We're not looping and we've reached the end of the sample data, but we
       // still need to provide more output, so generate silence for the
       // remaining.
-      for (unsigned i = 0; i < NumberOfChannels(); ++i)
+      for (unsigned i = 0; i < NumberOfChannels(); ++i) {
         memset(destination_channels_[i] + index, 0,
                sizeof(float) * frames_to_process);
+      }
     }
 
     Finish();
@@ -200,9 +202,10 @@
 
   // Potentially zero out initial frames leading up to the offset.
   if (destination_frame_offset) {
-    for (unsigned i = 0; i < number_of_channels; ++i)
+    for (unsigned i = 0; i < number_of_channels; ++i) {
       memset(destination_channels_[i], 0,
              sizeof(float) * destination_frame_offset);
+    }
   }
 
   // Offset the pointers to the correct offset frame.
@@ -220,8 +223,9 @@
           : buffer_length;
 
   // Do some sanity checking.
-  if (end_frame > buffer_length)
+  if (end_frame > buffer_length) {
     end_frame = buffer_length;
+  }
 
   // If the .loop attribute is true, then values of
   // m_loopStart == 0 && m_loopEnd == 0 implies that we should use the entire
@@ -253,8 +257,9 @@
   double computed_playback_rate = ComputePlaybackRate();
 
   // Sanity check that our playback rate isn't larger than the loop size.
-  if (computed_playback_rate > virtual_delta_frames)
+  if (computed_playback_rate > virtual_delta_frames) {
     return false;
+  }
 
   // Get local copy.
   double virtual_read_index = virtual_read_index_;
@@ -328,8 +333,9 @@
       if (read_index >= end_frame) {
         read_index -= delta_frames;
         if (RenderSilenceAndFinishIfNotLooping(bus, write_index,
-                                               frames_to_process))
+                                               frames_to_process)) {
           break;
+        }
       }
     }
     virtual_read_index = read_index;
@@ -353,8 +359,9 @@
       // Final sanity check on buffer access.
       // FIXME: as an optimization, try to get rid of this inner-loop check and
       // put assertions and guards before the loop.
-      if (read_index >= buffer_length || read_index2 >= buffer_length)
+      if (read_index >= buffer_length || read_index2 >= buffer_length) {
         break;
+      }
 
       // Linear interpolation.
       for (unsigned i = 0; i < number_of_channels; ++i) {
@@ -391,8 +398,9 @@
       if (virtual_read_index >= virtual_end_frame) {
         virtual_read_index -= virtual_delta_frames;
         if (RenderSilenceAndFinishIfNotLooping(bus, write_index,
-                                               frames_to_process))
+                                               frames_to_process)) {
           break;
+        }
       }
     }
   }
@@ -460,8 +468,9 @@
     // If this is a grain (as set by a previous call to start()), validate the
     // grain parameters now since it wasn't validated when start was called
     // (because there was no buffer then).
-    if (is_grain_)
+    if (is_grain_) {
       ClampGrainParameters(shared_buffer_.get());
+    }
   }
 
   virtual_read_index_ = 0;
@@ -484,8 +493,9 @@
   // If the duration was not explicitly given, use the buffer duration to set
   // the grain duration. Otherwise, we want to use the user-specified value, of
   // course.
-  if (!is_duration_given_)
+  if (!is_duration_given_) {
     grain_duration_ = buffer_duration - grain_offset_;
+  }
 
   if (is_duration_given_ && Loop()) {
     // We're looping a grain with a grain duration specified. Schedule the loop
@@ -580,8 +590,9 @@
   // So just set startTime to currentTime in this case to start the source now.
   start_time_ = std::max(when, Context()->currentTime());
 
-  if (Buffer())
+  if (Buffer()) {
     ClampGrainParameters(Buffer());
+  }
 
   SetPlaybackState(SCHEDULED_STATE);
 }
@@ -657,8 +668,9 @@
 bool AudioBufferSourceHandler::PropagatesSilence() const {
   DCHECK(Context()->IsAudioThread());
 
-  if (!IsPlayingOrScheduled() || HasFinished())
+  if (!IsPlayingOrScheduled() || HasFinished()) {
     return true;
+  }
 
   // Protect |shared_buffer_| with tryLock because it can be accessed by the
   // main thread.
@@ -771,11 +783,13 @@
 
   AudioBufferSourceNode* node = Create(*context, exception_state);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
-  if (options->hasBuffer())
+  if (options->hasBuffer()) {
     node->setBuffer(options->buffer(), exception_state);
+  }
   node->detune()->setValue(options->detune());
   node->setLoop(options->loop());
   node->setLoopEnd(options->loopEnd());
@@ -804,8 +818,9 @@
 void AudioBufferSourceNode::setBuffer(AudioBuffer* new_buffer,
                                       ExceptionState& exception_state) {
   GetAudioBufferSourceHandler().SetBuffer(new_buffer, exception_state);
-  if (!exception_state.HadException())
+  if (!exception_state.HadException()) {
     buffer_ = new_buffer;
+  }
 }
 
 AudioParam* AudioBufferSourceNode::playbackRate() const {
diff --git a/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.h b/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.h
index 68ca60f..a8e0e86 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.h
@@ -146,8 +146,9 @@
 
   bool DidSetLooping() const { return did_set_looping_; }
   void SetDidSetLooping(bool loop) {
-    if (loop)
+    if (loop) {
       did_set_looping_ = true;
+    }
   }
 
   // If m_isLooping is false, then this node will be done playing and become
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.cc b/third_party/blink/renderer/modules/webaudio/audio_context.cc
index 1c0f30808..4bf1e17 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_context.cc
@@ -269,8 +269,9 @@
     suspended_by_user_ = true;
 
     // Stop rendering now.
-    if (destination())
+    if (destination()) {
       SuspendRendering();
+    }
 
     // Since we don't have any way of knowing when the hardware actually stops,
     // we'll just resolve the promise now.
@@ -330,8 +331,9 @@
 bool AudioContext::IsPullingAudioGraph() const {
   DCHECK(IsMainThread());
 
-  if (!destination())
+  if (!destination()) {
     return false;
+  }
 
   RealtimeAudioDestinationHandler& destination_handler =
       static_cast<RealtimeAudioDestinationHandler&>(
@@ -348,8 +350,9 @@
 
   DCHECK(IsMainThread());
   LocalDOMWindow* window = LocalDOMWindow::From(script_state);
-  if (!window)
+  if (!window) {
     return result;
+  }
 
   if (!destination()) {
     result->setContextTime(0.0);
@@ -370,8 +373,9 @@
 
   double performance_time = performance->MonotonicTimeToDOMHighResTimeStamp(
       base::TimeTicks() + base::Seconds(position.timestamp));
-  if (performance_time < 0.0)
+  if (performance_time < 0.0) {
     performance_time = 0.0;
+  }
 
   result->setContextTime(position.position);
   result->setPerformanceTime(performance_time);
@@ -408,8 +412,9 @@
 void AudioContext::DidClose() {
   SetContextState(kClosed);
 
-  if (close_resolver_)
+  if (close_resolver_) {
     close_resolver_->Resolve();
+  }
 }
 
 bool AudioContext::IsContextCleared() const {
@@ -420,8 +425,9 @@
   DCHECK(IsMainThread());
   SendLogMessage(String::Format("%s", __func__));
 
-  if (!keep_alive_)
+  if (!keep_alive_) {
     keep_alive_ = this;
+  }
   BaseAudioContext::StartRendering();
 }
 
@@ -489,8 +495,9 @@
   DCHECK(IsMainThread());
 
   source_node_started_ = true;
-  if (!user_gesture_required_)
+  if (!user_gesture_required_) {
     return;
+  }
 
   MaybeAllowAutoplayWithUnlockType(AutoplayUnlockType::kSourceNodeStart);
 
@@ -540,8 +547,9 @@
 }
 
 void AudioContext::MaybeAllowAutoplayWithUnlockType(AutoplayUnlockType type) {
-  if (!user_gesture_required_ || !AreAutoplayRequirementsFulfilled())
+  if (!user_gesture_required_ || !AreAutoplayRequirementsFulfilled()) {
     return;
+  }
 
   DCHECK(!autoplay_status_.has_value() ||
          autoplay_status_ != AutoplayStatus::kSucceeded);
@@ -554,8 +562,9 @@
 }
 
 bool AudioContext::IsAllowedToStart() const {
-  if (!user_gesture_required_)
+  if (!user_gesture_required_) {
     return true;
+  }
 
   LocalDOMWindow* window = To<LocalDOMWindow>(GetExecutionContext());
   DCHECK(window);
@@ -586,8 +595,9 @@
 }
 
 void AudioContext::RecordAutoplayMetrics() {
-  if (!autoplay_status_.has_value() || !GetDocument())
+  if (!autoplay_status_.has_value() || !GetDocument()) {
     return;
+  }
 
   ukm::UkmRecorder* ukm_recorder = GetDocument()->UkmRecorder();
   DCHECK(ukm_recorder);
@@ -665,8 +675,9 @@
   DCHECK(IsMainThread());
 
   EnsureAudioContextManagerService();
-  if (audio_context_manager_.is_bound())
+  if (audio_context_manager_.is_bound()) {
     audio_context_manager_->AudioContextAudiblePlaybackStarted(context_id_);
+  }
 }
 
 void AudioContext::HandlePostRenderTasks() {
@@ -756,13 +767,15 @@
   DCHECK(IsMainThread());
 
   EnsureAudioContextManagerService();
-  if (audio_context_manager_.is_bound())
+  if (audio_context_manager_.is_bound()) {
     audio_context_manager_->AudioContextAudiblePlaybackStopped(context_id_);
+  }
 }
 
 void AudioContext::EnsureAudioContextManagerService() {
-  if (audio_context_manager_.is_bound() || !GetDocument())
+  if (audio_context_manager_.is_bound() || !GetDocument()) {
     return;
+  }
 
   GetDocument()->GetFrame()->GetBrowserInterfaceBroker().GetInterface(
       mojo::GenericPendingReceiver(
diff --git a/third_party/blink/renderer/modules/webaudio/audio_graph_tracer.cc b/third_party/blink/renderer/modules/webaudio/audio_graph_tracer.cc
index 66df7cb..f83240d4 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_graph_tracer.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_graph_tracer.cc
@@ -32,72 +32,84 @@
 
 void AudioGraphTracer::SetInspectorAgent(InspectorWebAudioAgent* agent) {
   inspector_agent_ = agent;
-  if (!inspector_agent_)
+  if (!inspector_agent_) {
     return;
-  for (const auto& context : contexts_)
+  }
+  for (const auto& context : contexts_) {
     inspector_agent_->DidCreateBaseAudioContext(context);
+  }
 }
 
 void AudioGraphTracer::DidCreateBaseAudioContext(BaseAudioContext* context) {
   DCHECK(!contexts_.Contains(context));
 
   contexts_.insert(context);
-  if (inspector_agent_)
+  if (inspector_agent_) {
     inspector_agent_->DidCreateBaseAudioContext(context);
+  }
 }
 
 void AudioGraphTracer::WillDestroyBaseAudioContext(BaseAudioContext* context) {
   DCHECK(contexts_.Contains(context));
 
   contexts_.erase(context);
-  if (inspector_agent_)
+  if (inspector_agent_) {
     inspector_agent_->WillDestroyBaseAudioContext(context);
+  }
 }
 
 void AudioGraphTracer::DidChangeBaseAudioContext(BaseAudioContext* context) {
   DCHECK(contexts_.Contains(context));
 
-  if (inspector_agent_)
+  if (inspector_agent_) {
     inspector_agent_->DidChangeBaseAudioContext(context);
+  }
 }
 
 BaseAudioContext* AudioGraphTracer::GetContextById(String contextId) {
   for (const auto& context : contexts_) {
-    if (context->Uuid() == contextId)
+    if (context->Uuid() == contextId) {
       return context;
+    }
   }
 
   return nullptr;
 }
 
 void AudioGraphTracer::DidCreateAudioListener(AudioListener* listener) {
-  if (inspector_agent_)
+  if (inspector_agent_) {
     inspector_agent_->DidCreateAudioListener(listener);
+  }
 }
 
 void AudioGraphTracer::WillDestroyAudioListener(AudioListener* listener) {
-  if (inspector_agent_)
+  if (inspector_agent_) {
     inspector_agent_->WillDestroyAudioListener(listener);
+  }
 }
 
 void AudioGraphTracer::DidCreateAudioNode(AudioNode* node) {
-  if (inspector_agent_)
+  if (inspector_agent_) {
     inspector_agent_->DidCreateAudioNode(node);
+  }
 }
 
 void AudioGraphTracer::WillDestroyAudioNode(AudioNode* node) {
-  if (inspector_agent_ && contexts_.Contains(node->context()))
+  if (inspector_agent_ && contexts_.Contains(node->context())) {
     inspector_agent_->WillDestroyAudioNode(node);
+  }
 }
 
 void AudioGraphTracer::DidCreateAudioParam(AudioParam* param) {
-  if (inspector_agent_)
+  if (inspector_agent_) {
     inspector_agent_->DidCreateAudioParam(param);
+  }
 }
 
 void AudioGraphTracer::WillDestroyAudioParam(AudioParam* param) {
-  if (inspector_agent_ && contexts_.Contains(param->Context()))
+  if (inspector_agent_ && contexts_.Contains(param->Context())) {
     inspector_agent_->WillDestroyAudioParam(param);
+  }
 }
 
 void AudioGraphTracer::DidConnectNodes(AudioNode* source_node,
diff --git a/third_party/blink/renderer/modules/webaudio/audio_listener.cc b/third_party/blink/renderer/modules/webaudio/audio_listener.cc
index 781b7e7a..d67bc27 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_listener.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_listener.cc
@@ -292,9 +292,10 @@
 void AudioListener::CreateAndLoadHRTFDatabaseLoader(float sample_rate) {
   DCHECK(IsMainThread());
 
-  if (!hrtf_database_loader_)
+  if (!hrtf_database_loader_) {
     hrtf_database_loader_ =
         HRTFDatabaseLoader::CreateAndLoadAsynchronouslyIfNecessary(sample_rate);
+  }
 }
 
 bool AudioListener::IsHRTFDatabaseLoaded() {
@@ -302,14 +303,16 @@
 }
 
 void AudioListener::WaitForHRTFDatabaseLoaderThreadCompletion() {
-  if (hrtf_database_loader_)
+  if (hrtf_database_loader_) {
     hrtf_database_loader_->WaitForLoaderThreadCompletion();
+  }
 }
 
 void AudioListener::MarkPannersAsDirty(unsigned type) {
   DCHECK(IsMainThread());
-  for (PannerHandler* panner : panners_)
+  for (PannerHandler* panner : panners_) {
     panner->MarkPannerAsDirty(type);
+  }
 }
 
 void AudioListener::setPosition(const gfx::Point3F& position,
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node.cc b/third_party/blink/renderer/modules/webaudio/audio_node.cc
index 3832401..72dd3cc 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_node.cc
@@ -115,8 +115,9 @@
   deferred_task_handler_->RemoveChangedChannelCountMode(this);
   deferred_task_handler_->RemoveChangedChannelInterpretation(this);
   deferred_task_handler_->RemoveAutomaticPullNode(this);
-  for (auto& output : outputs_)
+  for (auto& output : outputs_) {
     output->Dispose();
+  }
 }
 
 AudioNode* AudioHandler::GetNode() const {
@@ -244,8 +245,9 @@
       channel_count <= BaseAudioContext::MaxNumberOfChannels()) {
     if (channel_count_ != channel_count) {
       channel_count_ = channel_count;
-      if (channel_count_mode_ != kMax)
+      if (channel_count_mode_ != kMax) {
         UpdateChannelsForInputs();
+      }
     }
   } else {
     exception_state.ThrowDOMException(
@@ -291,8 +293,9 @@
     NOTREACHED();
   }
 
-  if (new_channel_count_mode_ != old_mode)
+  if (new_channel_count_mode_ != old_mode) {
     Context()->GetDeferredTaskHandler().AddChangedChannelCountMode(this);
+  }
 }
 
 String AudioHandler::ChannelInterpretation() {
@@ -324,20 +327,23 @@
     NOTREACHED();
   }
 
-  if (new_channel_interpretation_ != old_mode)
+  if (new_channel_interpretation_ != old_mode) {
     Context()->GetDeferredTaskHandler().AddChangedChannelInterpretation(this);
+  }
 }
 
 void AudioHandler::UpdateChannelsForInputs() {
-  for (auto& input : inputs_)
+  for (auto& input : inputs_) {
     input->ChangedOutputs();
+  }
 }
 
 void AudioHandler::ProcessIfNecessary(uint32_t frames_to_process) {
   DCHECK(Context()->IsAudioThread());
 
-  if (!IsInitialized())
+  if (!IsInitialized()) {
     return;
+  }
 
   TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("webaudio.audionode"),
                "AudioHandler::ProcessIfNecessary", "this",
@@ -407,26 +413,30 @@
   DCHECK(Context()->IsAudioThread());
 
   // Process all of the AudioNodes connected to our inputs.
-  for (auto& input : inputs_)
+  for (auto& input : inputs_) {
     input->Pull(nullptr, frames_to_process);
+  }
 }
 
 bool AudioHandler::InputsAreSilent() {
   for (auto& input : inputs_) {
-    if (!input->Bus()->IsSilent())
+    if (!input->Bus()->IsSilent()) {
       return false;
+    }
   }
   return true;
 }
 
 void AudioHandler::SilenceOutputs() {
-  for (auto& output : outputs_)
+  for (auto& output : outputs_) {
     output->Bus()->Zero();
+  }
 }
 
 void AudioHandler::UnsilenceOutputs() {
-  for (auto& output : outputs_)
+  for (auto& output : outputs_) {
     output->Bus()->ClearSilentFlag();
+  }
 }
 
 void AudioHandler::EnableOutputsIfNecessary() {
@@ -448,8 +458,9 @@
 
   if (is_disabled_ && connection_ref_count_ > 0) {
     is_disabled_ = false;
-    for (auto& output : outputs_)
+    for (auto& output : outputs_) {
       output->Enable();
+    }
   }
 }
 
@@ -486,8 +497,9 @@
     // the outputs so that the tail for the node can be output.
     // Otherwise, we can disable the outputs right away.
     if (RequiresTailProcessing()) {
-      if (deferred_task_handler_->AcceptsTailProcessing())
+      if (deferred_task_handler_->AcceptsTailProcessing()) {
         deferred_task_handler_->AddTailProcessingHandler(this);
+      }
     } else {
       DisableOutputs();
     }
@@ -496,8 +508,9 @@
 
 void AudioHandler::DisableOutputs() {
   is_disabled_ = true;
-  for (auto& output : outputs_)
+  for (auto& output : outputs_) {
     output->Disable();
+  }
 }
 
 void AudioHandler::MakeConnection() {
@@ -530,8 +543,9 @@
           node_count_[GetNodeType()], Context()->currentTime());
 #endif
 
-  if (!connection_ref_count_)
+  if (!connection_ref_count_) {
     DisableOutputsIfNecessary();
+  }
 }
 
 #if DEBUG_AUDIONODE_REFERENCES
@@ -657,8 +671,9 @@
   // construction is completed. The actual report will be done in the subclass
   // implementation. (A destination node is owned by the context and will be
   // reported by it.)
-  if (handler_->GetNodeType() != AudioHandler::NodeType::kNodeTypeDestination)
+  if (handler_->GetNodeType() != AudioHandler::NodeType::kNodeTypeDestination) {
     ReportDidCreate();
+  }
 
 #if DEBUG_AUDIONODE_REFERENCES
   fprintf(stderr, "[%16p]: %16p: %2d: AudioNode::AudioNode %16p\n", context(),
@@ -686,12 +701,15 @@
                                      ExceptionState& exception_state) {
   DCHECK(IsMainThread());
 
-  if (options->hasChannelCount())
+  if (options->hasChannelCount()) {
     setChannelCount(options->channelCount(), exception_state);
-  if (options->hasChannelCountMode())
+  }
+  if (options->hasChannelCountMode()) {
     setChannelCountMode(options->channelCountMode(), exception_state);
-  if (options->hasChannelInterpretation())
+  }
+  if (options->hasChannelInterpretation()) {
     setChannelInterpretation(options->channelInterpretation(), exception_state);
+  }
 }
 
 BaseAudioContext* AudioNode::context() const {
@@ -832,8 +850,9 @@
   AudioNodeOutput& output = Handler().Output(output_index);
   AudioNodeInput& input =
       destination.Handler().Input(input_index_of_destination);
-  if (!AudioNodeWiring::IsConnected(output, input))
+  if (!AudioNodeWiring::IsConnected(output, input)) {
     return false;
+  }
   AudioNodeWiring::Disconnect(output, input);
   connected_nodes_[output_index]->erase(&destination);
   return true;
@@ -842,8 +861,9 @@
 bool AudioNode::DisconnectFromOutputIfConnected(unsigned output_index,
                                                 AudioParam& param) {
   AudioNodeOutput& output = Handler().Output(output_index);
-  if (!AudioNodeWiring::IsConnected(output, param.Handler()))
+  if (!AudioNodeWiring::IsConnected(output, param.Handler())) {
     return false;
+  }
   AudioNodeWiring::Disconnect(output, param.Handler());
   connected_params_[output_index]->erase(&param);
   return true;
@@ -854,8 +874,9 @@
   BaseAudioContext::GraphAutoLocker locker(context());
 
   // Disconnect all outgoing connections.
-  for (unsigned i = 0; i < numberOfOutputs(); ++i)
+  for (unsigned i = 0; i < numberOfOutputs(); ++i) {
     DisconnectAllFromOutput(i);
+  }
 
   Handler().UpdatePullStatusIfNeeded();
 
@@ -908,8 +929,9 @@
     for (unsigned input_index = 0;
          input_index < destination->Handler().NumberOfInputs(); ++input_index) {
       if (DisconnectFromOutputIfConnected(output_index, *destination,
-                                          input_index))
+                                          input_index)) {
         number_of_disconnections++;
+      }
     }
   }
 
@@ -958,8 +980,9 @@
   for (unsigned input_index = 0; input_index < destination->numberOfInputs();
        ++input_index) {
     if (DisconnectFromOutputIfConnected(output_index, *destination,
-                                        input_index))
+                                        input_index)) {
       number_of_disconnections++;
+    }
   }
 
   // If there is no connection to the destination, throw an exception.
@@ -1049,8 +1072,9 @@
   // Disconnect if connected and increase |numberOfDisconnectios| by 1.
   for (unsigned output_index = 0; output_index < Handler().NumberOfOutputs();
        ++output_index) {
-    if (DisconnectFromOutputIfConnected(output_index, *destination_param))
+    if (DisconnectFromOutputIfConnected(output_index, *destination_param)) {
       number_of_disconnections++;
+    }
   }
 
   // Throw an exception when there is no valid connection to the destination.
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node_input.cc b/third_party/blink/renderer/modules/webaudio/audio_node_input.cc
index 438f27c..b91579b 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_node_input.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_node_input.cc
@@ -56,8 +56,9 @@
 
   unsigned number_of_input_channels = NumberOfChannels();
 
-  if (number_of_input_channels == internal_summing_bus_->NumberOfChannels())
+  if (number_of_input_channels == internal_summing_bus_->NumberOfChannels()) {
     return;
+  }
 
   internal_summing_bus_ = AudioBus::Create(
       number_of_input_channels, GetDeferredTaskHandler().RenderQuantumFrames());
@@ -65,8 +66,9 @@
 
 unsigned AudioNodeInput::NumberOfChannels() const {
   AudioHandler::ChannelCountMode mode = Handler().InternalChannelCountMode();
-  if (mode == AudioHandler::kExplicit)
+  if (mode == AudioHandler::kExplicit) {
     return Handler().ChannelCount();
+  }
 
   // Find the number of channels of the connection with the largest number of
   // channels.
@@ -79,9 +81,10 @@
     max_channels = std::max(max_channels, output->NumberOfChannels());
   }
 
-  if (mode == AudioHandler::kClampedMax)
+  if (mode == AudioHandler::kClampedMax) {
     max_channels =
         std::min(max_channels, static_cast<unsigned>(Handler().ChannelCount()));
+  }
 
   return max_channels;
 }
@@ -91,8 +94,9 @@
 
   // Handle single connection specially to allow for in-place processing.
   if (NumberOfRenderingConnections() == 1 &&
-      Handler().InternalChannelCountMode() == AudioHandler::kMax)
+      Handler().InternalChannelCountMode() == AudioHandler::kMax) {
     return RenderingOutput(0)->Bus();
+  }
 
   // Multiple connections case or complex ChannelCountMode (or no connections).
   return InternalSummingBus();
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node_output.cc b/third_party/blink/renderer/modules/webaudio/audio_node_output.cc
index 2210468..7a17b070 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_node_output.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_node_output.cc
@@ -79,8 +79,9 @@
 }
 
 void AudioNodeOutput::UpdateInternalBus() {
-  if (NumberOfChannels() == internal_bus_->NumberOfChannels())
+  if (NumberOfChannels() == internal_bus_->NumberOfChannels()) {
     return;
+  }
 
   internal_bus_ = AudioBus::Create(
       NumberOfChannels(), GetDeferredTaskHandler().RenderQuantumFrames());
@@ -110,8 +111,9 @@
   if (IsChannelCountKnown()) {
     // Announce to any nodes we're connected to that we changed our channel
     // count for its input.
-    for (AudioNodeInput* i : inputs_)
+    for (AudioNodeInput* i : inputs_) {
       i->Handler().CheckNumberOfChannelsForInput(i);
+    }
   }
 }
 
@@ -163,8 +165,9 @@
   // Disconnect changes inputs_, so we can't iterate directly over the hash set.
   Vector<AudioNodeInput*, 4> inputs;
   CopyToVector(inputs_, inputs);
-  for (AudioNodeInput* input : inputs)
+  for (AudioNodeInput* input : inputs) {
     AudioNodeWiring::Disconnect(*this, *input);
+  }
   DCHECK(inputs_.IsEmpty());
 }
 
@@ -174,8 +177,9 @@
   // Disconnect changes params_, so we can't iterate directly over the hash set.
   Vector<AudioParamHandler*, 4> params;
   CopyToVector(params_, params);
-  for (AudioParamHandler* param : params)
+  for (AudioParamHandler* param : params) {
     AudioNodeWiring::Disconnect(*this, *param);
+  }
   DCHECK(params_.IsEmpty());
 }
 
@@ -189,8 +193,9 @@
 
   if (is_enabled_) {
     is_enabled_ = false;
-    for (AudioNodeInput* input : inputs_)
+    for (AudioNodeInput* input : inputs_) {
       AudioNodeWiring::Disable(*this, *input);
+    }
   }
 }
 
@@ -199,8 +204,9 @@
 
   if (!is_enabled_) {
     is_enabled_ = true;
-    for (AudioNodeInput* input : inputs_)
+    for (AudioNodeInput* input : inputs_) {
       AudioNodeWiring::Enable(*this, *input);
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node_wiring.cc b/third_party/blink/renderer/modules/webaudio/audio_node_wiring.cc
index 3cbe48a6..2cc2ca28b 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_node_wiring.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_node_wiring.cc
@@ -32,12 +32,14 @@
                             AudioNodeOutputSet& outputs,
                             AudioNodeOutputSet& disabled_outputs) {
   auto it = outputs.find(&output);
-  if (it != outputs.end())
+  if (it != outputs.end()) {
     return {outputs, it, false};
+  }
 
   it = disabled_outputs.find(&output);
-  if (it != disabled_outputs.end())
+  if (it != disabled_outputs.end()) {
     return {disabled_outputs, it, true};
+  }
 
   NOTREACHED() << "The output must be connected to the input.";
   return {outputs, {}, false};
@@ -55,8 +57,9 @@
   DCHECK_EQ(input_connected_to_output, output_connected_to_input);
 
   // Do nothing if already connected.
-  if (input_connected_to_output)
+  if (input_connected_to_output) {
     return;
+  }
 
   (output.is_enabled_ ? input.outputs_ : input.disabled_outputs_)
       .insert(&output);
@@ -64,8 +67,9 @@
 
   // If it has gained an active connection, the input may need to have its
   // rendering state updated.
-  if (output.is_enabled_)
+  if (output.is_enabled_) {
     input.ChangedOutputs();
+  }
 
   // The input node's handler needs to know about this connection. This may
   // cause it to re-enable itself.
@@ -81,8 +85,9 @@
   DCHECK_EQ(param_connected_to_output, output_connected_to_param);
 
   // Do nothing if already connected.
-  if (param_connected_to_output)
+  if (param_connected_to_output) {
     return;
+  }
 
   param.outputs_.insert(&output);
   output.params_.insert(&param);
@@ -109,8 +114,9 @@
 
   // If an active connection was disconnected, the input may need to have its
   // rendering state updated.
-  if (!result.is_disabled)
+  if (!result.is_disabled) {
     input.ChangedOutputs();
+  }
 
   // The input node's handler may try to disable itself if this was the last
   // connection. This must happen after the set erasures above, or the disabling
@@ -146,8 +152,9 @@
 
   // Move from the active list to the disabled list.
   // Do nothing if this is the current state.
-  if (!input.disabled_outputs_.insert(&output).is_new_entry)
+  if (!input.disabled_outputs_.insert(&output).is_new_entry) {
     return;
+  }
   input.outputs_.erase(&output);
 
   // Since it has lost an active connection, the input may need to have its
@@ -173,8 +180,9 @@
 
   // Move from the disabled list to the active list.
   // Do nothing if this is the current state.
-  if (!input.outputs_.insert(&output).is_new_entry)
+  if (!input.outputs_.insert(&output).is_new_entry) {
     return;
+  }
   input.disabled_outputs_.erase(&output);
 
   // Since it has gained an active connection, the input may need to have its
@@ -217,10 +225,12 @@
 
   input.GetDeferredTaskHandler().AssertGraphOwner();
 
-  for (AudioNodeOutput* output : input.outputs_)
+  for (AudioNodeOutput* output : input.outputs_) {
     output->inputs_.erase(&input);
-  for (AudioNodeOutput* output : input.disabled_outputs_)
+  }
+  for (AudioNodeOutput* output : input.disabled_outputs_) {
     output->inputs_.erase(&input);
+  }
   input.outputs_.clear();
   input.disabled_outputs_.clear();
 }
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param.cc b/third_party/blink/renderer/modules/webaudio/audio_param.cc
index ea0efec1..c226481 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_param.cc
@@ -175,8 +175,9 @@
         DestinationHandler(), v, MinValue(), MaxValue(),
         GetDeferredTaskHandler().RenderQuantumFrames());
 
-    if (has_value)
+    if (has_value) {
       v = timeline_value;
+    }
   }
 
   SetIntrinsicValue(v);
@@ -221,8 +222,9 @@
     // If we get close enough then snap to actual value.
     // FIXME: the threshold needs to be adjustable depending on range - but
     // this is OK general purpose value.
-    if (fabs(smoothed_value - value) < kSnapThreshold)
+    if (fabs(smoothed_value - value) < kSnapThreshold) {
       smoothed_value = value;
+    }
     timeline_.SetSmoothedValue(smoothed_value);
   }
 
@@ -313,8 +315,9 @@
         DestinationHandler(), value, MinValue(), MaxValue(),
         GetDeferredTaskHandler().RenderQuantumFrames());
 
-    if (has_value)
+    if (has_value) {
       value = timeline_value;
+    }
 
     for (unsigned k = 0; k < number_of_values; ++k) {
       values[k] = value;
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_map.cc b/third_party/blink/renderer/modules/webaudio/audio_param_map.cc
index c05b93a0..32d3b52 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param_map.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_param_map.cc
@@ -22,8 +22,9 @@
             String& key,
             AudioParam*& audio_param,
             ExceptionState&) override {
-    if (current_index_ == parameter_names_.size())
+    if (current_index_ == parameter_names_.size()) {
       return false;
+    }
     key = parameter_names_[current_index_];
     audio_param = parameter_objects_[current_index_];
     ++current_index_;
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc b/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc
index a3136376..81e3643 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_param_timeline.cc
@@ -57,8 +57,9 @@
 static bool IsNonNegativeAudioParamTime(double time,
                                         ExceptionState& exception_state,
                                         String message = "Time") {
-  if (time >= 0)
+  if (time >= 0) {
     return true;
+  }
 
   exception_state.ThrowRangeError(
       message +
@@ -69,8 +70,9 @@
 static bool IsPositiveAudioParamTime(double time,
                                      ExceptionState& exception_state,
                                      String message) {
-  if (time > 0)
+  if (time > 0) {
     return true;
+  }
 
   exception_state.ThrowRangeError(
       message + " must be a finite positive number: " + String::Number(time));
@@ -415,8 +417,9 @@
                                         ExceptionState& exception_state) {
   DCHECK(IsMainThread());
 
-  if (!IsNonNegativeAudioParamTime(time, exception_state))
+  if (!IsNonNegativeAudioParamTime(time, exception_state)) {
     return;
+  }
 
   MutexLocker locker(events_lock_);
   InsertEvent(ParamEvent::CreateSetValueEvent(value, time), exception_state);
@@ -430,8 +433,9 @@
     ExceptionState& exception_state) {
   DCHECK(IsMainThread());
 
-  if (!IsNonNegativeAudioParamTime(time, exception_state))
+  if (!IsNonNegativeAudioParamTime(time, exception_state)) {
     return;
+  }
 
   MutexLocker locker(events_lock_);
   InsertEvent(
@@ -447,8 +451,9 @@
     ExceptionState& exception_state) {
   DCHECK(IsMainThread());
 
-  if (!IsNonNegativeAudioParamTime(time, exception_state))
+  if (!IsNonNegativeAudioParamTime(time, exception_state)) {
     return;
+  }
 
   if (!value) {
     exception_state.ThrowRangeError(
@@ -473,8 +478,9 @@
 
   if (!IsNonNegativeAudioParamTime(time, exception_state) ||
       !IsNonNegativeAudioParamTime(time_constant, exception_state,
-                                   "Time constant"))
+                                   "Time constant")) {
     return;
+  }
 
   MutexLocker locker(events_lock_);
 
@@ -495,8 +501,9 @@
   DCHECK(IsMainThread());
 
   if (!IsNonNegativeAudioParamTime(time, exception_state) ||
-      !IsPositiveAudioParamTime(duration, exception_state, "Duration"))
+      !IsPositiveAudioParamTime(duration, exception_state, "Duration")) {
     return;
+  }
 
   if (curve.size() < 2) {
     exception_state.ThrowDOMException(
@@ -605,8 +612,9 @@
       }
     }
 
-    if (events_[i]->Time() > insert_time)
+    if (events_[i]->Time() > insert_time) {
       break;
+    }
   }
 
   events_.insert(i, std::move(event));
@@ -622,8 +630,9 @@
     unsigned n_events = events_.size();
 
     // Clearly, if there are no scheduled events, we have no timeline values.
-    if (n_events == 0)
+    if (n_events == 0) {
       return false;
+    }
 
     // Handle the case where the first event (of certain types) is in the
     // future.  Then, no sample-accurate processing is needed because the event
@@ -649,8 +658,9 @@
     // complicated and keeping this consistent with |ValuesForFrameRangeImpl()|
     // will be hard, so it's probably best to let the general timeline handle
     // this until the events are in the past.
-    if (n_events >= 2)
+    if (n_events >= 2) {
       return true;
+    }
 
     // We have exactly one event in the timeline.
     switch (events_[0]->GetType()) {
@@ -701,8 +711,9 @@
     ExceptionState& exception_state) {
   DCHECK(IsMainThread());
 
-  if (!IsNonNegativeAudioParamTime(cancel_time, exception_state))
+  if (!IsNonNegativeAudioParamTime(cancel_time, exception_state)) {
     return;
+  }
 
   MutexLocker locker(events_lock_);
 
@@ -733,8 +744,9 @@
                                              ExceptionState& exception_state) {
   DCHECK(IsMainThread());
 
-  if (!IsNonNegativeAudioParamTime(cancel_time, exception_state))
+  if (!IsNonNegativeAudioParamTime(cancel_time, exception_state)) {
     return;
+  }
 
   MutexLocker locker(events_lock_);
 
@@ -849,8 +861,9 @@
   // Insert the new event, if any.
   if (new_event) {
     InsertEvent(std::move(new_event), exception_state);
-    if (new_set_value_event)
+    if (new_set_value_event) {
       InsertEvent(std::move(new_set_value_event), exception_state);
+    }
   }
 }
 
@@ -895,8 +908,9 @@
   MutexTryLocker try_locker(events_lock_);
   if (!try_locker.Locked()) {
     if (values) {
-      for (unsigned i = 0; i < number_of_values; ++i)
+      for (unsigned i = 0; i < number_of_values; ++i) {
         values[i] = default_value;
+      }
     }
     return default_value;
   }
@@ -950,8 +964,9 @@
 
     if (HandleAllEventsInThePast(current_time, sample_rate, default_value,
                                  number_of_values, values,
-                                 render_quantum_frames))
+                                 render_quantum_frames)) {
       return default_value;
+    }
   }
 
   // Maintain a running time (frame) and index for writing the values buffer.
@@ -1020,8 +1035,9 @@
     // where time2 exceeds the size of a size_t.
 
     size_t fill_to_end_frame = end_frame;
-    if (end_frame > time2 * sample_rate)
+    if (end_frame > time2 * sample_rate) {
       fill_to_end_frame = static_cast<size_t>(ceil(time2 * sample_rate));
+    }
 
     DCHECK_GE(fill_to_end_frame, start_frame);
     unsigned fill_to_frame =
@@ -1139,8 +1155,9 @@
     // bound from the firstEventTime.
     size_t fill_to_end_frame = end_frame;
     double first_event_frame = ceil(first_event_time * sample_rate);
-    if (end_frame > first_event_frame)
+    if (end_frame > first_event_frame) {
       fill_to_end_frame = first_event_frame;
+    }
     DCHECK_GE(fill_to_end_frame, start_frame);
 
     unsigned fill_to_frame =
@@ -1507,8 +1524,9 @@
   // Update |value| with the last value computed so that the
   // .value attribute of the AudioParam gets the correct linear
   // ramp value, in case the following loop doesn't execute.
-  if (write_index >= 1)
+  if (write_index >= 1) {
     value = values[write_index - 1];
+  }
 #endif
   // Serially process remaining values.
   for (; write_index < fill_to_frame; ++write_index) {
@@ -1541,8 +1559,9 @@
     // making it the default.
     value = value1;
 
-    for (; write_index < fill_to_frame; ++write_index)
+    for (; write_index < fill_to_frame; ++write_index) {
       values[write_index] = value;
+    }
   } else {
     double delta_time = time2 - time1;
     double num_sample_frames = delta_time * sample_rate;
@@ -1582,13 +1601,15 @@
     }
     // |value| got updated one extra time in the above loop.  Restore it to
     // the last computed value.
-    if (write_index >= 1)
+    if (write_index >= 1) {
       value /= multiplier;
+    }
 
     // Due to roundoff it's possible that value exceeds value2.  Clip value
     // to value2 if we are within 1/2 frame of time2.
-    if (current_frame > time2 * sample_rate - 0.5)
+    if (current_frame > time2 * sample_rate - 0.5) {
       value = value2;
+    }
   }
 
   return std::make_tuple(current_frame, value, write_index);
@@ -1649,8 +1670,9 @@
   if (HasSetTargetConverged(value, target, current_frame / sample_rate, time1,
                             time_constant)) {
     current_frame += fill_to_frame - write_index;
-    for (; write_index < fill_to_frame; ++write_index)
+    for (; write_index < fill_to_frame; ++write_index) {
       values[write_index] = target;
+    }
   } else {
 #if defined(ARCH_CPU_X86_FAMILY)
     if (fill_to_frame > write_index) {
@@ -1697,8 +1719,9 @@
     }
     // The previous loops may have updated |value| one extra time.
     // Reset it to the last computed value.
-    if (write_index >= 1)
+    if (write_index >= 1) {
       value = values[write_index - 1];
+    }
     current_frame = fill_to_end_frame;
   }
 
@@ -1735,8 +1758,9 @@
   if (!number_of_curve_points || duration <= 0 || sample_rate <= 0) {
     // Error condition - simply propagate previous value.
     current_frame = fill_to_end_frame;
-    for (; write_index < fill_to_frame; ++write_index)
+    for (; write_index < fill_to_frame; ++write_index) {
       values[write_index] = value;
+    }
     return std::make_tuple(current_frame, value, write_index);
   }
 
@@ -1751,10 +1775,11 @@
   // to be computed, so ceil is used.
   {
     double curve_end_frame = ceil(sample_rate * (time1 + duration));
-    if (end_frame > curve_end_frame)
+    if (end_frame > curve_end_frame) {
       fill_to_end_frame = static_cast<size_t>(curve_end_frame);
-    else
+    } else {
       fill_to_end_frame = end_frame;
+    }
   }
 
   // |fillToFrame| can be less than |startFrame| when the end of the
@@ -1843,8 +1868,9 @@
     // Pass along k to the serial loop.
     k = truncated_steps;
   }
-  if (write_index >= 1)
+  if (write_index >= 1) {
     value = values[write_index - 1];
+  }
 #endif
   for (; write_index < fill_to_frame; ++write_index, ++k) {
     // Compute current index this way to minimize round-off that would
@@ -1884,8 +1910,9 @@
   // curveData. Don't modify |value| unless there is time left.
   if (write_index < next_event_fill_to_frame) {
     value = curve_end_value;
-    for (; write_index < next_event_fill_to_frame; ++write_index)
+    for (; write_index < next_event_fill_to_frame; ++write_index) {
       values[write_index] = value;
+    }
   }
 
   // Re-adjust current time
@@ -1931,8 +1958,9 @@
   }
 
   // Simply stay at the current value.
-  for (; write_index < fill_to_frame; ++write_index)
+  for (; write_index < fill_to_frame; ++write_index) {
     values[write_index] = value;
+  }
 
   current_frame = fill_to_end_frame;
 
@@ -1945,8 +1973,9 @@
                                              uint32_t write_index) {
   uint32_t index = write_index;
 
-  for (; index < end_frame; ++index)
+  for (; index < end_frame; ++index) {
     values[index] = default_value;
+  }
 
   return index;
 }
diff --git a/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.cc b/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.cc
index c98cbe12..18027e1 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_scheduled_source_node.cc
@@ -94,8 +94,9 @@
 
   // If we know the end time and it's already passed, then don't bother doing
   // any more rendering this cycle.
-  if (end_time_ != kUnknownTime && end_frame <= quantum_start_frame)
+  if (end_time_ != kUnknownTime && end_frame <= quantum_start_frame) {
     Finish();
+  }
 
   PlaybackState state = GetPlaybackState();
 
@@ -138,9 +139,10 @@
   // Zero any initial frames representing silence leading up to a rendering
   // start time in the middle of the quantum.
   if (quantum_frame_offset) {
-    for (unsigned i = 0; i < output_bus->NumberOfChannels(); ++i)
+    for (unsigned i = 0; i < output_bus->NumberOfChannels(); ++i) {
       memset(output_bus->Channel(i)->MutableData(), 0,
              sizeof(float) * quantum_frame_offset);
+    }
   }
 
   // Handle silence after we're done playing.
@@ -159,14 +161,16 @@
                    frames_to_zero <= quantum_frame_size &&
                    zero_start_frame + frames_to_zero <= quantum_frame_size;
     if (is_safe) {
-      if (frames_to_zero > non_silent_frames_to_process)
+      if (frames_to_zero > non_silent_frames_to_process) {
         non_silent_frames_to_process = 0;
-      else
+      } else {
         non_silent_frames_to_process -= frames_to_zero;
+      }
 
-      for (unsigned i = 0; i < output_bus->NumberOfChannels(); ++i)
+      for (unsigned i = 0; i < output_bus->NumberOfChannels(); ++i) {
         memset(output_bus->Channel(i)->MutableData() + zero_start_frame, 0,
                sizeof(float) * frames_to_zero);
+      }
     }
 
     Finish();
@@ -266,8 +270,9 @@
   if (GetNode()) {
     DispatchEventResult result =
         GetNode()->DispatchEvent(*Event::Create(event_type_names::kEnded));
-    if (result == DispatchEventResult::kCanceledBeforeDispatch)
+    if (result == DispatchEventResult::kCanceledBeforeDispatch) {
       return;
+    }
   }
   on_ended_notification_pending_ = false;
 }
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc
index a261682..6810c10 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc
@@ -82,15 +82,17 @@
   //    TypeError .
   CallbackMethodRetriever retriever(processor_ctor);
   retriever.GetPrototypeObject(exception_state);
-  if (exception_state.HadException())
+  if (exception_state.HadException()) {
     return;
+  }
 
   // TODO(crbug.com/1077911): Do not extract process() function at the
   // registration step.
   v8::Local<v8::Function> v8_process =
       retriever.GetMethodOrThrow("process", exception_state);
-  if (exception_state.HadException())
+  if (exception_state.HadException()) {
     return;
+  }
   V8BlinkAudioWorkletProcessCallback* process =
       V8BlinkAudioWorkletProcessCallback::Create(v8_process);
 
@@ -124,8 +126,9 @@
     const HeapVector<Member<AudioParamDescriptor>>& given_param_descriptors =
         NativeValueTraits<IDLSequence<AudioParamDescriptor>>::NativeValue(
             isolate, v8_parameter_descriptors, exception_state);
-    if (exception_state.HadException())
+    if (exception_state.HadException()) {
       return;
+    }
 
     // 7.2. Let paramNames be an empty Array.
     HeapVector<Member<AudioParamDescriptor>> sanitized_param_descriptors;
@@ -220,8 +223,9 @@
 AudioWorkletProcessorDefinition* AudioWorkletGlobalScope::FindDefinition(
     const String& name) {
   const auto it = processor_definition_map_.find(name);
-  if (it == processor_definition_map_.end())
+  if (it == processor_definition_map_.end()) {
     return nullptr;
+  }
   return it->value.Get();
 }
 
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h
index c36e98cc..8a5eede 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.h
@@ -26,7 +26,6 @@
 class V8BlinkAudioWorkletProcessorConstructor;
 struct GlobalScopeCreationParams;
 
-
 // The storage for the construction of AudioWorkletProcessor, contains the
 // processor name and MessageChannelPort object.
 class MODULES_EXPORT ProcessorCreationParams final {
@@ -47,7 +46,6 @@
   MessagePortChannel message_port_channel_;
 };
 
-
 // This is constructed and destroyed on a worker thread, and all methods also
 // must be called on the worker thread.
 class MODULES_EXPORT AudioWorkletGlobalScope final : public WorkletGlobalScope {
@@ -109,12 +107,16 @@
   void SetObjectProxy(AudioWorkletObjectProxy&);
 
  private:
-  bool is_closing_ = false;
-
   typedef HeapHashMap<String, Member<AudioWorkletProcessorDefinition>>
       ProcessorDefinitionMap;
   typedef HeapVector<Member<AudioWorkletProcessor>> ProcessorInstances;
 
+  network::mojom::RequestDestination GetDestination() const override {
+    return network::mojom::RequestDestination::kAudioWorklet;
+  }
+
+  bool is_closing_ = false;
+
   ProcessorDefinitionMap processor_definition_map_;
   ProcessorInstances processor_instances_;
 
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_messaging_proxy.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_messaging_proxy.cc
index a6e0191..b34bc16 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_messaging_proxy.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_messaging_proxy.cc
@@ -111,11 +111,13 @@
     WorkerReportingProxy& worker_reporting_proxy,
     const bool has_realtime_constraint,
     const bool is_top_level_frame) {
-  if (!has_realtime_constraint)
+  if (!has_realtime_constraint) {
     return std::make_unique<OfflineAudioWorkletThread>(worker_reporting_proxy);
+  }
 
-  if (is_top_level_frame)
+  if (is_top_level_frame) {
     return std::make_unique<RealtimeAudioWorkletThread>(worker_reporting_proxy);
+  }
 
   return std::make_unique<SemiRealtimeAudioWorkletThread>(
       worker_reporting_proxy);
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_node.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_node.cc
index 63c829a8..4538425 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_node.cc
@@ -47,8 +47,9 @@
                              GetDeferredTaskHandler().RenderQuantumFrames()));
   }
 
-  for (unsigned i = 0; i < options->numberOfInputs(); ++i)
+  for (unsigned i = 0; i < options->numberOfInputs(); ++i) {
     AddInput();
+  }
   // The number of inputs does not change after the construnction, so it is
   // safe to reserve the array capacity and size.
   inputs_.ReserveInitialCapacity(options->numberOfInputs());
@@ -105,10 +106,12 @@
   // the rendering in the AudioWorkletGlobalScope.
   if (processor_ && !processor_->hasErrorOccurred()) {
     // If the input is not connected, inform the processor with nullptr.
-    for (unsigned i = 0; i < NumberOfInputs(); ++i)
+    for (unsigned i = 0; i < NumberOfInputs(); ++i) {
       inputs_[i] = Input(i).IsConnected() ? Input(i).Bus() : nullptr;
-    for (unsigned i = 0; i < NumberOfOutputs(); ++i)
+    }
+    for (unsigned i = 0; i < NumberOfOutputs(); ++i) {
       outputs_[i] = WrapRefCounted(Output(i).Bus());
+    }
 
     for (const auto& param_name : param_value_map_.Keys()) {
       auto* const param_handler = param_handler_map_.at(param_name);
@@ -134,8 +137,9 @@
     // The initialization of handler or the associated processor might not be
     // ready yet or it is in the error state. If so, zero out the connected
     // output.
-    for (unsigned i = 0; i < NumberOfOutputs(); ++i)
+    for (unsigned i = 0; i < NumberOfOutputs(); ++i) {
       Output(i).Bus()->Zero();
+    }
   }
 }
 
@@ -229,8 +233,9 @@
 void AudioWorkletHandler::NotifyProcessorError(
     AudioWorkletProcessorErrorState error_state) {
   DCHECK(IsMainThread());
-  if (!Context() || !Context()->GetExecutionContext() || !GetNode())
+  if (!Context() || !Context()->GetExecutionContext() || !GetNode()) {
     return;
+  }
 
   static_cast<AudioWorkletNode*>(GetNode())->FireProcessorError(error_state);
 }
@@ -250,8 +255,9 @@
     String param_name = param_info.Name().IsolatedCopy();
     AudioParamHandler::AutomationRate param_automation_rate(
         AudioParamHandler::AutomationRate::kAudio);
-    if (param_info.AutomationRate() == "k-rate")
+    if (param_info.AutomationRate() == "k-rate") {
       param_automation_rate = AudioParamHandler::AutomationRate::kControl;
+    }
     AudioParam* audio_param = AudioParam::Create(
         context, Uuid(), AudioParamHandler::kParamTypeAudioWorklet,
         param_info.DefaultValue(), param_automation_rate,
@@ -264,8 +270,9 @@
 
     if (options->hasParameterData()) {
       for (const auto& key_value_pair : options->parameterData()) {
-        if (key_value_pair.first == param_name)
+        if (key_value_pair.first == param_name) {
           audio_param->setValue(key_value_pair.second);
+        }
       }
     }
   }
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_object_proxy.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_object_proxy.cc
index 56abb4e..379abc8 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_object_proxy.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_object_proxy.cc
@@ -34,15 +34,17 @@
 void AudioWorkletObjectProxy::SynchronizeProcessorInfoList() {
   DCHECK(global_scope_);
 
-  if (global_scope_->NumberOfRegisteredDefinitions() == 0)
+  if (global_scope_->NumberOfRegisteredDefinitions() == 0) {
     return;
+  }
 
   std::unique_ptr<Vector<CrossThreadAudioWorkletProcessorInfo>>
       processor_info_list =
           global_scope_->WorkletProcessorInfoListForSynchronization();
 
-  if (processor_info_list->size() == 0)
+  if (processor_info_list->size() == 0) {
     return;
+  }
 
   PostCrossThreadTask(
       *GetParentExecutionContextTaskRunners()->Get(TaskType::kInternalLoading),
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_processor.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_processor.cc
index ae19dc84..e366b21 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_processor.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_processor.cc
@@ -65,8 +65,9 @@
         ClonePortTopology(isolate, context, inputs, inputs_,
                           input_array_buffers_);
     DCHECK(inputs_cloned_successfully);
-    if (!inputs_cloned_successfully)
+    if (!inputs_cloned_successfully) {
       return false;
+    }
   }
   DCHECK(!inputs_.IsEmpty());
   DCHECK(inputs_.Get(isolate)->IsArray());
@@ -83,8 +84,9 @@
         ClonePortTopology(isolate, context, outputs, outputs_,
                           output_array_buffers_);
     DCHECK(outputs_cloned_successfully);
-    if (!outputs_cloned_successfully)
+    if (!outputs_cloned_successfully) {
       return false;
+    }
   } else {
     // The reallocation was not needed, so the arrays need to be zeroed before
     // passing them to the author script.
@@ -102,8 +104,9 @@
     bool params_cloned_successfully =
         CloneParamValueMapToObject(isolate, context, param_value_map, params_);
     DCHECK(params_cloned_successfully);
-    if (!params_cloned_successfully)
+    if (!params_cloned_successfully) {
       return false;
+    }
   }
   DCHECK(!params_.IsEmpty());
   DCHECK(params_.Get(isolate)->IsObject());
@@ -178,8 +181,9 @@
     const TraceWrapperV8Reference<v8::Array>& audio_port_2) {
   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("audio-worklet"),
                "AudioWorkletProcessor::Process (compare topology)");
-  if (audio_port_2.IsEmpty())
+  if (audio_port_2.IsEmpty()) {
     return false;
+  }
 
   v8::Local<v8::Array> port_2_local = audio_port_2.Get(isolate);
   DCHECK(port_2_local->IsArray());
@@ -196,16 +200,18 @@
   uint32_t bus_index_counter = 0;
   for (const auto& audio_bus_1 : audio_port_1) {
     if (!port_2_local->Get(context, bus_index_counter).ToLocal(&value) ||
-        !value->IsArray())
+        !value->IsArray()) {
       return false;
+    }
 
     // Compare the length of AudioBus1[i] from AudioPort1 and AudioBus2[i] from
     // AudioPort2.
     unsigned number_of_channels =
         audio_bus_1 ? audio_bus_1->NumberOfChannels() : 0;
     v8::Local<v8::Array> audio_bus_2 = value.As<v8::Array>();
-    if (number_of_channels != audio_bus_2->Length())
+    if (number_of_channels != audio_bus_2->Length()) {
       return false;
+    }
 
     // If the channel count of AudioBus1[i] and AudioBus2[i] matches, then
     // iterate all the channels in AudioBus1[i] and see if any AudioChannel
@@ -213,13 +219,15 @@
     for (uint32_t channel_index = 0; channel_index < audio_bus_2->Length();
          ++channel_index) {
       if (!audio_bus_2->Get(context, channel_index).ToLocal(&value) ||
-          !value->IsFloat32Array())
+          !value->IsFloat32Array()) {
         return false;
+      }
       v8::Local<v8::Float32Array> float32_array = value.As<v8::Float32Array>();
 
       // If any array is transferred, we need to rebuild them.
-      if (float32_array->ByteLength() == 0)
+      if (float32_array->ByteLength() == 0) {
         return false;
+      }
     }
 
     bus_index_counter++;
@@ -235,22 +243,24 @@
   v8::TryCatch try_catch(isolate);
 
   bool port_frozen;
-  if (!audio_port_array
-           ->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen)
-           .To(&port_frozen))
+  if (!audio_port_array->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen)
+           .To(&port_frozen)) {
     return false;
+  }
 
   v8::Local<v8::Value> bus_value;
   for (uint32_t bus_index = 0; bus_index < audio_port_array->Length();
        ++bus_index) {
     if (!audio_port_array->Get(context, bus_index).ToLocal(&bus_value) ||
-        !bus_value->IsObject())
+        !bus_value->IsObject()) {
       return false;
+    }
     bool bus_frozen;
     if (!bus_value.As<v8::Object>()
-             ->SetIntegrityLevel(context,v8::IntegrityLevel::kFrozen)
-             .To(&bus_frozen))
+             ->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen)
+             .To(&bus_frozen)) {
       return false;
+    }
   }
 
   return true;
@@ -296,10 +306,11 @@
       v8::Local<v8::Float32Array> float32_array =
           v8::Float32Array::New(array_buffer, 0, bus_length);
       bool new_channel_added;
-      if (!new_audio_bus->CreateDataProperty(context, channel_index,
-                                             float32_array)
-                        .To(&new_channel_added))
+      if (!new_audio_bus
+               ->CreateDataProperty(context, channel_index, float32_array)
+               .To(&new_channel_added)) {
         return false;
+      }
       new_array_buffers.back().UncheckedAppend(
           TraceWrapperV8Reference<v8::ArrayBuffer>(isolate, array_buffer));
     }
@@ -307,8 +318,9 @@
     bus_index++;
   }
 
-  if (!FreezeAudioPort(isolate, context, new_port_array))
+  if (!FreezeAudioPort(isolate, context, new_port_array)) {
     return false;
+  }
 
   audio_port_2.Reset(isolate, new_port_array);
   array_buffers.swap(new_array_buffers);
@@ -384,8 +396,9 @@
     const TraceWrapperV8Reference<v8::Object>& params) {
   v8::TryCatch try_catch(isolate);
 
-  if (params.IsEmpty())
+  if (params.IsEmpty()) {
     return false;
+  }
 
   v8::Local<v8::Object> params_object = params.Get(isolate);
 
@@ -408,17 +421,19 @@
     // The |param_name| should exist in the |param| object.
     v8::Local<v8::Value> param_array_value;
     if (!params_object->Get(context, v8_param_name)
-                      .ToLocal(&param_array_value) ||
-        !param_array_value->IsFloat32Array())
+             .ToLocal(&param_array_value) ||
+        !param_array_value->IsFloat32Array()) {
       return false;
+    }
 
     // If the detected array length doesn't match or any underlying array
     // buffer is transferred, we have to reallocate.
     v8::Local<v8::Float32Array> float32_array =
         param_array_value.As<v8::Float32Array>();
     if (float32_array->Length() != array_size ||
-        float32_array->Buffer()->ByteLength() == 0)
+        float32_array->Buffer()->ByteLength() == 0) {
       return false;
+    }
   }
 
   return true;
@@ -468,9 +483,10 @@
 
   bool object_frozen;
   if (!new_params_object
-          ->SetIntegrityLevel(context,v8::IntegrityLevel::kFrozen)
-          .To(&object_frozen))
+           ->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen)
+           .To(&object_frozen)) {
     return false;
+  }
 
   params.Reset(isolate, new_params_object);
   return true;
@@ -503,8 +519,9 @@
     // The |float32_array| is neither 1 nor 128 frames, or the array buffer is
     // trasnferred/detached, do not proceed.
     if ((array_length != 1 && array_length != param_array->size()) ||
-        float32_array->Buffer()->ByteLength() == 0)
+        float32_array->Buffer()->ByteLength() == 0) {
       return false;
+    }
 
     memcpy(float32_array->Buffer()->GetBackingStore()->Data(),
            param_array->Data(), array_length * sizeof(float));
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_processor_definition.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_processor_definition.cc
index 778dfba..0551d181 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_processor_definition.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_processor_definition.cc
@@ -45,8 +45,9 @@
     AudioWorkletProcessorDefinition::GetAudioParamDescriptor (
         const String& key) const {
   for (const auto& descriptor : audio_param_descriptors_) {
-    if (descriptor->name() == key)
+    if (descriptor->name() == key) {
       return descriptor;
+    }
   }
   return nullptr;
 }
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_thread_test.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_thread_test.cc
index 25df204..483639f 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_thread_test.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_thread_test.cc
@@ -339,10 +339,11 @@
   void InitWithRealtimePrioritySettings(bool is_enabled_by_finch) {
     std::vector<base::Feature> enabled;
     std::vector<base::Feature> disabled;
-    if (is_enabled_by_finch)
+    if (is_enabled_by_finch) {
       enabled.push_back(features::kAudioWorkletThreadRealtimePriority);
-    else
+    } else {
       disabled.push_back(features::kAudioWorkletThreadRealtimePriority);
+    }
     feature_list_.InitWithFeatures(enabled, disabled);
   }
 
diff --git a/third_party/blink/renderer/modules/webaudio/base_audio_context.cc b/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
index 33b5b4c..a9e15ec71 100644
--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
@@ -114,8 +114,9 @@
 }
 
 void BaseAudioContext::Initialize() {
-  if (IsDestinationInitialized())
+  if (IsDestinationInitialized()) {
     return;
+  }
 
   audio_worklet_ = MakeGarbageCollected<AudioWorklet>(this);
 
@@ -149,15 +150,17 @@
 void BaseAudioContext::Uninitialize() {
   DCHECK(IsMainThread());
 
-  if (!IsDestinationInitialized())
+  if (!IsDestinationInitialized()) {
     return;
+  }
 
   // Report the inspector that the context will be destroyed.
   ReportWillBeDestroyed();
 
   // This stops the audio thread and all audio rendering.
-  if (destination_node_)
+  if (destination_node_) {
     destination_node_->Handler().Uninitialize();
+  }
 
   // Remove tail nodes since the context is done.
   GetDeferredTaskHandler().FinishTailProcessing();
@@ -186,14 +189,16 @@
 void BaseAudioContext::ContextLifecycleStateChanged(
     mojom::FrameLifecycleState state) {
   // Don't need to do anything for an offline context.
-  if (!HasRealtimeConstraint())
+  if (!HasRealtimeConstraint()) {
     return;
+  }
 
-  if (state == mojom::FrameLifecycleState::kRunning)
+  if (state == mojom::FrameLifecycleState::kRunning) {
     destination()->GetAudioDestinationHandler().Resume();
-  else if (state == mojom::FrameLifecycleState::kFrozen ||
-           state == mojom::FrameLifecycleState::kFrozenAutoResumeMedia)
+  } else if (state == mojom::FrameLifecycleState::kFrozen ||
+             state == mojom::FrameLifecycleState::kFrozenAutoResumeMedia) {
     destination()->GetAudioDestinationHandler().Pause();
+  }
 }
 
 void BaseAudioContext::ContextDestroyed() {
@@ -367,15 +372,17 @@
   if (audio_buffer) {
     // Resolve promise successfully and run the success callback
     resolver->Resolve(audio_buffer);
-    if (success_callback)
+    if (success_callback) {
       success_callback->InvokeAndReportException(this, audio_buffer);
+    }
   } else {
     // Reject the promise and run the error callback
     auto* error = MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kEncodingError, "Unable to decode audio data");
     resolver->Reject(error);
-    if (error_callback)
+    if (error_callback) {
       error_callback->InvokeAndReportException(this, error);
+    }
   }
 
   // We've resolved the promise.  Remove it now.
@@ -579,23 +586,27 @@
   switch (type) {
     case OscillatorHandler::SINE:
       // Initialize the table if necessary
-      if (!periodic_wave_sine_)
+      if (!periodic_wave_sine_) {
         periodic_wave_sine_ = PeriodicWave::CreateSine(sampleRate());
+      }
       return periodic_wave_sine_;
     case OscillatorHandler::SQUARE:
       // Initialize the table if necessary
-      if (!periodic_wave_square_)
+      if (!periodic_wave_square_) {
         periodic_wave_square_ = PeriodicWave::CreateSquare(sampleRate());
+      }
       return periodic_wave_square_;
     case OscillatorHandler::SAWTOOTH:
       // Initialize the table if necessary
-      if (!periodic_wave_sawtooth_)
+      if (!periodic_wave_sawtooth_) {
         periodic_wave_sawtooth_ = PeriodicWave::CreateSawtooth(sampleRate());
+      }
       return periodic_wave_sawtooth_;
     case OscillatorHandler::TRIANGLE:
       // Initialize the table if necessary
-      if (!periodic_wave_triangle_)
+      if (!periodic_wave_triangle_) {
         periodic_wave_triangle_ = PeriodicWave::CreateTriangle(sampleRate());
+      }
       return periodic_wave_triangle_;
     default:
       NOTREACHED();
@@ -643,8 +654,9 @@
 
   context_state_ = new_state;
 
-  if (new_state == kClosed)
+  if (new_state == kClosed) {
     GetDeferredTaskHandler().StopAcceptingTailProcessing();
+  }
 
   // Notify context that state changed
   if (GetExecutionContext()) {
@@ -725,8 +737,9 @@
   DCHECK(IsMainThread());
 
   // When a posted task is performed, the execution context might be gone.
-  if (!GetExecutionContext())
+  if (!GetExecutionContext()) {
     return;
+  }
 
   GraphAutoLocker locker(this);
 
@@ -751,8 +764,9 @@
 void BaseAudioContext::ScheduleMainThreadCleanup() {
   DCHECK(IsAudioThread());
 
-  if (has_posted_cleanup_task_)
+  if (has_posted_cleanup_task_) {
     return;
+  }
   PostCrossThreadTask(
       *task_runner_, FROM_HERE,
       CrossThreadBindOnce(&BaseAudioContext::PerformCleanupOnMainThread,
@@ -822,8 +836,9 @@
 }
 
 const SecurityOrigin* BaseAudioContext::GetSecurityOrigin() const {
-  if (GetExecutionContext())
+  if (GetExecutionContext()) {
     return GetExecutionContext()->GetSecurityOrigin();
+  }
 
   return nullptr;
 }
@@ -879,8 +894,9 @@
 
   AudioDestinationNode* destination_node = destination();
   if (!destination_node ||
-      !destination_node->GetAudioDestinationHandler().IsInitialized())
+      !destination_node->GetAudioDestinationHandler().IsInitialized()) {
     return -1;
+  }
 
   return destination_node->GetAudioDestinationHandler().MaxChannelCount();
 }
@@ -891,8 +907,9 @@
   AudioDestinationNode* destination_node = destination();
   if (!destination_node ||
       !destination_node->GetAudioDestinationHandler().IsInitialized() ||
-      !HasRealtimeConstraint())
+      !HasRealtimeConstraint()) {
     return -1;
+  }
 
   RealtimeAudioDestinationHandler& destination_handler =
       static_cast<RealtimeAudioDestinationHandler&>(
diff --git a/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc b/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc
index e8caa61..0b0419f 100644
--- a/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc
+++ b/third_party/blink/renderer/modules/webaudio/biquad_dsp_kernel.cc
@@ -180,8 +180,9 @@
   // for this block if necessary. We'll get them the next time around.
   {
     MutexTryLocker try_locker(process_lock_);
-    if (try_locker.Locked())
+    if (try_locker.Locked()) {
       UpdateCoefficientsIfNecessary(frames_to_process);
+    }
   }
 
   biquad_.Process(source, destination, frames_to_process);
@@ -206,8 +207,9 @@
 
   // Convert from frequency in Hz to normalized frequency (0 -> 1),
   // with 1 equal to the Nyquist frequency.
-  for (int k = 0; k < n_frequencies; ++k)
+  for (int k = 0; k < n_frequencies; ++k) {
     frequency[k] = frequency_hz[k] / nyquist;
+  }
 
   kernel.biquad_.GetFrequencyResponse(n_frequencies, frequency.data(),
                                       mag_response, phase_response);
diff --git a/third_party/blink/renderer/modules/webaudio/biquad_filter_node.cc b/third_party/blink/renderer/modules/webaudio/biquad_filter_node.cc
index e4233bc..80a54ef 100644
--- a/third_party/blink/renderer/modules/webaudio/biquad_filter_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/biquad_filter_node.cc
@@ -103,8 +103,9 @@
 
 void BiquadFilterHandler::NotifyBadState() const {
   DCHECK(IsMainThread());
-  if (!Context() || !Context()->GetExecutionContext())
+  if (!Context() || !Context()->GetExecutionContext()) {
     return;
+  }
 
   Context()->GetExecutionContext()->AddConsoleMessage(
       MakeGarbageCollected<ConsoleMessage>(
@@ -162,8 +163,9 @@
 
   // TODO(crbug.com/1055983): Remove this when the execution context validity
   // check is not required in the AudioNode factory methods.
-  if (!context.CheckExecutionContextAndThrowIfNecessary(exception_state))
+  if (!context.CheckExecutionContextAndThrowIfNecessary(exception_state)) {
     return nullptr;
+  }
 
   return MakeGarbageCollected<BiquadFilterNode>(context);
 }
@@ -173,8 +175,9 @@
                                            ExceptionState& exception_state) {
   BiquadFilterNode* node = Create(*context, exception_state);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
   node->HandleChannelOptions(options, exception_state);
 
@@ -245,8 +248,9 @@
 }
 
 bool BiquadFilterNode::SetType(BiquadProcessor::FilterType type) {
-  if (type > BiquadProcessor::FilterType::kAllpass)
+  if (type > BiquadProcessor::FilterType::kAllpass) {
     return false;
+  }
 
   base::UmaHistogramEnumeration("WebAudio.BiquadFilter.Type", type);
 
diff --git a/third_party/blink/renderer/modules/webaudio/biquad_processor.cc b/third_party/blink/renderer/modules/webaudio/biquad_processor.cc
index bf86e53..b9a2d7a9 100644
--- a/third_party/blink/renderer/modules/webaudio/biquad_processor.cc
+++ b/third_party/blink/renderer/modules/webaudio/biquad_processor.cc
@@ -49,8 +49,9 @@
       has_sample_accurate_values_(false) {}
 
 BiquadProcessor::~BiquadProcessor() {
-  if (IsInitialized())
+  if (IsInitialized()) {
     Uninitialize();
+  }
 }
 
 std::unique_ptr<AudioDSPKernel> BiquadProcessor::CreateKernel() {
@@ -105,8 +106,9 @@
       bool is_stable2 = parameter2_->Smooth();
       bool is_stable3 = parameter3_->Smooth();
       bool is_stable4 = parameter4_->Smooth();
-      if (!(is_stable1 && is_stable2 && is_stable3 && is_stable4))
+      if (!(is_stable1 && is_stable2 && is_stable3 && is_stable4)) {
         filter_coefficients_dirty_ = true;
+      }
     }
   }
 }
@@ -131,10 +133,11 @@
 
   // For each channel of our input, process using the corresponding
   // BiquadDSPKernel into the output channel.
-  for (unsigned i = 0; i < kernels_.size(); ++i)
+  for (unsigned i = 0; i < kernels_.size(); ++i) {
     kernels_[i]->Process(source->Channel(i)->Data(),
                          destination->Channel(i)->MutableData(),
                          frames_to_process);
+  }
 }
 
 void BiquadProcessor::ProcessOnlyAudioParams(uint32_t frames_to_process) {
diff --git a/third_party/blink/renderer/modules/webaudio/channel_merger_node.cc b/third_party/blink/renderer/modules/webaudio/channel_merger_node.cc
index 9ac2332a..541e1bc 100644
--- a/third_party/blink/renderer/modules/webaudio/channel_merger_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/channel_merger_node.cc
@@ -48,8 +48,9 @@
   SetInternalChannelCountMode(kExplicit);
 
   // Create the requested number of inputs.
-  for (unsigned i = 0; i < number_of_inputs; ++i)
+  for (unsigned i = 0; i < number_of_inputs; ++i) {
     AddInput();
+  }
 
   // Create the output with the requested number of channels.
   AddOutput(number_of_inputs);
@@ -173,8 +174,9 @@
   ChannelMergerNode* node =
       Create(*context, options->numberOfInputs(), exception_state);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
   node->HandleChannelOptions(options, exception_state);
 
diff --git a/third_party/blink/renderer/modules/webaudio/channel_splitter_node.cc b/third_party/blink/renderer/modules/webaudio/channel_splitter_node.cc
index 7a426e6a..492fc08 100644
--- a/third_party/blink/renderer/modules/webaudio/channel_splitter_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/channel_splitter_node.cc
@@ -47,8 +47,9 @@
 
   // Create a fixed number of outputs (able to handle the maximum number of
   // channels fed to an input).
-  for (unsigned i = 0; i < number_of_outputs; ++i)
+  for (unsigned i = 0; i < number_of_outputs; ++i) {
     AddOutput(1);
+  }
 
   Initialize();
 }
@@ -172,8 +173,9 @@
   ChannelSplitterNode* node =
       Create(*context, options->numberOfOutputs(), exception_state);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
   node->HandleChannelOptions(options, exception_state);
 
diff --git a/third_party/blink/renderer/modules/webaudio/constant_source_node.cc b/third_party/blink/renderer/modules/webaudio/constant_source_node.cc
index ef77f3a..e388a7b 100644
--- a/third_party/blink/renderer/modules/webaudio/constant_source_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/constant_source_node.cc
@@ -156,8 +156,9 @@
 
   ConstantSourceNode* node = Create(*context, exception_state);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
   node->offset()->setValue(options->offset());
 
diff --git a/third_party/blink/renderer/modules/webaudio/convolver_node.cc b/third_party/blink/renderer/modules/webaudio/convolver_node.cc
index a32e421e..c8539e2 100644
--- a/third_party/blink/renderer/modules/webaudio/convolver_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/convolver_node.cc
@@ -213,10 +213,11 @@
 
 double ConvolverHandler::TailTime() const {
   MutexTryLocker try_locker(process_lock_);
-  if (try_locker.Locked())
+  if (try_locker.Locked()) {
     return reverb_ ? reverb_->ImpulseResponseLength() /
                          static_cast<double>(Context()->sampleRate())
                    : 0;
+  }
   // Since we don't want to block the Audio Device thread, we return a large
   // value instead of trying to acquire the lock.
   return std::numeric_limits<double>::infinity();
@@ -224,10 +225,11 @@
 
 double ConvolverHandler::LatencyTime() const {
   MutexTryLocker try_locker(process_lock_);
-  if (try_locker.Locked())
+  if (try_locker.Locked()) {
     return reverb_ ? reverb_->LatencyFrames() /
                          static_cast<double>(Context()->sampleRate())
                    : 0;
+  }
   // Since we don't want to block the Audio Device thread, we return a large
   // value instead of trying to acquire the lock.
   return std::numeric_limits<double>::infinity();
@@ -338,16 +340,18 @@
                                      ExceptionState& exception_state) {
   ConvolverNode* node = Create(*context, exception_state);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
   node->HandleChannelOptions(options, exception_state);
 
   // It is important to set normalize first because setting the buffer will
   // examing the normalize attribute to see if normalization needs to be done.
   node->setNormalize(!options->disableNormalization());
-  if (options->hasBuffer())
+  if (options->hasBuffer()) {
     node->setBuffer(options->buffer(), exception_state);
+  }
   return node;
 }
 
@@ -362,8 +366,9 @@
 void ConvolverNode::setBuffer(AudioBuffer* new_buffer,
                               ExceptionState& exception_state) {
   GetConvolverHandler().SetBuffer(new_buffer, exception_state);
-  if (!exception_state.HadException())
+  if (!exception_state.HadException()) {
     buffer_ = new_buffer;
+  }
 }
 
 bool ConvolverNode::normalize() const {
diff --git a/third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc b/third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc
index 76aa9ac..28107a546 100644
--- a/third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc
@@ -112,8 +112,9 @@
 
 void DeferredTaskHandler::HandleDirtyAudioSummingJunctions() {
   AssertGraphOwner();
-  for (AudioSummingJunction* junction : dirty_summing_junctions_)
+  for (AudioSummingJunction* junction : dirty_summing_junctions_) {
     junction->UpdateRenderingState();
+  }
   dirty_summing_junctions_.clear();
 }
 
@@ -126,8 +127,9 @@
   // Note: the updating of rendering state may cause output nodes
   // further down the chain to be marked as dirty. These will not
   // be processed in this render quantum.
-  for (AudioNodeOutput* output : dirty_outputs)
+  for (AudioNodeOutput* output : dirty_outputs) {
     output->UpdateRenderingState();
+  }
 }
 
 void DeferredTaskHandler::AddAutomaticPullNode(
@@ -276,15 +278,17 @@
 
 void DeferredTaskHandler::UpdateChangedChannelCountMode() {
   AssertGraphOwner();
-  for (AudioHandler* node : deferred_count_mode_change_)
+  for (AudioHandler* node : deferred_count_mode_change_) {
     node->UpdateChannelCountMode();
+  }
   deferred_count_mode_change_.clear();
 }
 
 void DeferredTaskHandler::UpdateChangedChannelInterpretation() {
   AssertGraphOwner();
-  for (AudioHandler* node : deferred_channel_interpretation_change_)
+  for (AudioHandler* node : deferred_channel_interpretation_change_) {
     node->UpdateChannelInterpretation();
+  }
   deferred_channel_interpretation_change_.clear();
 }
 
@@ -387,10 +391,12 @@
   // be modified on the audio thread.
   GraphAutoLocker locker(*this);
 
-  for (auto& handler : rendering_orphan_handlers_)
+  for (auto& handler : rendering_orphan_handlers_) {
     handler->ClearContext();
-  for (auto& handler : deletable_orphan_handlers_)
+  }
+  for (auto& handler : deletable_orphan_handlers_) {
     handler->ClearContext();
+  }
 }
 
 void DeferredTaskHandler::SetAudioThreadToCurrentThread() {
@@ -435,8 +441,9 @@
       Vector<scoped_refptr<AudioHandler>> handlers_to_be_disabled;
 
       handlers_to_be_disabled.swap(tail_processing_handlers_);
-      for (auto& handler : handlers_to_be_disabled)
+      for (auto& handler : handlers_to_be_disabled) {
         handler->DisableOutputs();
+      }
     }
     DisableOutputsForTailProcessing();
   } while (tail_processing_handlers_.size() > 0 ||
diff --git a/third_party/blink/renderer/modules/webaudio/delay_node.cc b/third_party/blink/renderer/modules/webaudio/delay_node.cc
index 9b0e61c..04b49bb 100644
--- a/third_party/blink/renderer/modules/webaudio/delay_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/delay_node.cc
@@ -112,8 +112,9 @@
   // maxDelayTime has a default value specified.
   DelayNode* node = Create(*context, options->maxDelayTime(), exception_state);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
   node->HandleChannelOptions(options, exception_state);
 
diff --git a/third_party/blink/renderer/modules/webaudio/delay_processor.cc b/third_party/blink/renderer/modules/webaudio/delay_processor.cc
index addcccc..5b278bde 100644
--- a/third_party/blink/renderer/modules/webaudio/delay_processor.cc
+++ b/third_party/blink/renderer/modules/webaudio/delay_processor.cc
@@ -42,8 +42,9 @@
       max_delay_time_(max_delay_time) {}
 
 DelayProcessor::~DelayProcessor() {
-  if (IsInitialized())
+  if (IsInitialized()) {
     Uninitialize();
+  }
 }
 
 std::unique_ptr<AudioDSPKernel> DelayProcessor::CreateKernel() {
diff --git a/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.cc b/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.cc
index b7d6de9c..34884c63 100644
--- a/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/dynamics_compressor_node.cc
@@ -124,8 +124,9 @@
 }
 
 void DynamicsCompressorHandler::Initialize() {
-  if (IsInitialized())
+  if (IsInitialized()) {
     return;
+  }
 
   AudioHandler::Initialize();
   dynamics_compressor_ = std::make_unique<DynamicsCompressor>(
@@ -155,8 +156,9 @@
   if (channel_count > 0 && channel_count <= 2) {
     if (channel_count_ != channel_count) {
       channel_count_ = channel_count;
-      if (InternalChannelCountMode() != kMax)
+      if (InternalChannelCountMode() != kMax) {
         UpdateChannelsForInputs();
+      }
     }
   } else {
     exception_state.ThrowDOMException(
@@ -192,8 +194,9 @@
     new_channel_count_mode_ = old_mode;
   }
 
-  if (new_channel_count_mode_ != old_mode)
+  if (new_channel_count_mode_ != old_mode) {
     Context()->GetDeferredTaskHandler().AddChangedChannelCountMode(this);
+  }
 }
 // ----------------------------------------------------------------
 
@@ -263,8 +266,9 @@
     ExceptionState& exception_state) {
   DynamicsCompressorNode* node = Create(*context, exception_state);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
   node->HandleChannelOptions(options, exception_state);
 
diff --git a/third_party/blink/renderer/modules/webaudio/gain_node.cc b/third_party/blink/renderer/modules/webaudio/gain_node.cc
index ed374a443..9cfc78a 100644
--- a/third_party/blink/renderer/modules/webaudio/gain_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/gain_node.cc
@@ -164,8 +164,9 @@
                            ExceptionState& exception_state) {
   GainNode* node = Create(*context, exception_state);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
   node->HandleChannelOptions(options, exception_state);
 
diff --git a/third_party/blink/renderer/modules/webaudio/iir_dsp_kernel.cc b/third_party/blink/renderer/modules/webaudio/iir_dsp_kernel.cc
index f53a899..06a5c8b8 100644
--- a/third_party/blink/renderer/modules/webaudio/iir_dsp_kernel.cc
+++ b/third_party/blink/renderer/modules/webaudio/iir_dsp_kernel.cc
@@ -40,8 +40,9 @@
 
   // Convert from frequency in Hz to normalized frequency (0 -> 1),
   // with 1 equal to the Nyquist frequency.
-  for (int k = 0; k < n_frequencies; ++k)
+  for (int k = 0; k < n_frequencies; ++k) {
     frequency[k] = frequency_hz[k] / nyquist;
+  }
 
   iir_.GetFrequencyResponse(n_frequencies, frequency.data(), mag_response,
                             phase_response);
diff --git a/third_party/blink/renderer/modules/webaudio/iir_filter_node.cc b/third_party/blink/renderer/modules/webaudio/iir_filter_node.cc
index c9ac70c..fcf4d4a 100644
--- a/third_party/blink/renderer/modules/webaudio/iir_filter_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/iir_filter_node.cc
@@ -75,8 +75,9 @@
 
   // If necessary, normalize filter coefficients so that constant term is 1.
   if (coef[0] != 1) {
-    for (int m = 1; m <= order; ++m)
+    for (int m = 1; m <= order; ++m) {
       coef[m] /= coef[0];
+    }
     coef[0] = 1;
   }
 
@@ -85,14 +86,16 @@
   for (int n = order; n >= 1; --n) {
     double k = coef[n];
 
-    if (std::fabs(k) >= 1)
+    if (std::fabs(k) >= 1) {
       return false;
+    }
 
     // Note that A[n](1/z)/z^n is basically the coefficients of A[n]
     // in reverse order.
     double factor = 1 - k * k;
-    for (int m = 0; m <= n; ++m)
+    for (int m = 0; m <= n; ++m) {
       work[m] = (coef[m] - k * coef[n - m]) / factor;
+    }
     coef.swap(work);
   }
 
@@ -118,8 +121,9 @@
 
 void IIRFilterHandler::NotifyBadState() const {
   DCHECK(IsMainThread());
-  if (!Context() || !Context()->GetExecutionContext())
+  if (!Context() || !Context()->GetExecutionContext()) {
     return;
+  }
 
   Context()->GetExecutionContext()->AddConsoleMessage(
       MakeGarbageCollected<ConsoleMessage>(
@@ -152,8 +156,9 @@
 
   // TODO(crbug.com/1055983): Remove this when the execution context validity
   // check is not required in the AudioNode factory methods.
-  if (!context.CheckExecutionContextAndThrowIfNecessary(exception_state))
+  if (!context.CheckExecutionContextAndThrowIfNecessary(exception_state)) {
     return nullptr;
+  }
 
   if (feedback_coef.size() == 0 ||
       (feedback_coef.size() > IIRFilter::kMaxOrder + 1)) {
@@ -227,8 +232,9 @@
   IIRFilterNode* node = Create(*context, options->feedforward(),
                                options->feedback(), exception_state);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
   node->HandleChannelOptions(options, exception_state);
 
diff --git a/third_party/blink/renderer/modules/webaudio/iir_processor.cc b/third_party/blink/renderer/modules/webaudio/iir_processor.cc
index a60bea8..f87bf3e0 100644
--- a/third_party/blink/renderer/modules/webaudio/iir_processor.cc
+++ b/third_party/blink/renderer/modules/webaudio/iir_processor.cc
@@ -45,11 +45,13 @@
     // Thus, the feedback and feedforward coefficients need to be scaled by
     // 1/a[0].
     float scale = feedback_coef[0];
-    for (unsigned k = 1; k < feedback_length; ++k)
+    for (unsigned k = 1; k < feedback_length; ++k) {
       feedback_[k] /= scale;
+    }
 
-    for (unsigned k = 0; k < feedforward_length; ++k)
+    for (unsigned k = 0; k < feedforward_length; ++k) {
       feedforward_[k] /= scale;
+    }
 
     // The IIRFilter checks to make sure this coefficient is 1, so make it so.
     feedback_[0] = 1;
@@ -59,8 +61,9 @@
 }
 
 IIRProcessor::~IIRProcessor() {
-  if (IsInitialized())
+  if (IsInitialized()) {
     Uninitialize();
+  }
 }
 
 std::unique_ptr<AudioDSPKernel> IIRProcessor::CreateKernel() {
@@ -77,10 +80,11 @@
 
   // For each channel of our input, process using the corresponding IIRDSPKernel
   // into the output channel.
-  for (unsigned i = 0; i < kernels_.size(); ++i)
+  for (unsigned i = 0; i < kernels_.size(); ++i) {
     kernels_[i]->Process(source->Channel(i)->Data(),
                          destination->Channel(i)->MutableData(),
                          frames_to_process);
+  }
 }
 
 void IIRProcessor::GetFrequencyResponse(int n_frequencies,
diff --git a/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc b/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc
index fa438c2..27bc2e1d 100644
--- a/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc
+++ b/third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.cc
@@ -65,16 +65,18 @@
 InspectorWebAudioAgent::~InspectorWebAudioAgent() = default;
 
 void InspectorWebAudioAgent::Restore() {
-  if (!enabled_.Get())
+  if (!enabled_.Get()) {
     return;
+  }
 
   AudioGraphTracer* graph_tracer = AudioGraphTracer::FromPage(page_);
   graph_tracer->SetInspectorAgent(this);
 }
 
 Response InspectorWebAudioAgent::enable() {
-  if (enabled_.Get())
+  if (enabled_.Get()) {
     return Response::Success();
+  }
   enabled_.Set(true);
   AudioGraphTracer* graph_tracer = AudioGraphTracer::FromPage(page_);
   graph_tracer->SetInspectorAgent(this);
@@ -82,8 +84,9 @@
 }
 
 Response InspectorWebAudioAgent::disable() {
-  if (!enabled_.Get())
+  if (!enabled_.Get()) {
     return Response::Success();
+  }
   enabled_.Clear();
   AudioGraphTracer* graph_tracer = AudioGraphTracer::FromPage(page_);
   graph_tracer->SetInspectorAgent(nullptr);
@@ -94,12 +97,14 @@
     const protocol::WebAudio::GraphObjectId& contextId,
     std::unique_ptr<ContextRealtimeData>* out_data) {
   auto* const graph_tracer = AudioGraphTracer::FromPage(page_);
-  if (!enabled_.Get())
+  if (!enabled_.Get()) {
     return Response::ServerError("Enable agent first.");
+  }
 
   BaseAudioContext* context = graph_tracer->GetContextById(contextId);
-  if (!context)
+  if (!context) {
     return Response::ServerError("Cannot find BaseAudioContext with such id.");
+  }
 
   if (!context->HasRealtimeConstraint()) {
     return Response::ServerError(
diff --git a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.cc b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.cc
index fcdfcc91..f060bbe 100644
--- a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.cc
@@ -44,11 +44,13 @@
 namespace {
 
 void DidCreateMediaStreamAndTracks(MediaStreamDescriptor* stream) {
-  for (uint32_t i = 0; i < stream->NumberOfAudioComponents(); ++i)
+  for (uint32_t i = 0; i < stream->NumberOfAudioComponents(); ++i) {
     MediaStreamUtils::DidCreateMediaStreamTrack(stream->AudioComponent(i));
+  }
 
-  for (uint32_t i = 0; i < stream->NumberOfVideoComponents(); ++i)
+  for (uint32_t i = 0; i < stream->NumberOfVideoComponents(); ++i) {
     MediaStreamUtils::DidCreateMediaStreamTrack(stream->VideoComponent(i));
+  }
 }
 
 }  // namespace
@@ -237,8 +239,9 @@
 
   // TODO(crbug.com/1055983): Remove this when the execution context validity
   // check is not required in the AudioNode factory methods.
-  if (!context.CheckExecutionContextAndThrowIfNecessary(exception_state))
+  if (!context.CheckExecutionContextAndThrowIfNecessary(exception_state)) {
     return nullptr;
+  }
 
   return MakeGarbageCollected<MediaStreamAudioDestinationNode>(
       context, number_of_channels);
@@ -250,8 +253,9 @@
     ExceptionState& exception_state) {
   DCHECK(IsMainThread());
 
-  if (!context->CheckExecutionContextAndThrowIfNecessary(exception_state))
+  if (!context->CheckExecutionContextAndThrowIfNecessary(exception_state)) {
     return nullptr;
+  }
   // Default to stereo; |options| will update it appropriately if needed.
   MediaStreamAudioDestinationNode* node =
       MakeGarbageCollected<MediaStreamAudioDestinationNode>(*context, 2);
@@ -260,8 +264,9 @@
   // limit is different from the normal AudioNode::setChannelCount
   // limit of 32.  Error messages will sometimes show the wrong
   // limits.
-  if (options->hasChannelCount())
+  if (options->hasChannelCount()) {
     node->setChannelCount(options->channelCount(), exception_state);
+  }
 
   node->HandleChannelOptions(options, exception_state);
 
diff --git a/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.cc b/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.cc
index 63d13e2..0887430 100644
--- a/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.cc
@@ -178,8 +178,9 @@
 
   // TODO(crbug.com/1055983): Remove this when the execution context validity
   // check is not required in the AudioNode factory methods.
-  if (!context.CheckExecutionContextAndThrowIfNecessary(exception_state))
+  if (!context.CheckExecutionContextAndThrowIfNecessary(exception_state)) {
     return nullptr;
+  }
 
   // The constructor algorithm:
   // https://webaudio.github.io/web-audio-api/#mediastreamaudiosourcenode
@@ -197,8 +198,9 @@
   // (See: https://infra.spec.whatwg.org/#code-unit)
   MediaStreamTrack* audio_track = audio_tracks[0];
   for (auto track : audio_tracks) {
-    if (CodeUnitCompareLessThan(track->id(), audio_track->id()))
+    if (CodeUnitCompareLessThan(track->id(), audio_track->id())) {
       audio_track = track;
+    }
   }
 
   // 1.24.1. Step 5: The step is out of order because the constructor needs
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc b/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc
index acd099e..6684c75 100644
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc
@@ -367,8 +367,9 @@
         static_cast<OfflineAudioDestinationNode*>(destination())
             ->DestinationBuffer();
     DCHECK(rendered_buffer);
-    if (!rendered_buffer)
+    if (!rendered_buffer) {
       return;
+    }
 
     // Call the offline rendering completion event listener and resolve the
     // promise too.
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
index ecf14ca..9f18802 100644
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
@@ -80,21 +80,24 @@
 }
 
 void OfflineAudioDestinationHandler::Initialize() {
-  if (IsInitialized())
+  if (IsInitialized()) {
     return;
+  }
 
   AudioHandler::Initialize();
 }
 
 void OfflineAudioDestinationHandler::Uninitialize() {
-  if (!IsInitialized())
+  if (!IsInitialized()) {
     return;
+  }
 
   // See https://crbug.com/1110035 and https://crbug.com/1080821. Resetting the
   // thread unique pointer multiple times or not-resetting at all causes a
   // mysterious CHECK failure or a crash.
-  if (render_thread_)
+  if (render_thread_) {
     render_thread_.reset();
+  }
 
   AudioHandler::Uninitialize();
 }
@@ -191,8 +194,9 @@
     // Suspend the rendering if a scheduled suspend found at the current
     // sample frame. Otherwise render one quantum.
     if (RenderIfNotSuspended(nullptr, render_bus_.get(),
-                             GetDeferredTaskHandler().RenderQuantumFrames()))
+                             GetDeferredTaskHandler().RenderQuantumFrames())) {
       return;
+    }
 
     uint32_t frames_available_to_copy = std::min(
         frames_to_process_, GetDeferredTaskHandler().RenderQuantumFrames());
@@ -238,8 +242,9 @@
 void OfflineAudioDestinationHandler::NotifySuspend(size_t frame) {
   DCHECK(IsMainThread());
 
-  if (!IsExecutionContextDestroyed() && Context())
+  if (!IsExecutionContextDestroyed() && Context()) {
     Context()->ResolveSuspendOnMainThread(frame);
+  }
 }
 
 void OfflineAudioDestinationHandler::NotifyComplete() {
@@ -254,8 +259,9 @@
   }
 
   // The OfflineAudioContext might be gone.
-  if (Context() && Context()->GetExecutionContext())
+  if (Context() && Context()->GetExecutionContext()) {
     Context()->FireCompletionEvent();
+  }
 }
 
 bool OfflineAudioDestinationHandler::RenderIfNotSuspended(
@@ -275,8 +281,9 @@
   // TODO(hongchan): because the context can go away while rendering, so this
   // check cannot guarantee the safe execution of the following steps.
   DCHECK(Context());
-  if (!Context())
+  if (!Context()) {
     return false;
+  }
 
   Context()->GetDeferredTaskHandler().SetAudioThreadToCurrentThread();
 
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_worklet_thread.cc b/third_party/blink/renderer/modules/webaudio/offline_audio_worklet_thread.cc
index 7673535f..f6e2fdb 100644
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_worklet_thread.cc
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_worklet_thread.cc
@@ -27,14 +27,16 @@
   // OfflineAudioWorkletThread always uses a NORMAL priority thread.
   params.thread_priority = base::ThreadPriority::NORMAL;
 
-  if (++s_ref_count_ == 1)
+  if (++s_ref_count_ == 1) {
     EnsureSharedBackingThread(params);
+  }
 }
 
 OfflineAudioWorkletThread::~OfflineAudioWorkletThread() {
   DCHECK(IsMainThread());
-  if (--s_ref_count_ == 0)
+  if (--s_ref_count_ == 0) {
     ClearSharedBackingThread();
+  }
 }
 
 WorkerBackingThread& OfflineAudioWorkletThread::GetWorkerBackingThread() {
diff --git a/third_party/blink/renderer/modules/webaudio/oscillator_node.cc b/third_party/blink/renderer/modules/webaudio/oscillator_node.cc
index 321a65c..b91b74ed 100644
--- a/third_party/blink/renderer/modules/webaudio/oscillator_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/oscillator_node.cc
@@ -64,16 +64,17 @@
     // forcing the type to be 'custom".
     SetPeriodicWave(wave_table);
   } else {
-    if (oscillator_type == "sine")
+    if (oscillator_type == "sine") {
       SetType(SINE);
-    else if (oscillator_type == "square")
+    } else if (oscillator_type == "square") {
       SetType(SQUARE);
-    else if (oscillator_type == "sawtooth")
+    } else if (oscillator_type == "sawtooth") {
       SetType(SAWTOOTH);
-    else if (oscillator_type == "triangle")
+    } else if (oscillator_type == "triangle") {
       SetType(TRIANGLE);
-    else
+    } else {
       NOTREACHED();
+    }
   }
 
   // An oscillator is always mono.
@@ -857,8 +858,9 @@
              options->hasPeriodicWave() ? options->periodicWave() : nullptr,
              exception_state);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
   node->HandleChannelOptions(options, exception_state);
 
diff --git a/third_party/blink/renderer/modules/webaudio/panner_node.cc b/third_party/blink/renderer/modules/webaudio/panner_node.cc
index 956eb20..b4a9e8635 100644
--- a/third_party/blink/renderer/modules/webaudio/panner_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/panner_node.cc
@@ -42,8 +42,9 @@
 namespace blink {
 
 static void FixNANs(double& x) {
-  if (std::isnan(x) || std::isinf(x))
+  if (std::isnan(x) || std::isinf(x)) {
     x = 0.0;
+  }
 }
 
 PannerHandler::PannerHandler(AudioNode& node,
@@ -107,8 +108,9 @@
 void PannerHandler::ProcessIfNecessary(uint32_t frames_to_process) {
   DCHECK(Context()->IsAudioThread());
 
-  if (!IsInitialized())
+  if (!IsInitialized()) {
     return;
+  }
 
   // Ensure that we only process once per rendering quantum.
   // This handles the "fanout" problem where an output is connected to multiple
@@ -327,8 +329,9 @@
 }
 
 void PannerHandler::Initialize() {
-  if (IsInitialized())
+  if (IsInitialized()) {
     return;
+  }
 
   auto listener = Listener();
   panner_ = Panner::Create(panning_model_, Context()->sampleRate(),
@@ -344,8 +347,9 @@
 }
 
 void PannerHandler::Uninitialize() {
-  if (!IsInitialized())
+  if (!IsInitialized()) {
     return;
+  }
 
   panner_.reset();
   auto listener = Listener();
@@ -376,12 +380,13 @@
 void PannerHandler::SetPanningModel(const String& model) {
   // WebIDL should guarantee that we are never called with an invalid string
   // for the model.
-  if (model == "equalpower")
+  if (model == "equalpower") {
     SetPanningModel(Panner::PanningModel::kEqualPower);
-  else if (model == "HRTF")
+  } else if (model == "HRTF") {
     SetPanningModel(Panner::PanningModel::kHRTF);
-  else
+  } else {
     NOTREACHED();
+  }
 }
 
 // This method should only be called from setPanningModel(const String&)!
@@ -427,12 +432,13 @@
 }
 
 void PannerHandler::SetDistanceModel(const String& model) {
-  if (model == "linear")
+  if (model == "linear") {
     SetDistanceModel(DistanceEffect::kModelLinear);
-  else if (model == "inverse")
+  } else if (model == "inverse") {
     SetDistanceModel(DistanceEffect::kModelInverse);
-  else if (model == "exponential")
+  } else if (model == "exponential") {
     SetDistanceModel(DistanceEffect::kModelExponential);
+  }
 }
 
 bool PannerHandler::SetDistanceModel(unsigned model) {
@@ -457,8 +463,9 @@
 }
 
 void PannerHandler::SetRefDistance(double distance) {
-  if (RefDistance() == distance)
+  if (RefDistance() == distance) {
     return;
+  }
 
   // This synchronizes with process().
   MutexLocker process_locker(process_lock_);
@@ -467,8 +474,9 @@
 }
 
 void PannerHandler::SetMaxDistance(double distance) {
-  if (MaxDistance() == distance)
+  if (MaxDistance() == distance) {
     return;
+  }
 
   // This synchronizes with process().
   MutexLocker process_locker(process_lock_);
@@ -477,8 +485,9 @@
 }
 
 void PannerHandler::SetRolloffFactor(double factor) {
-  if (RolloffFactor() == factor)
+  if (RolloffFactor() == factor) {
     return;
+  }
 
   // This synchronizes with process().
   MutexLocker process_locker(process_lock_);
@@ -487,8 +496,9 @@
 }
 
 void PannerHandler::SetConeInnerAngle(double angle) {
-  if (ConeInnerAngle() == angle)
+  if (ConeInnerAngle() == angle) {
     return;
+  }
 
   // This synchronizes with process().
   MutexLocker process_locker(process_lock_);
@@ -497,8 +507,9 @@
 }
 
 void PannerHandler::SetConeOuterAngle(double angle) {
-  if (ConeOuterAngle() == angle)
+  if (ConeOuterAngle() == angle) {
     return;
+  }
 
   // This synchronizes with process().
   MutexLocker process_locker(process_lock_);
@@ -507,8 +518,9 @@
 }
 
 void PannerHandler::SetConeOuterGain(double angle) {
-  if (ConeOuterGain() == angle)
+  if (ConeOuterGain() == angle) {
     return;
+  }
 
   // This synchronizes with process().
   MutexLocker process_locker(process_lock_);
@@ -592,29 +604,34 @@
 
   // Source  in front or behind the listener
   double front_back = gfx::DotProduct(projected_source, listener_forward_norm);
-  if (front_back < 0.0)
+  if (front_back < 0.0) {
     azimuth = 360.0 - azimuth;
+  }
 
   // Make azimuth relative to "front" and not "right" listener vector
-  if ((azimuth >= 0.0) && (azimuth <= 270.0))
+  if ((azimuth >= 0.0) && (azimuth <= 270.0)) {
     azimuth = 90.0 - azimuth;
-  else
+  } else {
     azimuth = 450.0 - azimuth;
+  }
 
   // Elevation
   double elevation =
       90 - gfx::AngleBetweenVectorsInDegrees(source_listener, up);
   FixNANs(elevation);  // avoid illegal values
 
-  if (elevation > 90.0)
+  if (elevation > 90.0) {
     elevation = 180.0 - elevation;
-  else if (elevation < -90.0)
+  } else if (elevation < -90.0) {
     elevation = -180.0 - elevation;
+  }
 
-  if (out_azimuth)
+  if (out_azimuth) {
     *out_azimuth = azimuth;
-  if (out_elevation)
+  }
+  if (out_elevation) {
     *out_elevation = elevation;
+  }
 }
 
 float PannerHandler::CalculateDistanceConeGain(
@@ -663,11 +680,13 @@
 }
 
 void PannerHandler::MarkPannerAsDirty(unsigned dirty) {
-  if (dirty & PannerHandler::kAzimuthElevationDirty)
+  if (dirty & PannerHandler::kAzimuthElevationDirty) {
     is_azimuth_elevation_dirty_ = true;
+  }
 
-  if (dirty & PannerHandler::kDistanceConeGainDirty)
+  if (dirty & PannerHandler::kDistanceConeGainDirty) {
     is_distance_cone_gain_dirty_ = true;
+  }
 }
 
 void PannerHandler::SetChannelCount(unsigned channel_count,
@@ -679,8 +698,9 @@
   if (channel_count > 0 && channel_count <= 2) {
     if (channel_count_ != channel_count) {
       channel_count_ = channel_count;
-      if (InternalChannelCountMode() != kMax)
+      if (InternalChannelCountMode() != kMax) {
         UpdateChannelsForInputs();
+      }
     }
   } else {
     exception_state.ThrowDOMException(
@@ -714,8 +734,9 @@
     new_channel_count_mode_ = old_mode;
   }
 
-  if (new_channel_count_mode_ != old_mode)
+  if (new_channel_count_mode_ != old_mode) {
     Context()->GetDeferredTaskHandler().AddChangedChannelCountMode(this);
+  }
 }
 
 bool PannerHandler::HasSampleAccurateValues() const {
@@ -823,8 +844,9 @@
                                ExceptionState& exception_state) {
   PannerNode* node = Create(*context, exception_state);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
   node->HandleChannelOptions(options, exception_state);
 
diff --git a/third_party/blink/renderer/modules/webaudio/periodic_wave.cc b/third_party/blink/renderer/modules/webaudio/periodic_wave.cc
index b7f80c2e..407e0f6 100644
--- a/third_party/blink/renderer/modules/webaudio/periodic_wave.cc
+++ b/third_party/blink/renderer/modules/webaudio/periodic_wave.cc
@@ -107,10 +107,11 @@
 
   if (options->hasReal()) {
     real_coef = options->real();
-    if (options->hasImag())
+    if (options->hasImag()) {
       imag_coef = options->imag();
-    else
+    } else {
       imag_coef.resize(real_coef.size());
+    }
   } else if (options->hasImag()) {
     // |real| not given, but we have |imag|.
     imag_coef = options->imag();
@@ -468,8 +469,9 @@
         float max_value;
         vector_math::Vmaxmgv(data, 1, &max_value, fft_size);
 
-        if (max_value)
+        if (max_value) {
           normalization_scale = 1.0f / max_value;
+        }
       }
     }
 
diff --git a/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc b/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc
index b26b092..ce81945 100644
--- a/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc
+++ b/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc
@@ -63,8 +63,9 @@
 
   // Only allow powers of two within the allowed range.
   if (size > kMaxFFTSize || size < kMinFFTSize ||
-      !audio_utilities::IsPowerOfTwo(size))
+      !audio_utilities::IsPowerOfTwo(size)) {
     return false;
+  }
 
   if (fft_size_ != size) {
     analysis_frame_ = std::make_unique<FFTFrame>(size);
@@ -98,8 +99,9 @@
          frames_to_process * sizeof(*dest));
 
   write_index += frames_to_process;
-  if (write_index >= kInputBufferSize)
+  if (write_index >= kInputBufferSize) {
     write_index = 0;
+  }
   SetWriteIndex(write_index);
 }
 
@@ -241,10 +243,12 @@
           UCHAR_MAX * (db_mag - min_decibels) * range_scale_factor;
 
       // Clip to valid range.
-      if (scaled_value < 0)
+      if (scaled_value < 0) {
         scaled_value = 0;
-      if (scaled_value > UCHAR_MAX)
+      }
+      if (scaled_value > UCHAR_MAX) {
         scaled_value = UCHAR_MAX;
+      }
 
       destination[i] = static_cast<unsigned char>(scaled_value);
     }
@@ -325,10 +329,12 @@
       double scaled_value = 128 * (value + 1);
 
       // Clip to valid range.
-      if (scaled_value < 0)
+      if (scaled_value < 0) {
         scaled_value = 0;
-      if (scaled_value > UCHAR_MAX)
+      }
+      if (scaled_value > UCHAR_MAX) {
         scaled_value = UCHAR_MAX;
+      }
 
       destination[i] = static_cast<unsigned char>(scaled_value);
     }
diff --git a/third_party/blink/renderer/modules/webaudio/realtime_audio_worklet_thread.cc b/third_party/blink/renderer/modules/webaudio/realtime_audio_worklet_thread.cc
index ec8a1a7..88ce135 100644
--- a/third_party/blink/renderer/modules/webaudio/realtime_audio_worklet_thread.cc
+++ b/third_party/blink/renderer/modules/webaudio/realtime_audio_worklet_thread.cc
@@ -41,14 +41,16 @@
                  "RealtimeAudioWorkletThread() - NORMAL");
   }
 
-  if (++s_ref_count_ == 1)
+  if (++s_ref_count_ == 1) {
     EnsureSharedBackingThread(params);
+  }
 }
 
 RealtimeAudioWorkletThread::~RealtimeAudioWorkletThread() {
   DCHECK(IsMainThread());
-  if (--s_ref_count_ == 0)
+  if (--s_ref_count_ == 0) {
     ClearSharedBackingThread();
+  }
 }
 
 WorkerBackingThread& RealtimeAudioWorkletThread::GetWorkerBackingThread() {
diff --git a/third_party/blink/renderer/modules/webaudio/script_processor_node.cc b/third_party/blink/renderer/modules/webaudio/script_processor_node.cc
index fd2feca..e14e197 100644
--- a/third_party/blink/renderer/modules/webaudio/script_processor_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/script_processor_node.cc
@@ -140,8 +140,9 @@
 }
 
 void ScriptProcessorHandler::Initialize() {
-  if (IsInitialized())
+  if (IsInitialized()) {
     return;
+  }
   AudioHandler::Initialize();
 }
 
@@ -275,8 +276,9 @@
 void ScriptProcessorHandler::FireProcessEvent(uint32_t double_buffer_index) {
   DCHECK(IsMainThread());
 
-  if (!Context() || !Context()->GetExecutionContext())
+  if (!Context() || !Context()->GetExecutionContext()) {
     return;
+  }
 
   DCHECK_LT(double_buffer_index, 2u);
 
@@ -298,8 +300,9 @@
     base::WaitableEvent* waitable_event) {
   DCHECK(IsMainThread());
 
-  if (!Context() || !Context()->GetExecutionContext())
+  if (!Context() || !Context()->GetExecutionContext()) {
     return;
+  }
 
   DCHECK_LT(double_buffer_index, 2u);
   if (double_buffer_index > 1) {
@@ -369,8 +372,9 @@
     : AudioNode(context) {
   // Regardless of the allowed buffer sizes, we still need to process at the
   // granularity of the AudioNode.
-  if (buffer_size < context.GetDeferredTaskHandler().RenderQuantumFrames())
+  if (buffer_size < context.GetDeferredTaskHandler().RenderQuantumFrames()) {
     buffer_size = context.GetDeferredTaskHandler().RenderQuantumFrames();
+  }
 
   // Create double buffers on both the input and output sides.
   // These AudioBuffers will be directly accessed in the main thread by
@@ -408,10 +412,12 @@
   uint32_t buffer_size =
       1 << static_cast<uint32_t>(log2(4 * callback_buffer_size) + 0.5);
 
-  if (buffer_size < 256)
+  if (buffer_size < 256) {
     return 256;
-  if (buffer_size > 16384)
+  }
+  if (buffer_size > 16384) {
     return 16384;
+  }
 
   return buffer_size;
 }
@@ -522,8 +528,9 @@
       context, context.sampleRate(), buffer_size, number_of_input_channels,
       number_of_output_channels);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
   return node;
 }
@@ -625,13 +632,15 @@
 
 bool ScriptProcessorNode::HasPendingActivity() const {
   // To prevent the node from leaking after the context is closed.
-  if (context()->IsContextCleared())
+  if (context()->IsContextCleared()) {
     return false;
+  }
 
   // If |onaudioprocess| event handler is defined, the node should not be
   // GCed even if it is out of scope.
-  if (HasEventListeners(event_type_names::kAudioprocess))
+  if (HasEventListeners(event_type_names::kAudioprocess)) {
     return true;
+  }
 
   return false;
 }
diff --git a/third_party/blink/renderer/modules/webaudio/semi_realtime_audio_worklet_thread.cc b/third_party/blink/renderer/modules/webaudio/semi_realtime_audio_worklet_thread.cc
index 5a3de4b..8cd7411 100644
--- a/third_party/blink/renderer/modules/webaudio/semi_realtime_audio_worklet_thread.cc
+++ b/third_party/blink/renderer/modules/webaudio/semi_realtime_audio_worklet_thread.cc
@@ -36,14 +36,16 @@
     params.thread_priority = base::ThreadPriority::NORMAL;
   }
 
-  if (++s_ref_count_ == 1)
+  if (++s_ref_count_ == 1) {
     EnsureSharedBackingThread(params);
+  }
 }
 
 SemiRealtimeAudioWorkletThread::~SemiRealtimeAudioWorkletThread() {
   DCHECK(IsMainThread());
-  if (--s_ref_count_ == 0)
+  if (--s_ref_count_ == 0) {
     ClearSharedBackingThread();
+  }
 }
 
 WorkerBackingThread& SemiRealtimeAudioWorkletThread::GetWorkerBackingThread() {
diff --git a/third_party/blink/renderer/modules/webaudio/stereo_panner_node.cc b/third_party/blink/renderer/modules/webaudio/stereo_panner_node.cc
index f922794..564cf02c 100644
--- a/third_party/blink/renderer/modules/webaudio/stereo_panner_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/stereo_panner_node.cc
@@ -90,8 +90,9 @@
 }
 
 void StereoPannerHandler::Initialize() {
-  if (IsInitialized())
+  if (IsInitialized()) {
     return;
+  }
 
   stereo_panner_ = std::make_unique<StereoPanner>(Context()->sampleRate());
 
@@ -107,8 +108,9 @@
   if (channel_count > 0 && channel_count <= 2) {
     if (channel_count_ != channel_count) {
       channel_count_ = channel_count;
-      if (InternalChannelCountMode() != kMax)
+      if (InternalChannelCountMode() != kMax) {
         UpdateChannelsForInputs();
+      }
     }
   } else {
     exception_state.ThrowDOMException(
@@ -142,8 +144,9 @@
     new_channel_count_mode_ = old_mode;
   }
 
-  if (new_channel_count_mode_ != old_mode)
+  if (new_channel_count_mode_ != old_mode) {
     Context()->GetDeferredTaskHandler().AddChangedChannelCountMode(this);
+  }
 }
 
 // ----------------------------------------------------------------
@@ -174,8 +177,9 @@
                                            ExceptionState& exception_state) {
   StereoPannerNode* node = Create(*context, exception_state);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
   node->HandleChannelOptions(options, exception_state);
 
diff --git a/third_party/blink/renderer/modules/webaudio/wave_shaper_dsp_kernel.cc b/third_party/blink/renderer/modules/webaudio/wave_shaper_dsp_kernel.cc
index 2631073..fe6feb5 100644
--- a/third_party/blink/renderer/modules/webaudio/wave_shaper_dsp_kernel.cc
+++ b/third_party/blink/renderer/modules/webaudio/wave_shaper_dsp_kernel.cc
@@ -51,8 +51,9 @@
       v1_(4 * RenderQuantumFrames()),
       v2_(4 * RenderQuantumFrames()),
       f_(4 * RenderQuantumFrames()) {
-  if (processor->Oversample() != WaveShaperProcessor::kOverSampleNone)
+  if (processor->Oversample() != WaveShaperProcessor::kOverSampleNone) {
     LazyInitializeOversampling();
+  }
 }
 
 void WaveShaperDSPKernel::LazyInitializeOversampling() {
diff --git a/third_party/blink/renderer/modules/webaudio/wave_shaper_node.cc b/third_party/blink/renderer/modules/webaudio/wave_shaper_node.cc
index f3debc4..e1220fc 100644
--- a/third_party/blink/renderer/modules/webaudio/wave_shaper_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/wave_shaper_node.cc
@@ -68,13 +68,15 @@
                                        ExceptionState& exception_state) {
   WaveShaperNode* node = Create(*context, exception_state);
 
-  if (!node)
+  if (!node) {
     return nullptr;
+  }
 
   node->HandleChannelOptions(options, exception_state);
 
-  if (options->hasCurve())
+  if (options->hasCurve()) {
     node->setCurve(options->curve(), exception_state);
+  }
 
   node->setOversample(options->oversample());
 
@@ -136,8 +138,9 @@
 
 NotShared<DOMFloat32Array> WaveShaperNode::curve() {
   Vector<float>* curve = GetWaveShaperProcessor()->Curve();
-  if (!curve)
+  if (!curve) {
     return NotShared<DOMFloat32Array>(nullptr);
+  }
 
   unsigned size = curve->size();
 
diff --git a/third_party/blink/renderer/modules/webaudio/wave_shaper_processor.cc b/third_party/blink/renderer/modules/webaudio/wave_shaper_processor.cc
index c2cd527..009e687 100644
--- a/third_party/blink/renderer/modules/webaudio/wave_shaper_processor.cc
+++ b/third_party/blink/renderer/modules/webaudio/wave_shaper_processor.cc
@@ -38,8 +38,9 @@
       oversample_(kOverSampleNone) {}
 
 WaveShaperProcessor::~WaveShaperProcessor() {
-  if (IsInitialized())
+  if (IsInitialized()) {
     Uninitialize();
+  }
 }
 
 std::unique_ptr<AudioDSPKernel> WaveShaperProcessor::CreateKernel() {
@@ -108,10 +109,11 @@
   if (try_locker.Locked()) {
     // For each channel of our input, process using the corresponding
     // WaveShaperDSPKernel into the output channel.
-    for (unsigned i = 0; i < kernels_.size(); ++i)
+    for (unsigned i = 0; i < kernels_.size(); ++i) {
       kernels_[i]->Process(source->Channel(i)->Data(),
                            destination->Channel(i)->MutableData(),
                            frames_to_process);
+    }
   } else {
     // Too bad - the tryLock() failed. We must be in the middle of a setCurve()
     // call.
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
index c51494e3..6135cd9 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
@@ -264,7 +264,7 @@
 }
 
 void WebGL2RenderingContextBase::bufferData(GLenum target,
-                                            DOMArrayBuffer* data,
+                                            DOMArrayBufferBase* data,
                                             GLenum usage) {
   WebGLRenderingContextBase::bufferData(target, data, usage);
 }
@@ -298,7 +298,7 @@
 
 void WebGL2RenderingContextBase::bufferSubData(GLenum target,
                                                int64_t offset,
-                                               DOMArrayBuffer* data) {
+                                               DOMArrayBufferBase* data) {
   WebGLRenderingContextBase::bufferSubData(target, offset, data);
 }
 
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.h
index e76c624..4a92db97 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.h
@@ -42,11 +42,11 @@
   // base class.  This is because the above buffer{Sub}Data() hides the name
   // from base class.
   void bufferData(GLenum target, int64_t size, GLenum usage);
-  void bufferData(GLenum target, DOMArrayBuffer* data, GLenum usage);
+  void bufferData(GLenum target, DOMArrayBufferBase* data, GLenum usage);
   void bufferData(GLenum target,
                   MaybeShared<DOMArrayBufferView> data,
                   GLenum usage);
-  void bufferSubData(GLenum target, int64_t offset, DOMArrayBuffer* data);
+  void bufferSubData(GLenum target, int64_t offset, DOMArrayBufferBase* data);
   void bufferSubData(GLenum target,
                      int64_t offset,
                      const FlexibleArrayBufferView& data);
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 80f4f81..fdbdb0c 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -1736,7 +1736,7 @@
 
   gfx::Rect src_rect(image->Size());
   gfx::Rect dest_rect(resource_provider->Size());
-  PaintFlags flags;
+  cc::PaintFlags flags;
   flags.setBlendMode(SkBlendMode::kSrc);
   // We use this draw helper as we need to take into account the
   // ImageOrientation of the UnacceleratedStaticBitmapImage.
@@ -2120,7 +2120,7 @@
 }
 
 void WebGLRenderingContextBase::bufferData(GLenum target,
-                                           DOMArrayBuffer* data,
+                                           DOMArrayBufferBase* data,
                                            GLenum usage) {
   if (isContextLost())
     return;
@@ -2128,7 +2128,7 @@
     SynthesizeGLError(GL_INVALID_VALUE, "bufferData", "no data");
     return;
   }
-  BufferDataImpl(target, data->ByteLength(), data->Data(), usage);
+  BufferDataImpl(target, data->ByteLength(), data->DataMaybeShared(), usage);
 }
 
 void WebGLRenderingContextBase::bufferData(GLenum target,
@@ -2162,11 +2162,12 @@
 
 void WebGLRenderingContextBase::bufferSubData(GLenum target,
                                               int64_t offset,
-                                              DOMArrayBuffer* data) {
+                                              DOMArrayBufferBase* data) {
   if (isContextLost())
     return;
   DCHECK(data);
-  BufferSubDataImpl(target, offset, data->ByteLength(), data->Data());
+  BufferSubDataImpl(target, offset, data->ByteLength(),
+                    data->DataMaybeShared());
 }
 
 void WebGLRenderingContextBase::bufferSubData(
@@ -5167,7 +5168,7 @@
 
   gfx::Rect src_rect(image->Size());
   gfx::Rect dest_rect(size);
-  PaintFlags flags;
+  cc::PaintFlags flags;
   // TODO(ccameron): WebGL should produce sRGB images.
   // https://crbug.com/672299
   ImageDrawOptions draw_options;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
index 648cf25..4d411c5 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
@@ -193,11 +193,11 @@
                          GLenum dst_alpha);
 
   void bufferData(GLenum target, int64_t size, GLenum usage);
-  void bufferData(GLenum target, DOMArrayBuffer* data, GLenum usage);
+  void bufferData(GLenum target, DOMArrayBufferBase* data, GLenum usage);
   void bufferData(GLenum target,
                   MaybeShared<DOMArrayBufferView> data,
                   GLenum usage);
-  void bufferSubData(GLenum target, int64_t offset, DOMArrayBuffer* data);
+  void bufferSubData(GLenum target, int64_t offset, DOMArrayBufferBase* data);
   void bufferSubData(GLenum target,
                      int64_t offset,
                      const FlexibleArrayBufferView& data);
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.idl b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.idl
index a90022f..38fdac77 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.idl
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.idl
@@ -482,9 +482,9 @@
     // https://www.khronos.org/bugzilla/show_bug.cgi?id=1172
     void bufferData(GLenum target, GLsizeiptr size, GLenum usage);
     void bufferData(GLenum target, [AllowShared] ArrayBufferView data, GLenum usage);
-    void bufferData(GLenum target, ArrayBuffer? data, GLenum usage);
+    void bufferData(GLenum target, [AllowShared] ArrayBuffer? data, GLenum usage);
     void bufferSubData(GLenum target, GLintptr offset, [AllowShared, FlexibleArrayBufferView] ArrayBufferView data);
-    void bufferSubData(GLenum target, GLintptr offset, ArrayBuffer data);
+    void bufferSubData(GLenum target, GLintptr offset, [AllowShared] ArrayBuffer data);
 
     GLenum checkFramebufferStatus(GLenum target);
     [NoAllocDirectCall] void clear(GLbitfield mask);
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
index 07241f1c..47cc7c2 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_queue.cc
@@ -206,11 +206,12 @@
     return nullptr;
   }
 
-  if (canvas && !(canvas->IsWebGL() || canvas->IsRenderingContext2D())) {
+  if (canvas && !(canvas->IsWebGL() || canvas->IsRenderingContext2D() ||
+                  canvas->IsWebGPU())) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kOperationError,
-        "CopyExternalImageToTexture doesn't support canvas without 2d, webgl "
-        "or webgl2 context");
+        "CopyExternalImageToTexture doesn't support canvas without 2d, webgl,"
+        " webgl2 or webgpu context");
     return nullptr;
   }
 
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 2896e16..3e6b0cc 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -751,13 +751,10 @@
     "geometry/double_size.h",
     "geometry/float_polygon.cc",
     "geometry/float_polygon.h",
-    "geometry/float_rect_outsets.cc",
-    "geometry/float_rect_outsets.h",
     "geometry/float_rounded_rect.cc",
     "geometry/float_rounded_rect.h",
     "geometry/geometry_as_json.h",
     "geometry/geometry_hash_traits.h",
-    "geometry/int_rect_outsets.h",
     "geometry/layout_point.cc",
     "geometry/layout_point.h",
     "geometry/layout_rect.cc",
@@ -1050,7 +1047,6 @@
     "graphics/paint/paint_controller.h",
     "graphics/paint/paint_controller_debug_data.cc",
     "graphics/paint/paint_filter.h",
-    "graphics/paint/paint_flags.h",
     "graphics/paint/paint_property_node.cc",
     "graphics/paint/paint_property_node.h",
     "graphics/paint/paint_record.h",
@@ -1698,6 +1694,7 @@
     "//third_party/icu",
     "//third_party/libyuv",
     "//third_party/one_euro_filter",
+    "//third_party/snappy:snappy",
     "//third_party/webrtc_overrides:webrtc_component",
     "//third_party/zlib/google:compression_utils",
     "//ui/base/cursor/mojom:cursor_type_blink",
diff --git a/third_party/blink/renderer/platform/audio/audio_array.h b/third_party/blink/renderer/platform/audio/audio_array.h
index ce949a2..79b6131 100644
--- a/third_party/blink/renderer/platform/audio/audio_array.h
+++ b/third_party/blink/renderer/platform/audio/audio_array.h
@@ -30,7 +30,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_ARRAY_H_
 
 #include <string.h>
-
 #include "base/numerics/checked_math.h"
 #include "build/build_config.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
diff --git a/third_party/blink/renderer/platform/audio/audio_bus.h b/third_party/blink/renderer/platform/audio/audio_bus.h
index 734cb5d..3290de3 100644
--- a/third_party/blink/renderer/platform/audio/audio_bus.h
+++ b/third_party/blink/renderer/platform/audio/audio_bus.h
@@ -30,6 +30,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_BUS_H_
 
 #include "third_party/blink/renderer/platform/audio/audio_channel.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
diff --git a/third_party/blink/renderer/platform/audio/audio_channel.h b/third_party/blink/renderer/platform/audio/audio_channel.h
index 94f02729..9cede60 100644
--- a/third_party/blink/renderer/platform/audio/audio_channel.h
+++ b/third_party/blink/renderer/platform/audio/audio_channel.h
@@ -30,7 +30,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_CHANNEL_H_
 
 #include <memory>
-
 #include "base/numerics/checked_math.h"
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
diff --git a/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h b/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h
index d8e94a24..d1961d79 100644
--- a/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h
+++ b/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h
@@ -28,6 +28,7 @@
 
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
 #include "third_party/blink/renderer/platform/audio/audio_dsp_kernel.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/platform/audio/audio_destination.h b/third_party/blink/renderer/platform/audio/audio_destination.h
index b70ec75f..c2a6d2a 100644
--- a/third_party/blink/renderer/platform/audio/audio_destination.h
+++ b/third_party/blink/renderer/platform/audio/audio_destination.h
@@ -30,7 +30,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DESTINATION_H_
 
 #include <memory>
-
 #include "base/memory/scoped_refptr.h"
 #include "base/task/single_thread_task_runner.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -39,6 +38,7 @@
 #include "third_party/blink/renderer/platform/audio/audio_bus.h"
 #include "third_party/blink/renderer/platform/audio/audio_io_callback.h"
 #include "third_party/blink/renderer/platform/audio/media_multi_channel_resampler.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
diff --git a/third_party/blink/renderer/platform/audio/audio_destination_consumer.h b/third_party/blink/renderer/platform/audio/audio_destination_consumer.h
index 63ffe5bf5e..baff3d1 100644
--- a/third_party/blink/renderer/platform/audio/audio_destination_consumer.h
+++ b/third_party/blink/renderer/platform/audio/audio_destination_consumer.h
@@ -31,13 +31,11 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DESTINATION_CONSUMER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DESTINATION_CONSUMER_H_
 
-#include "third_party/blink/renderer/platform/platform_export.h"
-
 namespace blink {
 
 class AudioBus;
 
-class PLATFORM_EXPORT AudioDestinationConsumer {
+class AudioDestinationConsumer {
  public:
   virtual void SetFormat(int number_of_channels, float sample_rate) = 0;
   virtual void ConsumeAudio(AudioBus*, int number_of_frames) = 0;
diff --git a/third_party/blink/renderer/platform/audio/audio_dsp_kernel.h b/third_party/blink/renderer/platform/audio/audio_dsp_kernel.h
index 3b9390f6..7f00ae2a 100644
--- a/third_party/blink/renderer/platform/audio/audio_dsp_kernel.h
+++ b/third_party/blink/renderer/platform/audio/audio_dsp_kernel.h
@@ -32,6 +32,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DSP_KERNEL_H_
 
 #include "third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.h b/third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.h
index 83c76a4..836648c5 100644
--- a/third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.h
+++ b/third_party/blink/renderer/platform/audio/audio_dsp_kernel_processor.h
@@ -34,6 +34,7 @@
 #include <memory>
 #include "third_party/blink/renderer/platform/audio/audio_bus.h"
 #include "third_party/blink/renderer/platform/audio/audio_processor.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
diff --git a/third_party/blink/renderer/platform/audio/audio_resampler.h b/third_party/blink/renderer/platform/audio/audio_resampler.h
index d1fbcf0..3125d876 100644
--- a/third_party/blink/renderer/platform/audio/audio_resampler.h
+++ b/third_party/blink/renderer/platform/audio/audio_resampler.h
@@ -27,7 +27,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_RESAMPLER_H_
 
 #include <memory>
-
 #include "third_party/blink/renderer/platform/audio/audio_bus.h"
 #include "third_party/blink/renderer/platform/audio/audio_resampler_kernel.h"
 #include "third_party/blink/renderer/platform/audio/audio_source_provider.h"
@@ -40,7 +39,7 @@
 // The audio stream may be single or multi-channel.
 // The default constructor defaults to single-channel (mono).
 
-class PLATFORM_EXPORT AudioResampler {
+class AudioResampler {
   DISALLOW_NEW();
 
  public:
diff --git a/third_party/blink/renderer/platform/audio/audio_resampler_kernel.h b/third_party/blink/renderer/platform/audio/audio_resampler_kernel.h
index a5c3d68..6c23f638 100644
--- a/third_party/blink/renderer/platform/audio/audio_resampler_kernel.h
+++ b/third_party/blink/renderer/platform/audio/audio_resampler_kernel.h
@@ -27,7 +27,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_RESAMPLER_KERNEL_H_
 
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
-#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
@@ -37,7 +36,7 @@
 // AudioResamplerKernel does resampling on a single mono channel.
 // It uses a simple linear interpolation for good performance.
 
-class PLATFORM_EXPORT AudioResamplerKernel {
+class AudioResamplerKernel {
   USING_FAST_MALLOC(AudioResamplerKernel);
 
  public:
diff --git a/third_party/blink/renderer/platform/audio/audio_source_provider.h b/third_party/blink/renderer/platform/audio/audio_source_provider.h
index 3feeadf..9d8f40df 100644
--- a/third_party/blink/renderer/platform/audio/audio_source_provider.h
+++ b/third_party/blink/renderer/platform/audio/audio_source_provider.h
@@ -30,7 +30,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_SOURCE_PROVIDER_H_
 
 #include <cstddef>
-#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
@@ -39,7 +38,7 @@
 class AudioSourceProviderClient;
 
 // Abstract base-class for a pull-model client.
-class PLATFORM_EXPORT AudioSourceProvider {
+class AudioSourceProvider {
   USING_FAST_MALLOC(AudioSourceProvider);
 
  public:
diff --git a/third_party/blink/renderer/platform/audio/biquad.h b/third_party/blink/renderer/platform/audio/biquad.h
index 2e6554c..e0f42e23 100644
--- a/third_party/blink/renderer/platform/audio/biquad.h
+++ b/third_party/blink/renderer/platform/audio/biquad.h
@@ -30,9 +30,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_BIQUAD_H_
 
 #include <sys/types.h>
-
 #include <complex>
-
 #include "build/build_config.h"
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
diff --git a/third_party/blink/renderer/platform/audio/direct_convolver.h b/third_party/blink/renderer/platform/audio/direct_convolver.h
index 1b6d1995..7559e8c3 100644
--- a/third_party/blink/renderer/platform/audio/direct_convolver.h
+++ b/third_party/blink/renderer/platform/audio/direct_convolver.h
@@ -30,14 +30,12 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DIRECT_CONVOLVER_H_
 
 #include <memory>
-
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
-#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
 
-class PLATFORM_EXPORT DirectConvolver {
+class DirectConvolver {
   USING_FAST_MALLOC(DirectConvolver);
 
  public:
diff --git a/third_party/blink/renderer/platform/audio/down_sampler.h b/third_party/blink/renderer/platform/audio/down_sampler.h
index ffcc667..c0cf88c 100644
--- a/third_party/blink/renderer/platform/audio/down_sampler.h
+++ b/third_party/blink/renderer/platform/audio/down_sampler.h
@@ -33,6 +33,7 @@
 
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
 #include "third_party/blink/renderer/platform/audio/simple_fft_convolver.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/audio/dynamics_compressor.h b/third_party/blink/renderer/platform/audio/dynamics_compressor.h
index 6d631297..1f095bf 100644
--- a/third_party/blink/renderer/platform/audio/dynamics_compressor.h
+++ b/third_party/blink/renderer/platform/audio/dynamics_compressor.h
@@ -30,9 +30,9 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_DYNAMICS_COMPRESSOR_H_
 
 #include <memory>
-
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
 #include "third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
@@ -107,8 +107,8 @@
   float last_anchor_;
   float last_filter_stage_gain_;
 
-  std::unique_ptr<const float* []> source_channels_;
-  std::unique_ptr<float* []> destination_channels_;
+  std::unique_ptr<const float*[]> source_channels_;
+  std::unique_ptr<float*[]> destination_channels_;
 
   // The core compressor.
   DynamicsCompressorKernel compressor_;
diff --git a/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.h b/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.h
index badbf41..f56d3bf2 100644
--- a/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.h
+++ b/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.h
@@ -31,13 +31,12 @@
 
 #include <memory>
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
-#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
-class PLATFORM_EXPORT DynamicsCompressorKernel {
+class DynamicsCompressorKernel {
   DISALLOW_NEW();
 
  public:
diff --git a/third_party/blink/renderer/platform/audio/equal_power_panner.h b/third_party/blink/renderer/platform/audio/equal_power_panner.h
index 8de50db4..78481cc7 100644
--- a/third_party/blink/renderer/platform/audio/equal_power_panner.h
+++ b/third_party/blink/renderer/platform/audio/equal_power_panner.h
@@ -32,7 +32,7 @@
 
 // Common type of stereo panner as found in normal audio mixing equipment.
 
-class PLATFORM_EXPORT EqualPowerPanner final : public Panner {
+class EqualPowerPanner final : public Panner {
  public:
   EqualPowerPanner(float sample_rate);
 
diff --git a/third_party/blink/renderer/platform/audio/fft_convolver.h b/third_party/blink/renderer/platform/audio/fft_convolver.h
index daf6a3e..69b8945a 100644
--- a/third_party/blink/renderer/platform/audio/fft_convolver.h
+++ b/third_party/blink/renderer/platform/audio/fft_convolver.h
@@ -35,7 +35,7 @@
 
 namespace blink {
 
-class PLATFORM_EXPORT FFTConvolver {
+class FFTConvolver {
   USING_FAST_MALLOC(FFTConvolver);
 
  public:
diff --git a/third_party/blink/renderer/platform/audio/fft_frame.h b/third_party/blink/renderer/platform/audio/fft_frame.h
index c5f97df..724821e 100644
--- a/third_party/blink/renderer/platform/audio/fft_frame.h
+++ b/third_party/blink/renderer/platform/audio/fft_frame.h
@@ -30,7 +30,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_FFT_FRAME_H_
 
 #include <memory>
-
 #include "build/build_config.h"
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
diff --git a/third_party/blink/renderer/platform/audio/hrtf_database.h b/third_party/blink/renderer/platform/audio/hrtf_database.h
index 4ba38a3..65522c3 100644
--- a/third_party/blink/renderer/platform/audio/hrtf_database.h
+++ b/third_party/blink/renderer/platform/audio/hrtf_database.h
@@ -30,7 +30,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_DATABASE_H_
 
 #include <memory>
-
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/audio/hrtf_elevation.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -41,7 +40,7 @@
 
 class HRTFKernel;
 
-class PLATFORM_EXPORT HRTFDatabase {
+class HRTFDatabase {
   USING_FAST_MALLOC(HRTFDatabase);
 
  public:
diff --git a/third_party/blink/renderer/platform/audio/hrtf_database_loader.h b/third_party/blink/renderer/platform/audio/hrtf_database_loader.h
index a94997b4..8b1fdfa6 100644
--- a/third_party/blink/renderer/platform/audio/hrtf_database_loader.h
+++ b/third_party/blink/renderer/platform/audio/hrtf_database_loader.h
@@ -32,6 +32,7 @@
 #include <memory>
 #include "base/synchronization/waitable_event.h"
 #include "third_party/blink/renderer/platform/audio/hrtf_database.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
diff --git a/third_party/blink/renderer/platform/audio/hrtf_elevation.h b/third_party/blink/renderer/platform/audio/hrtf_elevation.h
index b133b80..e79cc62a1 100644
--- a/third_party/blink/renderer/platform/audio/hrtf_elevation.h
+++ b/third_party/blink/renderer/platform/audio/hrtf_elevation.h
@@ -30,9 +30,9 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_HRTF_ELEVATION_H_
 
 #include <memory>
-
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/audio/hrtf_kernel.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
diff --git a/third_party/blink/renderer/platform/audio/hrtf_kernel.h b/third_party/blink/renderer/platform/audio/hrtf_kernel.h
index 27da988..ac6c5c4f 100644
--- a/third_party/blink/renderer/platform/audio/hrtf_kernel.h
+++ b/third_party/blink/renderer/platform/audio/hrtf_kernel.h
@@ -31,9 +31,9 @@
 
 #include <memory>
 #include <utility>
-
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/platform/audio/fft_frame.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
diff --git a/third_party/blink/renderer/platform/audio/hrtf_panner.h b/third_party/blink/renderer/platform/audio/hrtf_panner.h
index 52ca79d2..3aa7864 100644
--- a/third_party/blink/renderer/platform/audio/hrtf_panner.h
+++ b/third_party/blink/renderer/platform/audio/hrtf_panner.h
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/platform/audio/fft_convolver.h"
 #include "third_party/blink/renderer/platform/audio/hrtf_database_loader.h"
 #include "third_party/blink/renderer/platform/audio/panner.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/platform/audio/panner.h b/third_party/blink/renderer/platform/audio/panner.h
index bbf6b70..067b7e1 100644
--- a/third_party/blink/renderer/platform/audio/panner.h
+++ b/third_party/blink/renderer/platform/audio/panner.h
@@ -30,7 +30,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_PANNER_H_
 
 #include <memory>
-
 #include "third_party/blink/renderer/platform/audio/audio_bus.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
diff --git a/third_party/blink/renderer/platform/audio/reverb.h b/third_party/blink/renderer/platform/audio/reverb.h
index d85d08a7..3d0f821d 100644
--- a/third_party/blink/renderer/platform/audio/reverb.h
+++ b/third_party/blink/renderer/platform/audio/reverb.h
@@ -30,8 +30,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_H_
 
 #include <memory>
-
 #include "third_party/blink/renderer/platform/audio/reverb_convolver.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
diff --git a/third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.h b/third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.h
index 1e63969..803c126 100644
--- a/third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.h
+++ b/third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.h
@@ -30,7 +30,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_ACCUMULATION_BUFFER_H_
 
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
-#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
@@ -40,7 +39,7 @@
 // offsets from the read position.  The read operation will zero the memory
 // just read from the buffer, so it will be ready for accumulation the next
 // time around.
-class PLATFORM_EXPORT ReverbAccumulationBuffer {
+class ReverbAccumulationBuffer {
   DISALLOW_NEW();
 
  public:
diff --git a/third_party/blink/renderer/platform/audio/reverb_convolver.h b/third_party/blink/renderer/platform/audio/reverb_convolver.h
index a805354c..3076bc4 100644
--- a/third_party/blink/renderer/platform/audio/reverb_convolver.h
+++ b/third_party/blink/renderer/platform/audio/reverb_convolver.h
@@ -30,13 +30,13 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_CONVOLVER_H_
 
 #include <memory>
-
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
 #include "third_party/blink/renderer/platform/audio/direct_convolver.h"
 #include "third_party/blink/renderer/platform/audio/fft_convolver.h"
 #include "third_party/blink/renderer/platform/audio/reverb_accumulation_buffer.h"
 #include "third_party/blink/renderer/platform/audio/reverb_convolver_stage.h"
 #include "third_party/blink/renderer/platform/audio/reverb_input_buffer.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
diff --git a/third_party/blink/renderer/platform/audio/reverb_convolver_stage.h b/third_party/blink/renderer/platform/audio/reverb_convolver_stage.h
index 60ad2ec..46804781 100644
--- a/third_party/blink/renderer/platform/audio/reverb_convolver_stage.h
+++ b/third_party/blink/renderer/platform/audio/reverb_convolver_stage.h
@@ -30,7 +30,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_REVERB_CONVOLVER_STAGE_H_
 
 #include <memory>
-
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
 #include "third_party/blink/renderer/platform/audio/fft_frame.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -46,7 +45,7 @@
 // sub-section of a large impulse response.  It incorporates a delay line to
 // account for the offset of the sub-section within the larger impulse
 // response.
-class PLATFORM_EXPORT ReverbConvolverStage {
+class ReverbConvolverStage {
   USING_FAST_MALLOC(ReverbConvolverStage);
 
  public:
diff --git a/third_party/blink/renderer/platform/audio/reverb_input_buffer.h b/third_party/blink/renderer/platform/audio/reverb_input_buffer.h
index f180d96..f5ff04a 100644
--- a/third_party/blink/renderer/platform/audio/reverb_input_buffer.h
+++ b/third_party/blink/renderer/platform/audio/reverb_input_buffer.h
@@ -31,14 +31,13 @@
 
 #include <atomic>
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
-#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
 
 // ReverbInputBuffer is used to buffer input samples for deferred processing by
 // the background threads.
-class PLATFORM_EXPORT ReverbInputBuffer {
+class ReverbInputBuffer {
   DISALLOW_NEW();
 
  public:
diff --git a/third_party/blink/renderer/platform/audio/simple_fft_convolver.h b/third_party/blink/renderer/platform/audio/simple_fft_convolver.h
index 505a2ea..3d812566 100644
--- a/third_party/blink/renderer/platform/audio/simple_fft_convolver.h
+++ b/third_party/blink/renderer/platform/audio/simple_fft_convolver.h
@@ -6,7 +6,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_SIMPLE_FFT_CONVOLVER_H_
 
 #include <memory>
-
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
 #include "third_party/blink/renderer/platform/audio/fft_frame.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -19,7 +18,7 @@
 // an FFT on every Process call. Therefore, the processing delay of
 // the SimpleFFTConvolver is the same as that of the DirectConvolver and thus
 // smaller than that of the FFTConvolver.
-class PLATFORM_EXPORT SimpleFFTConvolver {
+class SimpleFFTConvolver {
   USING_FAST_MALLOC(SimpleFFTConvolver);
 
  public:
diff --git a/third_party/blink/renderer/platform/audio/sinc_resampler.h b/third_party/blink/renderer/platform/audio/sinc_resampler.h
index 65f8905f..8cb1722 100644
--- a/third_party/blink/renderer/platform/audio/sinc_resampler.h
+++ b/third_party/blink/renderer/platform/audio/sinc_resampler.h
@@ -31,14 +31,13 @@
 
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
 #include "third_party/blink/renderer/platform/audio/audio_source_provider.h"
-#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
 
 // SincResampler is a high-quality sample-rate converter.
 
-class PLATFORM_EXPORT SincResampler {
+class SincResampler {
   USING_FAST_MALLOC(SincResampler);
 
  public:
diff --git a/third_party/blink/renderer/platform/audio/up_sampler.h b/third_party/blink/renderer/platform/audio/up_sampler.h
index a8597ed..c822a7e 100644
--- a/third_party/blink/renderer/platform/audio/up_sampler.h
+++ b/third_party/blink/renderer/platform/audio/up_sampler.h
@@ -32,10 +32,10 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_UP_SAMPLER_H_
 
 #include <memory>
-
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
 #include "third_party/blink/renderer/platform/audio/direct_convolver.h"
 #include "third_party/blink/renderer/platform/audio/simple_fft_convolver.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/audio/vector_math.h b/third_party/blink/renderer/platform/audio/vector_math.h
index 0ded275..89245d4 100644
--- a/third_party/blink/renderer/platform/audio/vector_math.h
+++ b/third_party/blink/renderer/platform/audio/vector_math.h
@@ -27,7 +27,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_VECTOR_MATH_H_
 
 #include <cstddef>
-
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 
diff --git a/third_party/blink/renderer/platform/audio/vector_math_scalar.h b/third_party/blink/renderer/platform/audio/vector_math_scalar.h
index 72cb874..aa861fb 100644
--- a/third_party/blink/renderer/platform/audio/vector_math_scalar.h
+++ b/third_party/blink/renderer/platform/audio/vector_math_scalar.h
@@ -7,7 +7,6 @@
 
 #include <algorithm>
 #include <cmath>
-
 #include "third_party/blink/renderer/platform/audio/audio_array.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
 
diff --git a/third_party/blink/renderer/platform/bindings/callback_function_base.cc b/third_party/blink/renderer/platform/bindings/callback_function_base.cc
index 5252971..78bfb58 100644
--- a/third_party/blink/renderer/platform/bindings/callback_function_base.cc
+++ b/third_party/blink/renderer/platform/bindings/callback_function_base.cc
@@ -21,12 +21,13 @@
   // Set |callback_relevant_script_state_| iff the creation context and the
   // incumbent context are the same origin-domain. Otherwise, leave it as
   // nullptr.
-  v8::Local<v8::Context> creation_context =
-      callback_function->GetCreationContextChecked();
+  v8::MaybeLocal<v8::Context> creation_context =
+      callback_function->GetCreationContext();
   if (BindingSecurityForPlatform::ShouldAllowAccessToV8Context(
           incumbent_script_state_->GetContext(), creation_context,
           BindingSecurityForPlatform::ErrorReportOption::kDoNotReport)) {
-    callback_relevant_script_state_ = ScriptState::From(creation_context);
+    callback_relevant_script_state_ =
+        ScriptState::From(creation_context.ToLocalChecked());
   }
 }
 
diff --git a/third_party/blink/renderer/platform/bindings/parkable_string.cc b/third_party/blink/renderer/platform/bindings/parkable_string.cc
index f139df09..b75b70e 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string.cc
+++ b/third_party/blink/renderer/platform/bindings/parkable_string.cc
@@ -21,6 +21,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/timer/elapsed_timer.h"
 #include "base/trace_event/typed_macros.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/bindings/parkable_string_manager.h"
 #include "third_party/blink/renderer/platform/crypto.h"
@@ -36,6 +37,7 @@
 #include "third_party/blink/renderer/platform/wtf/sanitizers.h"
 #include "third_party/blink/renderer/platform/wtf/thread_specific.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/snappy/src/snappy.h"
 #include "third_party/zlib/google/compression_utils.h"
 
 namespace blink {
@@ -588,34 +590,47 @@
   String uncompressed;
   base::StringPiece uncompressed_string_piece;
   size_t size = CharactersSizeInBytes();
+  char* char_data;
   if (is_8bit()) {
     LChar* data;
     uncompressed = String::CreateUninitialized(length(), data);
-    uncompressed_string_piece =
-        base::StringPiece(reinterpret_cast<const char*>(data), size);
+    char_data = reinterpret_cast<char*>(data);
   } else {
     UChar* data;
     uncompressed = String::CreateUninitialized(length(), data);
-    uncompressed_string_piece =
-        base::StringPiece(reinterpret_cast<const char*>(data), size);
+    char_data = reinterpret_cast<char*>(data);
   }
+  uncompressed_string_piece = base::StringPiece(char_data, size);
 
-  // If the buffer size is incorrect, then we have a corrupted data issue,
-  // and in such case there is nothing else to do than crash.
-  CHECK_EQ(compression::GetUncompressedSize(compressed_string_piece),
-           uncompressed_string_piece.size());
-  // If decompression fails, this is either because:
-  // 1. Compressed data is corrupted
-  // 2. Cannot allocate memory in zlib
-  //
-  // (1) is data corruption, and (2) is OOM. In all cases, we cannot
-  // recover the string we need, nothing else to do than to abort.
-  if (!compression::GzipUncompress(compressed_string_piece,
-                                   uncompressed_string_piece)) {
-    // Since this is almost always OOM, report it as such. We don't have
-    // certainty, but memory corruption should be much rarer, and could make us
-    // crash anywhere else.
-    OOM_CRASH(uncompressed_string_piece.size());
+  if (!features::ParkableStringsUseSnappy()) {
+    // If the buffer size is incorrect, then we have a corrupted data issue,
+    // and in such case there is nothing else to do than crash.
+    CHECK_EQ(compression::GetUncompressedSize(compressed_string_piece),
+             uncompressed_string_piece.size());
+    // If decompression fails, this is either because:
+    // 1. Compressed data is corrupted
+    // 2. Cannot allocate memory in zlib
+    //
+    // (1) is data corruption, and (2) is OOM. In all cases, we cannot
+    // recover the string we need, nothing else to do than to abort.
+    if (!compression::GzipUncompress(compressed_string_piece,
+                                     uncompressed_string_piece)) {
+      // Since this is almost always OOM, report it as such. We don't have
+      // certainty, but memory corruption should be much rarer, and could make
+      // us crash anywhere else.
+      OOM_CRASH(uncompressed_string_piece.size());
+    }
+  } else {
+    size_t uncompressed_size;
+
+    // As above, if size is incorrect, or if data is corrupted, prefer crashing.
+    CHECK(snappy::GetUncompressedLength(compressed_string_piece.data(),
+                                        compressed_string_piece.size(),
+                                        &uncompressed_size));
+    CHECK_EQ(uncompressed_size, size);
+    CHECK(snappy::RawUncompress(compressed_string_piece.data(),
+                                compressed_string_piece.size(), char_data))
+        << "Decompression failed, corrupted data?";
   }
 
   base::TimeDelta elapsed = timer.Elapsed();
@@ -677,8 +692,11 @@
     // discovered compressed size. This is done as a memory saving measure
     // because Vector::Shrink() does not resize the memory allocation.
     //
-    // The temporary buffer has the same size as the initial data. Compression
-    // will fail if this is not large enough.
+    // For zlib: the temporary buffer has the same size as the initial data.
+    // Compression will fail if this is not large enough.
+    // For snappy: the temporary buffer has size
+    // GetMaxCompressedLength(inital_data_size). If the compression does not
+    // compress, the result is discarded.
     //
     // This is not using:
     // - malloc() or any STL container: this is discouraged in blink, and there
@@ -686,18 +704,31 @@
     // - WTF::Vector<> as allocation failures result in an OOM crash, whereas
     //   we can fail gracefully. See crbug.com/905777 for an example of OOM
     //   triggered from there.
-    NullableCharBuffer buffer(params->size);
+    size_t buffer_size = features::ParkableStringsUseSnappy()
+                             ? snappy::MaxCompressedLength(params->size)
+                             : params->size;
+    NullableCharBuffer buffer(buffer_size);
     ok = buffer.data();
     size_t compressed_size;
+
     if (ok) {
-      // Use partition alloc for zlib's temporary data. This is crucial to avoid
-      // leaking memory on Android, see the details in crbug.com/931553.
-      auto fast_malloc = [](size_t size) {
-        return WTF::Partitions::FastMalloc(size, "ZlibTemporaryData");
-      };
-      ok = compression::GzipCompress(data, buffer.data(), buffer.size(),
-                                     &compressed_size, fast_malloc,
-                                     WTF::Partitions::FastFree);
+      if (features::ParkableStringsUseSnappy()) {
+        snappy::RawCompress(data.data(), params->size, buffer.data(),
+                            &compressed_size);
+
+        if (compressed_size > params->size) {
+          ok = false;
+        }
+      } else {
+        // Use partition alloc for zlib's temporary data. This is crucial to
+        // avoid leaking memory on Android, see the details in crbug.com/931553.
+        auto fast_malloc = [](size_t size) {
+          return WTF::Partitions::FastMalloc(size, "ZlibTemporaryData");
+        };
+        ok = compression::GzipCompress(data, buffer.data(), buffer.size(),
+                                       &compressed_size, fast_malloc,
+                                       WTF::Partitions::FastFree);
+      }
     }
 
 #if defined(ADDRESS_SANITIZER)
diff --git a/third_party/blink/renderer/platform/bindings/parkable_string_test.cc b/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
index d529397..8a4fb9c 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
+++ b/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
@@ -37,9 +37,11 @@
 namespace {
 
 constexpr size_t kSizeKb = 20;
+
 // Compressed size of the string returned by |MakeLargeString()|.
 // Update if the assertion in the |CheckCompressedSize()| test fails.
-constexpr size_t kCompressedSize = 55;
+constexpr size_t kCompressedSizeZlib = 55;
+constexpr size_t kCompressedSizeSnappy = 944;
 
 String MakeLargeString(char c = 'a') {
   Vector<char> data(kSizeKb * 1000, c);
@@ -57,13 +59,21 @@
 
 }  // namespace
 
-class ParkableStringTest : public ::testing::Test {
+class ParkableStringTest : public testing::TestWithParam<bool> {
  public:
   ParkableStringTest(ThreadPoolExecutionMode thread_pool_execution_mode =
                          ThreadPoolExecutionMode::DEFAULT)
-      : ::testing::Test(),
-        task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME,
-                          thread_pool_execution_mode) {}
+      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME,
+                          thread_pool_execution_mode) {
+    bool use_snappy = GetParam();
+    if (use_snappy) {
+      scoped_feature_list_.InitAndEnableFeature(
+          features::kUseSnappyForParkableStrings);
+    } else {
+      scoped_feature_list_.InitAndDisableFeature(
+          features::kUseSnappyForParkableStrings);
+    }
+  }
 
  protected:
   void RunPostedTasks() { task_environment_.RunUntilIdle(); }
@@ -135,12 +145,28 @@
     ParkableStringManager::Instance().SetDataAllocatorForTesting(nullptr);
   }
 
+  size_t GetExpectedCompressedSize() const {
+    if (features::ParkableStringsUseSnappy()) {
+      return kCompressedSizeSnappy;
+    } else {
+      return kCompressedSizeZlib;
+    }
+  }
+
+  base::test::ScopedFeatureList scoped_feature_list_;
   base::test::TaskEnvironment task_environment_;
 };
 
+INSTANTIATE_TEST_SUITE_P(WithOrWithoutSnappy,
+                         ParkableStringTest,
+                         ::testing::Bool());
+
 // The main aim of this test is to check that the compressed size of a string
-// doesn't change. If it does, |kCompressedsize| will need to be updated.
-TEST_F(ParkableStringTest, CheckCompressedSize) {
+// doesn't change. If it does, |kCompressedSizeZlib| and/or
+// |kCompressedSizeSnappy| will need to be updated.
+TEST_P(ParkableStringTest, CheckCompressedSize) {
+  const size_t kCompressedSize = GetExpectedCompressedSize();
+
   ParkableString parkable(MakeLargeString().ReleaseImpl());
   EXPECT_TRUE(
       parkable.Impl()->Park(ParkableStringImpl::ParkingMode::kCompress));
@@ -149,7 +175,7 @@
   EXPECT_EQ(kCompressedSize, parkable.Impl()->compressed_size());
 }
 
-TEST_F(ParkableStringTest, DontCompressRandomString) {
+TEST_P(ParkableStringTest, DontCompressRandomString) {
   // Make a large random string. Large to make sure it's parkable, and random to
   // ensure its compressed size is larger than the initial size (at least from
   // gzip's header). Mersenne-Twister implementation is specified, making the
@@ -165,7 +191,7 @@
   EXPECT_FALSE(parkable.Impl()->is_parked());
 }
 
-TEST_F(ParkableStringTest, ParkUnparkIdenticalContent) {
+TEST_P(ParkableStringTest, ParkUnparkIdenticalContent) {
   ParkableString parkable(MakeLargeString().ReleaseImpl());
   EXPECT_TRUE(
       parkable.Impl()->Park(ParkableStringImpl::ParkingMode::kCompress));
@@ -175,7 +201,7 @@
   EXPECT_EQ(MakeLargeString(), parkable.ToString());
 }
 
-TEST_F(ParkableStringTest, DecompressUtf16String) {
+TEST_P(ParkableStringTest, DecompressUtf16String) {
   UChar emoji_grinning_face[2] = {0xd83d, 0xde00};
   size_t size_in_chars = 2 * kSizeKb * 1000 / sizeof(UChar);
 
@@ -208,7 +234,7 @@
   EXPECT_EQ(copy, unparked);
 }
 
-TEST_F(ParkableStringTest, Simple) {
+TEST_P(ParkableStringTest, Simple) {
   ParkableString parkable_abc(String("abc").ReleaseImpl());
 
   EXPECT_TRUE(ParkableString().IsNull());
@@ -224,7 +250,7 @@
   EXPECT_EQ(copy.Impl(), parkable_abc.Impl());
 }
 
-TEST_F(ParkableStringTest, Park) {
+TEST_P(ParkableStringTest, Park) {
   {
     ParkableString parkable_a(MakeLargeString('a').ReleaseImpl());
     EXPECT_TRUE(parkable_a.may_be_parked());
@@ -255,7 +281,7 @@
   }
 }
 
-TEST_F(ParkableStringTest, EqualityNoUnparking) {
+TEST_P(ParkableStringTest, EqualityNoUnparking) {
   String large_string = MakeLargeString();
   String copy = large_string.IsolatedCopy();
   EXPECT_NE(large_string.Impl(), copy.Impl());
@@ -276,7 +302,7 @@
   EXPECT_EQ(1u, ParkableStringManager::Instance().Size());
 }
 
-TEST_F(ParkableStringTest, AbortParking) {
+TEST_P(ParkableStringTest, AbortParking) {
   {
     ParkableString parkable(MakeLargeString().ReleaseImpl());
     EXPECT_TRUE(parkable.may_be_parked());
@@ -345,7 +371,7 @@
   }
 }
 
-TEST_F(ParkableStringTest, AbortedParkingRetainsCompressedData) {
+TEST_P(ParkableStringTest, AbortedParkingRetainsCompressedData) {
   ParkableString parkable(MakeLargeString().ReleaseImpl());
   EXPECT_TRUE(parkable.may_be_parked());
   EXPECT_FALSE(parkable.Impl()->is_parked());
@@ -364,7 +390,7 @@
   EXPECT_TRUE(parkable.Impl()->is_parked());
 }
 
-TEST_F(ParkableStringTest, Unpark) {
+TEST_P(ParkableStringTest, Unpark) {
   ParkableString parkable(MakeLargeString().Impl());
   String unparked_copy = parkable.ToString().IsolatedCopy();
   EXPECT_TRUE(parkable.may_be_parked());
@@ -377,7 +403,7 @@
   EXPECT_FALSE(parkable.Impl()->is_parked());
 }
 
-TEST_F(ParkableStringTest, LockUnlock) {
+TEST_P(ParkableStringTest, LockUnlock) {
   ParkableString parkable(MakeLargeString().Impl());
   ParkableStringImpl* impl = parkable.Impl();
   EXPECT_EQ(0, impl->lock_depth_for_testing());
@@ -408,7 +434,7 @@
   EXPECT_TRUE(ParkAndWait(parkable));
 }
 
-TEST_F(ParkableStringTest, LockParkedString) {
+TEST_P(ParkableStringTest, LockParkedString) {
   ParkableString parkable = CreateAndParkAll();
   ParkableStringImpl* impl = parkable.Impl();
 
@@ -426,7 +452,7 @@
   EXPECT_TRUE(impl->is_parked());
 }
 
-TEST_F(ParkableStringTest, DelayFirstParkingOfString) {
+TEST_P(ParkableStringTest, DelayFirstParkingOfString) {
   base::test::ScopedFeatureList features;
   features.InitAndEnableFeature(features::kDelayFirstParkingOfStrings);
 
@@ -453,7 +479,7 @@
   EXPECT_TRUE(parkable.Impl()->is_parked());
 }
 
-TEST_F(ParkableStringTest, ManagerSimple) {
+TEST_P(ParkableStringTest, ManagerSimple) {
   auto& manager = ParkableStringManager::Instance();
   EXPECT_EQ(0u, manager.Size());
 
@@ -496,7 +522,7 @@
   EXPECT_TRUE(parkable.Impl()->is_parked());
 }
 
-TEST_F(ParkableStringTest, ManagerMultipleStrings) {
+TEST_P(ParkableStringTest, ManagerMultipleStrings) {
   auto& manager = ParkableStringManager::Instance();
   EXPECT_EQ(0u, manager.Size());
 
@@ -544,7 +570,7 @@
   EXPECT_EQ(0u, manager.Size());
 }
 
-TEST_F(ParkableStringTest, ShouldPark) {
+TEST_P(ParkableStringTest, ShouldPark) {
   String empty_string("");
   EXPECT_FALSE(ParkableStringManager::ShouldPark(*empty_string.Impl()));
   String parkable(MakeLargeString().ReleaseImpl());
@@ -566,7 +592,7 @@
   GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, )
 #endif
 
-TEST_F(ParkableStringTest, AsanPoisoningTest) {
+TEST_P(ParkableStringTest, AsanPoisoningTest) {
   ParkableString parkable(MakeLargeString().ReleaseImpl());
   const LChar* data = parkable.ToString().Characters8();
   EXPECT_TRUE(ParkAndWait(parkable));
@@ -574,7 +600,7 @@
 }
 
 // Non-regression test for crbug.com/905137.
-TEST_F(ParkableStringTest, CorrectAsanPoisoning) {
+TEST_P(ParkableStringTest, CorrectAsanPoisoning) {
   ParkableString parkable(MakeLargeString().ReleaseImpl());
   EXPECT_TRUE(
       parkable.Impl()->Park(ParkableStringImpl::ParkingMode::kCompress));
@@ -587,7 +613,9 @@
   RunPostedTasks();
 }
 
-TEST_F(ParkableStringTest, Compression) {
+TEST_P(ParkableStringTest, Compression) {
+  const size_t kCompressedSize = GetExpectedCompressedSize();
+
   base::HistogramTester histogram_tester;
 
   ParkableString parkable = CreateAndParkAll();
@@ -618,7 +646,7 @@
       "Memory.ParkableString.Decompression.ThroughputMBps", 2);
 }
 
-TEST_F(ParkableStringTest, SynchronousCompression) {
+TEST_P(ParkableStringTest, SynchronousCompression) {
   ParkableStringManager& manager = ParkableStringManager::Instance();
   ParkableString parkable = CreateAndParkAll();
 
@@ -630,7 +658,9 @@
   task_environment_.FastForwardUntilNoTasksRemain();
 }
 
-TEST_F(ParkableStringTest, ToAndFromDisk) {
+TEST_P(ParkableStringTest, ToAndFromDisk) {
+  const size_t kCompressedSize = GetExpectedCompressedSize();
+
   base::HistogramTester histogram_tester;
 
   ParkableString parkable(MakeLargeString('a').ReleaseImpl());
@@ -667,7 +697,7 @@
       "Memory.ParkableString.Read.SinceLastDiskWrite", 1);
 }
 
-TEST_F(ParkableStringTest, UnparkWhileWritingToDisk) {
+TEST_P(ParkableStringTest, UnparkWhileWritingToDisk) {
   base::HistogramTester histogram_tester;
 
   ParkableString parkable(MakeLargeString('a').ReleaseImpl());
@@ -697,7 +727,7 @@
       "Memory.ParkableString.Read.SinceLastDiskWrite", 0);
 }
 
-TEST_F(ParkableStringTest, NoCompetingWritingToDisk) {
+TEST_P(ParkableStringTest, NoCompetingWritingToDisk) {
   ParkableString parkable(MakeLargeString('a').ReleaseImpl());
   ParkableStringImpl* impl = parkable.Impl();
 
@@ -727,7 +757,7 @@
   EXPECT_EQ(ParkableStringImpl::Age::kOld, impl->age_for_testing());
 }
 
-TEST_F(ParkableStringTest, SynchronousToDisk) {
+TEST_P(ParkableStringTest, SynchronousToDisk) {
   base::HistogramTester histogram_tester;
 
   ParkableString parkable(MakeLargeString('a').ReleaseImpl());
@@ -763,7 +793,7 @@
       "Memory.ParkableString.Read.SinceLastDiskWrite", 2);
 }
 
-TEST_F(ParkableStringTest, OnPurgeMemory) {
+TEST_P(ParkableStringTest, OnPurgeMemory) {
   ParkableString parkable1 = CreateAndParkAll();
   ParkableString parkable2(MakeLargeString('b').ReleaseImpl());
 
@@ -787,7 +817,9 @@
   EXPECT_TRUE(parkable1.Impl()->has_compressed_data());
 }
 
-TEST_F(ParkableStringTest, ReportMemoryDump) {
+TEST_P(ParkableStringTest, ReportMemoryDump) {
+  const size_t kCompressedSize = GetExpectedCompressedSize();
+
   using base::trace_event::MemoryAllocatorDump;
   using testing::ByRef;
   using testing::Contains;
@@ -872,7 +904,7 @@
   EXPECT_THAT(dump->entries(), Contains(Eq(ByRef(compressed))));
 }
 
-TEST_F(ParkableStringTest, MemoryFootprintForDump) {
+TEST_P(ParkableStringTest, MemoryFootprintForDump) {
   constexpr size_t kActualSize =
       sizeof(ParkableStringImpl) + sizeof(ParkableStringImpl::ParkableMetadata);
 
@@ -899,7 +931,7 @@
   EXPECT_EQ(memory_footprint, parkable3.Impl()->MemoryFootprintForDump());
 }
 
-TEST_F(ParkableStringTest, CompressionDisabled) {
+TEST_P(ParkableStringTest, CompressionDisabled) {
   base::test::ScopedFeatureList features;
   features.InitAndDisableFeature(features::kCompressParkableStrings);
 
@@ -911,14 +943,14 @@
   EXPECT_FALSE(parkable.Impl()->may_be_parked());
 }
 
-TEST_F(ParkableStringTest, CompressionDisabledDisablesDisk) {
+TEST_P(ParkableStringTest, CompressionDisabledDisablesDisk) {
   base::test::ScopedFeatureList features;
   features.InitAndDisableFeature(features::kCompressParkableStrings);
 
   EXPECT_FALSE(features::IsParkableStringsToDiskEnabled());
 }
 
-TEST_F(ParkableStringTest, Aging) {
+TEST_P(ParkableStringTest, Aging) {
   ParkableString parkable(MakeLargeString().ReleaseImpl());
   EXPECT_EQ(ParkableStringImpl::Age::kYoung,
             parkable.Impl()->age_for_testing());
@@ -952,7 +984,7 @@
             parkable.Impl()->age_for_testing());
 }
 
-TEST_F(ParkableStringTest, OldStringsAreParked) {
+TEST_P(ParkableStringTest, OldStringsAreParked) {
   ParkableString parkable(MakeLargeString().ReleaseImpl());
   EXPECT_EQ(ParkableStringImpl::Age::kYoung,
             parkable.Impl()->age_for_testing());
@@ -979,7 +1011,7 @@
   EXPECT_FALSE(parkable.Impl()->is_parked());
 }
 
-TEST_F(ParkableStringTest, AgingTicksStopsAndRestarts) {
+TEST_P(ParkableStringTest, AgingTicksStopsAndRestarts) {
   ParkableString parkable(MakeLargeString().ReleaseImpl());
   EXPECT_GT(task_environment_.GetPendingMainThreadTaskCount(), 0u);
   WaitForAging();
@@ -1009,7 +1041,7 @@
   CheckOnlyCpuCostTaskRemains();
 }
 
-TEST_F(ParkableStringTest, AgingTicksStopsWithNoProgress) {
+TEST_P(ParkableStringTest, AgingTicksStopsWithNoProgress) {
   ParkableString parkable(MakeLargeString('a').ReleaseImpl());
   String retained = parkable.ToString();
 
@@ -1033,7 +1065,7 @@
 }
 
 // Flaky on a few platforms: crbug.com/1168170.
-TEST_F(ParkableStringTest, DISABLED_OnlyOneAgingTask) {
+TEST_P(ParkableStringTest, DISABLED_OnlyOneAgingTask) {
   ParkableString parkable1(MakeLargeString('a').ReleaseImpl());
   ParkableString parkable2(MakeLargeString('b').ReleaseImpl());
 
@@ -1054,7 +1086,9 @@
   EXPECT_EQ(2u, task_environment_.GetPendingMainThreadTaskCount());
 }
 
-TEST_F(ParkableStringTest, ReportTotalUnparkingTime) {
+TEST_P(ParkableStringTest, ReportTotalUnparkingTime) {
+  const size_t kCompressedSize = GetExpectedCompressedSize();
+
   base::ScopedMockElapsedTimersForTest mock_elapsed_timers;
   base::HistogramTester histogram_tester;
 
@@ -1107,7 +1141,9 @@
       (100 * kCompressedSize) / (kSizeKb * 1000), 1);
 }
 
-TEST_F(ParkableStringTest, ReportTotalDiskTime) {
+TEST_P(ParkableStringTest, ReportTotalDiskTime) {
+  const size_t kCompressedSize = GetExpectedCompressedSize();
+
   base::ScopedMockElapsedTimersForTest mock_elapsed_timers;
   base::HistogramTester histogram_tester;
   ASSERT_TRUE(features::IsParkableStringsToDiskEnabled());
@@ -1164,7 +1200,11 @@
       : ParkableStringTest(ThreadPoolExecutionMode::QUEUED) {}
 };
 
-TEST_F(ParkableStringTestWithQueuedThreadPool, AgingParkingInProgress) {
+INSTANTIATE_TEST_SUITE_P(WithOrWithoutSnappy,
+                         ParkableStringTestWithQueuedThreadPool,
+                         ::testing::Bool());
+
+TEST_P(ParkableStringTestWithQueuedThreadPool, AgingParkingInProgress) {
   ParkableString parkable(MakeLargeString().ReleaseImpl());
 
   WaitForAging();
diff --git a/third_party/blink/renderer/platform/exported/web_url_response.cc b/third_party/blink/renderer/platform/exported/web_url_response.cc
index 1bde9062..e561c20 100644
--- a/third_party/blink/renderer/platform/exported/web_url_response.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_response.cc
@@ -221,32 +221,6 @@
   resource_response_->SetHasMajorCertificateErrors(value);
 }
 
-void WebURLResponse::SetCTPolicyCompliance(
-    net::ct::CTPolicyCompliance compliance) {
-  switch (compliance) {
-    case net::ct::CTPolicyCompliance::
-        CT_POLICY_COMPLIANCE_DETAILS_NOT_AVAILABLE:
-    case net::ct::CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY:
-      resource_response_->SetCTPolicyCompliance(
-          ResourceResponse::kCTPolicyComplianceDetailsNotAvailable);
-      break;
-    case net::ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS:
-    case net::ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS:
-      resource_response_->SetCTPolicyCompliance(
-          ResourceResponse::kCTPolicyDoesNotComply);
-      break;
-    case net::ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS:
-      resource_response_->SetCTPolicyCompliance(
-          ResourceResponse::kCTPolicyComplies);
-      break;
-    case net::ct::CTPolicyCompliance::CT_POLICY_COUNT:
-      NOTREACHED();
-      resource_response_->SetCTPolicyCompliance(
-          ResourceResponse::kCTPolicyComplianceDetailsNotAvailable);
-      break;
-  };
-}
-
 void WebURLResponse::SetIsLegacyTLSVersion(bool value) {
   resource_response_->SetIsLegacyTLSVersion(value);
 }
diff --git a/third_party/blink/renderer/platform/geometry/float_rect_outsets.cc b/third_party/blink/renderer/platform/geometry/float_rect_outsets.cc
deleted file mode 100644
index 9d61d09..0000000
--- a/third_party/blink/renderer/platform/geometry/float_rect_outsets.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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.
-
-#include "third_party/blink/renderer/platform/geometry/float_rect_outsets.h"
-
-#include <algorithm>
-
-namespace blink {
-
-// Change outsets to be at least as large as |other|.
-void FloatRectOutsets::Unite(const FloatRectOutsets& other) {
-  top_ = std::max(top_, other.top_);
-  right_ = std::max(right_, other.right_);
-  bottom_ = std::max(bottom_, other.bottom_);
-  left_ = std::max(left_, other.left_);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/platform/geometry/float_rect_outsets.h b/third_party/blink/renderer/platform/geometry/float_rect_outsets.h
deleted file mode 100644
index 6759e0d..0000000
--- a/third_party/blink/renderer/platform/geometry/float_rect_outsets.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2014 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_PLATFORM_GEOMETRY_FLOAT_RECT_OUTSETS_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_RECT_OUTSETS_H_
-
-#include "third_party/blink/renderer/platform/platform_export.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-namespace blink {
-
-// Specifies floating-point lengths to be used to expand a rectangle.
-// For example, |top()| returns the distance the top edge should be moved
-// upward.
-//
-// Negative lengths can be used to express insets.
-class PLATFORM_EXPORT FloatRectOutsets {
-  STACK_ALLOCATED();
-
- public:
-  constexpr FloatRectOutsets() : top_(0), right_(0), bottom_(0), left_(0) {}
-
-  constexpr FloatRectOutsets(float top, float right, float bottom, float left)
-      : top_(top), right_(right), bottom_(bottom), left_(left) {}
-
-  constexpr float Top() const { return top_; }
-  constexpr float Right() const { return right_; }
-  constexpr float Bottom() const { return bottom_; }
-  constexpr float Left() const { return left_; }
-
-  void SetTop(float top) { top_ = top; }
-  void SetRight(float right) { right_ = right; }
-  void SetBottom(float bottom) { bottom_ = bottom; }
-  void SetLeft(float left) { left_ = left; }
-
-  // Change outsets to be at least as large as |other|.
-  void Unite(const FloatRectOutsets& other);
-
- private:
-  float top_;
-  float right_;
-  float bottom_;
-  float left_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_RECT_OUTSETS_H_
diff --git a/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc b/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc
index 5c71986..2a8db73 100644
--- a/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc
+++ b/third_party/blink/renderer/platform/geometry/float_rounded_rect.cc
@@ -91,54 +91,49 @@
     bottom_right_ = gfx::SizeF();
 }
 
-void FloatRoundedRect::Radii::Shrink(float top_width,
-                                     float bottom_width,
-                                     float left_width,
-                                     float right_width) {
-  DCHECK_GE(top_width, 0);
-  DCHECK_GE(bottom_width, 0);
-  DCHECK_GE(left_width, 0);
-  DCHECK_GE(right_width, 0);
+void FloatRoundedRect::Radii::Shrink(const gfx::InsetsF& insets) {
+  DCHECK_GE(insets.top(), 0);
+  DCHECK_GE(insets.bottom(), 0);
+  DCHECK_GE(insets.left(), 0);
+  DCHECK_GE(insets.right(), 0);
 
-  top_left_.set_width(std::max<float>(0, top_left_.width() - left_width));
-  top_left_.set_height(std::max<float>(0, top_left_.height() - top_width));
+  top_left_.set_width(std::max<float>(0, top_left_.width() - insets.left()));
+  top_left_.set_height(std::max<float>(0, top_left_.height() - insets.top()));
 
-  top_right_.set_width(std::max<float>(0, top_right_.width() - right_width));
-  top_right_.set_height(std::max<float>(0, top_right_.height() - top_width));
+  top_right_.set_width(std::max<float>(0, top_right_.width() - insets.right()));
+  top_right_.set_height(std::max<float>(0, top_right_.height() - insets.top()));
 
-  bottom_left_.set_width(std::max<float>(0, bottom_left_.width() - left_width));
+  bottom_left_.set_width(
+      std::max<float>(0, bottom_left_.width() - insets.left()));
   bottom_left_.set_height(
-      std::max<float>(0, bottom_left_.height() - bottom_width));
+      std::max<float>(0, bottom_left_.height() - insets.bottom()));
 
   bottom_right_.set_width(
-      std::max<float>(0, bottom_right_.width() - right_width));
+      std::max<float>(0, bottom_right_.width() - insets.right()));
   bottom_right_.set_height(
-      std::max<float>(0, bottom_right_.height() - bottom_width));
+      std::max<float>(0, bottom_right_.height() - insets.bottom()));
 }
 
-void FloatRoundedRect::Radii::Expand(float top_width,
-                                     float bottom_width,
-                                     float left_width,
-                                     float right_width) {
-  DCHECK_GE(top_width, 0);
-  DCHECK_GE(bottom_width, 0);
-  DCHECK_GE(left_width, 0);
-  DCHECK_GE(right_width, 0);
+void FloatRoundedRect::Radii::Expand(const gfx::OutsetsF& outsets) {
+  DCHECK_GE(outsets.top(), 0);
+  DCHECK_GE(outsets.bottom(), 0);
+  DCHECK_GE(outsets.left(), 0);
+  DCHECK_GE(outsets.right(), 0);
   if (top_left_.width() > 0 && top_left_.height() > 0) {
-    top_left_.set_width(top_left_.width() + left_width);
-    top_left_.set_height(top_left_.height() + top_width);
+    top_left_.set_width(top_left_.width() + outsets.left());
+    top_left_.set_height(top_left_.height() + outsets.top());
   }
   if (top_right_.width() > 0 && top_right_.height() > 0) {
-    top_right_.set_width(top_right_.width() + right_width);
-    top_right_.set_height(top_right_.height() + top_width);
+    top_right_.set_width(top_right_.width() + outsets.right());
+    top_right_.set_height(top_right_.height() + outsets.top());
   }
   if (bottom_left_.width() > 0 && bottom_left_.height() > 0) {
-    bottom_left_.set_width(bottom_left_.width() + left_width);
-    bottom_left_.set_height(bottom_left_.height() + bottom_width);
+    bottom_left_.set_width(bottom_left_.width() + outsets.left());
+    bottom_left_.set_height(bottom_left_.height() + outsets.bottom());
   }
   if (bottom_right_.width() > 0 && bottom_right_.height() > 0) {
-    bottom_right_.set_width(bottom_right_.width() + right_width);
-    bottom_right_.set_height(bottom_right_.height() + bottom_width);
+    bottom_right_.set_width(bottom_right_.width() + outsets.right());
+    bottom_right_.set_height(bottom_right_.height() + outsets.bottom());
   }
 }
 
diff --git a/third_party/blink/renderer/platform/geometry/float_rounded_rect.h b/third_party/blink/renderer/platform/geometry/float_rounded_rect.h
index b5fdf5fa..39c0f2ed 100644
--- a/third_party/blink/renderer/platform/geometry/float_rounded_rect.h
+++ b/third_party/blink/renderer/platform/geometry/float_rounded_rect.h
@@ -36,6 +36,8 @@
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/skia/include/core/SkRRect.h"
+#include "ui/gfx/geometry/insets_f.h"
+#include "ui/gfx/geometry/outsets_f.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/geometry/size_f.h"
 #include "ui/gfx/geometry/skia_conversions.h"
@@ -98,17 +100,11 @@
 
     void Scale(float factor);
 
-    void Expand(float top_width,
-                float bottom_width,
-                float left_width,
-                float right_width);
-    void Expand(float size) { Expand(size, size, size, size); }
+    void Expand(const gfx::OutsetsF& outsets);
+    void Expand(float size) { Expand(gfx::OutsetsF(size)); }
 
-    void Shrink(float top_width,
-                float bottom_width,
-                float left_width,
-                float right_width);
-    void Shrink(float size) { Shrink(size, size, size, size); }
+    void Shrink(const gfx::InsetsF& insets);
+    void Shrink(float size) { Shrink(gfx::InsetsF(size)); }
 
     // Reshapes the corners based on the algorithm in
     // https://drafts.csswg.org/css-backgrounds-3/#shadow-shape.
diff --git a/third_party/blink/renderer/platform/geometry/int_rect_outsets.h b/third_party/blink/renderer/platform/geometry/int_rect_outsets.h
deleted file mode 100644
index 16a301d..0000000
--- a/third_party/blink/renderer/platform/geometry/int_rect_outsets.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above
- *    copyright notice, this list of conditions and the following
- *    disclaimer.
- * 2. Redistributions in binary form must reproduce the above
- *    copyright notice, this list of conditions and the following
- *    disclaimer in the documentation and/or other materials
- *    provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
- * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_INT_RECT_OUTSETS_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_INT_RECT_OUTSETS_H_
-
-#include "third_party/blink/renderer/platform/platform_export.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-namespace blink {
-
-// Specifies integral lengths to be used to expand a rectangle.
-// For example, |top()| returns the distance the top edge should be moved
-// upward.
-//
-// Negative lengths can be used to express insets.
-class PLATFORM_EXPORT IntRectOutsets {
-  DISALLOW_NEW();
-
- public:
-  constexpr IntRectOutsets() : top_(0), right_(0), bottom_(0), left_(0) {}
-
-  constexpr IntRectOutsets(int top, int right, int bottom, int left)
-      : top_(top), right_(right), bottom_(bottom), left_(left) {}
-
-  constexpr int Top() const { return top_; }
-  constexpr int Right() const { return right_; }
-  constexpr int Bottom() const { return bottom_; }
-  constexpr int Left() const { return left_; }
-
-  void SetTop(int top) { top_ = top; }
-  void SetRight(int right) { right_ = right; }
-  void SetBottom(int bottom) { bottom_ = bottom; }
-  void SetLeft(int left) { left_ = left; }
-
-  constexpr bool IsZero() const {
-    return !Left() && !Right() && !Top() && !Bottom();
-  }
-
- private:
-  int top_;
-  int right_;
-  int bottom_;
-  int left_;
-};
-
-inline void operator+=(IntRectOutsets& a, const IntRectOutsets& b) {
-  a.SetTop(a.Top() + b.Top());
-  a.SetRight(a.Right() + b.Right());
-  a.SetBottom(a.Bottom() + b.Bottom());
-  a.SetLeft(a.Left() + b.Left());
-}
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_INT_RECT_OUTSETS_H_
diff --git a/third_party/blink/renderer/platform/geometry/layout_rect_outsets.h b/third_party/blink/renderer/platform/geometry/layout_rect_outsets.h
index b43c062..d1902f8 100644
--- a/third_party/blink/renderer/platform/geometry/layout_rect_outsets.h
+++ b/third_party/blink/renderer/platform/geometry/layout_rect_outsets.h
@@ -31,12 +31,13 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_RECT_OUTSETS_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_RECT_OUTSETS_H_
 
-#include "third_party/blink/renderer/platform/geometry/float_rect_outsets.h"
-#include "third_party/blink/renderer/platform/geometry/int_rect_outsets.h"
 #include "third_party/blink/renderer/platform/geometry/layout_size.h"
 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "ui/gfx/geometry/insets_f.h"
+#include "ui/gfx/geometry/outsets.h"
+#include "ui/gfx/geometry/outsets_f.h"
 
 namespace blink {
 
@@ -61,17 +62,11 @@
         bottom_(LayoutUnit(bottom)),
         left_(LayoutUnit(left)) {}
 
-  LayoutRectOutsets(const IntRectOutsets& outsets)
-      : top_(LayoutUnit(outsets.Top())),
-        right_(LayoutUnit(outsets.Right())),
-        bottom_(LayoutUnit(outsets.Bottom())),
-        left_(LayoutUnit(outsets.Left())) {}
-
-  LayoutRectOutsets(const FloatRectOutsets& outsets)
-      : top_(LayoutUnit(outsets.Top())),
-        right_(LayoutUnit(outsets.Right())),
-        bottom_(LayoutUnit(outsets.Bottom())),
-        left_(LayoutUnit(outsets.Left())) {}
+  explicit LayoutRectOutsets(const gfx::Outsets& outsets)
+      : top_(LayoutUnit(outsets.top())),
+        right_(LayoutUnit(outsets.right())),
+        bottom_(LayoutUnit(outsets.bottom())),
+        left_(LayoutUnit(outsets.left())) {}
 
   constexpr LayoutUnit Top() const { return top_; }
   constexpr LayoutUnit Right() const { return right_; }
@@ -101,6 +96,21 @@
 
   String ToString() const;
 
+  explicit operator gfx::OutsetsF() const {
+    return gfx::OutsetsF()
+        .set_left(left_.ToFloat())
+        .set_right(right_.ToFloat())
+        .set_top(top_.ToFloat())
+        .set_bottom(bottom_.ToFloat());
+  }
+  explicit operator gfx::InsetsF() const {
+    return gfx::InsetsF()
+        .set_left(-left_.ToFloat())
+        .set_right(-right_.ToFloat())
+        .set_top(-top_.ToFloat())
+        .set_bottom(-bottom_.ToFloat());
+  }
+
  private:
   LayoutUnit top_;
   LayoutUnit right_;
@@ -142,11 +152,11 @@
 }
 
 inline LayoutRectOutsets EnclosingLayoutRectOutsets(
-    const FloatRectOutsets& rect) {
-  return LayoutRectOutsets(LayoutUnit::FromFloatCeil(rect.Top()),
-                           LayoutUnit::FromFloatCeil(rect.Right()),
-                           LayoutUnit::FromFloatCeil(rect.Bottom()),
-                           LayoutUnit::FromFloatCeil(rect.Left()));
+    const gfx::OutsetsF& outsets) {
+  return LayoutRectOutsets(LayoutUnit::FromFloatCeil(outsets.top()),
+                           LayoutUnit::FromFloatCeil(outsets.right()),
+                           LayoutUnit::FromFloatCeil(outsets.bottom()),
+                           LayoutUnit::FromFloatCeil(outsets.left()));
 }
 
 PLATFORM_EXPORT std::ostream& operator<<(std::ostream&,
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image.cc b/third_party/blink/renderer/platform/graphics/bitmap_image.cc
index 5edee796..054ac06 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image.cc
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image.cc
@@ -32,13 +32,13 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/histogram_macros.h"
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h"
 #include "third_party/blink/renderer/platform/graphics/dark_mode_filter_helper.h"
 #include "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/image_observer.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
@@ -252,7 +252,7 @@
 }
 
 void BitmapImage::Draw(cc::PaintCanvas* canvas,
-                       const PaintFlags& flags,
+                       const cc::PaintFlags& flags,
                        const gfx::RectF& dst_rect,
                        const gfx::RectF& src_rect,
                        const ImageDrawOptions& draw_options) {
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
index 095a07e..305610d 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
@@ -33,6 +33,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "build/build_config.h"
 #include "cc/layers/texture_layer.h"
+#include "cc/paint/paint_flags.h"
 #include "cc/test/skia_common.h"
 #include "cc/test/stub_decode_cache.h"
 #include "components/viz/common/resources/release_callback.h"
@@ -50,7 +51,6 @@
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_host.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/test/fake_canvas_resource_host.h"
 #include "third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h"
@@ -179,7 +179,7 @@
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(gfx::Size(300, 150), RasterMode::kGPU, kNonOpaque);
   EXPECT_TRUE(bridge->IsValid());
-  PaintFlags flags;
+  cc::PaintFlags flags;
   uint32_t gen_id = bridge->GetOrCreateResourceProvider()->ContentUniqueID();
   bridge->GetPaintCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
   EXPECT_EQ(gen_id, bridge->GetOrCreateResourceProvider()->ContentUniqueID());
@@ -294,7 +294,7 @@
   {
     std::unique_ptr<Canvas2DLayerBridge> bridge =
         MakeBridge(gfx::Size(300, 300), RasterMode::kGPU, kNonOpaque);
-    PaintFlags flags;
+    cc::PaintFlags flags;
     bridge->GetPaintCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
     scoped_refptr<StaticBitmapImage> image = bridge->NewImageSnapshot();
     EXPECT_TRUE(bridge->IsValid());
@@ -304,7 +304,7 @@
   {
     std::unique_ptr<Canvas2DLayerBridge> bridge =
         MakeBridge(gfx::Size(300, 300), RasterMode::kGPU, kNonOpaque);
-    PaintFlags flags;
+    cc::PaintFlags flags;
     bridge->GetPaintCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
     scoped_refptr<StaticBitmapImage> image = bridge->NewImageSnapshot();
     EXPECT_TRUE(bridge->IsValid());
@@ -314,7 +314,7 @@
   {
     std::unique_ptr<Canvas2DLayerBridge> bridge =
         MakeBridge(gfx::Size(300, 300), RasterMode::kCPU, kNonOpaque);
-    PaintFlags flags;
+    cc::PaintFlags flags;
     bridge->GetPaintCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
     scoped_refptr<StaticBitmapImage> image = bridge->NewImageSnapshot();
     EXPECT_TRUE(bridge->IsValid());
@@ -324,7 +324,7 @@
   {
     std::unique_ptr<Canvas2DLayerBridge> bridge =
         MakeBridge(gfx::Size(300, 300), RasterMode::kCPU, kNonOpaque);
-    PaintFlags flags;
+    cc::PaintFlags flags;
     bridge->GetPaintCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
     scoped_refptr<StaticBitmapImage> image = bridge->NewImageSnapshot();
     EXPECT_TRUE(bridge->IsValid());
@@ -720,7 +720,7 @@
 
   viz::TransferableResource resources[3];
   viz::ReleaseCallback callbacks[3];
-  PaintFlags flags;
+  cc::PaintFlags flags;
 
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(gfx::Size(300, 150), RasterMode::kGPU, kNonOpaque);
@@ -759,7 +759,7 @@
 
   viz::TransferableResource resources[2];
   viz::ReleaseCallback callbacks[2];
-  PaintFlags flags;
+  cc::PaintFlags flags;
 
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(gfx::Size(300, 150), RasterMode::kGPU, kNonOpaque);
@@ -967,7 +967,7 @@
   auto host = std::make_unique<CustomFakeCanvasResourceHost>(size);
   std::unique_ptr<Canvas2DLayerBridge> bridge =
       MakeBridge(size, RasterMode::kGPU, kOpaque, std::move(host));
-  PaintFlags flags;
+  cc::PaintFlags flags;
 
   // MakeBridge() results in a call to restore the matrix. So we already have 1.
   EXPECT_EQ(bridge->GetPaintCanvas()->getTotalMatrix().get(SkMatrix::kMTransX),
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index 5c16fca..df0b467 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -647,6 +647,18 @@
     resource_->WaitSyncToken(sync_token);
   }
 
+  void OnFlushForImage(cc::PaintImage::ContentId content_id) override {
+    CanvasResourceProvider::OnFlushForImage(content_id);
+    if (cached_snapshot_ &&
+        cached_snapshot_->PaintImageForCurrentFrame().GetContentIdForFrame(0) ==
+            content_id) {
+      // This handles the case where the cached snapshot is referenced by an
+      // ImageBitmap that is being transferred to a worker.
+      cached_snapshot_.reset();
+    }
+  }
+
+ private:
   const bool is_accelerated_;
   const uint32_t shared_image_usage_flags_;
   bool current_resource_has_write_access_ = false;
@@ -1214,6 +1226,14 @@
   return surface_.get();
 }
 
+void CanvasResourceProvider::NotifyWillTransfer(
+    cc::PaintImage::ContentId content_id) {
+  // This is called when an ImageBitmap is about to be transferred. All
+  // references to such a bitmap on the current thread must be released, which
+  // means that DisplayItemLists that reference it must be flushed.
+  GetFlushForImageListener()->NotifyFlushForImage(content_id);
+}
+
 void CanvasResourceProvider::EnsureSkiaCanvas() {
   WillDraw();
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
index 423a6407..e54ee9b 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
@@ -280,6 +280,8 @@
 
   void ClearFrame() { clear_frame_ = true; }
 
+  static void NotifyWillTransfer(cc::PaintImage::ContentId content_id);
+
  protected:
   class CanvasImageProvider;
 
@@ -331,9 +333,10 @@
   mutable sk_sp<SkSurface> surface_;  // mutable for lazy init
   SkSurface::ContentChangeMode mode_ = SkSurface::kRetain_ContentChangeMode;
 
+  virtual void OnFlushForImage(cc::PaintImage::ContentId content_id);
+
  private:
   friend class FlushForImageListener;
-  void OnFlushForImage(cc::PaintImage::ContentId content_id);
   virtual sk_sp<SkSurface> CreateSkSurface() const = 0;
   virtual scoped_refptr<CanvasResource> CreateResource();
   virtual bool UseOopRasterization() { return false; }
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
index c6be6f0..b363e09b 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -11,6 +11,7 @@
 #include "cc/document_transition/document_transition_request.h"
 #include "cc/layers/scrollbar_layer_base.h"
 #include "cc/paint/display_item_list.h"
+#include "cc/paint/paint_flags.h"
 #include "cc/trees/effect_node.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/mutator_host.h"
@@ -25,7 +26,6 @@
 #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_artifact.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
 #include "third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h"
 #include "third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h"
@@ -632,7 +632,7 @@
 SynthesizedClip::PaintContentsToDisplayList() {
   auto cc_list = base::MakeRefCounted<cc::DisplayItemList>(
       cc::DisplayItemList::kTopLevelDisplayItemList);
-  PaintFlags flags;
+  cc::PaintFlags flags;
   flags.setAntiAlias(true);
   cc_list->StartPaint();
   if (rrect_is_local_) {
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
index a4f93b4..01993594 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
@@ -542,7 +542,7 @@
     // alpha value, but that breaks slimming paint reftests.
     auto alpha = base::ClampFloor<uint8_t>(255 * effect.Opacity());
     if (has_other_effects) {
-      PaintFlags flags;
+      cc::PaintFlags flags;
       flags.setBlendMode(effect.BlendMode());
       flags.setAlpha(alpha);
       save_layer_id = cc_list_.push<cc::SaveLayerOp>(nullptr, &flags);
@@ -554,7 +554,7 @@
     // The size parameter is only used to computed the origin of zoom
     // operation, which we never generate.
     gfx::SizeF empty;
-    PaintFlags filter_flags;
+    cc::PaintFlags filter_flags;
     filter_flags.setImageFilter(cc::RenderSurfaceFilters::BuildImageFilter(
         effect.Filter().AsCcFilterOperations(), empty));
     save_layer_id = cc_list_.push<cc::SaveLayerOp>(nullptr, &filter_flags);
diff --git a/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc b/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc
index 66178fa7..01dd7c73 100644
--- a/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc
+++ b/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc
@@ -44,7 +44,7 @@
 
 void CrossfadeGeneratedImage::DrawCrossfade(
     cc::PaintCanvas* canvas,
-    const PaintFlags& flags,
+    const cc::PaintFlags& flags,
     const ImageDrawOptions& draw_options) {
   gfx::RectF from_image_rect(gfx::SizeF(from_image_->Size()));
   gfx::RectF to_image_rect(gfx::SizeF(to_image_->Size()));
@@ -54,12 +54,12 @@
   // applied here instead of inside the layer.  This probably faulty behavior
   // was maintained in order to preserve pre-existing behavior while refactoring
   // this code.  This should be investigated further. crbug.com/472634
-  PaintFlags layer_flags;
+  cc::PaintFlags layer_flags;
   layer_flags.setBlendMode(flags.getBlendMode());
   PaintCanvasAutoRestore ar(canvas, false);
   canvas->saveLayer(nullptr, &layer_flags);
 
-  PaintFlags image_flags(flags);
+  cc::PaintFlags image_flags(flags);
   image_flags.setBlendMode(SkBlendMode::kSrcOver);
   image_flags.setColor(ScaleAlpha(flags.getColor(), 1 - percentage_));
   // TODO(junov): This code should probably be propagating the
@@ -76,7 +76,7 @@
 }
 
 void CrossfadeGeneratedImage::Draw(cc::PaintCanvas* canvas,
-                                   const PaintFlags& flags,
+                                   const cc::PaintFlags& flags,
                                    const gfx::RectF& dst_rect,
                                    const gfx::RectF& src_rect,
                                    const ImageDrawOptions& draw_options) {
@@ -98,7 +98,7 @@
   // Draw nothing if either of the images hasn't loaded yet.
   if (from_image_ == Image::NullImage() || to_image_ == Image::NullImage())
     return;
-  PaintFlags flags = context.FillFlags();
+  cc::PaintFlags flags = context.FillFlags();
   flags.setBlendMode(SkBlendMode::kSrcOver);
   gfx::RectF dest_rect(size_);
   ImageDrawOptions draw_options(options);
diff --git a/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.cc b/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.cc
index 497fd15..e671dbb 100644
--- a/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.cc
+++ b/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.cc
@@ -9,7 +9,8 @@
 
 namespace blink {
 
-PaintFilterEffect::PaintFilterEffect(Filter* filter, const PaintFlags& flags)
+PaintFilterEffect::PaintFilterEffect(Filter* filter,
+                                     const cc::PaintFlags& flags)
     : FilterEffect(filter), flags_(flags) {
   SetOperatingInterpolationSpace(kInterpolationSpaceSRGB);
 }
diff --git a/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.h b/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.h
index cd24b79..d88a6cba 100644
--- a/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.h
+++ b/third_party/blink/renderer/platform/graphics/filters/paint_filter_effect.h
@@ -5,14 +5,14 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_PAINT_FILTER_EFFECT_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_FILTERS_PAINT_FILTER_EFFECT_H_
 
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 
 namespace blink {
 
 class PLATFORM_EXPORT PaintFilterEffect : public FilterEffect {
  public:
-  PaintFilterEffect(Filter*, const PaintFlags&);
+  PaintFilterEffect(Filter*, const cc::PaintFlags&);
   ~PaintFilterEffect() override;
 
   FilterEffectType GetFilterEffectType() const override {
@@ -24,7 +24,7 @@
   sk_sp<PaintFilter> CreateImageFilter() override;
 
  private:
-  PaintFlags flags_;
+  cc::PaintFlags flags_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/generated_image.cc b/third_party/blink/renderer/platform/graphics/generated_image.cc
index 4c32d83..2337a649 100644
--- a/third_party/blink/renderer/platform/graphics/generated_image.cc
+++ b/third_party/blink/renderer/platform/graphics/generated_image.cc
@@ -56,7 +56,7 @@
   sk_sp<PaintShader> tile_shader = CreateShader(
       tile_rect, &pattern_matrix, tiling_info.image_rect, draw_options);
 
-  PaintFlags fill_flags(base_flags);
+  cc::PaintFlags fill_flags(base_flags);
   fill_flags.setShader(std::move(tile_shader));
   fill_flags.setColor(SK_ColorBLACK);
 
diff --git a/third_party/blink/renderer/platform/graphics/gradient.cc b/third_party/blink/renderer/platform/graphics/gradient.cc
index dada93de..b1acf04 100644
--- a/third_party/blink/renderer/platform/graphics/gradient.cc
+++ b/third_party/blink/renderer/platform/graphics/gradient.cc
@@ -171,7 +171,7 @@
   return shader;
 }
 
-void Gradient::ApplyToFlags(PaintFlags& flags,
+void Gradient::ApplyToFlags(cc::PaintFlags& flags,
                             const SkMatrix& local_matrix,
                             const ImageDrawOptions& draw_options) {
   if (is_dark_mode_enabled_ != draw_options.apply_dark_mode) {
diff --git a/third_party/blink/renderer/platform/graphics/gradient.h b/third_party/blink/renderer/platform/graphics/gradient.h
index f040a1cc2..7bcd3b2 100644
--- a/third_party/blink/renderer/platform/graphics/gradient.h
+++ b/third_party/blink/renderer/platform/graphics/gradient.h
@@ -30,10 +30,10 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GRADIENT_H_
 
 #include "base/memory/scoped_refptr.h"
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/graphics/dark_mode_filter.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_shader.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -112,7 +112,7 @@
   }
   void AddColorStops(const Vector<Gradient::ColorStop>&);
 
-  void ApplyToFlags(PaintFlags&,
+  void ApplyToFlags(cc::PaintFlags&,
                     const SkMatrix& local_matrix,
                     const ImageDrawOptions& draw_options);
 
diff --git a/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc b/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc
index 35132cc7..54d6b5b 100644
--- a/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc
+++ b/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc
@@ -31,7 +31,7 @@
 namespace blink {
 
 void GradientGeneratedImage::Draw(cc::PaintCanvas* canvas,
-                                  const PaintFlags& flags,
+                                  const cc::PaintFlags& flags,
                                   const gfx::RectF& dest_rect,
                                   const gfx::RectF& src_rect,
                                   const ImageDrawOptions& draw_options) {
@@ -45,7 +45,7 @@
   SkRect visible_dest_rect;
   transform.mapRect(&visible_dest_rect, visible_src_rect);
 
-  PaintFlags gradient_flags(flags);
+  cc::PaintFlags gradient_flags(flags);
   gradient_->ApplyToFlags(gradient_flags, transform, draw_options);
   canvas->drawRect(visible_dest_rect, gradient_flags);
 }
@@ -55,14 +55,14 @@
                                       const ImageDrawOptions& draw_options) {
   // TODO(ccameron): This function should not ignore |context|'s color behavior.
   // https://crbug.com/672306
-  PaintFlags gradient_flags(context.FillFlags());
+  cc::PaintFlags gradient_flags(context.FillFlags());
   gradient_->ApplyToFlags(gradient_flags, SkMatrix::I(), draw_options);
 
   context.DrawRect(gfx::RectFToSkRect(src_rect), gradient_flags,
                    AutoDarkMode::Disabled());
 }
 
-bool GradientGeneratedImage::ApplyShader(PaintFlags& flags,
+bool GradientGeneratedImage::ApplyShader(cc::PaintFlags& flags,
                                          const SkMatrix& local_matrix,
                                          const gfx::RectF& dst_rect,
                                          const gfx::RectF& src_rect,
diff --git a/third_party/blink/renderer/platform/graphics/gradient_generated_image.h b/third_party/blink/renderer/platform/graphics/gradient_generated_image.h
index 445607e..1757b43 100644
--- a/third_party/blink/renderer/platform/graphics/gradient_generated_image.h
+++ b/third_party/blink/renderer/platform/graphics/gradient_generated_image.h
@@ -43,7 +43,7 @@
 
   ~GradientGeneratedImage() override = default;
 
-  bool ApplyShader(PaintFlags&,
+  bool ApplyShader(cc::PaintFlags&,
                    const SkMatrix&,
                    const gfx::RectF& dst_rect,
                    const gfx::RectF& src_rect,
@@ -51,7 +51,7 @@
 
  protected:
   void Draw(cc::PaintCanvas*,
-            const PaintFlags&,
+            const cc::PaintFlags&,
             const gfx::RectF& dest_rect,
             const gfx::RectF& src_rect,
             const ImageDrawOptions&) override;
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index bc867a1..3799694 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -113,7 +113,7 @@
   // This helper's lifetime should never exceed |flags|'.
   DarkModeFlags(GraphicsContext* context,
                 const AutoDarkMode& auto_dark_mode,
-                const PaintFlags& flags) {
+                const cc::PaintFlags& flags) {
     if (auto_dark_mode.enabled) {
       dark_mode_flags_ = context->GetDarkModeFilter()->ApplyToFlagsIfNeeded(
           flags, auto_dark_mode.role);
@@ -127,11 +127,12 @@
 
   bool applied_dark_mode() const { return !!dark_mode_flags_; }
 
-  operator const PaintFlags&() const { return *flags_; }
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  operator const cc::PaintFlags&() const { return *flags_; }
 
  private:
-  const PaintFlags* flags_;
-  absl::optional<PaintFlags> dark_mode_flags_;
+  const cc::PaintFlags* flags_;
+  absl::optional<cc::PaintFlags> dark_mode_flags_;
 };
 
 GraphicsContext::GraphicsContext(PaintController& paint_controller)
@@ -210,7 +211,8 @@
 }
 #endif
 
-void GraphicsContext::SaveLayer(const SkRect* bounds, const PaintFlags* flags) {
+void GraphicsContext::SaveLayer(const SkRect* bounds,
+                                const cc::PaintFlags* flags) {
   DCHECK(canvas_);
   canvas_->saveLayer(bounds, flags);
 }
@@ -268,7 +270,7 @@
                                  const gfx::RectF* bounds,
                                  ColorFilter color_filter,
                                  sk_sp<PaintFilter> image_filter) {
-  PaintFlags layer_flags;
+  cc::PaintFlags layer_flags;
   layer_flags.setAlpha(static_cast<unsigned char>(opacity * 255));
   layer_flags.setBlendMode(xfermode);
   layer_flags.setColorFilter(WebCoreColorFilterToSkiaColorFilter(color_filter));
@@ -326,7 +328,7 @@
     return;
   DCHECK(canvas_);
 
-  PaintFlags flags;
+  cc::PaintFlags flags;
   flags.setBlendMode(op);
 
   SkSamplingOptions sampling(cc::PaintFlags::FilterQualityToSkSamplingOptions(
@@ -366,7 +368,7 @@
                                    gfx::PointF& p2,
                                    const int path_length,
                                    const int width,
-                                   const PaintFlags& flags,
+                                   const cc::PaintFlags& flags,
                                    const bool is_vertical_line,
                                    const AutoDarkMode& auto_dark_mode) {
   // For narrow lines, we always want integral dot and dash sizes, and start
@@ -425,7 +427,7 @@
   }
 
   if (use_start_dot || use_end_dot) {
-    PaintFlags fill_flags;
+    cc::PaintFlags fill_flags;
     fill_flags.setColor(flags.getColor());
     if (use_start_dot) {
       SkRect start_dot;
@@ -464,7 +466,7 @@
                                const gfx::Point& point2,
                                const AutoDarkMode& auto_dark_mode,
                                bool is_text_line,
-                               const PaintFlags* paint_flags) {
+                               const cc::PaintFlags* paint_flags) {
   DCHECK(canvas_);
 
   StrokeStyle pen_style = GetStrokeStyle();
@@ -519,7 +521,7 @@
 void GraphicsContext::DrawLineForText(const gfx::PointF& pt,
                                       float width,
                                       const AutoDarkMode& auto_dark_mode,
-                                      const PaintFlags* paint_flags) {
+                                      const cc::PaintFlags* paint_flags) {
   if (width <= 0)
     return;
 
@@ -538,7 +540,7 @@
           gfx::RectFToSkRect(GetRectForTextLine(pt, width, StrokeThickness()));
       DrawRect(r, *paint_flags, auto_dark_mode);
     } else {
-      PaintFlags flags;
+      cc::PaintFlags flags;
       flags = ImmutableState()->FillFlags();
       // Text lines are drawn using the stroke color.
       flags.setColor(StrokeColor().Rgb());
@@ -562,9 +564,9 @@
   if (ImmutableState()->GetStrokeData().Style() != kNoStroke &&
       ImmutableState()->StrokeColor().Alpha()) {
     // Stroke a width: 1 inset border
-    PaintFlags flags(ImmutableState()->FillFlags());
+    cc::PaintFlags flags(ImmutableState()->FillFlags());
     flags.setColor(StrokeColor().Rgb());
-    flags.setStyle(PaintFlags::kStroke_Style);
+    flags.setStyle(cc::PaintFlags::kStroke_Style);
     flags.setStrokeWidth(1);
 
     sk_rect.inset(0.5f, 0.5f);
@@ -575,7 +577,7 @@
 void GraphicsContext::DrawText(const Font& font,
                                const TextRunPaintInfo& text_info,
                                const gfx::PointF& point,
-                               const PaintFlags& flags,
+                               const cc::PaintFlags& flags,
                                DOMNodeId node_id,
                                const AutoDarkMode& auto_dark_mode) {
   font.DrawText(canvas_, text_info, point, device_scale_factor_, node_id,
@@ -587,7 +589,7 @@
 void GraphicsContext::DrawText(const Font& font,
                                const NGTextFragmentPaintInfo& text_info,
                                const gfx::PointF& point,
-                               const PaintFlags& flags,
+                               const cc::PaintFlags& flags,
                                DOMNodeId node_id,
                                const AutoDarkMode& auto_dark_mode) {
   font.DrawText(canvas_, text_info, point, device_scale_factor_, node_id,
@@ -602,11 +604,11 @@
   TextDrawingModeFlags mode_flags = TextDrawingMode();
 
   if (mode_flags & kTextModeFill) {
-    const PaintFlags& flags = ImmutableState()->FillFlags();
+    const cc::PaintFlags& flags = ImmutableState()->FillFlags();
     DarkModeFlags dark_flags(this, auto_dark_mode, flags);
     if (UNLIKELY(ShouldDrawDarkModeTextContrastOutline(flags, dark_flags))) {
-      PaintFlags outline_flags(flags);
-      outline_flags.setStyle(PaintFlags::kStroke_Style);
+      cc::PaintFlags outline_flags(flags);
+      outline_flags.setStyle(cc::PaintFlags::kStroke_Style);
       outline_flags.setStrokeWidth(1);
       draw_text(outline_flags);
     }
@@ -615,7 +617,7 @@
 
   if ((mode_flags & kTextModeStroke) && GetStrokeStyle() != kNoStroke &&
       StrokeThickness() > 0) {
-    PaintFlags flags(ImmutableState()->StrokeFlags());
+    cc::PaintFlags flags(ImmutableState()->StrokeFlags());
     if (mode_flags & kTextModeFill) {
       // shadow was already applied during fill pass
       flags.setLooper(nullptr);
@@ -630,7 +632,7 @@
                                        const gfx::PointF& point,
                                        DOMNodeId node_id,
                                        const AutoDarkMode& auto_dark_mode) {
-  DrawTextPasses(auto_dark_mode, [&](const PaintFlags& flags) {
+  DrawTextPasses(auto_dark_mode, [&](const cc::PaintFlags& flags) {
     font.DrawText(canvas_, text_info, point, device_scale_factor_, node_id,
                   flags,
                   printing_ ? Font::DrawType::kGlyphsAndClusters
@@ -639,7 +641,7 @@
 }
 
 bool GraphicsContext::ShouldDrawDarkModeTextContrastOutline(
-    const PaintFlags& original_flags,
+    const cc::PaintFlags& original_flags,
     const DarkModeFlags& dark_flags) const {
   if (!dark_flags.applied_dark_mode())
     return false;
@@ -651,7 +653,7 @@
   // TODO(pdr): Calculate a contrast ratio using luminance (see:
   // https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html).
   constexpr int kMinimumDifferenceSq = 90000;
-  Color dark_color(static_cast<const PaintFlags&>(dark_flags).getColor());
+  Color dark_color(static_cast<const cc::PaintFlags&>(dark_flags).getColor());
   Color original_color(original_flags.getColor());
   return DifferenceSquared(dark_color, original_color) > kMinimumDifferenceSq;
 }
@@ -680,7 +682,7 @@
     const gfx::PointF& point,
     const AutoDarkMode& auto_dark_mode) {
   DrawTextPasses(auto_dark_mode, [&font, &text_info, &mark, &point,
-                                  this](const PaintFlags& flags) {
+                                  this](const cc::PaintFlags& flags) {
     font.DrawEmphasisMarks(canvas_, text_info, mark, point,
                            device_scale_factor_, flags);
   });
@@ -711,7 +713,7 @@
     Font::CustomFontNotReadyAction custom_font_not_ready_action) {
   DrawTextPasses(
       auto_dark_mode, [&font, &run_info, &point, custom_font_not_ready_action,
-                       this](const PaintFlags& flags) {
+                       this](const cc::PaintFlags& flags) {
         if (font.DrawBidiText(canvas_, run_info, point,
                               custom_font_not_ready_action,
                               device_scale_factor_, flags,
@@ -747,7 +749,7 @@
 
   const gfx::RectF src = src_ptr ? *src_ptr : gfx::RectF(image->Rect());
 
-  PaintFlags image_flags = ImmutableState()->FillFlags();
+  cc::PaintFlags image_flags = ImmutableState()->FillFlags();
   image_flags.setBlendMode(op);
   image_flags.setColor(SK_ColorBLACK);
 
@@ -787,7 +789,7 @@
 
   SkSamplingOptions sampling =
       ComputeSamplingOptions(image, dest.Rect(), src_rect);
-  PaintFlags image_flags = ImmutableState()->FillFlags();
+  cc::PaintFlags image_flags = ImmutableState()->FillFlags();
   image_flags.setBlendMode(op);
   image_flags.setColor(SK_ColorBLACK);
 
@@ -860,7 +862,7 @@
   if (!image)
     return;
 
-  PaintFlags image_flags = ImmutableState()->FillFlags();
+  cc::PaintFlags image_flags = ImmutableState()->FillFlags();
   image_flags.setBlendMode(op);
   SkSamplingOptions sampling = ImageSamplingOptions();
   ImageDrawOptions draw_options(
@@ -873,28 +875,28 @@
 }
 
 void GraphicsContext::DrawOval(const SkRect& oval,
-                               const PaintFlags& flags,
+                               const cc::PaintFlags& flags,
                                const AutoDarkMode& auto_dark_mode) {
   DCHECK(canvas_);
   canvas_->drawOval(oval, DarkModeFlags(this, auto_dark_mode, flags));
 }
 
 void GraphicsContext::DrawPath(const SkPath& path,
-                               const PaintFlags& flags,
+                               const cc::PaintFlags& flags,
                                const AutoDarkMode& auto_dark_mode) {
   DCHECK(canvas_);
   canvas_->drawPath(path, DarkModeFlags(this, auto_dark_mode, flags));
 }
 
 void GraphicsContext::DrawRect(const SkRect& rect,
-                               const PaintFlags& flags,
+                               const cc::PaintFlags& flags,
                                const AutoDarkMode& auto_dark_mode) {
   DCHECK(canvas_);
   canvas_->drawRect(rect, DarkModeFlags(this, auto_dark_mode, flags));
 }
 
 void GraphicsContext::DrawRRect(const SkRRect& rrect,
-                                const PaintFlags& flags,
+                                const cc::PaintFlags& flags,
                                 const AutoDarkMode& auto_dark_mode) {
   DCHECK(canvas_);
   canvas_->drawRRect(rrect, DarkModeFlags(this, auto_dark_mode, flags));
@@ -931,7 +933,7 @@
                                const Color& color,
                                const AutoDarkMode& auto_dark_mode,
                                SkBlendMode xfer_mode) {
-  PaintFlags flags = ImmutableState()->FillFlags();
+  cc::PaintFlags flags = ImmutableState()->FillFlags();
   flags.setColor(color.Rgb());
   flags.setBlendMode(xfer_mode);
 
@@ -951,7 +953,7 @@
     return;
   }
 
-  PaintFlags flags = ImmutableState()->FillFlags();
+  cc::PaintFlags flags = ImmutableState()->FillFlags();
   flags.setColor(color.Rgb());
 
   DrawRRect(SkRRect(rrect), flags, auto_dark_mode);
@@ -1012,7 +1014,7 @@
           SkRRect(outer), SkRRect(inner),
           DarkModeFlags(this, auto_dark_mode, ImmutableState()->FillFlags()));
     } else {
-      PaintFlags flags(ImmutableState()->FillFlags());
+      cc::PaintFlags flags(ImmutableState()->FillFlags());
       flags.setColor(color.Rgb());
       canvas_->drawDRRect(SkRRect(outer), SkRRect(inner),
                           DarkModeFlags(this, auto_dark_mode, flags));
@@ -1026,9 +1028,9 @@
   SkRRect stroke_r_rect(outer);
   stroke_r_rect.inset(stroke_width / 2, stroke_width / 2);
 
-  PaintFlags stroke_flags(ImmutableState()->FillFlags());
+  cc::PaintFlags stroke_flags(ImmutableState()->FillFlags());
   stroke_flags.setColor(color.Rgb());
-  stroke_flags.setStyle(PaintFlags::kStroke_Style);
+  stroke_flags.setStyle(cc::PaintFlags::kStroke_Style);
   stroke_flags.setStrokeWidth(stroke_width);
 
   canvas_->drawRRect(stroke_r_rect,
@@ -1040,7 +1042,7 @@
     const FloatRoundedRect& rounded_hole_rect,
     const Color& color,
     const AutoDarkMode& auto_dark_mode) {
-  PaintFlags flags(ImmutableState()->FillFlags());
+  cc::PaintFlags flags(ImmutableState()->FillFlags());
   flags.setColor(color.Rgb());
   canvas_->drawDRRect(SkRRect::MakeRect(gfx::RectFToSkRect(rect)),
                       SkRRect(rounded_hole_rect),
@@ -1068,7 +1070,7 @@
 void GraphicsContext::StrokeRect(const gfx::RectF& rect,
                                  float line_width,
                                  const AutoDarkMode& auto_dark_mode) {
-  PaintFlags flags(ImmutableState()->StrokeFlags());
+  cc::PaintFlags flags(ImmutableState()->StrokeFlags());
   flags.setStrokeWidth(WebCoreFloatToSkScalar(line_width));
   // Reset the dash effect to account for the width
   ImmutableState()->GetStrokeData().SetupPaintDashPathEffect(&flags);
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.h b/third_party/blink/renderer/platform/graphics/graphics_context.h
index be6cb7e..89ea4a5 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.h
@@ -31,6 +31,7 @@
 #include <memory>
 
 #include "base/dcheck_is_on.h"
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/public/mojom/frame/color_scheme.mojom-blink-forward.h"
 #include "third_party/blink/renderer/platform/fonts/font.h"
 #include "third_party/blink/renderer/platform/graphics/dark_mode_filter.h"
@@ -215,8 +216,9 @@
   }
 
   SkSamplingOptions ImageSamplingOptions() const {
-    return PaintFlags::FilterQualityToSkSamplingOptions(
-        static_cast<PaintFlags::FilterQuality>(ImageInterpolationQuality()));
+    return cc::PaintFlags::FilterQualityToSkSamplingOptions(
+        static_cast<cc::PaintFlags::FilterQuality>(
+            ImageInterpolationQuality()));
   }
 
   // Specify the device scale factor which may change the way document markers
@@ -244,7 +246,7 @@
                 const gfx::Point&,
                 const AutoDarkMode& auto_dark_mode,
                 bool is_text_line = false,
-                const PaintFlags* flags = nullptr);
+                const cc::PaintFlags* flags = nullptr);
 
   void FillPath(const Path&, const AutoDarkMode& auto_dark_mode);
 
@@ -320,16 +322,16 @@
   // Also drawLine(const gfx::Point& point1, const gfx::Point& point2) and
   // fillRoundedRect().
   void DrawOval(const SkRect&,
-                const PaintFlags&,
+                const cc::PaintFlags&,
                 const AutoDarkMode& auto_dark_mode);
   void DrawPath(const SkPath&,
-                const PaintFlags&,
+                const cc::PaintFlags&,
                 const AutoDarkMode& auto_dark_mode);
   void DrawRect(const SkRect&,
-                const PaintFlags&,
+                const cc::PaintFlags&,
                 const AutoDarkMode& auto_dark_mode);
   void DrawRRect(const SkRRect&,
-                 const PaintFlags&,
+                 const cc::PaintFlags&,
                  const AutoDarkMode& auto_dark_mode);
 
   void Clip(const gfx::Rect& rect) { ClipRect(gfx::RectToSkRect(rect)); }
@@ -368,7 +370,7 @@
   void DrawText(const Font&,
                 const TextRunPaintInfo&,
                 const gfx::PointF&,
-                const PaintFlags&,
+                const cc::PaintFlags&,
                 DOMNodeId,
                 const AutoDarkMode& auto_dark_mode);
 
@@ -377,7 +379,7 @@
   void DrawText(const Font&,
                 const NGTextFragmentPaintInfo&,
                 const gfx::PointF&,
-                const PaintFlags&,
+                const cc::PaintFlags&,
                 DOMNodeId,
                 const AutoDarkMode& auto_dark_mode);
 
@@ -410,7 +412,7 @@
   void DrawLineForText(const gfx::PointF&,
                        float width,
                        const AutoDarkMode& auto_dark_mode,
-                       const PaintFlags* flags = nullptr);
+                       const cc::PaintFlags* flags = nullptr);
 
   // beginLayer()/endLayer() behave like save()/restore() for CTM and clip
   // states. Apply SkBlendMode when the layer is composited on the backdrop
@@ -444,13 +446,15 @@
                          float width,
                          const AutoDarkMode& auto_dark_mode);
 
-  const PaintFlags& FillFlags() const { return ImmutableState()->FillFlags(); }
+  const cc::PaintFlags& FillFlags() const {
+    return ImmutableState()->FillFlags();
+  }
   // If the length of the path to be stroked is known, pass it in for correct
   // dash or dot placement. Border painting uses a stroke thickness determined
   // by the corner miters. Set the dash_thickness to a non-zero number for
   // cases where dashes should be based on a different thickness.
-  const PaintFlags& StrokeFlags(const int length = 0,
-                                const int dash_thickness = 0) const {
+  const cc::PaintFlags& StrokeFlags(const int length = 0,
+                                    const int dash_thickness = 0) const {
     return ImmutableState()->StrokeFlags(length, dash_thickness);
   }
 
@@ -462,14 +466,15 @@
   void Translate(float x, float y);
   // ---------- End transformation methods -----------------
 
-  PaintFlags::FilterQuality ComputeFilterQuality(Image*,
-                                                 const gfx::RectF& dest,
-                                                 const gfx::RectF& src) const;
+  cc::PaintFlags::FilterQuality ComputeFilterQuality(
+      Image*,
+      const gfx::RectF& dest,
+      const gfx::RectF& src) const;
 
   SkSamplingOptions ComputeSamplingOptions(Image* image,
                                            const gfx::RectF& dest,
                                            const gfx::RectF& src) const {
-    return PaintFlags::FilterQualityToSkSamplingOptions(
+    return cc::PaintFlags::FilterQualityToSkSamplingOptions(
         ComputeFilterQuality(image, dest, src));
   }
 
@@ -532,7 +537,7 @@
   template <typename DrawTextFunc>
   void DrawTextPasses(const AutoDarkMode& auto_dark_mode, const DrawTextFunc&);
 
-  void SaveLayer(const SkRect* bounds, const PaintFlags*);
+  void SaveLayer(const SkRect* bounds, const cc::PaintFlags*);
   void RestoreLayer();
 
   // SkCanvas wrappers.
@@ -561,7 +566,7 @@
   class DarkModeFlags;
 
   bool ShouldDrawDarkModeTextContrastOutline(
-      const PaintFlags& original_flags,
+      const cc::PaintFlags& original_flags,
       const DarkModeFlags& dark_flags) const;
 
   // This is owned by paint_recorder_. Never delete this object.
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context_state.cc b/third_party/blink/renderer/platform/graphics/graphics_context_state.cc
index 0052bc7..a4e33f83ba 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context_state.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context_state.cc
@@ -23,10 +23,10 @@
       interpolation_quality_(kInterpolationDefault),
       save_count_(0),
       should_antialias_(true) {
-  stroke_flags_.setStyle(PaintFlags::kStroke_Style);
+  stroke_flags_.setStyle(cc::PaintFlags::kStroke_Style);
   stroke_flags_.setStrokeWidth(SkFloatToScalar(stroke_data_.Thickness()));
-  stroke_flags_.setStrokeCap(PaintFlags::kDefault_Cap);
-  stroke_flags_.setStrokeJoin(PaintFlags::kDefault_Join);
+  stroke_flags_.setStrokeCap(cc::PaintFlags::kDefault_Cap);
+  stroke_flags_.setStrokeJoin(cc::PaintFlags::kDefault_Join);
   stroke_flags_.setStrokeMiter(SkFloatToScalar(stroke_data_.MiterLimit()));
   stroke_flags_.setFilterQuality(FilterQualityForPaint(interpolation_quality_));
   stroke_flags_.setAntiAlias(should_antialias_);
@@ -48,7 +48,7 @@
   new (this) GraphicsContextState(source);
 }
 
-const PaintFlags& GraphicsContextState::StrokeFlags(
+const cc::PaintFlags& GraphicsContextState::StrokeFlags(
     const int stroked_path_length,
     const int dash_thickness) const {
   stroke_data_.SetupPaintDashPathEffect(&stroke_flags_, stroked_path_length,
@@ -72,12 +72,12 @@
 
 void GraphicsContextState::SetLineCap(LineCap cap) {
   stroke_data_.SetLineCap(cap);
-  stroke_flags_.setStrokeCap((PaintFlags::Cap)cap);
+  stroke_flags_.setStrokeCap(static_cast<cc::PaintFlags::Cap>(cap));
 }
 
 void GraphicsContextState::SetLineJoin(LineJoin join) {
   stroke_data_.SetLineJoin(join);
-  stroke_flags_.setStrokeJoin((PaintFlags::Join)join);
+  stroke_flags_.setStrokeJoin(static_cast<cc::PaintFlags::Join>(join));
 }
 
 void GraphicsContextState::SetMiterLimit(float miter_limit) {
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context_state.h b/third_party/blink/renderer/platform/graphics/graphics_context_state.h
index 6bf33c3..7804718 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context_state.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_context_state.h
@@ -32,9 +32,9 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/draw_looper_builder.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/stroke_data.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/skia/include/core/SkColorFilter.h"
@@ -57,11 +57,11 @@
 
   void Copy(const GraphicsContextState&);
 
-  // PaintFlags objects that reflect the current state. If the length of the
+  // cc::PaintFlags objects that reflect the current state. If the length of the
   // path to be stroked is known, pass it in for correct dash or dot placement.
-  const PaintFlags& StrokeFlags(const int stroked_path_length = 0,
-                                const int dash_thickness = 0) const;
-  const PaintFlags& FillFlags() const { return fill_flags_; }
+  const cc::PaintFlags& StrokeFlags(const int stroked_path_length = 0,
+                                    const int dash_thickness = 0) const;
+  const cc::PaintFlags& FillFlags() const { return fill_flags_; }
 
   uint16_t SaveCount() const { return save_count_; }
   void IncrementSaveCount() { ++save_count_; }
@@ -117,8 +117,8 @@
 
   // This is mutable to enable dash path effect updates when the paint is
   // fetched for use.
-  mutable PaintFlags stroke_flags_;
-  PaintFlags fill_flags_;
+  mutable cc::PaintFlags stroke_flags_;
+  cc::PaintFlags fill_flags_;
 
   StrokeData stroke_data_;
 
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context_test.cc b/third_party/blink/renderer/platform/graphics/graphics_context_test.cc
index 671c724..4148268 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context_test.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context_test.cc
@@ -143,7 +143,7 @@
   Path path;
   path.MoveTo(gfx::PointF(10, 10));
   path.AddLineTo(gfx::PointF(40, 40));
-  PaintFlags flags;
+  cc::PaintFlags flags;
   flags.setColor(transparent.Rgb());
   flags.setBlendMode(SkBlendMode::kSrcOut);
   context.DrawPath(path.GetSkPath(), flags, AutoDarkModeDisabled());
diff --git a/third_party/blink/renderer/platform/graphics/image.cc b/third_party/blink/renderer/platform/graphics/image.cc
index f5bb9ef3..0f3aede1 100644
--- a/third_party/blink/renderer/platform/graphics/image.cc
+++ b/third_party/blink/renderer/platform/graphics/image.cc
@@ -202,7 +202,7 @@
 
   PaintRecorder recorder;
   cc::PaintCanvas* canvas = recorder.beginRecording(tile_rect);
-  PaintFlags flags;
+  cc::PaintFlags flags;
   flags.setAntiAlias(should_antialias);
   canvas->drawImageRect(
       image, gfx::RectToSkRect(subset_rect),
@@ -289,7 +289,7 @@
   // If the shader could not be instantiated (e.g. non-invertible matrix),
   // draw transparent.
   // Note: we can't simply bail, because of arbitrary blend mode.
-  PaintFlags flags(base_flags);
+  cc::PaintFlags flags(base_flags);
   flags.setColor(tile_shader ? SK_ColorBLACK : SK_ColorTRANSPARENT);
   flags.setShader(std::move(tile_shader));
   if (draw_options.apply_dark_mode) {
@@ -330,7 +330,7 @@
       .set_is_multipart(is_multipart_);
 }
 
-bool Image::ApplyShader(PaintFlags& flags,
+bool Image::ApplyShader(cc::PaintFlags& flags,
                         const SkMatrix& local_matrix,
                         const gfx::RectF& dst_rect,
                         const gfx::RectF& src_rect,
diff --git a/third_party/blink/renderer/platform/graphics/paint/cull_rect.h b/third_party/blink/renderer/platform/graphics/paint/cull_rect.h
index 1ef0a16..e8df507 100644
--- a/third_party/blink/renderer/platform/graphics/paint/cull_rect.h
+++ b/third_party/blink/renderer/platform/graphics/paint/cull_rect.h
@@ -47,11 +47,10 @@
   // the cull rect is in the space of the parent the transform node.
   void ApplyTransform(const TransformPaintPropertyNode&);
 
-  // For CullRectUpdate only. Similar to the above but also applies clips and
-  // expands for all directly composited transforms (including scrolling and
-  // non-scrolling ones). |root| is used to calculate the expansion distance in
-  // the local space, to make the expansion distance approximately the same in
-  // the root space.
+  // Similar to the above but also applies clips and expands for all directly
+  // composited transforms (including scrolling and non-scrolling ones).
+  // |root| is used to calculate the expansion distance in the local space,
+  // to make the expansion distance approximately the same in the root space.
   // Returns whether the cull rect has been expanded.
   bool ApplyPaintProperties(const PropertyTreeState& root,
                             const PropertyTreeState& source,
diff --git a/third_party/blink/renderer/platform/graphics/paint/drawing_display_item_test.cc b/third_party/blink/renderer/platform/graphics/paint/drawing_display_item_test.cc
index a68c6d2d..1fe47ce 100644
--- a/third_party/blink/renderer/platform/graphics/paint/drawing_display_item_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/drawing_display_item_test.cc
@@ -5,10 +5,10 @@
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
 
 #include "cc/paint/display_item_list.h"
+#include "cc/paint/paint_flags.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
 #include "third_party/blink/renderer/platform/testing/fake_display_item_client.h"
@@ -34,7 +34,7 @@
   PaintRecorder recorder;
   cc::PaintCanvas* canvas =
       recorder.beginRecording(record_bounds.width(), record_bounds.height());
-  canvas->drawRect(gfx::RectFToSkRect(record_bounds), PaintFlags());
+  canvas->drawRect(gfx::RectFToSkRect(record_bounds), cc::PaintFlags());
   return recorder.finishRecordingAsPicture();
 }
 
@@ -47,7 +47,7 @@
       recorder.beginRecording(record_bounds.width(), record_bounds.height());
   canvas->save();
   canvas->translate(dx, dy);
-  canvas->drawRect(gfx::RectFToSkRect(record_bounds), PaintFlags());
+  canvas->drawRect(gfx::RectFToSkRect(record_bounds), cc::PaintFlags());
   canvas->restore();
   return recorder.finishRecordingAsPicture();
 }
@@ -161,7 +161,7 @@
   PaintRecorder recorder;
   cc::PaintCanvas* canvas =
       recorder.beginRecording(record_bounds.width(), record_bounds.height());
-  canvas->drawOval(gfx::RectFToSkRect(record_bounds), PaintFlags());
+  canvas->drawOval(gfx::RectFToSkRect(record_bounds), cc::PaintFlags());
 
   DrawingDisplayItem item(client_->Id(), DisplayItem::Type::kDocumentBackground,
                           ToEnclosingRect(record_bounds),
@@ -179,7 +179,7 @@
   constexpr int kSize = 100;
   SkBitmap bitmap;
   bitmap.allocN32Pixels(kSize, kSize);
-  PaintFlags flags;
+  cc::PaintFlags flags;
   flags.setAntiAlias(true);
   flags.setColor(SK_ColorWHITE);
   for (float r = kRadiusStep; r < kSize / 2; r += kRadiusStep) {
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_chunker_test.cc b/third_party/blink/renderer/platform/graphics/paint/paint_chunker_test.cc
index e4e54d8..6f2ea09 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_chunker_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_chunker_test.cc
@@ -52,7 +52,7 @@
 sk_sp<const PaintRecord> OpaquePaintRecord(const gfx::Rect& visual_rect) {
   PaintRecorder recorder;
   auto* canvas = recorder.beginRecording(gfx::RectToSkRect(visual_rect));
-  PaintFlags flags;
+  cc::PaintFlags flags;
   flags.setColor(SK_ColorBLACK);
   canvas->drawRect(gfx::RectToSkRect(visual_rect), flags);
   return recorder.finishRecordingAsPicture();
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc b/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc
index a7cb63d..d0914af 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc
@@ -1816,7 +1816,7 @@
   builder.lineTo(100, 0);
   builder.close();
   SkPath path = builder.detach();
-  PaintFlags flags;
+  cc::PaintFlags flags;
   flags.setAntiAlias(true);
   for (unsigned i = 0; i < count; i++)
     context.DrawPath(path, flags, AutoDarkMode::Disabled());
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_flags.h b/third_party/blink/renderer/platform/graphics/paint/paint_flags.h
deleted file mode 100644
index 5572d1c..0000000
--- a/third_party/blink/renderer/platform/graphics/paint/paint_flags.h
+++ /dev/null
@@ -1,14 +0,0 @@
-// 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.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_FLAGS_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_FLAGS_H_
-
-#include "cc/paint/paint_flags.h"
-
-namespace blink {
-using cc::PaintFlags;
-}
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_FLAGS_H_
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_record_builder_test.cc b/third_party/blink/renderer/platform/graphics/paint/paint_record_builder_test.cc
index 2a0e1024..5853942 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_record_builder_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_record_builder_test.cc
@@ -26,7 +26,7 @@
   EXPECT_FALSE(ClientCacheIsValid(context.GetPaintController(), client));
 
   MockPaintCanvas canvas;
-  PaintFlags flags;
+  cc::PaintFlags flags;
   EXPECT_CALL(canvas, drawPicture(_)).Times(1);
   builder->EndRecording(canvas);
 
@@ -43,7 +43,7 @@
       MakeGarbageCollected<PaintRecordBuilder>(GetPaintController());
   auto& context = builder->Context();
   MockPaintCanvas canvas;
-  PaintFlags flags;
+  cc::PaintFlags flags;
   {
     PaintController::CycleScope cycle_scope(GetPaintController());
     InitRootChunk();
diff --git a/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.cc b/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.cc
index 9d3e93b9..f1eee9d 100644
--- a/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.cc
@@ -167,7 +167,7 @@
 // reordering, property changes) of chunks. The logic is similar to
 // PaintController::GenerateRasterInvalidations(). The complexity is between
 // O(n) and O(m*n) where m and n are the numbers of old and new chunks,
-// respectively. Normally both m and n are small numbers. The best caseis that
+// respectively. Normally both m and n are small numbers. The best case is that
 // all old chunks have matching new chunks in the same order. The worst case is
 // that no matching chunks except the first one (which always matches otherwise
 // we won't reuse the RasterInvalidator), which is rare. In
@@ -176,10 +176,9 @@
 void RasterInvalidator::GenerateRasterInvalidations(
     RasterInvalidationFunction function,
     const PaintChunkSubset& new_chunks,
-    const PropertyTreeState& layer_state,
-    bool layer_offset_changed,
+    bool layer_offset_or_state_changed,
     Vector<PaintChunkInfo>& new_chunks_info) {
-  ChunkToLayerMapper mapper(layer_state, layer_offset_);
+  ChunkToLayerMapper mapper(layer_state_, layer_offset_);
   Vector<bool> old_chunks_matched;
   old_chunks_matched.resize(old_paint_chunks_info_.size());
   wtf_size_t old_index = 0;
@@ -217,10 +216,11 @@
 
     // No need to invalidate if the chunk is moved from cached subsequence and
     // its paint properties didn't change relative to the layer.
-    if (!layer_offset_changed && reason == PaintInvalidationReason::kNone &&
+    if (!layer_offset_or_state_changed &&
+        reason == PaintInvalidationReason::kNone &&
         new_chunk.is_moved_from_cached_subsequence &&
         !new_chunk.properties.GetPropertyTreeState().Changed(
-            PaintPropertyChangeType::kChangedOnlySimpleValues, layer_state)) {
+            PaintPropertyChangeType::kChangedOnlySimpleValues, layer_state_)) {
       new_chunks_info.emplace_back(old_chunk_info, it);
     } else {
       mapper.SwitchToChunk(new_chunk);
@@ -228,7 +228,7 @@
 
       if (reason == PaintInvalidationReason::kNone) {
         reason = ChunkPropertiesChanged(new_chunk, old_chunk, new_chunk_info,
-                                        old_chunk_info, layer_state);
+                                        old_chunk_info, layer_state_);
       }
 
       if (IsFullPaintInvalidationReason(reason)) {
@@ -329,10 +329,12 @@
   if (RasterInvalidationTracking::ShouldAlwaysTrack())
     EnsureTracking();
 
-  bool layer_offset_changed = layer_offset_ != layer_offset;
+  bool layer_offset_or_state_changed =
+      layer_offset_ != layer_offset || layer_state_ != layer_state;
   bool layer_bounds_was_empty = layer_bounds_.IsEmpty();
   layer_offset_ = layer_offset;
   layer_bounds_ = layer_bounds;
+  layer_state_ = layer_state;
   current_paint_artifact_ = &new_chunks.GetPaintArtifact();
 
   Vector<PaintChunkInfo> new_chunks_info;
@@ -357,8 +359,7 @@
     }
   } else {
     GenerateRasterInvalidations(raster_invalidation_function, new_chunks,
-                                layer_state, layer_offset_changed,
-                                new_chunks_info);
+                                layer_offset_or_state_changed, new_chunks_info);
   }
 
   old_paint_chunks_info_ = std::move(new_chunks_info);
diff --git a/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h b/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h
index e624434c..2b337c86 100644
--- a/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h
+++ b/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h
@@ -106,8 +106,7 @@
 
   void GenerateRasterInvalidations(RasterInvalidationFunction,
                                    const PaintChunkSubset&,
-                                   const PropertyTreeState& layer_state,
-                                   bool layer_offset_changed,
+                                   bool layer_offset_or_state_changed,
                                    Vector<PaintChunkInfo>& new_chunks_info);
 
   ALWAYS_INLINE const PaintChunk& GetOldChunk(wtf_size_t index) const;
@@ -154,6 +153,7 @@
 
   gfx::Vector2dF layer_offset_;
   gfx::Size layer_bounds_;
+  PropertyTreeState layer_state_ = PropertyTreeState::Root();
   Vector<PaintChunkInfo> old_paint_chunks_info_;
   scoped_refptr<const PaintArtifact> current_paint_artifact_;
   scoped_refptr<const PaintArtifact> old_paint_artifact_;
diff --git a/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc b/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc
index 4f3f94b..f340971 100644
--- a/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc
@@ -170,6 +170,38 @@
   FinishCycle(chunks);
 }
 
+TEST_P(RasterInvalidatorTest, LayerStateChangeWithCachedSubsequence) {
+  auto t1 = Create2DTranslation(t0(), 100, 50);
+  PropertyTreeState chunk_state(*t1, c0(), e0());
+  PaintChunkSubset chunks(
+      TestPaintArtifact().Chunk(0).Properties(chunk_state).Build());
+
+  invalidator_.Generate(base::DoNothing(), chunks, kDefaultLayerOffset,
+                        kDefaultLayerBounds, DefaultPropertyTreeState());
+  FinishCycle(chunks);
+
+  invalidator_.SetTracksRasterInvalidations(true);
+  auto new_layer_state = chunk_state;
+  PaintChunkSubset new_chunks(TestPaintArtifact()
+                                  .Chunk(0)
+                                  .Properties(chunk_state)
+                                  .IsMovedFromCachedSubsequence()
+                                  .Build());
+
+  invalidator_.Generate(base::DoNothing(), new_chunks, kDefaultLayerOffset,
+                        kDefaultLayerBounds, new_layer_state);
+  // Change of layer state causes change of chunk0's transform to layer.
+  auto old_mapper = [](gfx::Rect& r) { r.Offset(100, 50); };
+  EXPECT_THAT(
+      TrackedRasterInvalidations(),
+      ElementsAre(ChunkInvalidation(
+                      chunks, 0, PaintInvalidationReason::kPaintProperty,
+                      -kDefaultLayerOffset, base::BindRepeating(old_mapper)),
+                  ChunkInvalidation(chunks, 0,
+                                    PaintInvalidationReason::kPaintProperty)));
+  FinishCycle(chunks);
+}
+
 TEST_P(RasterInvalidatorTest, ReorderChunks) {
   PaintChunkSubset chunks(
       TestPaintArtifact().Chunk(0).Chunk(1).Chunk(2).Build());
diff --git a/third_party/blink/renderer/platform/graphics/paint_generated_image.cc b/third_party/blink/renderer/platform/graphics/paint_generated_image.cc
index 4155ff12..6ff8972 100644
--- a/third_party/blink/renderer/platform/graphics/paint_generated_image.cc
+++ b/third_party/blink/renderer/platform/graphics/paint_generated_image.cc
@@ -12,7 +12,7 @@
 namespace blink {
 
 void PaintGeneratedImage::Draw(cc::PaintCanvas* canvas,
-                               const PaintFlags& flags,
+                               const cc::PaintFlags& flags,
                                const gfx::RectF& dest_rect,
                                const gfx::RectF& src_rect,
                                const ImageDrawOptions&) {
diff --git a/third_party/blink/renderer/platform/graphics/paint_record_pattern.cc b/third_party/blink/renderer/platform/graphics/paint_record_pattern.cc
index aa0bd47e7..b2483d1 100644
--- a/third_party/blink/renderer/platform/graphics/paint_record_pattern.cc
+++ b/third_party/blink/renderer/platform/graphics/paint_record_pattern.cc
@@ -4,7 +4,6 @@
 
 #include "third_party/blink/renderer/platform/graphics/paint_record_pattern.h"
 
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_shader.h"
 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
diff --git a/third_party/blink/renderer/platform/graphics/path.cc b/third_party/blink/renderer/platform/graphics/path.cc
index f923e0f3..5abff42 100644
--- a/third_party/blink/renderer/platform/graphics/path.cc
+++ b/third_party/blink/renderer/platform/graphics/path.cc
@@ -92,7 +92,7 @@
 
 SkPath Path::StrokePath(const StrokeData& stroke_data,
                         float stroke_precision) const {
-  PaintFlags flags;
+  cc::PaintFlags flags;
   stroke_data.SetupPaint(&flags);
 
   SkPath stroke_path;
diff --git a/third_party/blink/renderer/platform/graphics/pattern.cc b/third_party/blink/renderer/platform/graphics/pattern.cc
index 34298e7..f8780ece 100644
--- a/third_party/blink/renderer/platform/graphics/pattern.cc
+++ b/third_party/blink/renderer/platform/graphics/pattern.cc
@@ -28,7 +28,6 @@
 #include "third_party/blink/renderer/platform/graphics/pattern.h"
 
 #include "third_party/blink/renderer/platform/graphics/image_pattern.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
 #include "third_party/blink/renderer/platform/graphics/paint_record_pattern.h"
 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
@@ -55,7 +54,7 @@
 
 Pattern::~Pattern() = default;
 
-void Pattern::ApplyToFlags(PaintFlags& flags,
+void Pattern::ApplyToFlags(cc::PaintFlags& flags,
                            const SkMatrix& local_matrix) const {
   if (!cached_shader_ || local_matrix != cached_shader_->GetLocalMatrix())
     cached_shader_ = CreateShader(local_matrix);
diff --git a/third_party/blink/renderer/platform/graphics/placeholder_image.cc b/third_party/blink/renderer/platform/graphics/placeholder_image.cc
index 3c19e17..634c8ec 100644
--- a/third_party/blink/renderer/platform/graphics/placeholder_image.cc
+++ b/third_party/blink/renderer/platform/graphics/placeholder_image.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/cxx17_backports.h"
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/public/resources/grit/blink_image_resources.h"
 #include "third_party/blink/public/strings/grit/blink_strings.h"
 #include "third_party/blink/renderer/platform/fonts/font.h"
@@ -18,7 +19,6 @@
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/image_observer.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
@@ -51,7 +51,7 @@
 constexpr int kFontSize = 14;
 
 void DrawIcon(cc::PaintCanvas* canvas,
-              const PaintFlags& flags,
+              const cc::PaintFlags& flags,
               float x,
               float y,
               const SkSamplingOptions& sampling,
@@ -74,7 +74,7 @@
 }
 
 void DrawCenteredIcon(cc::PaintCanvas* canvas,
-                      const PaintFlags& flags,
+                      const cc::PaintFlags& flags,
                       const gfx::RectF& dest_rect,
                       const SkSamplingOptions& sampling,
                       float scale_factor) {
@@ -252,7 +252,7 @@
 
   PaintRecorder paint_recorder;
   Draw(paint_recorder.beginRecording(gfx::RectToSkRect(dest_rect)),
-       PaintFlags(), gfx::RectF(dest_rect), gfx::RectF(dest_rect),
+       cc::PaintFlags(), gfx::RectF(dest_rect), gfx::RectF(dest_rect),
        ImageDrawOptions());
 
   paint_record_for_current_frame_ = paint_recorder.finishRecordingAsPicture();
@@ -273,7 +273,7 @@
 }
 
 void PlaceholderImage::Draw(cc::PaintCanvas* canvas,
-                            const PaintFlags& base_flags,
+                            const cc::PaintFlags& base_flags,
                             const gfx::RectF& dest_rect,
                             const gfx::RectF& src_rect,
                             const ImageDrawOptions& draw_options) {
@@ -283,8 +283,8 @@
     return;
   }
 
-  PaintFlags flags(base_flags);
-  flags.setStyle(PaintFlags::kFill_Style);
+  cc::PaintFlags flags(base_flags);
+  flags.setStyle(cc::PaintFlags::kFill_Style);
   flags.setColor(SkColorSetARGB(0x80, 0xD9, 0xD9, 0xD9));
   canvas->drawRect(gfx::RectFToSkRect(dest_rect), flags);
 
@@ -356,7 +356,7 @@
 }
 
 void PlaceholderImage::DrawPattern(GraphicsContext& context,
-                                   const PaintFlags& base_flags,
+                                   const cc::PaintFlags& base_flags,
                                    const gfx::RectF& dest_rect,
                                    const ImageTilingInfo& tiling_info,
                                    const ImageDrawOptions& draw_options) {
diff --git a/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc b/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc
index 9f74134..d8e1596c4 100644
--- a/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc
+++ b/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc
@@ -7,6 +7,7 @@
 #include <stdint.h>
 
 #include "base/memory/scoped_refptr.h"
+#include "cc/paint/paint_flags.h"
 #include "cc/paint/skottie_wrapper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -18,7 +19,6 @@
 #include "third_party/blink/renderer/platform/graphics/image.h"
 #include "third_party/blink/renderer/platform/graphics/image_orientation.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
@@ -63,8 +63,9 @@
                               FloatNear(expected_rect.width(), 0.01)),
                      Property(&SkRect::height,
                               FloatNear(expected_rect.height(), 0.01))),
-               AllOf(Property(&PaintFlags::getStyle, PaintFlags::kFill_Style),
-                     Property(&PaintFlags::getColor,
+               AllOf(Property(&cc::PaintFlags::getStyle,
+                              cc::PaintFlags::kFill_Style),
+                     Property(&cc::PaintFlags::getColor,
                               SkColorSetARGB(0x80, 0xD9, 0xD9, 0xD9)))))
       .Times(1);
 }
@@ -76,7 +77,7 @@
   EXPECT_CALL(canvas, drawImageRect(_, _, _, _, _, _)).Times(0);
   EXPECT_CALL(canvas, drawTextBlob(_, _, _, _)).Times(0);
 
-  image.Draw(&canvas, PaintFlags(), dest_rect,
+  image.Draw(&canvas, cc::PaintFlags(), dest_rect,
              gfx::RectF(0.0f, 0.0f, 100.0f, 100.0f), ImageDrawOptions());
 }
 
@@ -109,7 +110,7 @@
 
   ImageDrawOptions draw_options;
   draw_options.respect_orientation = kDoNotRespectImageOrientation;
-  image.Draw(&canvas, PaintFlags(), dest_rect,
+  image.Draw(&canvas, cc::PaintFlags(), dest_rect,
              gfx::RectF(0.0f, 0.0f, 100.0f, 100.0f), draw_options);
 }
 
@@ -188,8 +189,9 @@
                         scale_factor * (kBaseTextPaddingY + kBaseFontSize),
                     0.01),
           AllOf(
-              Property(&PaintFlags::getStyle, PaintFlags::kFill_Style),
-              Property(&PaintFlags::getColor, SkColorSetARGB(0xAB, 0, 0, 0)))))
+              Property(&cc::PaintFlags::getStyle, cc::PaintFlags::kFill_Style),
+              Property(&cc::PaintFlags::getColor,
+                       SkColorSetARGB(0xAB, 0, 0, 0)))))
       .WillOnce(InvokeWithoutArgs([&image, scale_factor]() {
         EXPECT_NEAR(
             scale_factor * kBaseFontSize,
@@ -199,7 +201,7 @@
 
   ImageDrawOptions draw_options;
   draw_options.respect_orientation = kDoNotRespectImageOrientation;
-  image.Draw(&canvas, PaintFlags(), dest_rect,
+  image.Draw(&canvas, cc::PaintFlags(), dest_rect,
              gfx::RectF(0.0f, 0.0f, 100.0f, 100.0f), draw_options);
 }
 
@@ -291,7 +293,7 @@
   ImageDrawOptions draw_options;
   draw_options.respect_orientation = kDoNotRespectImageOrientation;
   PlaceholderImage::Create(nullptr, gfx::Size(800, 600), 0)
-      ->Draw(&canvas, PaintFlags(), gfx::RectF(0.0f, 0.0f, 800.0f, 600.0f),
+      ->Draw(&canvas, cc::PaintFlags(), gfx::RectF(0.0f, 0.0f, 800.0f, 600.0f),
              // The source rectangle is outside the 800x600 bounds of the image,
              // so nothing should be drawn.
              gfx::RectF(1000.0f, 0.0f, 800.0f, 600.0f), draw_options);
@@ -441,8 +443,9 @@
                         kScaleFactor * (kBaseTextPaddingY + kBaseFontSize),
                     0.01),
           AllOf(
-              Property(&PaintFlags::getStyle, PaintFlags::kFill_Style),
-              Property(&PaintFlags::getColor, SkColorSetARGB(0xAB, 0, 0, 0)))))
+              Property(&cc::PaintFlags::getStyle, cc::PaintFlags::kFill_Style),
+              Property(&cc::PaintFlags::getColor,
+                       SkColorSetARGB(0xAB, 0, 0, 0)))))
       .WillOnce(InvokeWithoutArgs([image]() {
         EXPECT_NEAR(
             kScaleFactor * kBaseFontSize,
@@ -452,7 +455,7 @@
 
   ImageDrawOptions draw_options;
   draw_options.respect_orientation = kDoNotRespectImageOrientation;
-  image->Draw(&canvas, PaintFlags(), dest_rect,
+  image->Draw(&canvas, cc::PaintFlags(), dest_rect,
               gfx::RectF(0.0f, 0.0f, 100.0f, 100.0f), draw_options);
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/skia/skia_utils.cc b/third_party/blink/renderer/platform/graphics/skia/skia_utils.cc
index d2ef79e..cd64cef 100644
--- a/third_party/blink/renderer/platform/graphics/skia/skia_utils.cc
+++ b/third_party/blink/renderer/platform/graphics/skia/skia_utils.cc
@@ -33,9 +33,9 @@
 #include "base/allocator/partition_allocator/partition_alloc.h"
 #include "base/numerics/safe_conversions.h"
 #include "build/build_config.h"
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/geometry/layout_rect.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
 #include "third_party/skia/include/effects/SkCornerPathEffect.h"
@@ -407,10 +407,10 @@
                           SkFloatToScalar(rect.Height()));
 }
 
-static PaintFlags PaintFlagsForFocusRing(SkColor color, float width) {
-  PaintFlags flags;
+static cc::PaintFlags PaintFlagsForFocusRing(SkColor color, float width) {
+  cc::PaintFlags flags;
   flags.setAntiAlias(true);
-  flags.setStyle(PaintFlags::kStroke_Style);
+  flags.setStyle(cc::PaintFlags::kStroke_Style);
   flags.setColor(color);
   flags.setStrokeWidth(width);
   return flags;
@@ -428,7 +428,7 @@
                            SkColor color,
                            float width,
                            float corner_radius) {
-  PaintFlags path_flags = PaintFlagsForFocusRing(color, width);
+  cc::PaintFlags path_flags = PaintFlagsForFocusRing(color, width);
   if (corner_radius) {
     path_flags.setPathEffect(
         SkCornerPathEffect::Make(SkFloatToScalar(corner_radius)));
diff --git a/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc b/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
index 2c95f25..e49c1b0f 100644
--- a/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
+++ b/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
@@ -44,7 +44,7 @@
 }
 
 void StaticBitmapImage::DrawHelper(cc::PaintCanvas* canvas,
-                                   const PaintFlags& flags,
+                                   const cc::PaintFlags& flags,
                                    const gfx::RectF& dst_rect,
                                    const gfx::RectF& src_rect,
                                    const ImageDrawOptions& draw_options,
diff --git a/third_party/blink/renderer/platform/graphics/stroke_data.cc b/third_party/blink/renderer/platform/graphics/stroke_data.cc
index ceeffb0d..98fbf50 100644
--- a/third_party/blink/renderer/platform/graphics/stroke_data.cc
+++ b/third_party/blink/renderer/platform/graphics/stroke_data.cc
@@ -28,7 +28,6 @@
 
 #include <memory>
 
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/stroke_data.h"
 #include "third_party/skia/include/effects/SkDashPathEffect.h"
 
@@ -55,10 +54,10 @@
   dash_ = SkDashPathEffect::Make(intervals.get(), count, dash_offset);
 }
 
-void StrokeData::SetupPaint(PaintFlags* flags,
+void StrokeData::SetupPaint(cc::PaintFlags* flags,
                             const int length,
                             const int dash_thickness) const {
-  flags->setStyle(PaintFlags::kStroke_Style);
+  flags->setStyle(cc::PaintFlags::kStroke_Style);
   flags->setStrokeWidth(SkFloatToScalar(thickness_));
   flags->setStrokeCap(line_cap_);
   flags->setStrokeJoin(line_join_);
@@ -67,7 +66,7 @@
   SetupPaintDashPathEffect(flags, length, dash_thickness);
 }
 
-void StrokeData::SetupPaintDashPathEffect(PaintFlags* flags,
+void StrokeData::SetupPaintDashPathEffect(cc::PaintFlags* flags,
                                           const int length,
                                           const int dash_thickness) const {
   int dash_width = dash_thickness ? dash_thickness : thickness_;
@@ -97,7 +96,7 @@
       flags->setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
     }
   } else if (style_ == kDottedStroke) {
-    flags->setStrokeCap((PaintFlags::Cap)kRoundCap);
+    flags->setStrokeCap(cc::PaintFlags::Cap::kRound_Cap);
     // Adjust the width to get equal dot spacing as much as possible.
     float per_dot_length = dash_width * 2;
     if (length < per_dot_length) {
diff --git a/third_party/blink/renderer/platform/graphics/stroke_data.h b/third_party/blink/renderer/platform/graphics/stroke_data.h
index da33fc2..cf5e3b9 100644
--- a/third_party/blink/renderer/platform/graphics/stroke_data.h
+++ b/third_party/blink/renderer/platform/graphics/stroke_data.h
@@ -30,10 +30,10 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_STROKE_DATA_H_
 
 #include "base/memory/scoped_refptr.h"
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/dash_array.h"
 #include "third_party/blink/renderer/platform/graphics/gradient.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/pattern.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -48,22 +48,19 @@
   DISALLOW_NEW();
 
  public:
-  StrokeData()
-      : style_(kSolidStroke),
-        thickness_(0),
-        line_cap_(PaintFlags::kDefault_Cap),
-        line_join_(PaintFlags::kDefault_Join),
-        miter_limit_(4) {}
-
   StrokeStyle Style() const { return style_; }
   void SetStyle(StrokeStyle style) { style_ = style; }
 
   float Thickness() const { return thickness_; }
   void SetThickness(float thickness) { thickness_ = thickness; }
 
-  void SetLineCap(LineCap cap) { line_cap_ = (PaintFlags::Cap)cap; }
+  void SetLineCap(LineCap cap) {
+    line_cap_ = static_cast<cc::PaintFlags::Cap>(cap);
+  }
 
-  void SetLineJoin(LineJoin join) { line_join_ = (PaintFlags::Join)join; }
+  void SetLineJoin(LineJoin join) {
+    line_join_ = static_cast<cc::PaintFlags::Join>(join);
+  }
 
   float MiterLimit() const { return miter_limit_; }
   void SetMiterLimit(float miter_limit) { miter_limit_ = miter_limit; }
@@ -76,13 +73,13 @@
   // dash/dot. If non-zero, dash_thickness is the thickness to use when
   // deciding on dash sizes. Used in border painting when we stroke thick
   // to allow for clipping at corners, but still want small dashes.
-  void SetupPaint(PaintFlags*,
+  void SetupPaint(cc::PaintFlags*,
                   const int length = 0,
                   const int dash_thickess = 0) const;
 
   // Setup any DashPathEffect on the paint. See SetupPaint above for parameter
   // information.
-  void SetupPaintDashPathEffect(PaintFlags*,
+  void SetupPaintDashPathEffect(cc::PaintFlags*,
                                 const int path_length = 0,
                                 const int dash_thickness = 0) const;
 
@@ -115,11 +112,11 @@
                                  float gap_length);
 
  private:
-  StrokeStyle style_;
-  float thickness_;
-  PaintFlags::Cap line_cap_;
-  PaintFlags::Join line_join_;
-  float miter_limit_;
+  StrokeStyle style_ = kSolidStroke;
+  float thickness_ = 0;
+  cc::PaintFlags::Cap line_cap_ = cc::PaintFlags::kDefault_Cap;
+  cc::PaintFlags::Join line_join_ = cc::PaintFlags::kDefault_Join;
+  float miter_limit_ = 4;
   sk_sp<SkPathEffect> dash_;
 };
 
diff --git a/third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h b/third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h
index d1ccb3fb..c4bf76a 100644
--- a/third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h
+++ b/third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h
@@ -6,8 +6,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TEST_MOCK_PAINT_CANVAS_H_
 
 #include "cc/paint/paint_canvas.h"
+#include "cc/paint/paint_flags.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
 #include "third_party/skia/include/core/SkTextBlob.h"
 
@@ -25,7 +25,8 @@
   MOCK_METHOD1(setNodeId, void(int));
   MOCK_METHOD0(flush, void());
   MOCK_METHOD0(save, int());
-  MOCK_METHOD2(saveLayer, int(const SkRect* bounds, const PaintFlags* flags));
+  MOCK_METHOD2(saveLayer,
+               int(const SkRect* bounds, const cc::PaintFlags* flags));
   MOCK_METHOD2(saveLayerAlpha, int(const SkRect* bounds, uint8_t alpha));
   MOCK_METHOD0(restore, void());
   MOCK_CONST_METHOD0(getSaveCount, int());
@@ -57,36 +58,38 @@
                     SkScalar y0,
                     SkScalar x1,
                     SkScalar y1,
-                    const PaintFlags& flags));
-  MOCK_METHOD2(drawRect, void(const SkRect& rect, const PaintFlags& flags));
-  MOCK_METHOD2(drawIRect, void(const SkIRect& rect, const PaintFlags& flags));
-  MOCK_METHOD2(drawOval, void(const SkRect& oval, const PaintFlags& flags));
-  MOCK_METHOD2(drawRRect, void(const SkRRect& rrect, const PaintFlags& flags));
+                    const cc::PaintFlags& flags));
+  MOCK_METHOD2(drawRect, void(const SkRect& rect, const cc::PaintFlags& flags));
+  MOCK_METHOD2(drawIRect,
+               void(const SkIRect& rect, const cc::PaintFlags& flags));
+  MOCK_METHOD2(drawOval, void(const SkRect& oval, const cc::PaintFlags& flags));
+  MOCK_METHOD2(drawRRect,
+               void(const SkRRect& rrect, const cc::PaintFlags& flags));
   MOCK_METHOD3(drawDRRect,
                void(const SkRRect& outer,
                     const SkRRect& inner,
-                    const PaintFlags& flags));
+                    const cc::PaintFlags& flags));
   MOCK_METHOD4(drawRoundRect,
                void(const SkRect& rect,
                     SkScalar rx,
                     SkScalar ry,
-                    const PaintFlags& flags));
+                    const cc::PaintFlags& flags));
   MOCK_METHOD3(drawPath,
                void(const SkPath& path,
-                    const PaintFlags& flags,
+                    const cc::PaintFlags& flags,
                     cc::UsePaintCache use_paint_cache));
   MOCK_METHOD5(drawImage,
                void(const PaintImage& image,
                     SkScalar left,
                     SkScalar top,
                     const SkSamplingOptions&,
-                    const PaintFlags* flags));
+                    const cc::PaintFlags* flags));
   MOCK_METHOD6(drawImageRect,
                void(const PaintImage& image,
                     const SkRect& src,
                     const SkRect& dst,
                     const SkSamplingOptions&,
-                    const PaintFlags* flags,
+                    const cc::PaintFlags* flags,
                     SkCanvas::SrcRectConstraint constraint));
   MOCK_METHOD4(drawSkottie,
                void(scoped_refptr<cc::SkottieWrapper> skottie,
@@ -97,16 +100,18 @@
                void(const SkBitmap& bitmap,
                     SkScalar left,
                     SkScalar top,
-                    const PaintFlags* flags));
-  MOCK_METHOD4(
-      drawTextBlob,
-      void(sk_sp<SkTextBlob>, SkScalar x, SkScalar y, const PaintFlags& flags));
+                    const cc::PaintFlags* flags));
+  MOCK_METHOD4(drawTextBlob,
+               void(sk_sp<SkTextBlob>,
+                    SkScalar x,
+                    SkScalar y,
+                    const cc::PaintFlags& flags));
   MOCK_METHOD5(drawTextBlob,
                void(sk_sp<SkTextBlob>,
                     SkScalar x,
                     SkScalar y,
                     cc::NodeId node_id,
-                    const PaintFlags& flags));
+                    const cc::PaintFlags& flags));
 
   MOCK_METHOD1(drawPicture, void(sk_sp<const PaintRecord> record));
   MOCK_CONST_METHOD0(isClipEmpty, bool());
diff --git a/third_party/blink/renderer/platform/heap/heap_test_objects.h b/third_party/blink/renderer/platform/heap/heap_test_objects.h
index 1f06b681..7ac375c 100644
--- a/third_party/blink/renderer/platform/heap/heap_test_objects.h
+++ b/third_party/blink/renderer/platform/heap/heap_test_objects.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TEST_OBJECTS_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TEST_OBJECTS_H_
 
+#include "base/callback_forward.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h b/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h
index c25abee..120b4cc 100644
--- a/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h
+++ b/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h
@@ -49,10 +49,6 @@
   }
   const KURL& WebBundlePhysicalUrl() const override;
   int GetOutstandingThrottledLimit() const override { return 0; }
-  scoped_refptr<SecurityOrigin> GetLitePageSubresourceRedirectOrigin()
-      const override {
-    return nullptr;
-  }
 
  private:
   const Member<const FetchClientSettingsObject> fetch_client_settings_object_;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index dca16f4..e188b438 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -1323,7 +1323,7 @@
 
   if (resource->MustRefetchDueToIntegrityMetadata(params)) {
     if (!params.IsSpeculativePreload() && !params.IsLinkPreload())
-      PrintPreloadWarning(resource, Resource::MatchStatus::kIntegrityMismatch);
+      PrintPreloadMismatch(resource, Resource::MatchStatus::kIntegrityMismatch);
     return nullptr;
   }
 
@@ -1336,18 +1336,19 @@
 
   const ResourceRequest& request = params.GetResourceRequest();
   if (request.DownloadToBlob()) {
-    PrintPreloadWarning(resource, Resource::MatchStatus::kBlobRequest);
+    PrintPreloadMismatch(resource, Resource::MatchStatus::kBlobRequest);
     return nullptr;
   }
 
   if (IsImageResourceDisallowedToBeReused(*resource)) {
-    PrintPreloadWarning(resource, Resource::MatchStatus::kImageLoadingDisabled);
+    PrintPreloadMismatch(resource,
+                         Resource::MatchStatus::kImageLoadingDisabled);
     return nullptr;
   }
 
   const Resource::MatchStatus match_status = resource->CanReuse(params);
   if (match_status != Resource::MatchStatus::kOk) {
-    PrintPreloadWarning(resource, match_status);
+    PrintPreloadMismatch(resource, match_status);
     return nullptr;
   }
 
@@ -1357,8 +1358,8 @@
   return resource;
 }
 
-void ResourceFetcher::PrintPreloadWarning(Resource* resource,
-                                          Resource::MatchStatus status) {
+void ResourceFetcher::PrintPreloadMismatch(Resource* resource,
+                                           Resource::MatchStatus status) {
   if (!resource->IsLinkPreload())
     return;
 
@@ -1410,6 +1411,9 @@
   console_logger_->AddConsoleMessage(mojom::ConsoleMessageSource::kOther,
                                      mojom::ConsoleMessageLevel::kWarning,
                                      builder.ToString());
+  TRACE_EVENT2("blink,blink.resource", "ResourceFetcher::PrintPreloadMismatch",
+               "url", resource->Url().ElidedString().Utf8(), "MatchStatus",
+               status);
 }
 
 void ResourceFetcher::InsertAsPreloadIfNecessary(Resource* resource,
@@ -1833,6 +1837,8 @@
     console_logger_->AddConsoleMessage(
         mojom::blink::ConsoleMessageSource::kJavaScript,
         mojom::blink::ConsoleMessageLevel::kWarning, message);
+    TRACE_EVENT1("blink,blink.resource", "ResourceFetcher::WarnUnusedPreloads",
+                 "url", resource->Url().ElidedString().Utf8());
   }
 }
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
index 87a8994..695eb2b 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
@@ -353,7 +353,7 @@
                                       ResourceClient*);
 
   Resource* MatchPreload(const FetchParameters& params, ResourceType);
-  void PrintPreloadWarning(Resource*, Resource::MatchStatus);
+  void PrintPreloadMismatch(Resource*, Resource::MatchStatus);
   void InsertAsPreloadIfNecessary(Resource*,
                                   const FetchParameters& params,
                                   ResourceType);
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.cc
index 5bc68178..bd1da47a 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.cc
@@ -27,8 +27,6 @@
       properties_->IsSubframeDeprioritizationEnabled();
   web_bundle_physical_url_ = properties_->WebBundlePhysicalUrl();
   outstanding_throttled_limit_ = properties_->GetOutstandingThrottledLimit();
-  litepage_subresource_redirect_origin_ =
-      properties_->GetLitePageSubresourceRedirectOrigin();
 
   properties_ = nullptr;
 }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h
index 08ac543..b04a973 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h
@@ -96,11 +96,6 @@
   virtual const KURL& WebBundlePhysicalUrl() const = 0;
 
   virtual int GetOutstandingThrottledLimit() const = 0;
-
-  // Returns the LitePage origin the subresources such as images should be
-  // redirected to when the kSubresourceRedirect feature is enabled.
-  virtual scoped_refptr<SecurityOrigin> GetLitePageSubresourceRedirectOrigin()
-      const = 0;
 };
 
 // A delegating ResourceFetcherProperties subclass which can be retained
@@ -172,12 +167,6 @@
                        : outstanding_throttled_limit_;
   }
 
-  scoped_refptr<SecurityOrigin> GetLitePageSubresourceRedirectOrigin()
-      const override {
-    return properties_ ? properties_->GetLitePageSubresourceRedirectOrigin()
-                       : litepage_subresource_redirect_origin_;
-  }
-
  private:
   // |properties_| is null if and only if detached.
   Member<const ResourceFetcherProperties> properties_;
@@ -191,7 +180,6 @@
   bool is_subframe_deprioritization_enabled_ = false;
   KURL web_bundle_physical_url_;
   int outstanding_throttled_limit_ = 0;
-  scoped_refptr<SecurityOrigin> litepage_subresource_redirect_origin_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_response.cc b/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
index 946a69d..e600b24 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
@@ -450,10 +450,6 @@
   resource_load_timing_ = std::move(resource_load_timing);
 }
 
-void ResourceResponse::SetCTPolicyCompliance(CTPolicyCompliance compliance) {
-  ct_policy_compliance_ = compliance;
-}
-
 AtomicString ResourceResponse::ConnectionInfoString() const {
   std::string connection_info_string =
       net::HttpResponseInfo::ConnectionInfoToString(connection_info_);
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_response.h b/third_party/blink/renderer/platform/loader/fetch/resource_response.h
index d892d0cf..00ace06 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_response.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_response.h
@@ -70,12 +70,6 @@
     kHTTPVersion_2_0
   };
 
-  enum CTPolicyCompliance {
-    kCTPolicyComplianceDetailsNotAvailable,
-    kCTPolicyComplies,
-    kCTPolicyDoesNotComply
-  };
-
   ResourceResponse();
   explicit ResourceResponse(const KURL& current_request_url);
   ResourceResponse(const ResourceResponse&);
@@ -196,11 +190,6 @@
     has_major_certificate_errors_ = has_major_certificate_errors;
   }
 
-  CTPolicyCompliance GetCTPolicyCompliance() const {
-    return ct_policy_compliance_;
-  }
-  void SetCTPolicyCompliance(CTPolicyCompliance);
-
   bool IsLegacyTLSVersion() const { return is_legacy_tls_version_; }
   void SetIsLegacyTLSVersion(bool value) { is_legacy_tls_version_ = value; }
 
@@ -448,10 +437,6 @@
   network::mojom::IPAddressSpace address_space_ =
       network::mojom::IPAddressSpace::kUnknown;
 
-  // The Certificate Transparency policy compliance status of the resource.
-  CTPolicyCompliance ct_policy_compliance_ =
-      kCTPolicyComplianceDetailsNotAvailable;
-
   bool was_cached_ : 1;
   bool connection_reused_ : 1;
   bool is_null_ : 1;
diff --git a/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc b/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc
index 4552d0c..5b61304 100644
--- a/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc
@@ -25,8 +25,6 @@
 
 namespace blink {
 
-constexpr size_t kDefaultMaxBufferedBodyBytesPerRequest = 200 * 1000;
-
 class ResponseBodyLoader::DelegatingBytesConsumer final
     : public BytesConsumer,
       public BytesConsumer::Client {
@@ -291,11 +289,7 @@
 class ResponseBodyLoader::Buffer final
     : public GarbageCollected<ResponseBodyLoader::Buffer> {
  public:
-  explicit Buffer(ResponseBodyLoader* owner)
-      : owner_(owner),
-        max_bytes_to_read_(GetLoadingTasksUnfreezableParamAsInt(
-            "max_buffered_bytes",
-            kDefaultMaxBufferedBodyBytesPerRequest)) {}
+  explicit Buffer(ResponseBodyLoader* owner) : owner_(owner) {}
 
   bool IsEmpty() const { return buffered_data_.IsEmpty(); }
 
@@ -309,15 +303,6 @@
     buffered_data_.emplace_back(std::move(new_chunk));
   }
 
-  void AddToPerRequestBytes(size_t available) {
-    total_bytes_read_ += available;
-  }
-
-  // Return false if the data size exceeds |max_bytes_to_read_|.
-  bool IsUnderPerRequestBytesLimit() {
-    return total_bytes_read_ <= max_bytes_to_read_;
-  }
-
   // Dispatches the frontmost chunk in |buffered_data_|. Returns the size of
   // the data that got dispatched.
   size_t DispatchChunk(size_t max_chunk_size) {
@@ -351,7 +336,6 @@
   Deque<Vector<char>> buffered_data_;
   size_t offset_in_current_chunk_ = 0;
   size_t total_bytes_read_ = 0;
-  const size_t max_bytes_to_read_;
 };
 
 ResponseBodyLoader::ResponseBodyLoader(
@@ -473,16 +457,12 @@
     return;
   back_forward_cache_loader_helper_->DidBufferLoadWhileInBackForwardCache(
       num_bytes);
-  body_buffer_->AddToPerRequestBytes(num_bytes);
 }
 
 bool ResponseBodyLoader::CanContinueBufferingWhileInBackForwardCache() {
-  if (!OnlyUsePerProcessBufferLimit() &&
-      !body_buffer_->IsUnderPerRequestBytesLimit()) {
-    return false;
-  }
-  return BackForwardCacheBufferLimitTracker::Get()
-      .IsUnderPerProcessBufferLimit();
+  return OnlyUsePerProcessBufferLimit() &&
+         BackForwardCacheBufferLimitTracker::Get()
+             .IsUnderPerProcessBufferLimit();
 }
 
 void ResponseBodyLoader::Start() {
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/mojo_url_loader_client.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/mojo_url_loader_client.cc
index 8dd4c389..e82bae1 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/mojo_url_loader_client.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/mojo_url_loader_client.cc
@@ -32,7 +32,6 @@
 namespace blink {
 namespace {
 
-constexpr size_t kDefaultMaxBufferedBodyBytesPerRequest = 100 * 1000;
 constexpr base::TimeDelta kGracePeriodToFinishLoadingWhileInBackForwardCache =
     base::Seconds(60);
 
@@ -167,10 +166,7 @@
         writable_(std::move(writable)),
         writable_watcher_(FROM_HERE,
                           mojo::SimpleWatcher::ArmingPolicy::MANUAL,
-                          std::move(task_runner)),
-        max_bytes_drained_(GetLoadingTasksUnfreezableParamAsInt(
-            "max_buffered_bytes",
-            kDefaultMaxBufferedBodyBytesPerRequest)) {
+                          std::move(task_runner)) {
     pipe_drainer_ =
         std::make_unique<mojo::DataPipeDrainer>(this, std::move(readable));
     writable_watcher_.Watch(
@@ -191,15 +187,9 @@
     SCOPED_CRASH_KEY_STRING256("OnDataAvailable", "last_loaded_url",
                                owner_->last_loaded_url().GetString().Utf8());
 
-    total_bytes_drained_ += num_bytes;
-    TRACE_EVENT2("loading", "MojoURLLoaderClient::BodyBuffer::OnDataAvailable",
-                 "total_bytes_drained", static_cast<int>(total_bytes_drained_),
-                 "added_bytes", static_cast<int>(num_bytes));
-
     if (owner_->freeze_mode() == WebLoaderFreezeMode::kBufferIncoming) {
       owner_->DidBufferLoadWhileInBackForwardCache(num_bytes);
-      if (total_bytes_drained_ > max_bytes_drained_ ||
-          !owner_->CanContinueBufferingWhileInBackForwardCache()) {
+      if (!owner_->CanContinueBufferingWhileInBackForwardCache()) {
         owner_->EvictFromBackForwardCache(
             blink::mojom::RendererEvictionReason::kNetworkExceedsBufferLimit);
         return;
@@ -279,8 +269,6 @@
   // memory as soon as we finish sending a chunk completely.
   base::queue<std::vector<char>> buffered_body_;
   uint32_t offset_in_current_chunk_ = 0;
-  size_t total_bytes_drained_ = 0;
-  const size_t max_bytes_drained_;
   bool draining_ = true;
 };
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader.cc
index dcba614ca..65b80704 100644
--- a/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader.cc
@@ -680,7 +680,6 @@
   response->SetExpectedContentLength(head.content_length);
   response->SetHasMajorCertificateErrors(
       net::IsCertStatusError(head.cert_status));
-  response->SetCTPolicyCompliance(head.ct_policy_compliance);
   response->SetIsLegacyTLSVersion(head.is_legacy_tls_version);
   response->SetHasRangeRequested(head.has_range_requested);
   response->SetTimingAllowPassed(head.timing_allow_passed);
diff --git a/third_party/blink/renderer/platform/loader/subresource_integrity.cc b/third_party/blink/renderer/platform/loader/subresource_integrity.cc
index 4dd1b182..4ea2e225 100644
--- a/third_party/blink/renderer/platform/loader/subresource_integrity.cc
+++ b/third_party/blink/renderer/platform/loader/subresource_integrity.cc
@@ -36,15 +36,7 @@
 
 static bool DigestsEqual(const DigestValue& digest1,
                          const DigestValue& digest2) {
-  if (digest1.size() != digest2.size())
-    return false;
-
-  for (wtf_size_t i = 0; i < digest1.size(); i++) {
-    if (digest1[i] != digest2[i])
-      return false;
-  }
-
-  return true;
+  return digest1 == digest2;
 }
 
 void SubresourceIntegrity::ReportInfo::AddUseCount(UseCounterFeature feature) {
diff --git a/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h b/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h
index 7b5efc8..07836ba 100644
--- a/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h
+++ b/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h
@@ -63,11 +63,6 @@
     return IsMainFrame() ? 3 : 2;
   }
 
-  scoped_refptr<SecurityOrigin> GetLitePageSubresourceRedirectOrigin()
-      const override {
-    return nullptr;
-  }
-
   void SetIsMainFrame(bool value) { is_main_frame_ = value; }
   void SetControllerServiceWorkerMode(ControllerServiceWorkerMode mode) {
     service_worker_mode_ = mode;
diff --git a/third_party/blink/renderer/platform/scheduler/DEPS b/third_party/blink/renderer/platform/scheduler/DEPS
index b3df537..ad115ca 100644
--- a/third_party/blink/renderer/platform/scheduler/DEPS
+++ b/third_party/blink/renderer/platform/scheduler/DEPS
@@ -14,7 +14,6 @@
   "+base/command_line.h",
   "+base/compiler_specific.h",
   "+base/containers/circular_deque.h",
-  "+base/containers/small_map.h",
   "+base/feature_list.h",
   "+base/format_macros.h",
   "+base/gtest_prod_util.h",
diff --git a/third_party/blink/renderer/platform/testing/test_paint_artifact.cc b/third_party/blink/renderer/platform/testing/test_paint_artifact.cc
index 19d39eb..3870ebb 100644
--- a/third_party/blink/renderer/platform/testing/test_paint_artifact.cc
+++ b/third_party/blink/renderer/platform/testing/test_paint_artifact.cc
@@ -6,11 +6,11 @@
 
 #include <memory>
 #include "cc/layers/layer.h"
+#include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
 #include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_artifact.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
@@ -90,7 +90,7 @@
   PaintRecorder recorder;
   cc::PaintCanvas* canvas = recorder.beginRecording(gfx::RectToSkRect(bounds));
   if (!bounds.IsEmpty()) {
-    PaintFlags flags;
+    cc::PaintFlags flags;
     flags.setColor(color.Rgb());
     canvas->drawRect(gfx::RectToSkRect(bounds), flags);
   }
diff --git a/third_party/blink/renderer/platform/timer.cc b/third_party/blink/renderer/platform/timer.cc
index d73cfc680..afa42ac 100644
--- a/third_party/blink/renderer/platform/timer.cc
+++ b/third_party/blink/renderer/platform/timer.cc
@@ -117,7 +117,8 @@
     delayed_task_handle_.CancelTask();
 
     delayed_task_handle_ = web_task_runner_->PostCancelableDelayedTask(
-        location_, BindTimerClosure(), delay);
+        base::subtle::PostDelayedTaskPassKey(), location_, BindTimerClosure(),
+        delay);
   }
 }
 
diff --git a/third_party/blink/renderer/platform/wtf/std_lib_extras.h b/third_party/blink/renderer/platform/wtf/std_lib_extras.h
index a2b3edb..cb061bd 100644
--- a/third_party/blink/renderer/platform/wtf/std_lib_extras.h
+++ b/third_party/blink/renderer/platform/wtf/std_lib_extras.h
@@ -28,8 +28,8 @@
 
 #include <cstddef>
 
+#include "base/check.h"
 #include "base/dcheck_is_on.h"
-#include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
 #include "build/build_config.h"
 #include "third_party/blink/renderer/platform/wtf/leak_annotations.h"
diff --git a/third_party/blink/renderer/platform/wtf/vector.h b/third_party/blink/renderer/platform/wtf/vector.h
index ee934dad..cdbee39 100644
--- a/third_party/blink/renderer/platform/wtf/vector.h
+++ b/third_party/blink/renderer/platform/wtf/vector.h
@@ -27,6 +27,7 @@
 #include <iterator>
 #include <utility>
 
+#include "base/compiler_specific.h"
 #include "base/dcheck_is_on.h"
 #include "base/template_util.h"
 #include "build/build_config.h"
@@ -1289,7 +1290,7 @@
 
   // Remove all the elements. This function actually releases the backing
   // buffer, thus any iterators will get invalidated (including begin()).
-  void clear() { ShrinkCapacity(0); }
+  REINITIALIZES_AFTER_MOVE void clear() { ShrinkCapacity(0); }
 
   // Insertion to the back. All of these functions except uncheckedAppend() may
   // cause a reallocation.
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index ea586fc5..442efef9 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -304,6 +304,8 @@
             # Chromium geometry types.
             'gfx::Insets',
             'gfx::InsetsF',
+            'gfx::Outsets',
+            'gfx::OutsetsF',
             'gfx::Point',
             'gfx::PointF',
             'gfx::Point3F',
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_uploader.py b/third_party/blink/tools/blinkpy/w3c/wpt_uploader.py
index a43c7f6..b4554e3 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_uploader.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_uploader.py
@@ -86,7 +86,14 @@
                 ... more artifacts
             ]
         }
-        Returns a list of URLs for wpt report
+        
+        An example of the url as below:
+        https://results.usercontent.cr.dev/invocations/ \
+        task-chromium-swarm.appspot.com-58590ed6228fd611/ \
+        artifacts/wpt_reports_android_webview_01.json? \
+        token=AXsiX2kiOiIxNjQxNzYyNzU0MDkxIiwiX3giOiIzNjAwMDAwIn24WM72ciT_oYJG0hGx6MShOXu8SyVxfB_fw
+        
+        Returns a sorted(based on shard number) list of URLs for wpt report
         """
 
         invocation = "invocations/build-%s" % build_id
@@ -106,6 +113,11 @@
         for artifact in artifacts:
             if artifact.get("artifactId").startswith("wpt_reports"):
                 rv.append(artifact.get("fetchUrl"))
+
+        if len(rv) > 0:
+            pos = rv[0].find("wpt_reports")
+            rv.sort(key=lambda x: x[pos:])
+
         return rv
 
     def fetch_latest_complete_build(self, project, bucket, builder_name):
@@ -167,9 +179,9 @@
                 return 0
             res = session.post(url=url, files=files)
             if res.status_code == 200:
-                _log.info("Successfully uploaded wpt report with response: " + res.text)
+                _log.info("Successfully uploaded wpt report with response: " + res.text.strip())
                 report_id = res.text.split()[1]
-                _log.info("Report uploaded to https://staging.wpt.fyi/api/runs?run_id=%s" % report_id)
+                _log.info("Report uploaded to https://staging.wpt.fyi/results?run_id=%s" % report_id)
                 return 0
             else:
                 _log.error("Upload wpt report failed with status code: %d", res.status_code)
diff --git a/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py b/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py
index d458c49..141617c 100644
--- a/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py
+++ b/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py
@@ -257,7 +257,7 @@
                 (ResultType.Skip not in exp.results) and
                 (test_expectations.get_expectations(exp.test).is_slow_test or
                     port.is_slow_wpt_test(exp.test))):
-            error = "{}:{} '{}' is a [ Slow ] and [ Timeout ] test.".format(
+            error = "{}:{} '{}' is a [ Slow ] and [ Timeout ] test: you must add [ Skip ] (see crrev.com/c/3381301).".format(
                 host.filesystem.basename(path), exp.lineno, exp.test)
             _log.warning(error)
             rv.append(error)
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 59e79cfa..6934b100 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1728,6 +1728,64 @@
 crbug.com/1068610 external/wpt/css/css-color/xyz-003.html [ Failure ]
 crbug.com/1068610 external/wpt/css/css-color/xyz-004.html [ Failure ]
 crbug.com/1068610 external/wpt/css/css-color/xyz-005.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/color-mix-basic-001.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/color-mix-non-srgb-001.html [ Failure ]
+crbug.com/1068610 virtual/system-color-compute/external/wpt/css/css-color/color-mix-basic-001.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/tagged-images-003.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/tagged-images-004.html [ Failure ]
+crbug.com/1068610 [ Mac11 ] external/wpt/css/css-color/color-resolving-hwb.html [ Failure Skip Timeout ]
+crbug.com/1068610 [ Win10.20h2 ] external/wpt/css/css-color/color-resolving-hwb.html [ Failure Skip Timeout ]
+crbug.com/1068610 external/wpt/css/css-color/color-mix-percents-01.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/color-mix-percents-02.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/display-p3-001.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/display-p3-002.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/display-p3-003.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/display-p3-004.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/display-p3-005.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/display-p3-006.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/color-contrast-001.html [ Failure ]
+crbug.com/1068610 [ Mac11-arm64 ] external/wpt/css/css-color/parsing/relative-color-computed.html [ Crash Failure ]
+crbug.com/1068610 [ Mac11-arm64 ] virtual/system-color-compute/external/wpt/css/css-color/parsing/relative-color-valid.html [ Crash Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklab-001.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklab-002.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklab-003.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklab-004.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklab-005.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklab-006.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklab-007.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklab-008.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklch-001.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklch-002.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklch-003.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklch-004.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklch-005.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklch-006.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklch-007.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/oklch-008.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/srgb-linear-001.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/srgb-linear-002.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/srgb-linear-003.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/srgb-linear-004.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/xyz-d50-001.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/xyz-d50-002.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/xyz-d50-003.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/xyz-d50-005.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/xyz-d65-001.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/xyz-d65-002.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/xyz-d65-003.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/xyz-d65-004.html [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
+crbug.com/1068610 [ Linux ] external/wpt/css/css-color/t422-rgba-a0.6-a.xht [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/t32-opacity-basic-0.6-a.xht [ Failure ]
+crbug.com/1068610 [ Linux ] external/wpt/css/css-color/t425-hsla-basic-a.xht [ Failure ]
+crbug.com/1068610 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/color-function-parsing.html [ Failure ]
+crbug.com/1068610 [ Mac ] external/wpt/css/css-color/color-function-parsing.html [ Failure ]
+crbug.com/1068610 [ Linux ] external/wpt/css/css-color/t422-rgba-onscreen-multiple-boxes-c.xht [ Failure ]
+crbug.com/1068610 [ Linux ] external/wpt/css/css-color/t425-hsla-onscreen-multiple-boxes-c.xht [ Failure ]
+crbug.com/1068610 [ Linux ] external/wpt/css/css-color/t425-hsla-onscreen-b.xht [ Failure ]
+crbug.com/1068610 [ Win ] external/wpt/css/css-color/t425-hsla-onscreen-b.xht [ Failure ]
+crbug.com/1068610 external/wpt/css/css-color/at-color-profile-001.html [ Failure ]
 
 # @supports
 crbug.com/1269580 external/wpt/css/css-conditional/at-supports-namespace-001.html [ Failure ]
@@ -3015,41 +3073,16 @@
 crbug.com/626703 [ Linux ] external/wpt/url/failure.html [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/url/url-constructor.any.html [ Failure ]
 crbug.com/626703 [ Mac11 ] external/wpt/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-006.html [ Failure ]
+crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-flexbox/flexbox_flow-column-wrap.html [ Crash Failure ]
 
 # ====== New tests from wpt-importer added here ======
-crbug.com/626703 external/wpt/css/css-color/at-color-profile-001.html [ Failure ]
+crbug.com/626703 [ Mac11 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/geometry-border-image-002.https.html [ Failure ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/web-share/share-url-invalid.https.html [ Timeout ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/webxr/depth-sensing/cpu/depth_sensing_cpu_staleView.https.html [ Timeout ]
 crbug.com/626703 [ Mac11 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/parse-input-arguments-009.https.html [ Failure ]
 crbug.com/626703 [ Mac10.14 ] virtual/scroll-unification/external/wpt/dom/events/scrolling/overscroll-event-fired-to-document.html [ Timeout ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/at-color-profile-001.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/color-mix-basic-001.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/color-mix-non-srgb-001.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/color-mix-non-srgb-001.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/color-mix-non-srgb-001.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/color-mix-non-srgb-001.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/color-mix-non-srgb-001.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/color-mix-non-srgb-001.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/registered-property-value-002.https.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/color-mix-basic-001.html [ Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/color-mix-non-srgb-001.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/color-mix-non-srgb-001.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/color-mix-non-srgb-001.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/color-mix-non-srgb-001.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/system-color-compute/external/wpt/css/css-color/color-mix-non-srgb-001.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/color-mix-non-srgb-001.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/tagged-images-003.html [ Failure ]
 crbug.com/626703 [ Mac10.14 ] virtual/portals/external/wpt/portals/no-portal-in-sandboxed-popup.html [ Timeout ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/tagged-images-003.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/tagged-images-003.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/tagged-images-003.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/tagged-images-003.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/tagged-images-003.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/tagged-images-003.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/tagged-images-003.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/tagged-images-004.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/tagged-images-003.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/tagged-images-004.html [ Failure ]
+crbug.com/626703 [ Mac11 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/registered-property-value-002.https.html [ Failure ]
 crbug.com/626703 [ Mac10.13 ] virtual/plz-dedicated-worker/external/wpt/resource-timing/object-not-found-adds-entry.html [ Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/css/css-color-adjust/inheritance.html [ Crash Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/fetch/private-network-access/fetch.window.html [ Timeout ]
@@ -3071,17 +3104,12 @@
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/fetch/private-network-access/fetch.https.window.html?include=from-treat-as-public [ Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] virtual/plz-dedicated-worker/external/wpt/fetch/private-network-access/fetch.window.html [ Timeout ]
 crbug.com/626703 [ Mac11 ] virtual/without-coep-for-shared-worker/external/wpt/html/cross-origin-embedder-policy/reporting-subresource-corp.https.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/color-resolving-hwb.html [ Failure Skip Timeout ]
-crbug.com/626703 [ Win10.20h2 ] external/wpt/css/css-color/color-resolving-hwb.html [ Failure Skip Timeout ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/color-resolving-hwb.html [ Failure Skip Timeout ]
 crbug.com/626703 [ Mac10.15 ] external/wpt/html/cross-origin-embedder-policy/reporting-subresource-corp.https.html [ Skip Timeout ]
 crbug.com/626703 external/wpt/css/css-text/hyphens/hyphenate-character-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/hyphens/hyphenate-character-002.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/hyphens/hyphenate-character-003.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/hyphens/hyphenate-character-004.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/content-security-policy/inside-worker/dedicatedworker-script-src.html [ Timeout ]
-crbug.com/626703 external/wpt/css/css-color/color-mix-percents-01.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/color-mix-percents-02.html [ Failure ]
 crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-overflow/scrollbar-gutter-002.html [ Failure ]
 crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-overflow/scrollbar-gutter-002.html [ Failure ]
 crbug.com/626703 [ Mac11 ] external/wpt/css/css-overflow/scrollbar-gutter-002.html [ Failure ]
@@ -3097,32 +3125,12 @@
 crbug.com/626703 [ Mac11-arm64 ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-no-subdomain-child2-yes-subdomainport.sub.https.html [ Crash Failure ]
 crbug.com/626703 [ Mac11-arm64 ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-no-child1-yes-subdomain-child2-no-subdomain.sub.https.html [ Crash Failure ]
 crbug.com/626703 [ Mac11-arm64 ] virtual/plz-dedicated-worker/external/wpt/content-security-policy/inside-worker/serviceworker-report-only.https.sub.html [ Timeout ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/color-mix-percents-01.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/color-mix-percents-02.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/CSS2/normal-flow/width-081.xht [ Crash Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-contain/contain-layout-ignored-cases-no-principal-box-002.html [ Crash Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-writing-modes/margin-collapse-vrl-014.xht [ Crash Failure ]
 crbug.com/626703 [ Mac11-arm64 ] virtual/not-site-per-process/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/1-iframe/parent-no-child-yes-port.sub.https.html [ Crash Failure ]
 crbug.com/626703 [ Mac11 ] virtual/threaded/external/wpt/scroll-animations/css/at-scroll-timeline-paused-animations.html [ Crash Failure ]
 crbug.com/626703 [ Mac11-arm64 ] virtual/threaded/external/wpt/scroll-animations/css/at-scroll-timeline-responsiveness-from-endpoint.html [ Crash Failure ]
-crbug.com/626703 external/wpt/css/css-color/display-p3-001.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/display-p3-002.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/display-p3-003.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/display-p3-004.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/display-p3-005.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/display-p3-006.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/display-p3-001.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/display-p3-002.html [ Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/display-p3-003.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/display-p3-003.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/display-p3-003.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/display-p3-003.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/display-p3-003.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/display-p3-003.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/display-p3-003.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/display-p3-004.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/display-p3-005.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/display-p3-006.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/fetch/api/basic/status.h2.any.worker.html [ Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/infrastructure/server/http2-websocket.sub.h2.any.worker.html [ Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/Close-1000-reason.any.worker.html?wpt_flags=h2 [ Timeout ]
@@ -3137,22 +3145,7 @@
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/constructor/022.html?wpt_flags=h2 [ Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/interfaces/WebSocket/events/018.html?wpt_flags=h2 [ Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/websockets/unload-a-document/005.html?wpt_flags=h2 [ Skip Timeout ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/xyz-d65-004.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-007.html [ Crash Failure ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/system-color-compute/external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/color-mix-non-srgb-001.html [ Crash Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/color-mix-non-srgb-001.html [ Crash Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/parsing/relative-color-computed.html [ Crash Failure ]
 crbug.com/626703 [ Linux ] external/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-007.html [ Failure ]
 crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-007.html [ Failure ]
 crbug.com/626703 [ Mac10.13 ] external/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-007.html [ Failure ]
@@ -3161,263 +3154,9 @@
 crbug.com/626703 [ Win ] external/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-007.html [ Failure ]
 crbug.com/626703 [ Mac11 ] external/wpt/html/semantics/grouping-content/the-li-element/grouping-li-reftest-007.html [ Crash Failure ]
 crbug.com/626703 [ Mac11 ] external/wpt/web-locks/signal.tentative.https.any.html [ Crash Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/color-contrast-001.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/system-color-compute/external/wpt/css/css-color/parsing/relative-color-valid.html [ Crash Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/oklab-001.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/oklab-006.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/oklab-007.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/oklch-001.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/oklch-003.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/srgb-linear-002.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/xyz-d50-005.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/oklab-001.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/oklab-001.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/oklab-001.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/oklab-001.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/oklab-001.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/oklab-001.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/oklab-001.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/oklab-002.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/oklab-003.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/oklab-004.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/oklab-005.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/oklab-005.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/oklab-005.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/oklab-005.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/oklab-005.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/oklab-005.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/oklab-005.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/oklab-005.html [ Crash Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/oklab-006.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/oklab-006.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/oklab-006.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/oklab-006.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/oklab-006.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/oklab-006.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/oklab-006.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/oklab-007.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/oklab-007.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/oklab-007.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/oklab-007.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/oklab-007.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/oklab-007.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/oklab-007.html [ Crash Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/oklab-008.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/oklab-008.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/oklab-008.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/oklab-008.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/oklab-008.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/oklab-008.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/oklab-008.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/oklab-008.html [ Crash Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/oklch-001.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/oklch-001.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/oklch-001.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/oklch-001.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/oklch-001.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/oklch-001.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/oklch-001.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/oklch-002.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/oklch-003.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/oklch-003.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/oklch-003.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/oklch-003.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/oklch-003.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/oklch-003.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/oklch-003.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/oklch-004.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/oklch-005.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/oklch-006.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/oklch-007.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/oklch-008.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/srgb-linear-001.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/srgb-linear-002.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/srgb-linear-002.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/srgb-linear-002.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/srgb-linear-002.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/srgb-linear-002.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/srgb-linear-002.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/srgb-linear-002.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/srgb-linear-003.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/srgb-linear-004.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/xyz-d50-001.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/xyz-d50-002.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/xyz-d50-002.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/xyz-d50-002.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/xyz-d50-002.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/xyz-d50-002.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/xyz-d50-002.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/xyz-d50-002.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/xyz-d50-002.html [ Crash Failure ]
-crbug.com/626703 external/wpt/css/css-color/xyz-d50-003.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/xyz-d50-005.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/xyz-d50-005.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/xyz-d50-005.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/xyz-d50-005.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/xyz-d50-005.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/xyz-d50-005.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/xyz-d50-005.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/xyz-d65-001.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/xyz-d65-002.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/xyz-d65-002.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/xyz-d65-002.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/xyz-d65-002.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/xyz-d65-002.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/xyz-d65-002.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/xyz-d65-002.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-color/xyz-d65-002.html [ Crash Failure ]
-crbug.com/626703 external/wpt/css/css-color/xyz-d65-003.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/xyz-d65-004.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/xyz-d65-004.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/xyz-d65-004.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/xyz-d65-004.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/xyz-d65-004.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/xyz-d65-004.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/xyz-d65-004.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-masking/mask-image/mask-mode-b.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-masking/mask-image/mask-repeat-1.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] virtual/background-svg-in-lcp/external/wpt/largest-contentful-paint/toJSON.html [ Crash ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/oklab-001.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/oklab-002.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/oklab-003.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/oklab-004.html [ Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/oklab-005.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/oklab-005.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/oklab-005.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/oklab-005.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/oklab-005.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/oklab-005.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/oklab-005.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/oklab-006.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/oklab-007.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/oklab-008.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/oklch-001.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/oklch-002.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/oklch-003.html [ Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/oklch-004.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-004.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-004.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-004.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-004.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-004.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/oklch-004.html [ Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/oklch-005.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-005.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-005.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-005.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-005.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-005.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/oklch-005.html [ Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/oklch-006.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-006.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-006.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-006.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-006.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-006.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/oklch-006.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-006.html [ Crash Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/oklch-007.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-007.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-007.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-007.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-007.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/oklch-007.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/oklch-007.html [ Crash Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/oklch-008.html [ Crash Failure ]
-crbug.com/626703 [ Mac ] virtual/system-color-compute/external/wpt/css/css-color/oklch-008.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/oklch-008.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-001.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-002.html [ Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-003.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-003.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-003.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-003.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-003.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-003.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-003.html [ Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-004.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-004.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-004.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-004.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-004.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-004.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/srgb-linear-004.html [ Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-001.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-001.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-001.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-001.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-001.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-001.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-001.html [ Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-002.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-002.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-002.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-002.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-002.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-002.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-002.html [ Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-003.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-003.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-003.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-003.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-003.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-003.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-003.html [ Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-004.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/xyz-d50-005.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-001.html [ Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-002.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-002.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-002.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-002.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-002.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-002.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-002.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-002.html [ Crash Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-003.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-003.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-003.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-003.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-003.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-003.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-003.html [ Failure ]
-crbug.com/626703 virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-004.html [ Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
-crbug.com/626703 [ Mac10.13 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
-crbug.com/626703 [ Mac10.14 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
-crbug.com/626703 [ Mac10.15 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
-crbug.com/626703 [ Win ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-005.html [ Failure ]
-crbug.com/626703 [ Mac11 ] virtual/system-color-compute/external/wpt/css/css-color/xyz-d65-005.html [ Crash Failure ]
-crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/css-flexbox/flexbox_flow-column-wrap.html [ Crash Failure ]
 crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-borderBox-1a.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-masking/clip-path/clip-path-borderBox-1b.html [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/css/css-masking/clip-path/clip-path-borderBox-1c.html [ Failure ]
@@ -3669,7 +3408,6 @@
 crbug.com/626703 [ Mac11-arm64 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-onsignalingstatechanged.https.html [ Timeout ]
 crbug.com/626703 [ Mac10.15 ] external/wpt/streams/piping/abort.any.html [ Failure Timeout ]
 crbug.com/626703 [ Mac10.15 ] external/wpt/streams/piping/abort.any.sharedworker.html [ Crash Failure ]
-crbug.com/626703 [ Mac11-arm64 ] virtual/consume-code-cache-off-thread/external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error.html [ Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] virtual/css-calc-infinity-and-nan-disabled/external/wpt/css/css-values/ch-unit-011.html [ Failure ]
 crbug.com/626703 [ Win10.20h2 ] external/wpt/html/cross-origin-embedder-policy/cross-origin-isolated-permission.https.html [ Timeout ]
 crbug.com/626703 [ Win10.20h2 ] virtual/without-coep-for-shared-worker/external/wpt/html/cross-origin-embedder-policy/credentialless/cache-storage.tentative.https.window.html?service_worker [ Skip Timeout ]
@@ -4262,9 +4000,6 @@
 crbug.com/626703 external/wpt/fetch/connection-pool/network-partition-key.html [ Failure Skip Timeout ]
 crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-backspace.tentative.html [ Failure Skip Timeout ]
 crbug.com/626703 external/wpt/input-events/input-events-get-target-ranges-forwarddelete.tentative.html [ Failure Skip Timeout ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/t422-rgba-a0.6-a.xht [ Failure ]
-crbug.com/626703 external/wpt/css/css-color/t32-opacity-basic-0.6-a.xht [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/t425-hsla-basic-a.xht [ Failure ]
 crbug.com/626703 external/wpt/wasm/jsapi/functions/incumbent.html [ Crash ]
 crbug.com/626703 [ Mac ] external/wpt/dom/events/scrolling/scrollend-event-for-user-scroll.html [ Failure Timeout ]
 crbug.com/626703 [ Win ] external/wpt/dom/events/scrolling/scrollend-event-for-user-scroll.html [ Failure Timeout ]
@@ -4385,8 +4120,6 @@
 crbug.com/626703 [ Linux ] external/wpt/websockets/stream/tentative/constructor.any.html?wpt_flags=h2 [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/websockets/stream/tentative/constructor.any.sharedworker.html?wpt_flags=h2 [ Failure ]
 crbug.com/626703 [ Linux ] virtual/plz-dedicated-worker/external/wpt/resource-timing/cross-origin-start-end-time-with-redirects.html [ Failure ]
-crbug.com/626703 [ Linux ] virtual/system-color-compute/external/wpt/css/css-color/color-function-parsing.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-color/color-function-parsing.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/resource-timing/cross-origin-start-end-time-with-redirects.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/websockets/Create-blocked-port.any.worker.html?wpt_flags=h2 [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/resource-timing/cross-origin-start-end-time-with-redirects.html [ Failure ]
@@ -4532,10 +4265,6 @@
 crbug.com/626703 external/wpt/css/css-tables/fixup-dynamic-anonymous-inline-table-002.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-tables/fixup-dynamic-anonymous-table-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-tables/fixup-dynamic-anonymous-inline-table-001.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/t422-rgba-onscreen-multiple-boxes-c.xht [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/t425-hsla-onscreen-multiple-boxes-c.xht [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-color/t425-hsla-onscreen-b.xht [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-color/t425-hsla-onscreen-b.xht [ Failure ]
 crbug.com/626703 external/wpt/acid/acid2/reftest.html [ Failure Pass ]
 crbug.com/626703 external/wpt/acid/acid3/test.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-ui/cursor-auto-006.html [ Skip ]
@@ -7904,17 +7633,26 @@
 crbug.com/1280736 [ Win10.20h2 ] http/tests/inspector-protocol/network/blocked-setcookie-same-site-strict.js [ Pass Timeout ]
 crbug.com/1280736 [ Win10.20h2 ] http/tests/inspector-protocol/network/blocked-setcookie-same-site-lax.js [ Pass Timeout ]
 
+# crbug.com/1285304 issues (Note - because color-scheme is in SlowTests, this must be Skip):
+crbug.com/1285304 fast/forms/color-scheme/suggestion-picker/time-suggestion-picker-appearance.html [ Skip Pass Failure Timeout ]
+
 # Sheriff 2022-01-04
 crbug.com/1283865 external/wpt/webmessaging/without-ports/020.html [ Failure Pass ]
 
 # Sheriff 2022-01-05
 crbug.com/1284572 [ Linux ] virtual/threaded/http/tests/devtools/isolated-code-cache/same-origin-module-test.js [ Failure Pass ]
 
+# Incomplete support for mouse wheel in test_driver.
+crbug.com/1285411 external/wpt/css/css-scroll-snap/input/mouse-wheel.html [ Timeout ]
+
 # Sheriff 2022-01-07
 crbug.com/1285348 [ Mac ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/hidpi/canvas-transform.https.html [ Failure Pass ]
 crbug.com/1285350 virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/parse-input-arguments-011.https.html [ Failure Pass ]
-crbug.com/1285426 [ Mac ] virtual/controls-refresh-hc/fast/forms/color-scheme/week-picker/week-picker-appearance-highlight-es.html [ Crash Pass ]
 crbug.com/1285438 fast/layers/clip-rects-transformed.html [ Failure Pass ]
 crbug.com/1285431 [ Mac ] virtual/backface-visibility-interop/compositing/overflow/transform-should-update-absolute-clip-rects.html [ Crash Failure Pass ]
 crbug.com/1285437 virtual/scroll-unification-prefer_compositing_to_lcd_text/fast/scroll-behavior/smooth-scroll/ongoing-smooth-scroll-vertical-rl-anchors.html [ Pass Timeout ]
 crbug.com/1285436 virtual/partitioned-cookies/http/tests/inspector-protocol/network/blocked-setcookie-same-site-lax.js [ Crash Failure Pass Timeout ]
+
+# Sheriff 2022-01-11
+crbug.com/1286437 [ Mac ] media/picture-in-picture/v2/request-picture-in-picture-twice.html [ Failure Pass ]
+crbug.com/1286448 [ Mac ] virtual/without-coep-for-shared-worker/external/wpt/html/cross-origin-embedder-policy/anonymous-iframe/cache-storage.tentative.https.window.html [ Crash Pass ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index c5ea8eb..a72ca0b 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -104,15 +104,6 @@
     "args": ["--enable-features=V8OffThreadFinalization,SmallScriptStreaming,SharedArrayBuffer"]
   },
   {
-    "prefix": "consume-code-cache-off-thread",
-    "bases": [
-      "external/wpt/html/semantics/scripting-1",
-      "external/wpt/workers/modules",
-      "external/wpt/worklets"
-    ],
-    "args": ["--enable-features=ConsumeCodeCacheOffThread"]
-  },
-  {
     "prefix": "exotic-color-space",
     "bases": ["images"],
     "args": ["--force-color-profile=srgb",
@@ -975,6 +966,7 @@
     "prefix": "document-transition",
     "bases": ["wpt_internal/document-transition"],
     "args": ["--enable-blink-features=DocumentTransition",
+             "--enable-features=DocumentTransitionRenderer",
              "--enable-threaded-compositing",
              "--enable-gpu-rasterization"]
   },
diff --git a/third_party/blink/web_tests/WebGPUExpectations b/third_party/blink/web_tests/WebGPUExpectations
index 06cba300..d821a933 100644
--- a/third_party/blink/web_tests/WebGPUExpectations
+++ b/third_party/blink/web_tests/WebGPUExpectations
@@ -70,6 +70,9 @@
 # Dawn implements validation of the limit at createShaderModule time, while the CTS checks at createRenderPipeline time.
 crbug.com/dawn/986 wpt_internal/webgpu/cts.https.html?q=webgpu:api,validation,vertex_state:vertex_shader_input_location_limit:* [ Failure ]
 
+# WebGPU allows copy from webgpu context in CopyExternalImageToTexture(). Disable related cts temporarily.
+crbug.com/1282838 wpt_internal/webgpu/cts.https.html?q=webgpu:api,validation,queue,copyToTexture,CopyExternalImageToTexture:source_canvas,contexts:* [ Failure ]
+
 # These tests aren't working on CQ, unclear whether the test or harness (or Chrome) is broken.
 # Mac: mostly works
 # Linux: Crashes
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 2f3aef32..45523ec7 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -328,6 +328,13 @@
        {}
       ]
      ],
+     "chrome-bug-1285596-crash.html": [
+      "756db89b0e19cc4035ff72c834927deb52f5a54b",
+      [
+       null,
+       {}
+      ]
+     ],
      "flexbox": {
       "fixed-flex-item-inside-abs-flex-in-multicol-crash.html": [
        "29ba56748f9a81615977ded26f4299fa744475f0",
@@ -414,6 +421,20 @@
        {}
       ]
      ],
+     "uncontained-oof-in-inline-after-break-000-crash.html": [
+      "4d301e497749ddf0d2bfca3a5976a9877b45cff8",
+      [
+       null,
+       {}
+      ]
+     ],
+     "uncontained-oof-in-inline-after-break-001-crash.html": [
+      "37b8d3504a78a1918958176d39985706be1ff35f",
+      [
+       null,
+       {}
+      ]
+     ],
      "wide-line-after-floats-crash.html": [
       "c2ced71e197d890c9c12e7cc4b77265d46fe6b38",
       [
@@ -2109,6 +2130,15 @@
      ]
     }
    },
+   "domxpath": {
+    "xpath-evaluate-crash.html": [
+     "d32254936f6c629d856e25052e007c8de1d6f2c5",
+     [
+      null,
+      {}
+     ]
+    ]
+   },
    "editing": {
     "crashtests": {
      "backcolor-in-nested-editing-host-td-from-DOMAttrModified.html": [
@@ -69702,6 +69732,32 @@
         {}
        ]
       ],
+      "mix-blend-mode-plus-lighter-svg.html": [
+       "4762389ca9257440a1297a3e246af0eddd78bf22",
+       [
+        null,
+        [
+         [
+          "/css/compositing/mix-blend-mode/reference/mix-blend-mode-plus-lighter-svg-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "mix-blend-mode-plus-lighter.html": [
+       "ae3dff155dce88166be3bb28ac40e2531720e2f3",
+       [
+        null,
+        [
+         [
+          "/css/compositing/mix-blend-mode/reference/mix-blend-mode-plus-lighter-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "mix-blend-mode-rotated-clip.html": [
        "9e915ac3cca49d127104e43a7fc50fa734eb9da1",
        [
@@ -132937,6 +132993,19 @@
        {}
       ]
      ],
+     "multicol-fill-balance-018.html": [
+      "692071c4834bfa64326823ea365db57200dd6605",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "multicol-fill-balance-nested-000.html": [
       "5e466df8077545b4d6474389d296bc26c5b28b86",
       [
@@ -227008,7 +227077,7 @@
     },
     "support": {
      "Blob.js": [
-      "04069acd3ccbe713ad03f6e0a7d63f3e5a3c81b9",
+      "2c249746858918963f7877d1d26c4a64c90c054b",
       []
      ],
      "document-domain-setter.sub.html": [
@@ -229055,7 +229124,7 @@
        []
       ],
       "accept-ch-test.js": [
-       "e8901dda3313778d5807519f2ec6cad05588bbeb",
+       "e8438829d8850eb9cb7fe777967d80d5517f5e8d",
        []
       ],
       "accept-ch.html": [
@@ -229074,12 +229143,16 @@
        "a0077c0577fc31220a63d0424f3fd4d1ab2de2bc",
        []
       ],
+      "do-expect-received.py": [
+       "d2a544e46a32a21000a843d4148052496a6c75e7",
+       []
+      ],
       "do-not-expect-client-hints-headers.html": [
        "2421eea18ea806698c25afad0b6f6421c914986b",
        []
       ],
       "do-not-expect-received.py": [
-       "821936a808baf3dd439cd7c59b2c1e07e994948c",
+       "48ebc21d14dcd662cd5eb40b2a6e95d30f046ea3",
        []
       ],
       "echo-client-hints-received.py": [
@@ -229110,6 +229183,14 @@
        "27140bf36e4dbd22b8f7190587f42a570c9d12bd",
        []
       ],
+      "meta-name-accept-ch.html": [
+       "525faaae0d5709542f924526efe697424ec545a8",
+       []
+      ],
+      "meta-name-accept-ch.html.headers": [
+       "27140bf36e4dbd22b8f7190587f42a570c9d12bd",
+       []
+      ],
       "no-accept-ch.html": [
        "16ed6c1a7cb1812559108c0369da2eaa63580984",
        []
@@ -229140,6 +229221,10 @@
      "34edb7b82b9657e0beaf9b1669d0854d365770ed",
      []
     ],
+    "meta-name-accept-ch-merge.https.html.headers": [
+     "34edb7b82b9657e0beaf9b1669d0854d365770ed",
+     []
+    ],
     "resources": {
      "accept-ch-different.html": [
       "05cc0b61b07c36fca5816e277df1dc98e70d9ba1",
@@ -239621,6 +239706,14 @@
         "275105c5dbb03cf1b82eb058e856d53b129ecbe9",
         []
        ],
+       "mix-blend-mode-plus-lighter-ref.html": [
+        "e5acd26b6fd498d12f2591d4b04aa3c8f748e153",
+        []
+       ],
+       "mix-blend-mode-plus-lighter-svg-ref.html": [
+        "9193bb574b3f9537a26f5ce0c5430f1685058c63",
+        []
+       ],
        "mix-blend-mode-rotated-clip-ref.html": [
         "377ed7c879fd8ec15ff660da617294bc119206e0",
         []
@@ -255710,7 +255803,7 @@
       ]
      },
      "grid-layout-properties-expected.txt": [
-      "9194f236593bf293e4f8b090da8555ddf9dbfa83",
+      "cc24cf61c496dd541664efef4042c94f76142108",
       []
      ],
      "grid-model": {
@@ -255997,14 +256090,6 @@
       "grid-columns-rows-get-set-multiple-expected.txt": [
        "512bd0d2222370b0c0e1e9fc675213bc03179962",
        []
-      ],
-      "grid-shorthand-valid-expected.txt": [
-       "f6180f77830e1581abd0fe2c2181c6dac35639b9",
-       []
-      ],
-      "grid-template-shorthand-valid-expected.txt": [
-       "ae7aee7ee0c314c42e7cf12fd7ccce46c502ac06",
-       []
       ]
      },
      "reference": {
@@ -269977,18 +270062,10 @@
         "87f94502c0baf64c4f5f8677c9ea581b5ff0cff3",
         []
        ],
-       "grid-expected.txt": [
-        "a3a050d2d494207e6729bd53afe1ae034294c187",
-        []
-       ],
        "grid-gap-expected.txt": [
         "6a86bf7a9a6c0cc6cba9cc96d9eb6519cbe51aeb",
         []
        ],
-       "grid-template-expected.txt": [
-        "10c8a23d58295af9de0e41e2cbafc75a0bd9a76a",
-        []
-       ],
        "image-rendering-expected.txt": [
         "00e659a204d28ece90629c8b9ccdb89957d60f04",
         []
@@ -282392,6 +282469,10 @@
        "000a5cc25bb72b334d41ff05e7b8f22691f48f30",
        []
       ],
+      "fetcher.js": [
+       "3a1859876d406cebe9a2bbc63952d019ddc5ed06",
+       []
+      ],
       "preflight.py": [
        "bc2250456e04dbb35edb0496685d0af698fded6b",
        []
@@ -282404,8 +282485,12 @@
        "bca71ad910cb189c2de6298b4ea59b5594aba637",
        []
       ],
+      "shared-fetcher.js": [
+       "30bde1e0542d144f9b5b0e47824802df13556ee2",
+       []
+      ],
       "shared-worker-fetcher.html": [
-       "8bea2497f9fd2ce3ac850b4a293ff6432ae41a8d",
+       "4af4b1f23959e00c50d48a926f24d97edb8edb90",
        []
       ],
       "socket-opener.html": [
@@ -282413,11 +282498,11 @@
        []
       ],
       "support.sub.js": [
-       "1dba6d535277a14b13cc9c4beed36b8e9e8db850",
+       "9fcba9d8edeaffd71f393097e6859232de12e29d",
        []
       ],
       "worker-fetcher.html": [
-       "9031e6a54c35ac4805c2d2c35dd8032802f43618",
+       "bd155a532bd2d9dd2b5f96383cb8bde27a289856",
        []
       ],
       "xhr-sender.html": [
@@ -282437,17 +282522,33 @@
       "6c834a89c8d481231d211249ff5451aaa9164f5b",
       []
      ],
+     "shared-worker-fetch.https.window-expected.txt": [
+      "31ae173f70264676caf5d4173fd308f9f20acb1b",
+      []
+     ],
+     "shared-worker-fetch.window-expected.txt": [
+      "cd62872e656ae0b85bc3257d8e1b789e087b9935",
+      []
+     ],
      "shared-worker.https.window-expected.txt": [
-      "6181be5586d612fbc003b371088d38a847d2bd9e",
+      "c7cd66da3743edf09e60f658824fea067e4da015",
       []
      ],
      "shared-worker.window-expected.txt": [
-      "5afa07e7133d3497b9b725695c7e6e8e6e2764df",
+      "8718ffde7bc70a2802ec04ecd5c1e3b42e0dca32",
       []
      ],
      "websocket.window-expected.txt": [
       "11e2dcac903cb8fb7f68563036c4b8a4a9c1a9e7",
       []
+     ],
+     "worker-fetch.https.window-expected.txt": [
+      "0b54e1fb1a79866ca2e731de446b21f1d79e3a5d",
+      []
+     ],
+     "worker-fetch.window-expected.txt": [
+      "cd62872e656ae0b85bc3257d8e1b789e087b9935",
+      []
      ]
     },
     "range": {
@@ -288069,7 +288170,7 @@
          []
         ],
         "drawing-text-to-the-canvas.yaml": [
-         "8f08c5ef1c70b6b230c14fe88300ccd7dcaa11db",
+         "846c3f925df58e45fd60a60a6bfccd7673fdc8fe",
          []
         ],
         "fill-and-stroke-styles.yaml": [
@@ -288167,7 +288268,7 @@
          []
         ],
         "text.yaml": [
-         "b65dbe68be73d974496bf43efd82865506f7a72c",
+         "9713626e2f55b1bea4486bd2e5d8ca17801e5e0e",
          []
         ],
         "the-canvas-state.yaml": [
@@ -288228,7 +288329,7 @@
       ],
       "resources": {
        "common.js": [
-        "86de148321d15b38a52cf1a0562de617e1633327",
+        "e11e2307defd6e4bba7006e2add47e7aad965a6f",
         []
        ],
        "serviceworker-partitioning-helper.js": [
@@ -309057,7 +309158,7 @@
      []
     ],
     "testdriver.js": [
-     "74713729ed1fad2a84cf945e7aee72a85d1e9b07",
+     "0737e64a50b313a357a4587c1cb49f7927ac7d42",
      []
     ],
     "testdriver.js.headers": [
@@ -309065,7 +309166,7 @@
      []
     ],
     "testharness.js": [
-     "5ebecca1e18fd92fb5661c1a5c94cfdc67567c26",
+     "ca0a92a89d9950253ed0a5e7b2fe69afba711a3b",
      []
     ],
     "testharness.js.headers": [
@@ -316051,7 +316152,7 @@
        []
       ],
       "accumulation-per-property-002-expected.txt": [
-       "357a3c326e931046ef5cf8a67898fc67f44c2804",
+       "88869a407c1e44d624d4c68d7d12f1fb4e9dad0f",
        []
       ],
       "addition-per-property-001-expected.txt": [
@@ -316059,7 +316160,7 @@
        []
       ],
       "addition-per-property-002-expected.txt": [
-       "0e7f2b0b8eee56f51b02421646784b1ce4ea540c",
+       "4a8ceb771def1ac7e00ca74e554badbceee2c027",
        []
       ],
       "interpolation-per-property-001-expected.txt": [
@@ -316067,11 +316168,11 @@
        []
       ],
       "interpolation-per-property-002-expected.txt": [
-       "74ac012b78c356fe0337d24f7d54e680834e2d7a",
+       "f59cd817733c54a322e18d8ac8fe06f985c2962b",
        []
       ],
       "property-list.js": [
-       "e9b7c524a749f2a8372868a1245224cf0f7d01d5",
+       "4be66a631aba293c3f562b78b2903208a51d6e14",
        []
       ],
       "property-types.js": [
@@ -345156,6 +345257,103 @@
        }
       ]
      ],
+     "meta-name-cross-origin-iframe-not-setting-other-origins.https.html": [
+      "9c263ee789d29184e90257bf07691eb5da22e8c5",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
+     "meta-name-cross-origin-iframe-not-setting-own-origin.https.html": [
+      "1e8d1b6a63102456e1c6baec33753bbd98f61601",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
+     "meta-name-cross-origin-iframe-with-hints.https.sub.html": [
+      "f74e4f4c34bdfd3bae08dad2ee949637b1d1bca9",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
+     "meta-name-cross-origin-iframe-without-hints.https.sub.html": [
+      "bb9ced2c8695e63dd286dbee48b8cba306ad9b5c",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
+     "meta-name-cross-origin-navigation.https.html": [
+      "d5a46ed2d11d330ceb5b26bdfce0b40340286878",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
+     "meta-name-cross-origin-subresource-with-hints.https.sub.html": [
+      "67c0727eb79f09d92821223fbe54c568ea6a8bd6",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
+     "meta-name-cross-origin-subresource-without-hints.https.sub.html": [
+      "a96ec4d794682c7eb74e24f9746789c3a610fe77",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
+     "meta-name-cross-origin-subresource.https.html": [
+      "f29a4bec458f408ffae0067cd4d3b632f23c3a33",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
+     "meta-name-same-origin-iframe.https.html": [
+      "73201072084dd181d97bc66cc302e9ff04335565",
+      [
+       null,
+       {}
+      ]
+     ],
+     "meta-name-same-origin-navigation.https.html": [
+      "6e9dc706fefc1d91ee6b8dbfa89a77b94ebf0044",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
+     "meta-name-same-origin-subresource.https.html": [
+      "98b41674e9f6f0b564b60bed8ec0667a9e0e070e",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
      "same-origin-iframe.https.html": [
       "c7680945443e7477d6c9533cbe8012783a0a91a5",
       [
@@ -345369,6 +345567,34 @@
       {}
      ]
     ],
+    "meta-name-accept-ch-injection.https.html": [
+     "34124f06a6abc2537ebfb9226557dcd2bd470d96",
+     [
+      null,
+      {}
+     ]
+    ],
+    "meta-name-accept-ch-malformed-header.https.html": [
+     "54362de04ce8eb4eb5f2fc9cb61ccf1ec893f092",
+     [
+      null,
+      {}
+     ]
+    ],
+    "meta-name-accept-ch-merge.https.html": [
+     "143a5eb7d35612d4f7838d31a3fbd25c3e3c5c03",
+     [
+      null,
+      {}
+     ]
+    ],
+    "meta-name-accept-ch-non-secure.http.html": [
+     "4b763ead52311a5aa22c096d575613e00c12495c",
+     [
+      null,
+      {}
+     ]
+    ],
     "sec-ch-quotes.https.html": [
      "d772ea8b5c3bb5ae852864fdc22b9a9e53c5bc9e",
      [
@@ -365946,14 +366172,14 @@
        ]
       ],
       "grid-shorthand-invalid.html": [
-       "642a4abab3b8177982c4006e4a5c0477d035305a",
+       "653b51943726f47e6304898f1a11a1d222d44cf4",
        [
         null,
         {}
        ]
       ],
       "grid-shorthand-valid.html": [
-       "80ee50a6e273f1f4ae1a86e6a95fcc5a984d4f10",
+       "75f98da5e4ab425b26972f75f2d92149f06e6cc9",
        [
         null,
         {}
@@ -366100,7 +366326,7 @@
        ]
       ],
       "grid-template-shorthand-valid.html": [
-       "8790627f4a6d13444d9232af6bca5af172149c54",
+       "49bf4a895bdc99cdc97d373192ae0e005ad66c22",
        [
         null,
         {}
@@ -375491,7 +375717,7 @@
     },
     "css-text-decor": {
      "inheritance.html": [
-      "9ee65b4e5926f043b4841bc1f5a7cf84e214ce49",
+      "ff4676fb9398e0f0159d0b93575631fcca81244c",
       [
        null,
        {}
@@ -375618,7 +375844,7 @@
        ]
       ],
       "text-emphasis-position-computed.html": [
-       "f288fdf57edc5cf2e463441415c755e1d99e51ce",
+       "5898d70fbf385b95770cf25ade39ec3c28481202",
        [
         null,
         {}
@@ -415644,6 +415870,42 @@
        }
       ]
      ],
+     "shared-worker-fetch.https.window.js": [
+      "b5789346e391852b633392291b25a2a6a3026a3e",
+      [
+       "fetch/private-network-access/shared-worker-fetch.https.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/support.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
+     "shared-worker-fetch.window.js": [
+      "215cf9b1c70529859dfbddfd033a6b225ea0063e",
+      [
+       "fetch/private-network-access/shared-worker-fetch.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/support.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
      "shared-worker.https.window.js": [
       "91d9186860e9966a44880fc35b92e95b440af1fd",
       [
@@ -415708,6 +415970,42 @@
        }
       ]
      ],
+     "worker-fetch.https.window.js": [
+      "78e51b0f8af848b69a2e6f1b12230bde14b66ebd",
+      [
+       "fetch/private-network-access/worker-fetch.https.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/support.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
+     "worker-fetch.window.js": [
+      "77be8bb98f09bc825b35571a8630723327a8ff1f",
+      [
+       "fetch/private-network-access/worker-fetch.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "resources/support.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
      "worker.https.window.js": [
       "fd38f7cf70eb3909fe95fe68a43e74283be163e4",
       [
@@ -423758,7 +424056,7 @@
         ]
        ],
        "2d.text.drawing.style.spacing.html": [
-        "35f25875e0ea23543d204b288626e159c33832f3",
+        "b5a411e1db0197ec71af54b6d1d77aa0ce31e5eb",
         [
          null,
          {}
@@ -440199,14 +440497,14 @@
         ]
        ],
        "2d.text.drawing.style.spacing.html": [
-        "43e959f67846972cc8e3f6b88cc72bf3fd2f54d9",
+        "ea480c4690e3270c7d30842b545c94708ca9c931",
         [
          null,
          {}
         ]
        ],
        "2d.text.drawing.style.spacing.worker.js": [
-        "6bf7c1393fa4993cc6ff9617036de100ef7a34c3",
+        "a413f9270af45781f5ceccbe782643bf7fd3d4e6",
         [
          "html/canvas/offscreen/text/2d.text.drawing.style.spacing.worker.html",
          {}
@@ -441772,6 +442070,36 @@
         }
        ]
       ],
+      "cache-storage.tentative.https.window.js": [
+       "9c18a7fae391fd883470859d09841b79b20bf935",
+       [
+        "html/cross-origin-embedder-policy/anonymous-iframe/cache-storage.tentative.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/common/get-host-info.sub.js"
+          ],
+          [
+           "script",
+           "/common/utils.js"
+          ],
+          [
+           "script",
+           "/common/dispatcher/dispatcher.js"
+          ],
+          [
+           "script",
+           "../credentialless/resources/common.js"
+          ],
+          [
+           "script",
+           "./resources/common.js"
+          ]
+         ]
+        }
+       ]
+      ],
       "cookie-store.tentative.https.window.js": [
        "60a856c7584681d20d12b72dcd951cd4fd43e2a1",
        [
@@ -454619,7 +454947,7 @@
         ]
        ],
        "inert-label-focus.html": [
-        "ee0c91c412098cb8c86df7ce323efc8b26ac6f01",
+        "05f4069d78f57e5215c2eae8576e0d2c50cb1223",
         [
          null,
          {
diff --git a/third_party/blink/web_tests/external/wpt/FileAPI/support/Blob.js b/third_party/blink/web_tests/external/wpt/FileAPI/support/Blob.js
index 04069ac..2c249746 100644
--- a/third_party/blink/web_tests/external/wpt/FileAPI/support/Blob.js
+++ b/third_party/blink/web_tests/external/wpt/FileAPI/support/Blob.js
@@ -1,6 +1,6 @@
 'use strict'
 
-function test_blob(fn, expectations) {
+self.test_blob = (fn, expectations) => {
   var expected = expectations.expected,
       type = expectations.type,
       desc = expectations.desc;
@@ -24,7 +24,7 @@
   });
 }
 
-function test_blob_binary(fn, expectations) {
+self.test_blob_binary = (fn, expectations) => {
   var expected = expectations.expected,
       type = expectations.type,
       desc = expectations.desc;
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-iframe-not-setting-other-origins.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-iframe-not-setting-other-origins.https.html
new file mode 100644
index 0000000..9c263ee7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-iframe-not-setting-other-origins.https.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<title>Meta-name cross origin iframe not setting other origins</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+run_test({ name: "meta-name cross origin iframe not setting other origins",
+    initial_url: echo,
+    accept_url: host_info.HTTPS_REMOTE_ORIGIN + metaname_accept,
+    expect_url: do_not_expect,
+    type: "iframe" });
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-iframe-not-setting-own-origin.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-iframe-not-setting-own-origin.https.html
new file mode 100644
index 0000000..1e8d1b6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-iframe-not-setting-own-origin.https.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<title>Meta-name cross origin iframe not setting own origin</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+run_test({ name: "meta-name cross origin iframe not setting own origin",
+    initial_url: host_info.HTTPS_REMOTE_ORIGIN + echo,
+    accept_url: host_info.HTTPS_REMOTE_ORIGIN + metaname_accept,
+    expect_url: host_info.HTTPS_REMOTE_ORIGIN + do_not_expect,
+    type: "iframe" });
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-iframe-with-hints.https.sub.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-iframe-with-hints.https.sub.html
new file mode 100644
index 0000000..f74e4f4c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-iframe-with-hints.https.sub.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<meta name="Accept-CH" content="sec-ch-device-memory=( https://www1.{{host}}:{{ports[https][0]}}/ ), device-memory=( https://www1.{{host}}:{{ports[https][0]}}/ )">
+<title>Meta-name cross origin iframe with hints</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+verify_iframe_state(
+  host_info.HTTPS_REMOTE_ORIGIN + "/client-hints/accept-ch-stickiness/resources/do-expect-received.py",
+  "meta-name cross origin iframe with hints");
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-iframe-without-hints.https.sub.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-iframe-without-hints.https.sub.html
new file mode 100644
index 0000000..bb9ced2c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-iframe-without-hints.https.sub.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<meta name="Accept-CH" content="sec-ch-device-memory=( https://{{host}}:{{ports[https][0]}}/ ), device-memory=( https://{{host}}:{{ports[https][0]}}/ )">
+<title>Meta-name cross origin iframe without hints</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+verify_iframe_state(
+  host_info.HTTPS_REMOTE_ORIGIN + "/client-hints/accept-ch-stickiness/resources/do-not-expect-received.py",
+  "meta-name cross origin iframe without hints");
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-navigation.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-navigation.https.html
new file mode 100644
index 0000000..d5a46ed
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-navigation.https.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<title>Meta-name cross origin navigation</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+run_test({ name: "meta-name cross origin navigation",
+    initial_url: echo,
+    accept_url: host_info.HTTPS_REMOTE_ORIGIN + metaname_accept,
+    expect_url: host_info.HTTPS_REMOTE_ORIGIN + do_not_expect,
+    type: "navigation" });
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-subresource-with-hints.https.sub.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-subresource-with-hints.https.sub.html
new file mode 100644
index 0000000..67c0727
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-subresource-with-hints.https.sub.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<meta name="Accept-CH" content="sec-ch-device-memory=( https://www1.{{host}}:{{ports[https][0]}}/ ), device-memory=( https://www1.{{host}}:{{ports[https][0]}}/ )">
+<title>Meta-name cross origin subresource with hints</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+verify_subresource_state(
+  host_info.HTTPS_REMOTE_ORIGIN + "/client-hints/accept-ch-stickiness/resources/do-expect-received.py",
+  "meta-name cross origin subresource with hints");
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-subresource-without-hints.https.sub.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-subresource-without-hints.https.sub.html
new file mode 100644
index 0000000..a96ec4d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-subresource-without-hints.https.sub.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<meta name="Accept-CH" content="sec-ch-device-memory=( https://{{host}}:{{ports[https][0]}}/ ), device-memory=( https://{{host}}:{{ports[https][0]}}/ )">
+<title>Meta-name cross origin subresource without hints</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+verify_subresource_state(
+  host_info.HTTPS_REMOTE_ORIGIN + "/client-hints/accept-ch-stickiness/resources/do-not-expect-received.py",
+  "meta-name cross origin subresource without hints");
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-subresource.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-subresource.https.html
new file mode 100644
index 0000000..f29a4be
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-subresource.https.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<title>Meta-name cross origin subresource</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+run_test({ name: "meta-name cross origin subresource",
+    initial_url: host_info.HTTPS_REMOTE_ORIGIN + echo,
+    accept_url: host_info.HTTPS_REMOTE_ORIGIN + metaname_accept,
+    expect_url: host_info.HTTPS_REMOTE_ORIGIN + do_not_expect,
+    type: "subresource" });
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-same-origin-iframe.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-same-origin-iframe.https.html
new file mode 100644
index 0000000..7320107
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-same-origin-iframe.https.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<!-- <meta name="timeout" content="long"> -->
+<title>Meta-name same origin iframe</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+run_test({ name: "meta-name same origin iframe",
+    initial_url: echo,
+    accept_url: metaname_accept,
+    expect_url: do_not_expect,
+    type: "iframe" });
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-same-origin-navigation.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-same-origin-navigation.https.html
new file mode 100644
index 0000000..6e9dc706
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-same-origin-navigation.https.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<title>Meta-name same origin navigation</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+run_test({ name: "meta-name same origin navigation",
+    initial_url: echo,
+    accept_url: metaname_accept,
+    expect_url: do_not_expect,
+    type: "navigation" });
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-same-origin-subresource.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-same-origin-subresource.https.html
new file mode 100644
index 0000000..98b41674
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/meta-name-same-origin-subresource.https.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<title>Meta-name same origin subresource</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+run_test({ name: "meta-name same origin subresource",
+    initial_url: echo,
+    accept_url: metaname_accept,
+    expect_url: do_not_expect,
+    type: "subresource" });
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-test.js b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-test.js
index e8901dda3..e843882 100644
--- a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-test.js
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-test.js
@@ -3,6 +3,7 @@
 const accept_blank = "/client-hints/accept-ch-stickiness/resources/accept-ch-blank.html";
 const no_accept = "/client-hints/accept-ch-stickiness/resources/no-accept-ch.html";
 const httpequiv_accept = "/client-hints/accept-ch-stickiness/resources/http-equiv-accept-ch.html";
+const metaname_accept = "/client-hints/accept-ch-stickiness/resources/meta-name-accept-ch.html";
 const expect = "/client-hints/accept-ch-stickiness/resources/expect-client-hints-headers.html"
 const do_not_expect = "/client-hints/accept-ch-stickiness/resources/do-not-expect-client-hints-headers.html"
 
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/do-expect-received.py b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/do-expect-received.py
new file mode 100644
index 0000000..d2a544e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/do-expect-received.py
@@ -0,0 +1,20 @@
+def main(request, response):
+    """
+    Check that headers sent to navigate here *do* contain the device-memory client
+    hint, and report success/failure in a way compatible with
+    verify_{subresource|iframe}_state() in accept-ch-test.js
+    """
+
+    if b"device-memory" in request.headers and b"sec-ch-device-memory" in request.headers:
+      result = u"PASS"
+    else:
+      result = u"FAIL"
+
+    content = u'''
+<script>
+  let messagee = window.opener || window.parent;
+  messagee.postMessage("%s" , "*");
+</script>
+''' % (result)
+    headers = [(b"Content-Type", b"text/html"), (b"Access-Control-Allow-Origin", b"*")]
+    return 200, headers, content
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/do-not-expect-received.py b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/do-not-expect-received.py
index 821936a..48ebc21d 100644
--- a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/do-not-expect-received.py
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/do-not-expect-received.py
@@ -2,7 +2,7 @@
     """
     Check that headers sent to navigate here *do not* contain the device-memory client
     hint, and report success/failure in a way compatible with
-    verify_subresource_state() in accept-ch-test.js
+    verify_{subresource|iframe}_state() in accept-ch-test.js
     """
 
     if b"device-memory" in request.headers or b"sec-ch-device-memory" in request.headers:
@@ -12,7 +12,8 @@
 
     content = u'''
 <script>
-  window.opener.postMessage("%s" , "*");
+  let messagee = window.opener || window.parent;
+  messagee.postMessage("%s" , "*");
 </script>
 ''' % (result)
     headers = [(b"Content-Type", b"text/html"), (b"Access-Control-Allow-Origin", b"*")]
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/meta-name-accept-ch.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/meta-name-accept-ch.html
new file mode 100644
index 0000000..525faaa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/meta-name-accept-ch.html
@@ -0,0 +1,8 @@
+<html>
+<meta name="Accept-CH" content="sec-ch-device-memory,device-memory">
+<body>
+<script>
+  window.top.opener.postMessage('Loaded', '*');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/meta-name-accept-ch.html.headers b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/meta-name-accept-ch.html.headers
new file mode 100644
index 0000000..27140bf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/meta-name-accept-ch.html.headers
@@ -0,0 +1,2 @@
+Access-Control-Allow-Origin: *
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/meta-name-accept-ch-injection.https.html b/third_party/blink/web_tests/external/wpt/client-hints/meta-name-accept-ch-injection.https.html
new file mode 100644
index 0000000..34124f0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/meta-name-accept-ch-injection.https.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+  <title>Accept-CH meta-name injection test</title>
+  <meta name="Accept-CH" content="">
+</head>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+// Even though the next line injects an "Accept-CH" meta-name header, the
+// browser should NOT attach the specified client hints in the request headers
+// because javascript injected accept-ch meta-name headers are not trusted.
+document.getElementsByTagName('meta')[0].setAttribute("content", "dpr,sec-ch-dpr,device-memory,sec-ch-device-memory,viewport-width,sec-ch-viewport-width,rtt,downlink,ect");
+document.head.outerHTML += '<meta name="Accept-CH" content="sec-ch-ua-arch,sec-ch-ua-platform,sec-ch-ua-model">';
+document.head.innerHTML += '<meta name="Accept-CH" content="sec-ch-ua-full-version,sec-ch-ua-bitness,sec-ch-ua-full-version-list,sec-ch-ua-platform-version,sec-ch-prefers-color-scheme,sec-ch-viewport-height">';
+
+// resources/echo-client-hints-received.py sets the response headers depending on the set
+// of client hints it receives in the request headers.
+promise_test(t => {
+  return fetch("/client-hints/resources/echo-client-hints-received.py").then(r => {
+    assert_equals(r.status, 200)
+    // Verify that the browser does not include client hints for javascript
+    // injected headers on the XHR.
+    assert_false(r.headers.has("device-memory-received"), "device-memory-received");
+    assert_false(r.headers.has("device-memory-deprecated-received"), "device-memory-deprecated-received");
+    assert_false(r.headers.has("dpr-received"), "dpr-received");
+    assert_false(r.headers.has("dpr-deprecated-received"), "dpr-deprecated-received");
+    assert_false(r.headers.has("viewport-width-received"), "viewport-width-received");
+    assert_false(r.headers.has("viewport-width-deprecated-received"), "viewport-width-deprecated-received");
+    assert_false(r.headers.has("rtt-received"), "rtt-received");
+    assert_false(r.headers.has("downlink-received"), "downlink-received");
+    assert_false(r.headers.has("ect-received"), "ect-received");
+    assert_false(r.headers.has("prefers-color-scheme-received"), "prefers-color-scheme-received");
+  });
+}, "Accept-CH meta-name injection test");
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/meta-name-accept-ch-malformed-header.https.html b/third_party/blink/web_tests/external/wpt/client-hints/meta-name-accept-ch-malformed-header.https.html
new file mode 100644
index 0000000..54362de
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/meta-name-accept-ch-malformed-header.https.html
@@ -0,0 +1,31 @@
+<html>
+<meta name="Accept-CH" content="Sec-CH-DPR Sec-CH-Width">
+<title>Accept-CH malformed meta-name test</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+
+promise_test(t => {
+  return fetch("/client-hints/resources/echo-client-hints-received.py").then(r => {
+    assert_equals(r.status, 200)
+    // Verify that the browser does not include client hints in the headers
+    // since Accept-CH value in meta-name is malformed (includes whitespace
+    // between attributes instead of comma).
+    assert_false(r.headers.has("device-memory-received"), "device-memory-received");
+    assert_false(r.headers.has("device-memory-deprecated-received"), "device-memory-deprecated-received");
+    assert_false(r.headers.has("dpr-received"), "dpr-received");
+    assert_false(r.headers.has("dpr-deprecated-received"), "dpr-deprecated-received");
+    assert_false(r.headers.has("viewport-width-received"), "viewport-width-received");
+    assert_false(r.headers.has("viewport-width-deprecated-received"), "viewport-width-deprecated-received");
+    assert_false(r.headers.has("rtt-received"), "rtt-received");
+    assert_false(r.headers.has("downlink-received"), "downlink-received");
+    assert_false(r.headers.has("ect-received"), "ect-received");
+  });
+}, "Accept-CH malformed meta-name test");
+
+</script>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/meta-name-accept-ch-merge.https.html b/third_party/blink/web_tests/external/wpt/client-hints/meta-name-accept-ch-merge.https.html
new file mode 100644
index 0000000..143a5eb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/meta-name-accept-ch-merge.https.html
@@ -0,0 +1,47 @@
+<html>
+<head>
+<meta name="Accept-CH" content="sec-ch-viewport-width, viewport-width, rtt">
+<meta name="Accept-CH" content="downlink, ect, sec-ch-prefers-color-scheme">
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script>
+
+// Test of merge of meta-name headers on top of accept-ch provided ones.
+//
+// resources/echo-client-hints-received.py sets the response headers depending on the set
+// of client hints it receives in the request headers.
+
+promise_test(t => {
+  return fetch(get_host_info()["HTTPS_ORIGIN"] + "/client-hints/resources/echo-client-hints-received.py").then(r => {
+    assert_equals(r.status, 200)
+    // Verify that the browser includes client hints in the headers.
+    assert_true(r.headers.has("device-memory-received"), "device-memory-received");
+    assert_true(r.headers.has("device-memory-deprecated-received"), "device-memory-deprecated-received");
+    assert_true(r.headers.has("dpr-received"), "dpr-received");
+    assert_true(r.headers.has("dpr-deprecated-received"), "dpr-deprecated-received");
+    assert_true(r.headers.has("viewport-width-received"), "viewport-width-received");
+    assert_true(r.headers.has("viewport-width-deprecated-received"), "viewport-width-deprecated-received");
+
+    assert_true(r.headers.has("rtt-received"), "rtt-received");
+    var rtt = parseInt(r.headers.get("rtt-received"));
+    assert_greater_than_equal(rtt, 0);
+    assert_less_than_equal(rtt, 3000);
+    assert_equals(rtt % 50, 0, 'rtt must be a multiple of 50 msec');
+
+    assert_true(r.headers.has("downlink-received"), "downlink-received");
+    var downlinkKbps  = r.headers.get("downlink-received") * 1000;
+    assert_greater_than_equal(downlinkKbps, 0);
+    assert_less_than_equal(downlinkKbps, 10000);
+
+    assert_in_array(r.headers.get("ect-received"), ["slow-2g", "2g",
+          "3g", "4g"], 'ect-received is unexpected');
+    assert_true(r.headers.has("prefers-color-scheme-received"), "prefers-color-scheme-received");
+  });
+}, "Accept-CH header test");
+
+</script>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/meta-name-accept-ch-merge.https.html.headers b/third_party/blink/web_tests/external/wpt/client-hints/meta-name-accept-ch-merge.https.html.headers
new file mode 100644
index 0000000..34edb7b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/meta-name-accept-ch-merge.https.html.headers
@@ -0,0 +1,2 @@
+Accept-CH: sec-ch-device-memory, device-memory, sec-ch-dpr, dpr
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/meta-name-accept-ch-non-secure.http.html b/third_party/blink/web_tests/external/wpt/client-hints/meta-name-accept-ch-non-secure.http.html
new file mode 100644
index 0000000..4b763ea
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/meta-name-accept-ch-non-secure.http.html
@@ -0,0 +1,39 @@
+<html>
+<meta name="Accept-CH" content="Sec-CH-DPR, DPR, Sec-CH-Width, Width, Sec-CH-Viewport-Width, Viewport-Width, Sec-CH-Device-Memory, Device-Memory, rtt, downlink, ect">
+<title>Accept-CH meta-name insecure transport test</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+
+// Even though this HTML file contains "Accept-CH" meta-name headers, the
+// browser should NOT attach the specified client hints in the HTTP request
+// headers since the page is being fetched over an insecure transport.
+// Test this functionality by fetching an XHR from this page hosted on
+// an insecure HTTP server.
+
+// resources/echo-client-hints-received.py sets the response headers depending on the set
+// of client hints it receives in the request headers.
+
+promise_test(t => {
+  return fetch("/client-hints/resources/echo-client-hints-received.py").then(r => {
+    assert_equals(r.status, 200)
+    // Verify that the browser does not include client hints in the headers
+    // when fetching the XHR from an insecure HTTP server.
+    assert_false(r.headers.has("device-memory-received"), "device-memory-received");
+    assert_false(r.headers.has("device-memory-deprecated-received"), "device-memory-deprecated-received");
+    assert_false(r.headers.has("dpr-received"), "dpr-received");
+    assert_false(r.headers.has("dpr-deprecated-received"), "dpr-deprecated-received");
+    assert_false(r.headers.has("viewport-width-received"), "viewport-width-received");
+    assert_false(r.headers.has("viewport-width-deprecated-received"), "viewport-width-deprecated-received");
+    assert_false(r.headers.has("rtt-received"), "rtt-received");
+    assert_false(r.headers.has("downlink-received"), "downlink-received");
+    assert_false(r.headers.has("ect-received"), "ect-received");
+    assert_false(r.headers.has("prefers-color-scheme-received"), "prefers-color-scheme-received");
+  });
+}, "Accept-CH meta-name test over insecure transport");
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/resources/echo-ua-client-hints-received.py b/third_party/blink/web_tests/external/wpt/client-hints/resources/echo-ua-client-hints-received.py
index 91cc606..ea295ab 100644
--- a/third_party/blink/web_tests/external/wpt/client-hints/resources/echo-ua-client-hints-received.py
+++ b/third_party/blink/web_tests/external/wpt/client-hints/resources/echo-ua-client-hints-received.py
@@ -22,7 +22,7 @@
 
     for header in client_hint_headers:
         if request_client_hints[header] is not None:
-            response.headers.set(header + b"-recieved", request_client_hints[header])
+            response.headers.set(header + b"-received", request_client_hints[header])
 
     headers = []
     content = u""
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/sec-ch-quotes.https.html b/third_party/blink/web_tests/external/wpt/client-hints/sec-ch-quotes.https.html
index d772ea8..6a15b0727 100644
--- a/third_party/blink/web_tests/external/wpt/client-hints/sec-ch-quotes.https.html
+++ b/third_party/blink/web_tests/external/wpt/client-hints/sec-ch-quotes.https.html
@@ -7,7 +7,7 @@
 <script>
 const verify_headers = (header_list, response, verification_func) => {
   header_list.forEach(header => {
-    const value = response.headers.get(header+"-recieved");
+    const value = response.headers.get(header+"-received");
     if(value) {
       verification_func(value);
     }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/chrome-bug-1285596-crash.html b/third_party/blink/web_tests/external/wpt/css/css-break/chrome-bug-1285596-crash.html
new file mode 100644
index 0000000..756db89
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/chrome-bug-1285596-crash.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1285596">
+<div id="elm" style="columns:2;">
+  <div style="display:flex;"></div>
+  <div style="display:grid;"></div>
+  <div style="display:table;"></div>
+</div>
+<div style="display:table-row-group;"></div>
+<script>
+  document.body.offsetTop;
+  elm.style.display = "table-row-group";
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/uncontained-oof-in-inline-after-break-000-crash.html b/third_party/blink/web_tests/external/wpt/css/css-break/uncontained-oof-in-inline-after-break-000-crash.html
new file mode 100644
index 0000000..4d301e4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/uncontained-oof-in-inline-after-break-000-crash.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+  <link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1285743">
+  <div style="columns:2; line-height:20px;">
+    <div id="b" style="height:20px; column-fill:auto;">
+      <br>
+      <span>
+        x
+        <div id="c" style="position:absolute; top:0; left:0; width:10px; column-fill:auto; height:40px;">
+          <br><br><br><br><br>
+        </div>
+      </span>
+    </div>
+  </div>
+  <script>
+    requestAnimationFrame(()=> {
+      requestAnimationFrame(()=> {
+        b.style.columns = "2";
+        c.style.columns = "3";
+        document.documentElement.classList.remove("reftest-wait");
+      });
+    });
+  </script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/uncontained-oof-in-inline-after-break-001-crash.html b/third_party/blink/web_tests/external/wpt/css/css-break/uncontained-oof-in-inline-after-break-001-crash.html
new file mode 100644
index 0000000..37b8d35
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/uncontained-oof-in-inline-after-break-001-crash.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1285743">
+<div style="columns:2; column-fill:auto; height:20px; line-height:20px; orphans:1; widows:1;">
+  <br>
+  <br>
+  <span>
+    <div style="position:absolute;"></div>
+  </span>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-layout-properties-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-layout-properties-expected.txt
index 9194f23..cc24cf61 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-layout-properties-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-layout-properties-expected.txt
@@ -34,10 +34,10 @@
 PASS grid-template
 FAIL grid-template.initial assert_equals: initial value of grid-template should be 50px 50px 50px / 150px expected "50px 50px 50px / 150px" but got "50px 50px 50px / 150px / none"
 FAIL grid-template.none assert_equals: none expected "50px 50px 50px / 150px" but got "50px 50px 50px / 150px / none"
-FAIL grid-template.<grid-template-rows> / <grid-template-columns> assert_equals: <grid-template-rows> / <grid-template-columns> expected "100px 100px / 200px 200px" but got ""
-FAIL grid-template.<line-names> assert_equals: <line-names> expected "[a] auto [b] auto [c] / [d] auto [e] auto [f]" but got ""
-FAIL grid-template.<string>+ assert_equals: <string>+ expected "\"a b\" \"a b\"" but got ""
-FAIL grid-template.<string><track-size>+ assert_equals: <string><track-size>+ expected "100px / \"a b\" 50px" but got ""
+FAIL grid-template.<grid-template-rows> / <grid-template-columns> assert_equals: <grid-template-rows> / <grid-template-columns> expected "100px 100px / 200px 200px" but got "100px 100px / 200px 200px / none"
+FAIL grid-template.<line-names> assert_equals: <line-names> expected "[a] auto [b] auto [c] / [d] auto [e] auto [f]" but got "[a] 50px [b] 50px [c] / [d] 150px [e] 100px [f] / none"
+FAIL grid-template.<string>+ assert_equals: <string>+ expected "\"a b\" \"a b\"" but got "\"b\" \"b\""
+FAIL grid-template.<string><track-size>+ assert_equals: <string><track-size>+ expected "100px / \"a b\" 50px" but got "\"b\" \"b\""
 FAIL grid-template.reset assert_equals: reset expected "50px 50px 50px / 150px" but got "50px 50px 50px / 150px / none"
 PASS grid-auto-columns
 PASS grid-auto-columns.initial
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-shorthand-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-shorthand-invalid.html
index 642a4ab..653b519 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-shorthand-invalid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-shorthand-invalid.html
@@ -42,6 +42,7 @@
 test_invalid_value("grid", 'none / [] "a"');
 test_invalid_value("grid", 'none / "a" []');
 test_invalid_value("grid", 'none / "a" [] 10px');
+test_invalid_value("grid", 'auto-flow 100px');
 
 // FIXME: add more values to test full syntax
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-shorthand-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-shorthand-valid-expected.txt
deleted file mode 100644
index f6180f7..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-shorthand-valid-expected.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-This is a testharness.js-based test.
-FAIL e.style['grid'] = "none" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "none / none" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "auto / auto" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "none / [a] 0px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "none / [] 0px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "[a] 10px / auto" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "[a] 10px / none" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "[] 10px [] / [] auto []" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "[a] \"a\" 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "[a] \"a\" 10px []" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "[] \"a\" 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "[a] \"a\" 10px [a]" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\"" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" auto" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" / 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" / 20%" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" / 5fr" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "[a] \"a\"" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "[a] \"a\" [a]" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "[] \"a\"" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" [] [] \"b\"" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" [] \"b\"" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" [a] [b] \"b\"" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" [a] \"b\"" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" / 0" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" 10px / 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" [a] \"b\" 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" [a] \"b\" 10px [a]" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" [a] [a] \"b\" 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" [a] [] \"b\" 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" 10px [a] \"b\" [a]" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" [a] \"b\" [a]" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "[a] \"a\" [a] \"b\" [a]" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" \"a\" [a] \"b\" [a]" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" [a] \"b\" [a] / 0" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid'] = "\"a\" \"a\" [a] [a] \"b\" / auto" should set the property value assert_not_equals: property should be set got disallowed value ""
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-shorthand-valid.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-shorthand-valid.html
index 80ee50a6..75f98da5 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-shorthand-valid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-shorthand-valid.html
@@ -29,6 +29,7 @@
 test_valid_value("grid", '[a] "a" 10px [a]');
 test_valid_value("grid", '"a"');
 test_valid_value("grid", '"a" auto', '"a"');
+test_valid_value("grid", '"a a a"', '"a a a"');
 test_valid_value("grid", '"a" / 10px');
 test_valid_value("grid", '"a" / 20%');
 test_valid_value("grid", '"a" / 5fr');
@@ -51,6 +52,14 @@
 test_valid_value("grid", '"a" "a" [a] "b" [a]');
 test_valid_value("grid", '"a" [a] "b" [a] / 0', '"a" [a] "b" [a] / 0px');
 test_valid_value("grid", '"a" "a" [a] [a] "b" / auto', '"a" "a" [a a] "b" / auto');
+test_valid_value("grid", '100px / auto-flow dense 100px');
+test_valid_value("grid", 'auto-flow dense 1fr / 100px');
+test_valid_value("grid", '100px / auto-flow 100px');
+test_valid_value("grid", 'auto-flow 1fr / 100px');
+test_valid_value("grid", 'none / auto-flow 100px');
+test_valid_value("grid", 'auto-flow 1fr / none');
+test_valid_value("grid", 'auto / auto-flow 100px');
+test_valid_value("grid", 'auto-flow 1fr / auto');
 
 // FIXME: add more values to test full syntax
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-shorthand-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-shorthand-valid-expected.txt
deleted file mode 100644
index ae7aee7..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-shorthand-valid-expected.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-This is a testharness.js-based test.
-FAIL e.style['grid-template'] = "none" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "none / none" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "auto / auto" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "none / [a] 0px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "none / [] 0px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "[a] 10px / auto" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "[a] 10px / none" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "[] 10px [] / [] auto []" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "[a] \"a\" 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "[a] \"a\" 10px []" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "[] \"a\" 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "[a] \"a\" 10px [a]" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\"" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" auto" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" / 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" / 20%" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" / 5fr" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "[a] \"a\"" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "[a] \"a\" [a]" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "[] \"a\"" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" [] [] \"b\"" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" [] \"b\"" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" [a] [b] \"b\"" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" [a] \"b\"" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" / 0" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" 10px / 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" [a] \"b\" 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" [a] \"b\" 10px [a]" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" [a] [a] \"b\" 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" [a] [] \"b\" 10px" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" 10px [a] \"b\" [a]" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" [a] \"b\" [a]" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "[a] \"a\" [a] \"b\" [a]" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" \"a\" [a] \"b\" [a]" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" [a] \"b\" [a] / 0" should set the property value assert_not_equals: property should be set got disallowed value ""
-FAIL e.style['grid-template'] = "\"a\" \"a\" [a] [a] \"b\" / auto" should set the property value assert_not_equals: property should be set got disallowed value ""
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-shorthand-valid.html b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-shorthand-valid.html
index 8790627..49bf4a8 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-shorthand-valid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/parsing/grid-template-shorthand-valid.html
@@ -28,6 +28,7 @@
 test_valid_value("grid-template", '[a] "a" 10px [a]');
 test_valid_value("grid-template", '"a"');
 test_valid_value("grid-template", '"a" auto', '"a"');
+test_valid_value("grid-template", '"a a a"', '"a a a"');
 test_valid_value("grid-template", '"a" / 10px');
 test_valid_value("grid-template", '"a" / 20%');
 test_valid_value("grid-template", '"a" / 5fr');
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/input/mouse-wheel.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/input/mouse-wheel.html
new file mode 100644
index 0000000..287e594
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/input/mouse-wheel.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" />
+<title>Mouse-wheel scroll snapping speed</title>
+<meta name="assert"
+      content="Test passes if mousewheel snaps without pausing">
+<style>
+  #scroller {
+    scroll-snap-type: block mandatory;
+    overflow: scroll;
+    height:  400px;
+    width: 400px
+  }
+  #space {
+    width: 200px;
+    height: 4000px;
+  }
+  .box {
+    scroll-snap-align: start;
+    background: blue;
+    margin-bottom:  10px;
+    width: 100px;
+    height: 100px;
+  }
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="../support/common.js"></script>
+<div id="scroller">
+  <div class="box"></div>
+  <div class="box"></div>
+  <div id="space"></div>
+</div>
+<script>
+promise_test(async t => {
+  const scroller = document.getElementById("scroller");
+  scroller.scrollTo(0, 0);
+  assert_equals(scroller.scrollTop, 0, "verify test pre-condition");
+  const scrollTop = () => {
+    return scroller.scrollTop;
+  };
+  const scrollPromise = waitForScrollEvent(scroller);
+  const wheelPromise = waitForWheelEvent(scroller);
+  const actions = new test_driver.Actions()
+       .scroll(50, 50, 0, 50, {origin: scroller, duration: 100});
+  await actions.send();
+  await wheelPromise;
+  await scrollPromise;
+  let scrollEndTime;
+  let snapEndTime;
+  // Detect first pause in scrolling.
+  const scrollStabilizedPromise =
+      waitForAnimationEnd(scrollTop).then((timestamp) => {
+        scrollEndTime = timestamp;
+      });
+  const snapEndPromise =
+      waitForScrollTo(scroller, () => {
+        return scroller.scrollTop;
+      }, 110).then((evt) => {
+    snapEndTime = evt.timestamp;
+  });
+  await Promise.all([scrollStabilizedPromise, snapEndPromise]);
+  assert_equals(scroller.scrollTop, 110,
+                'Failed to advance to next snap target');
+  assert_true(snapEndTime < scrollEndTime,
+              'Detected pause in scrolling before reaching snap target');
+}, "Wheel-scroll triggers snap to target position immediately.");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/support/common.js b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/support/common.js
index 7b91100..f83122e8 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/support/common.js
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/support/common.js
@@ -41,7 +41,7 @@
     // MAX_UNCHANGED_FRAMES with no change have been observed.
       if (time - start_time > TIMEOUT ||
           frames - last_changed_frame >= MAX_UNCHANGED_FRAMES) {
-        resolve();
+        resolve(time);
       } else {
         current_value = getValue();
         if (last_value != current_value) {
@@ -55,22 +55,38 @@
   });
 }
 
-function waitForScrollEvent(eventTarget) {
+
+function waitForEvent(eventTarget, type) {
   return new Promise((resolve, reject) => {
-    const scrollListener = () => {
-      eventTarget.removeEventListener('scroll', scrollListener);
-      resolve();
+    const eventListener = (evt) => {
+      eventTarget.removeEventListener('scroll', eventListener);
+      resolve(evt);
     };
-    eventTarget.addEventListener('scroll', scrollListener);
+    eventTarget.addEventListener(type, eventListener);
   });
 }
 
+function waitForScrollEvent(eventTarget) {
+  return waitForEvent(eventTarget, 'scroll');
+}
+
+function waitForWheelEvent(eventTarget) {
+  return waitForEvent(eventTarget, 'wheel');
+}
+
+// TODO: Update tests to replace call to this method with calls to
+// waitForScrollTo, since this method does not test that scrolling has in fact
+// stopped.
 function waitForScrollEnd(eventTarget, getValue, targetValue) {
+  return waitForScrollTo(eventTarget, getValue, targetValue);
+}
+
+function waitForScrollTo(eventTarget, getValue, targetValue) {
   return new Promise((resolve, reject) => {
-    const scrollListener = () => {
+    const scrollListener = (evt) => {
       if (getValue() == targetValue) {
         eventTarget.removeEventListener('scroll', scrollListener);
-        resolve();
+        resolve(evt);
       }
     };
     if (getValue() == targetValue)
@@ -79,4 +95,3 @@
       eventTarget.addEventListener('scroll', scrollListener);
   });
 }
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/inheritance.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/inheritance.html
index 9ee65b4..ff4676f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text-decor/inheritance.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/inheritance.html
@@ -25,7 +25,7 @@
 assert_not_inherited('text-decoration-line', 'none', 'line-through');
 assert_not_inherited('text-decoration-style', 'solid', 'dashed');
 assert_inherited('text-emphasis-color', 'rgba(2, 3, 4, 0.5)', 'rgba(42, 53, 64, 0.75)');
-assert_inherited('text-emphasis-position', 'over right', 'under left');
+assert_inherited('text-emphasis-position', 'over', 'under left');
 assert_inherited('text-emphasis-style', 'none', 'triangle');
 assert_inherited('text-shadow', 'none', 'rgba(42, 53, 64, 0.75) 10px 20px 0px');
 assert_inherited('text-underline-position', 'auto', 'under');
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-emphasis-position-computed.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-emphasis-position-computed.html
index f288fdf..5898d70f 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-emphasis-position-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-emphasis-position-computed.html
@@ -8,8 +8,8 @@
 // Computed value: specified keyword(s)
 test_computed_value('text-emphasis-position', 'over');
 test_computed_value('text-emphasis-position', 'under');
-test_computed_value('text-emphasis-position', 'over right');
+test_computed_value('text-emphasis-position', 'over right', 'over');
 test_computed_value('text-emphasis-position', 'over left');
-test_computed_value('text-emphasis-position', 'under right');
+test_computed_value('text-emphasis-position', 'under right', 'under');
 test_computed_value('text-emphasis-position', 'under left');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/grid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/grid-expected.txt
deleted file mode 100644
index a3a050d2..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/grid-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL 'grid' does not supported 'auto-flow / 1fr 1fr 1fr' assert_not_equals: Unsupported value must not be null got disallowed value null
-FAIL 'grid' does not supported 'auto-flow dense / 40px 40px 1fr' assert_not_equals: Unsupported value must not be null got disallowed value null
-FAIL 'grid' does not supported 'repeat(3, 80px) / auto-flow' assert_not_equals: Unsupported value must not be null got disallowed value null
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/grid-template-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/grid-template-expected.txt
deleted file mode 100644
index 10c8a23..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/grid-template-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-FAIL 'grid-template' does not supported 'none' assert_not_equals: Unsupported value must not be null got disallowed value null
-FAIL 'grid-template' does not supported '100px 1fr / 50px 1fr' assert_not_equals: Unsupported value must not be null got disallowed value null
-FAIL 'grid-template' does not supported '[linename] 100px / [columnname1] 30% [columname2] 70%' assert_not_equals: Unsupported value must not be null got disallowed value null
-FAIL 'grid-template' does not supported 'fit-content(100px) / fit-content(40%)' assert_not_equals: Unsupported value must not be null got disallowed value null
-FAIL 'grid-template' does not supported '"a a a" "b b b"' assert_not_equals: Unsupported value must not be null got disallowed value null
-FAIL 'grid-template' does not supported '[header-top] "a a a" [header-bottom] [main-top] "b b b" 1fr [main-bottom] / auto 1fr auto' assert_not_equals: Unsupported value must not be null got disallowed value null
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/fetch/metadata/audio-worklet.https.html b/third_party/blink/web_tests/external/wpt/fetch/metadata/audio-worklet.https.html
new file mode 100644
index 0000000..3b768ef
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/metadata/audio-worklet.https.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/common/utils.js></script>
+<script>
+
+  promise_test(async t => {
+    const nonce = token();
+    const key = "worklet-destination" + nonce;
+    const context = new AudioContext();
+
+    await context.audioWorklet.addModule("/fetch/metadata/resources/record-header.py?file=" + key);
+    const expected = {"site": "same-origin", "user": "", "mode": "cors", "dest": "audioworklet"};
+    await validate_expectations(key, expected);
+  }, "The fetch metadata for audio worklet");
+
+</script>
+<body></body>
diff --git a/third_party/blink/web_tests/external/wpt/fetch/metadata/paint-worklet.https.html b/third_party/blink/web_tests/external/wpt/fetch/metadata/paint-worklet.https.html
new file mode 100644
index 0000000..49fc776
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/metadata/paint-worklet.https.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/fetch/metadata/resources/helper.js></script>
+<script src=/common/utils.js></script>
+<script>
+
+  promise_test(async t => {
+    const nonce = token();
+    const key = "worklet-destination" + nonce;
+
+    await CSS.paintWorklet.addModule("/fetch/metadata/resources/record-header.py?file=" + key);
+    const expected = {"site": "same-origin", "user": "", "mode": "cors", "dest": "paintworklet"};
+    await validate_expectations(key, expected);
+  }, "The fetch metadata for paint worklet");
+
+</script>
+<body></body>
diff --git a/third_party/blink/web_tests/external/wpt/fetch/metadata/resources/record-header.py b/third_party/blink/web_tests/external/wpt/fetch/metadata/resources/record-header.py
index a966603..f2e7273 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/metadata/resources/record-header.py
+++ b/third_party/blink/web_tests/external/wpt/fetch/metadata/resources/record-header.py
@@ -122,6 +122,11 @@
       response.headers.set(b"Content-Type", b"application/javascript")
       return b"self.postMessage('loaded');"
 
+    ## Return a valid worklet
+    if key.startswith(b"worklet"):
+      response.headers.set(b"Content-Type", b"application/javascript")
+      return b""
+
     ## Return a valid XSLT
     if key.startswith(b"xslt"):
       response.headers.set(b"Content-Type", b"text/xsl")
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/fetcher.js b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/fetcher.js
new file mode 100644
index 0000000..3a18598
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/fetcher.js
@@ -0,0 +1,20 @@
+async function doFetch(url) {
+  const response = await fetch(url);
+  const body = await response.text();
+  return {
+    status: response.status,
+    body,
+  };
+}
+
+async function fetchAndPost(url) {
+  try {
+    const message = await doFetch(url);
+    self.postMessage(message);
+  } catch(e) {
+    self.postMessage({ error: e.name });
+  }
+}
+
+const url = new URL(self.location.href).searchParams.get("url");
+fetchAndPost(url);
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/shared-fetcher.js b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/shared-fetcher.js
new file mode 100644
index 0000000..30bde1e0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/shared-fetcher.js
@@ -0,0 +1,23 @@
+async function doFetch(url) {
+  const response = await fetch(url);
+  const body = await response.text();
+  return {
+    status: response.status,
+    body,
+  };
+}
+
+async function fetchAndPost(url, port) {
+  try {
+    const message = await doFetch(url);
+    port.postMessage(message);
+  } catch(e) {
+    port.postMessage({ error: e.name });
+  }
+}
+
+const url = new URL(self.location.href).searchParams.get("url");
+
+self.addEventListener("connect", async (evt) => {
+  await fetchAndPost(url, evt.ports[0]);
+});
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/shared-worker-fetcher.html b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/shared-worker-fetcher.html
index 8bea2497..4af4b1f 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/shared-worker-fetcher.html
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/shared-worker-fetcher.html
@@ -8,15 +8,11 @@
     const worker = new SharedWorker(url);
 
     worker.onerror = (evt) => {
-      parent.postMessage({ loaded: false }, "*");
+      parent.postMessage({ error: evt.message || "unknown error" }, "*");
     };
 
     worker.port.addEventListener("message", (evt) => {
-      const message = {
-        loaded: true,
-        message: evt.data,
-      };
-      parent.postMessage(message, "*");
+      parent.postMessage(evt.data, "*");
     });
     worker.port.start();
   });
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/support.sub.js b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/support.sub.js
index 1dba6d5..9fcba9d 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/support.sub.js
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/support.sub.js
@@ -339,8 +339,8 @@
 }
 
 const WorkerScriptTestResult = {
-  FAILURE: { loaded: false },
-  SUCCESS: { loaded: true, message: "success" },
+  SUCCESS: { loaded: true },
+  FAILURE: { error: "unknown error" },
 };
 
 async function workerScriptTest(t, { source, target, expected }) {
@@ -349,7 +349,7 @@
 
   const targetUrl =
       resolveUrl("resources/preflight.py", targetResolveOptions(target));
-  targetUrl.searchParams.append("body", "postMessage('success')")
+  targetUrl.searchParams.append("body", "postMessage({ loaded: true })")
   targetUrl.searchParams.append("mime-type", "application/javascript")
 
   const iframe = await appendIframe(t, document, sourceUrl);
@@ -357,10 +357,10 @@
 
   iframe.contentWindow.postMessage({ url: targetUrl.href }, "*");
 
-  const { loaded, message } = await reply;
+  const { error, loaded } = await reply;
 
+  assert_equals(error, expected.error, "worker error");
   assert_equals(loaded, expected.loaded, "response loaded");
-  assert_equals(message, expected.message, "response message");
 }
 
 async function sharedWorkerScriptTest(t, { source, target, expected }) {
@@ -370,15 +370,63 @@
   const targetUrl =
       resolveUrl("resources/preflight.py", targetResolveOptions(target));
   targetUrl.searchParams.append(
-      "body", "onconnect = (e) => e.ports[0].postMessage('success')")
+      "body", "onconnect = (e) => e.ports[0].postMessage({ loaded: true })")
 
   const iframe = await appendIframe(t, document, sourceUrl);
   const reply = futureMessage();
 
   iframe.contentWindow.postMessage({ url: targetUrl.href }, "*");
 
-  const { loaded, message } = await reply;
+  const { error, loaded } = await reply;
 
+  assert_equals(error, expected.error, "worker error");
   assert_equals(loaded, expected.loaded, "response loaded");
-  assert_equals(message, expected.message, "response message");
+}
+
+// Results that may be expected in tests.
+const WorkerFetchTestResult = {
+  SUCCESS: { status: 200, body: "success" },
+  FAILURE: { error: "TypeError" },
+};
+
+async function workerFetchTest(t, { source, target, expected }) {
+  const targetUrl =
+      resolveUrl("resources/preflight.py", targetResolveOptions(target));
+
+  const sourceUrl =
+      resolveUrl("resources/fetcher.js", sourceResolveOptions(source));
+  sourceUrl.searchParams.append("url", targetUrl.href);
+
+  const fetcherUrl = new URL("worker-fetcher.html", sourceUrl);
+
+  const reply = futureMessage();
+  const iframe = await appendIframe(t, document, fetcherUrl);
+
+  iframe.contentWindow.postMessage({ url: sourceUrl.href }, "*");
+
+  const { error, status, message } = await reply;
+  assert_equals(error, expected.error, "fetch error");
+  assert_equals(status, expected.status, "response status");
+  assert_equals(message, expected.message, "response body");
+}
+
+async function sharedWorkerFetchTest(t, { source, target, expected }) {
+  const targetUrl =
+      resolveUrl("resources/preflight.py", targetResolveOptions(target));
+
+  const sourceUrl =
+      resolveUrl("resources/shared-fetcher.js", sourceResolveOptions(source));
+  sourceUrl.searchParams.append("url", targetUrl.href);
+
+  const fetcherUrl = new URL("shared-worker-fetcher.html", sourceUrl);
+
+  const reply = futureMessage();
+  const iframe = await appendIframe(t, document, fetcherUrl);
+
+  iframe.contentWindow.postMessage({ url: sourceUrl.href }, "*");
+
+  const { error, status, message } = await reply;
+  assert_equals(error, expected.error, "fetch error");
+  assert_equals(status, expected.status, "response status");
+  assert_equals(message, expected.message, "response body");
 }
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/worker-fetcher.html b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/worker-fetcher.html
index 9031e6a5..bd155a5 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/worker-fetcher.html
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/resources/worker-fetcher.html
@@ -8,15 +8,11 @@
     const worker = new Worker(url);
 
     worker.addEventListener("message", (evt) => {
-      const message = {
-        loaded: true,
-        message: evt.data,
-      };
-      parent.postMessage(message, "*");
+      parent.postMessage(evt.data, "*");
     });
 
     worker.addEventListener("error", (evt) => {
-      parent.postMessage({ loaded: false }, "*");
+      parent.postMessage({ error: evt.message || "unknown error" }, "*");
     });
   });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.https.window-expected.txt
new file mode 100644
index 0000000..31ae173
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.https.window-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+PASS local to local: success.
+FAIL private to local: failure. assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined
+FAIL private to local: success. assert_equals: fetch error expected (undefined) undefined but got (string) "TypeError"
+PASS private to private: success.
+FAIL public to local: failed preflight. assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined
+FAIL public to local: success. assert_equals: fetch error expected (undefined) undefined but got (string) "TypeError"
+FAIL public to private: failed preflight. assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined
+FAIL public to private: success. assert_equals: fetch error expected (undefined) undefined but got (string) "TypeError"
+PASS public to public: success.
+FAIL treat-as-public to local: failure. assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined
+PASS treat-as-public to private: failure.
+PASS treat-as-public to public: success.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.https.window.js b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.https.window.js
new file mode 100644
index 0000000..b5789346
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.https.window.js
@@ -0,0 +1,131 @@
+// META: script=/common/utils.js
+// META: script=resources/support.sub.js
+//
+// Spec: https://wicg.github.io/private-network-access/#integration-fetch
+//
+// These tests check that fetches from within `SharedWorker` scripts are subject
+// to Private Network Access checks, just like fetches from within documents.
+//
+// This file covers only those tests that must execute in a secure context.
+// Other tests are defined in: shared-worker-fetch.window.js
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTPS_LOCAL },
+  target: { server: Server.HTTPS_LOCAL },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "local to local: success.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTPS_PRIVATE },
+  target: {
+    server: Server.HTTPS_LOCAL,
+    behavior: { response: ResponseBehavior.allowCrossOrigin() },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "private to local: failure.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTPS_PRIVATE },
+  target: {
+    server: Server.HTTPS_LOCAL,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "private to local: success.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTPS_PRIVATE },
+  target: { server: Server.HTTPS_PRIVATE },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "private to private: success.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTPS_PUBLIC },
+  target: {
+    server: Server.HTTPS_LOCAL,
+    behavior: { response: ResponseBehavior.allowCrossOrigin() },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "public to local: failed preflight.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTPS_PUBLIC },
+  target: {
+    server: Server.HTTPS_LOCAL,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "public to local: success.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTPS_PUBLIC },
+  target: {
+    server: Server.HTTPS_PRIVATE,
+    behavior: { response: ResponseBehavior.allowCrossOrigin() },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "public to private: failed preflight.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTPS_PUBLIC },
+  target: {
+    server: Server.HTTPS_PRIVATE,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "public to private: success.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTPS_PUBLIC },
+  target: { server: Server.HTTPS_PUBLIC },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "public to public: success.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: {
+    server: Server.HTTPS_LOCAL,
+    treatAsPublic: true,
+  },
+  target: {
+    server: Server.HTTPS_LOCAL,
+    behavior: { preflight: PreflightBehavior.success(token()) },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "treat-as-public to local: failure.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: {
+    server: Server.HTTPS_LOCAL,
+    treatAsPublic: true,
+  },
+  target: {
+    server: Server.HTTPS_PRIVATE,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "treat-as-public to private: failure.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: {
+    server: Server.HTTPS_LOCAL,
+    treatAsPublic: true,
+  },
+  target: {
+    server: Server.HTTPS_PUBLIC,
+    behavior: { response: ResponseBehavior.allowCrossOrigin() },
+  },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "treat-as-public to public: success.");
+
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.window-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.window-expected.txt
new file mode 100644
index 0000000..cd62872
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.window-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+PASS local to local: success.
+PASS private to local: failure.
+PASS private to private: success.
+PASS public to local: failure.
+PASS public to private: failure.
+PASS public to public: success.
+FAIL treat-as-public to local: failure. assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined
+PASS treat-as-public to private: failure.
+PASS treat-as-public to public: success.
+PASS private https to local: failure.
+PASS pubiic https to local: failure.
+PASS public https to local: failure.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.window.js b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.window.js
new file mode 100644
index 0000000..215cf9b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker-fetch.window.js
@@ -0,0 +1,142 @@
+// META: script=/common/utils.js
+// META: script=resources/support.sub.js
+//
+// Spec: https://wicg.github.io/private-network-access/#integration-fetch
+//
+// These tests check that fetches from within `SharedWorker` scripts are subject
+// to Private Network Access checks, just like fetches from within documents.
+//
+// This file covers only those tests that must execute in a non-secure context.
+// Other tests are defined in: shared-worker-fetch.https.window.js
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTP_LOCAL },
+  target: { server: Server.HTTP_LOCAL },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "local to local: success.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTP_PRIVATE },
+  target: {
+    server: Server.HTTP_LOCAL,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "private to local: failure.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTP_PRIVATE },
+  target: { server: Server.HTTP_PRIVATE },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "private to private: success.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTP_PUBLIC },
+  target: {
+    server: Server.HTTP_LOCAL,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "public to local: failure.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTP_PUBLIC },
+  target: {
+    server: Server.HTTP_PRIVATE,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "public to private: failure.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTP_PUBLIC },
+  target: { server: Server.HTTP_PUBLIC },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "public to public: success.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: {
+    server: Server.HTTP_LOCAL,
+    treatAsPublic: true,
+  },
+  target: {
+    server: Server.HTTP_LOCAL,
+    behavior: { preflight: PreflightBehavior.success(token()) },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "treat-as-public to local: failure.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: {
+    server: Server.HTTP_LOCAL,
+    treatAsPublic: true,
+  },
+  target: {
+    server: Server.HTTP_PRIVATE,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "treat-as-public to private: failure.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: {
+    server: Server.HTTP_LOCAL,
+    treatAsPublic: true,
+  },
+  target: {
+    server: Server.HTTP_PUBLIC,
+    behavior: { response: ResponseBehavior.allowCrossOrigin() },
+  },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "treat-as-public to public: success.");
+
+// The following tests verify that workers served over HTTPS are not allowed to
+// make private network requests because they are not secure contexts.
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTPS_PRIVATE },
+  target: {
+    server: Server.HTTP_LOCAL,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "private https to local: failure.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTPS_PUBLIC },
+  target: {
+    server: Server.HTTP_LOCAL,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "pubiic https to local: failure.");
+
+promise_test(t => sharedWorkerFetchTest(t, {
+  source: { server: Server.HTTPS_PUBLIC },
+  target: {
+    server: Server.HTTP_LOCAL,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "public https to local: failure.");
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker.https.window-expected.txt
index 6181be5..c7cd66da 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker.https.window-expected.txt
@@ -1,8 +1,8 @@
 This is a testharness.js-based test.
-FAIL treat-as-public to local: failed preflight. assert_equals: response loaded expected false but got true
-FAIL treat-as-public to local: success. assert_equals: response loaded expected true but got false
-FAIL treat-as-public to private: failed preflight. assert_equals: response loaded expected false but got true
-FAIL treat-as-public to private: success. assert_equals: response loaded expected true but got false
+FAIL treat-as-public to local: failed preflight. assert_equals: worker error expected (string) "unknown error" but got (undefined) undefined
+FAIL treat-as-public to local: success. assert_equals: worker error expected (undefined) undefined but got (string) "unknown error"
+FAIL treat-as-public to private: failed preflight. assert_equals: worker error expected (string) "unknown error" but got (undefined) undefined
+FAIL treat-as-public to private: success. assert_equals: worker error expected (undefined) undefined but got (string) "unknown error"
 PASS public to public: success.
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker.window-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker.window-expected.txt
index 5afa07e7..8718ffde 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/shared-worker.window-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
-FAIL treat-as-public to local: failure. assert_equals: response loaded expected false but got true
-FAIL treat-as-public to private: failure. assert_equals: response loaded expected false but got true
+FAIL treat-as-public to local: failure. assert_equals: worker error expected (string) "unknown error" but got (undefined) undefined
+FAIL treat-as-public to private: failure. assert_equals: worker error expected (string) "unknown error" but got (undefined) undefined
 PASS public to public: success.
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/worker-fetch.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/worker-fetch.https.window-expected.txt
new file mode 100644
index 0000000..0b54e1f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/worker-fetch.https.window-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+PASS local to local: success.
+PASS private to local: failure.
+PASS private to local: success.
+PASS private to private: success.
+PASS public to local: failed preflight.
+PASS public to local: success.
+PASS public to private: failed preflight.
+PASS public to private: success.
+PASS public to public: success.
+FAIL treat-as-public to local: failure. assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined
+PASS treat-as-public to private: failure.
+PASS treat-as-public to public: success.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/worker-fetch.https.window.js b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/worker-fetch.https.window.js
new file mode 100644
index 0000000..78e51b0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/worker-fetch.https.window.js
@@ -0,0 +1,130 @@
+// META: script=/common/utils.js
+// META: script=resources/support.sub.js
+//
+// Spec: https://wicg.github.io/private-network-access/#integration-fetch
+//
+// These tests check that fetches from within `Worker` scripts are subject to
+// Private Network Access checks, just like fetches from within documents.
+//
+// This file covers only those tests that must execute in a secure context.
+// Other tests are defined in: worker-fetch.window.js
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTPS_LOCAL },
+  target: { server: Server.HTTPS_LOCAL },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "local to local: success.");
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTPS_PRIVATE },
+  target: {
+    server: Server.HTTPS_LOCAL,
+    behavior: { response: ResponseBehavior.allowCrossOrigin() },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "private to local: failure.");
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTPS_PRIVATE },
+  target: {
+    server: Server.HTTPS_LOCAL,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "private to local: success.");
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTPS_PRIVATE },
+  target: { server: Server.HTTPS_PRIVATE },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "private to private: success.");
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTPS_PUBLIC },
+  target: {
+    server: Server.HTTPS_LOCAL,
+    behavior: { response: ResponseBehavior.allowCrossOrigin() },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "public to local: failed preflight.");
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTPS_PUBLIC },
+  target: {
+    server: Server.HTTPS_LOCAL,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "public to local: success.");
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTPS_PUBLIC },
+  target: {
+    server: Server.HTTPS_PRIVATE,
+    behavior: { response: ResponseBehavior.allowCrossOrigin() },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "public to private: failed preflight.");
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTPS_PUBLIC },
+  target: {
+    server: Server.HTTPS_PRIVATE,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "public to private: success.");
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTPS_PUBLIC },
+  target: { server: Server.HTTPS_PUBLIC },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "public to public: success.");
+
+promise_test(t => workerFetchTest(t, {
+  source: {
+    server: Server.HTTPS_LOCAL,
+    treatAsPublic: true,
+  },
+  target: {
+    server: Server.HTTPS_LOCAL,
+    behavior: { preflight: PreflightBehavior.success(token()) },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "treat-as-public to local: failure.");
+
+promise_test(t => workerFetchTest(t, {
+  source: {
+    server: Server.HTTPS_LOCAL,
+    treatAsPublic: true,
+  },
+  target: {
+    server: Server.HTTPS_PRIVATE,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "treat-as-public to private: failure.");
+
+promise_test(t => workerFetchTest(t, {
+  source: {
+    server: Server.HTTPS_LOCAL,
+    treatAsPublic: true,
+  },
+  target: {
+    server: Server.HTTPS_PUBLIC,
+    behavior: { response: ResponseBehavior.allowCrossOrigin() },
+  },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "treat-as-public to public: success.");
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/worker-fetch.window-expected.txt b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/worker-fetch.window-expected.txt
new file mode 100644
index 0000000..cd62872
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/worker-fetch.window-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+PASS local to local: success.
+PASS private to local: failure.
+PASS private to private: success.
+PASS public to local: failure.
+PASS public to private: failure.
+PASS public to public: success.
+FAIL treat-as-public to local: failure. assert_equals: fetch error expected (string) "TypeError" but got (undefined) undefined
+PASS treat-as-public to private: failure.
+PASS treat-as-public to public: success.
+PASS private https to local: failure.
+PASS pubiic https to local: failure.
+PASS public https to local: failure.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/fetch/private-network-access/worker-fetch.window.js b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/worker-fetch.window.js
new file mode 100644
index 0000000..77be8bb9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/private-network-access/worker-fetch.window.js
@@ -0,0 +1,142 @@
+// META: script=/common/utils.js
+// META: script=resources/support.sub.js
+//
+// Spec: https://wicg.github.io/private-network-access/#integration-fetch
+//
+// These tests check that fetches from within `Worker` scripts are subject to
+// Private Network Access checks, just like fetches from within documents.
+//
+// This file covers only those tests that must execute in a non-secure context.
+// Other tests are defined in: worker-fetch.https.window.js
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTP_LOCAL },
+  target: { server: Server.HTTP_LOCAL },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "local to local: success.");
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTP_PRIVATE },
+  target: {
+    server: Server.HTTP_LOCAL,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "private to local: failure.");
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTP_PRIVATE },
+  target: { server: Server.HTTP_PRIVATE },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "private to private: success.");
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTP_PUBLIC },
+  target: {
+    server: Server.HTTP_LOCAL,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "public to local: failure.");
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTP_PUBLIC },
+  target: {
+    server: Server.HTTP_PRIVATE,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "public to private: failure.");
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTP_PUBLIC },
+  target: { server: Server.HTTP_PUBLIC },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "public to public: success.");
+
+promise_test(t => workerFetchTest(t, {
+  source: {
+    server: Server.HTTP_LOCAL,
+    treatAsPublic: true,
+  },
+  target: {
+    server: Server.HTTP_LOCAL,
+    behavior: { preflight: PreflightBehavior.success(token()) },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "treat-as-public to local: failure.");
+
+promise_test(t => workerFetchTest(t, {
+  source: {
+    server: Server.HTTP_LOCAL,
+    treatAsPublic: true,
+  },
+  target: {
+    server: Server.HTTP_PRIVATE,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "treat-as-public to private: failure.");
+
+promise_test(t => workerFetchTest(t, {
+  source: {
+    server: Server.HTTP_LOCAL,
+    treatAsPublic: true,
+  },
+  target: {
+    server: Server.HTTP_PUBLIC,
+    behavior: { response: ResponseBehavior.allowCrossOrigin() },
+  },
+  expected: WorkerFetchTestResult.SUCCESS,
+}), "treat-as-public to public: success.");
+
+// The following tests verify that workers served over HTTPS are not allowed to
+// make private network requests because they are not secure contexts.
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTPS_PRIVATE },
+  target: {
+    server: Server.HTTP_LOCAL,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "private https to local: failure.");
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTPS_PUBLIC },
+  target: {
+    server: Server.HTTP_LOCAL,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "pubiic https to local: failure.");
+
+promise_test(t => workerFetchTest(t, {
+  source: { server: Server.HTTPS_PUBLIC },
+  target: {
+    server: Server.HTTP_LOCAL,
+    behavior: {
+      preflight: PreflightBehavior.success(token()),
+      response: ResponseBehavior.allowCrossOrigin(),
+    },
+  },
+  expected: WorkerFetchTestResult.FAILURE,
+}), "public https to local: failure.");
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/anonymous-iframe/cache-storage.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/anonymous-iframe/cache-storage.tentative.https.window.js
new file mode 100644
index 0000000..9c18a7f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/anonymous-iframe/cache-storage.tentative.https.window.js
@@ -0,0 +1,61 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=../credentialless/resources/common.js
+// META: script=./resources/common.js
+
+// A script storing a value into the CacheStorage.
+const store_script = (key, value, done) =>  `
+  const cache = await caches.open("v1");
+  const request = new Request("/${key}.txt");
+  const response = new Response("${value}", {
+    headers: { "content-type": "plain/txt" }
+  });
+  await cache.put(request, response.clone());
+  send("${done}", "stored")
+`;
+
+// A script loading a value from the CacheStorage.
+const load_script = (key, done) => `
+  const cache = await caches.open("v1");
+  const request = new Request("/${key}.txt");
+  try {
+    const response = await cache.match(request);
+    const value = await response.text();
+    send("${done}", value);
+  } catch (error) {
+    send("${done}", "not found");
+  }
+`;
+
+promise_test(async test => {
+  const origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+  const key_1 = token();
+  const key_2 = token();
+
+  // 2 actors: An anonymous iframe and a normal one.
+  const iframe_anonymous = newAnonymousIframe(origin);
+  const iframe_normal = newIframe(origin);
+  const response_queue_1 = token();
+  const response_queue_2 = token();
+
+  // 1. Each of them store a value in CacheStorage with different keys.
+  send(iframe_anonymous , store_script(key_1, "value_1", response_queue_1));
+  send(iframe_normal, store_script(key_2, "value_2", response_queue_2));
+  assert_equals(await receive(response_queue_1), "stored");
+  assert_equals(await receive(response_queue_2), "stored");
+
+  // 2. Each of them tries to retrieve the value from opposite side, without
+  //    success.
+  send(iframe_anonymous , load_script(key_2, response_queue_1));
+  send(iframe_normal, load_script(key_1, response_queue_2));
+  assert_equals(await receive(response_queue_1), "not found");
+  assert_equals(await receive(response_queue_2), "not found");
+
+  // 3. Each of them tries to retrieve the value from their side, with success:
+  send(iframe_anonymous , load_script(key_1, response_queue_1));
+  send(iframe_normal, load_script(key_2, response_queue_2));
+  assert_equals(await receive(response_queue_1), "value_1");
+  assert_equals(await receive(response_queue_2), "value_2");
+})
+
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/anonymous-iframe/resources/common.js b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/anonymous-iframe/resources/common.js
index 86de148..e11e2307 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/anonymous-iframe/resources/common.js
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/anonymous-iframe/resources/common.js
@@ -8,3 +8,14 @@
   document.body.appendChild(iframe);
   return sub_document_token;
 };
+
+// Create a normal iframe. The new document will execute any scripts sent
+// toward the token it returns.
+const newIframe = (child_origin) => {
+  const sub_document_token = token();
+  let iframe = document.createElement('iframe');
+  iframe.src = child_origin + executor_path + `&uuid=${sub_document_token}`;
+  iframe.anonymous = false
+  document.body.appendChild(iframe);
+  return sub_document_token;
+};
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-keyboard.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-keyboard.tentative.html
index 4edf9336..9f937d7 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-keyboard.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-keyboard.tentative.html
@@ -15,6 +15,20 @@
   <selectmenu id="selectMenu1">
     <option id="selectMenu1-child0">one</option>
   </selectmenu>
+
+  <selectmenu id="selectMenu2" disabled>
+    <div id="selectMenu2-button0" slot="button" behavior="button" tabindex="0">button</div>
+    <option disabled>one</option>
+    <option>two</option>
+    <option>three</option>
+  </selectmenu>
+
+  <selectmenu id="selectMenu3">
+    <div id="selectMenu3-button0" slot="button" behavior="button" tabindex="0">button</div>
+    <option>one</option>
+    <option disabled>two</option>
+    <option>three</option>
+  </selectmenu>
 <script>
 // See https://w3c.github.io/webdriver/#keyboard-actions
 const KEY_CODE_MAP = {
@@ -72,7 +86,7 @@
 
   await test_driver.send_keys(selectMenu, KEY_CODE_MAP.Enter);
   assert_false(selectMenu.open, "Enter key should close selectmenu");
-}, "Validate Enter, Up/Down Arrow, and Space keyboard accessibility support for <selecmenu>");
+}, "Validate Enter, Up/Down Arrow, and Space keyboard accessibility support for <selectmenu>");
 
 promise_test(async t => {
   const selectMenuOption = document.getElementById("selectMenu1-child0");
@@ -80,4 +94,30 @@
   event.initEvent("keydown");
   selectMenuOption.dispatchEvent(event);
 }, "Firing a synthetic event at a selectmenu's option doesn't crash");
+
+promise_test(async t => {
+  const selectMenu2 = document.querySelector("#selectMenu2");
+  const selectMenu2Button = document.querySelector("#selectMenu2-button0");
+  assert_false(selectMenu2.open, "selectmenu should not be initially open");
+
+  await test_driver.send_keys(selectMenu2Button, KEY_CODE_MAP.Enter);
+  assert_false(selectMenu2.open, "Enter key should not open a disabled selectmenu");
+  await clickOn(selectMenu2);
+  assert_false(selectMenu2.open, "Click should not open a disabled selectmenu");
+  assert_equals(selectMenu2.value, "one");
+
+  const selectMenu3 = document.querySelector("#selectMenu3");
+  const selectMenu3Button = document.querySelector("#selectMenu3-button0");
+  assert_false(selectMenu3.open, "selectmenu should not be initially open");
+
+  await test_driver.send_keys(selectMenu3Button, KEY_CODE_MAP.Enter);
+  assert_true(selectMenu3.open, "Enter key should open selectmenu");
+  assert_equals(selectMenu3.value, "one");
+
+  await test_driver.send_keys(selectMenu3, KEY_CODE_MAP.ArrowDown);
+  assert_equals(selectMenu3.value, "three", "Down arrow should go to next non-disabled option");
+
+  await test_driver.send_keys(selectMenu3, KEY_CODE_MAP.ArrowUp);
+  assert_equals(selectMenu3.value, "one", "Up arrow should go to the previous non-disabled option");
+}, "Validate Enter, Up/Down Arrow keyboard accessibility support for disabled <selectmenu>");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-many-options.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-many-options.tentative.html
index 8de1318e..442722fc 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-many-options.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-many-options.tentative.html
@@ -14,6 +14,15 @@
     top: 0px;
     left: 0px;
   }
+
+  #selectMenu0-popup {
+    border: 1px solid rgba(0, 0, 0, 0.15);
+    border-radius: 4px;
+    box-shadow: 0px 12.8px 28.8px rgba(0, 0, 0, 0.13), 0px 0px 9.2px rgba(0, 0, 0, 0.11);
+    box-sizing: border-box;
+    overflow: auto;
+    padding: 4px;
+  }
 </style>
 
 <selectmenu id="selectMenu0">
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-popup-position-with-zoom.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-popup-position-with-zoom.tentative.html
index caf1bcf4d..55ffbce 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-popup-position-with-zoom.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-popup-position-with-zoom.tentative.html
@@ -98,8 +98,8 @@
     const selectMenu0Button = document.getElementById("selectMenu0-button");
 
     await clickOn(selectMenu0);
-    assert_equals(Math.round(selectMenu0.getBoundingClientRect().bottom), Math.round(selectMenu0Popup.getBoundingClientRect().top));
-    assert_equals(Math.round(selectMenu0.getBoundingClientRect().left), Math.round(selectMenu0Popup.getBoundingClientRect().left));
+    assert_equals(Math.abs(Math.trunc(selectMenu0.getBoundingClientRect().bottom - selectMenu0Popup.getBoundingClientRect().top)), 0);
+    assert_equals(Math.abs(Math.trunc(selectMenu0.getBoundingClientRect().left - selectMenu0Popup.getBoundingClientRect().left)), 0);
   }, "The popup should be bottom left positioned");
 
   promise_test(async () => {
@@ -108,8 +108,8 @@
     const selectMenu1Button = document.getElementById("selectMenu1-button");
 
     selectMenu1Button.click();
-    assert_equals(Math.round(selectMenu1.getBoundingClientRect().top), Math.round(selectMenu1Popup.getBoundingClientRect().bottom * 2));
-    assert_equals(Math.round(selectMenu1.getBoundingClientRect().left), Math.round(selectMenu1Popup.getBoundingClientRect().left * 2));
+    assert_equals(Math.abs(Math.trunc(selectMenu1.getBoundingClientRect().top - selectMenu1Popup.getBoundingClientRect().bottom * 2)), 0);
+    assert_equals(Math.abs(Math.trunc(selectMenu1.getBoundingClientRect().left - selectMenu1Popup.getBoundingClientRect().left * 2)), 0);
   }, "The popup should be top left positioned");
 
   promise_test(async () => {
@@ -118,8 +118,8 @@
     const selectMenu2Button = document.getElementById("selectMenu2-button");
 
     selectMenu2Button.click();
-    assert_equals(Math.round(selectMenu2.getBoundingClientRect().bottom), Math.round(selectMenu2Popup.getBoundingClientRect().top));
-    assert_equals(Math.round(selectMenu2.getBoundingClientRect().right), Math.round(selectMenu2Popup.getBoundingClientRect().right));
+    assert_equals(Math.abs(Math.trunc(selectMenu2.getBoundingClientRect().bottom - selectMenu2Popup.getBoundingClientRect().top)), 0);
+    assert_equals(Math.abs(Math.trunc(selectMenu2.getBoundingClientRect().right - selectMenu2Popup.getBoundingClientRect().right)), 0);
   }, "The popup should be bottom right positioned");
 
   promise_test(async () => {
@@ -128,8 +128,8 @@
     const selectMenu3Button = document.getElementById("selectMenu3-button");
 
     selectMenu3Button.click();
-    assert_equals(Math.round(selectMenu3.getBoundingClientRect().top), Math.round(selectMenu3Popup.getBoundingClientRect().bottom * 1.5));
-    assert_equals(Math.round(selectMenu3.getBoundingClientRect().right), Math.round(selectMenu3Popup.getBoundingClientRect().right * 1.5));
+    assert_equals(Math.abs(Math.trunc(selectMenu3.getBoundingClientRect().top - selectMenu3Popup.getBoundingClientRect().bottom * 1.5)), 0);
+    assert_equals(Math.abs(Math.trunc(selectMenu3.getBoundingClientRect().right - selectMenu3Popup.getBoundingClientRect().right * 1.5)), 0);
   }, "The popup should be top right positioned");
 
 </script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-popup-position.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-popup-position.tentative.html
index 036c739f..559e3c9 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-popup-position.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-selectmenu-element/selectmenu-popup-position.tentative.html
@@ -81,8 +81,8 @@
     const selectMenu0Popup = document.getElementById("selectMenu0-popup");
 
     await clickOn(selectMenu0);
-    assert_equals(Math.round(selectMenu0.getBoundingClientRect().bottom), Math.round(selectMenu0Popup.getBoundingClientRect().top));
-    assert_equals(Math.round(selectMenu0.getBoundingClientRect().left), Math.round(selectMenu0Popup.getBoundingClientRect().left));
+    assert_equals(Math.abs(Math.trunc(selectMenu0.getBoundingClientRect().bottom - selectMenu0Popup.getBoundingClientRect().top)), 0);
+    assert_equals(Math.abs(Math.trunc(selectMenu0.getBoundingClientRect().left - selectMenu0Popup.getBoundingClientRect().left)), 0);
   }, "The popup should be bottom left positioned");
 
   promise_test(async () => {
@@ -90,8 +90,8 @@
     const selectMenu1Popup = document.getElementById("selectMenu1-popup");
 
     await clickOn(selectMenu1);
-    assert_equals(Math.round(selectMenu1.getBoundingClientRect().top), Math.round(selectMenu1Popup.getBoundingClientRect().bottom));
-    assert_equals(Math.round(selectMenu1.getBoundingClientRect().left), Math.round(selectMenu1Popup.getBoundingClientRect().left));
+    assert_equals(Math.abs(Math.trunc(selectMenu1.getBoundingClientRect().top - selectMenu1Popup.getBoundingClientRect().bottom)), 0);
+    assert_equals(Math.abs(Math.trunc(selectMenu1.getBoundingClientRect().left - selectMenu1Popup.getBoundingClientRect().left)), 0);
   }, "The popup should be top left positioned");
 
   promise_test(async () => {
@@ -99,8 +99,8 @@
     const selectMenu2Popup = document.getElementById("selectMenu2-popup");
 
     await clickOn(selectMenu2);
-    assert_equals(Math.round(selectMenu2.getBoundingClientRect().bottom), Math.round(selectMenu2Popup.getBoundingClientRect().top));
-    assert_equals(Math.round(selectMenu2.getBoundingClientRect().right), Math.round(selectMenu2Popup.getBoundingClientRect().right));
+    assert_equals(Math.abs(Math.trunc(selectMenu2.getBoundingClientRect().bottom - selectMenu2Popup.getBoundingClientRect().top)), 0);
+    assert_equals(Math.abs(Math.trunc(selectMenu2.getBoundingClientRect().right - selectMenu2Popup.getBoundingClientRect().right)), 0);
   }, "The popup should be bottom right positioned");
 
   promise_test(async () => {
@@ -108,8 +108,8 @@
     const selectMenu3Popup = document.getElementById("selectMenu3-popup");
 
     await clickOn(selectMenu3);
-    assert_equals(Math.round(selectMenu3.getBoundingClientRect().top), Math.round(selectMenu3Popup.getBoundingClientRect().bottom));
-    assert_equals(Math.round(selectMenu3.getBoundingClientRect().right), Math.round(selectMenu3Popup.getBoundingClientRect().right));
+    assert_equals(Math.abs(Math.trunc(selectMenu3.getBoundingClientRect().top - selectMenu3Popup.getBoundingClientRect().bottom)), 0);
+    assert_equals(Math.abs(Math.trunc(selectMenu3.getBoundingClientRect().right - selectMenu3Popup.getBoundingClientRect().right)), 0);
   }, "The popup should be top right positioned");
 
 </script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html
index ee0c91c..05f4069d 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/inert-label-focus.html
@@ -25,8 +25,8 @@
       absoluteTop += parentNode.offsetTop;
     }
 
-    const x = absoluteLeft + element.offsetWidth / 2;
-    const y = absoluteTop + element.offsetHeight / 2;
+    const x = Math.round(absoluteLeft + element.offsetWidth / 2);
+    const y = Math.round(absoluteTop + element.offsetHeight / 2);
     const actions = new test_driver.Actions()
       .pointerMove(x, y)
       .pointerDown()
diff --git a/third_party/blink/web_tests/external/wpt/permissions-policy/reporting/payment-report-only.https.html b/third_party/blink/web_tests/external/wpt/permissions-policy/reporting/payment-report-only.https.html
index f5105c3..d4425f9f 100644
--- a/third_party/blink/web_tests/external/wpt/permissions-policy/reporting/payment-report-only.https.html
+++ b/third_party/blink/web_tests/external/wpt/permissions-policy/reporting/payment-report-only.https.html
@@ -3,6 +3,8 @@
   <head>
     <script src='/resources/testharness.js'></script>
     <script src='/resources/testharnessreport.js'></script>
+    <script src='/resources/testdriver.js'></script>
+    <script src='/resources/testdriver-vendor.js'></script>
   </head>
   <body>
     <script>
@@ -19,6 +21,7 @@
                           {types: ['permissions-policy-violation']}).observe();
   });
   try {
+    await test_driver.bless();
     const request = new PaymentRequest(
       [{ supportedMethods: 'basic-card' }],
       { total: { label: 'Total', amount: { currency: 'USD', value: 0 }}},
diff --git a/third_party/blink/web_tests/external/wpt/resources/testdriver.js b/third_party/blink/web_tests/external/wpt/resources/testdriver.js
index 7471372..0737e64 100644
--- a/third_party/blink/web_tests/external/wpt/resources/testdriver.js
+++ b/third_party/blink/web_tests/external/wpt/resources/testdriver.js
@@ -506,7 +506,7 @@
         /**
          * Removes all the credentials stored in a virtual authenticator
          *
-         * Matches the `Remoce All Credentials
+         * Matches the `Remove All Credentials
          * <https://w3c.github.io/webauthn/#sctn-automation-remove-all-credentials>`_
          * WebDriver command.
          *
diff --git a/third_party/blink/web_tests/external/wpt/resources/testharness.js b/third_party/blink/web_tests/external/wpt/resources/testharness.js
index 5ebecca..ca0a92a 100644
--- a/third_party/blink/web_tests/external/wpt/resources/testharness.js
+++ b/third_party/blink/web_tests/external/wpt/resources/testharness.js
@@ -1812,7 +1812,7 @@
      *
      * @param {number} actual - Test value.
      * @param {number} lower - Number that ``actual`` must be greater than or equal to.
-     * @param {number} upper - Number that ``actual`` must be less than or eqaul to.
+     * @param {number} upper - Number that ``actual`` must be less than or equal to.
      * @param {string} [description] - Description of the condition being tested.
      */
     function assert_between_inclusive(actual, lower, upper, description)
@@ -2072,7 +2072,7 @@
      * "WrongDocumentError") or the name of the corresponding error
      * code (e.g. "``HIERARCHY_REQUEST_ERR``", "``WRONG_DOCUMENT_ERR``").
      * @param {Function} descriptionOrFunc - The function expected to
-     * throw (if the exeception comes from another global), or the
+     * throw (if the exception comes from another global), or the
      * optional description of the condition being tested (if the
      * exception comes from the current global).
      * @param {string} [description] - Description of the condition
diff --git a/third_party/blink/web_tests/external/wpt/urlpattern/resources/urlpatterntestdata.json b/third_party/blink/web_tests/external/wpt/urlpattern/resources/urlpatterntestdata.json
index f20d62fd..671751fd 100644
--- a/third_party/blink/web_tests/external/wpt/urlpattern/resources/urlpatterntestdata.json
+++ b/third_party/blink/web_tests/external/wpt/urlpattern/resources/urlpatterntestdata.json
@@ -2508,7 +2508,7 @@
     "pattern": [{ "pathname": "{:foo}{(.*)bar}" }],
     "inputs": [{ "pathname": "foobarbaz" }],
     "expected_obj": {
-      "pathname": ":foo{(.*)bar}"
+      "pathname": ":foo{*bar}"
     },
     "expected_match": null
   },
@@ -2516,7 +2516,7 @@
     "pattern": [{ "pathname": "{:foo}{bar(.*)}" }],
     "inputs": [{ "pathname": "foobarbaz" }],
     "expected_obj": {
-      "pathname": ":foo{bar(.*)}"
+      "pathname": ":foo{bar*}"
     },
     "expected_match": {
       "pathname": { "input": "foobarbaz", "groups": { "foo": "foo", "0": "baz" }}
@@ -2636,5 +2636,34 @@
     "expected_match": {
       "pathname": { "input": "bazbar", "groups": { "foo": "baz" }}
     }
+  },
+  {
+    "pattern": [{ "pathname": "*/*" }],
+    "inputs": [{ "pathname": "foo/bar" }],
+    "expected_match": {
+      "pathname": { "input": "foo/bar", "groups": { "0": "foo", "1": "bar" }}
+    }
+  },
+  {
+    "pattern": [{ "pathname": "*\\/*" }],
+    "inputs": [{ "pathname": "foo/bar" }],
+    "expected_obj": {
+      "pathname": "*/{*}"
+    },
+    "expected_match": {
+      "pathname": { "input": "foo/bar", "groups": { "0": "foo", "1": "bar" }}
+    }
+  },
+  {
+    "pattern": [{ "pathname": "*/{*}" }],
+    "inputs": [{ "pathname": "foo/bar" }],
+    "expected_match": {
+      "pathname": { "input": "foo/bar", "groups": { "0": "foo", "1": "bar" }}
+    }
+  },
+  {
+    "pattern": [{ "pathname": "*//*" }],
+    "inputs": [{ "pathname": "foo/bar" }],
+    "expected_match": null
   }
 ]
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-002-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-002-expected.txt
index 357a3c3..88869a4 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-002-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-002-expected.txt
@@ -194,8 +194,8 @@
 PASS text-emphasis-color supports animating as color of rgba()
 PASS text-emphasis-color supports animating as color of hsla()
 PASS text-emphasis-position (type: discrete) has testAccumulation function
-PASS text-emphasis-position: "under left" onto "over right"
-PASS text-emphasis-position: "over right" onto "under left"
+PASS text-emphasis-position: "under left" onto "over"
+PASS text-emphasis-position: "over" onto "under left"
 PASS text-emphasis-style (type: discrete) has testAccumulation function
 PASS text-emphasis-style: "open dot" onto "circle"
 PASS text-emphasis-style: "circle" onto "open dot"
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-002-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-002-expected.txt
index 0e7f2b0..4a8ceb77 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-002-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-002-expected.txt
@@ -194,8 +194,8 @@
 PASS text-emphasis-color supports animating as color of rgba()
 PASS text-emphasis-color supports animating as color of hsla()
 PASS text-emphasis-position (type: discrete) has testAddition function
-PASS text-emphasis-position: "under left" onto "over right"
-PASS text-emphasis-position: "over right" onto "under left"
+PASS text-emphasis-position: "under left" onto "over"
+PASS text-emphasis-position: "over" onto "under left"
 PASS text-emphasis-style (type: discrete) has testAddition function
 PASS text-emphasis-style: "open dot" onto "circle"
 PASS text-emphasis-style: "circle" onto "open dot"
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-002-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-002-expected.txt
index 74ac012b..f59cd81 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-002-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/interpolation-per-property-002-expected.txt
@@ -231,9 +231,9 @@
 PASS text-emphasis-color supports animating as color of rgba()
 PASS text-emphasis-color supports animating as color of hsla()
 PASS text-emphasis-position (type: discrete) has testInterpolation function
-PASS text-emphasis-position uses discrete animation when animating between "over right" and "under left" with linear easing
-PASS text-emphasis-position uses discrete animation when animating between "over right" and "under left" with effect easing
-PASS text-emphasis-position uses discrete animation when animating between "over right" and "under left" with keyframe easing
+PASS text-emphasis-position uses discrete animation when animating between "over" and "under left" with linear easing
+PASS text-emphasis-position uses discrete animation when animating between "over" and "under left" with effect easing
+PASS text-emphasis-position uses discrete animation when animating between "over" and "under left" with keyframe easing
 PASS text-emphasis-style (type: discrete) has testInterpolation function
 PASS text-emphasis-style uses discrete animation when animating between "circle" and "open dot" with linear easing
 PASS text-emphasis-style uses discrete animation when animating between "circle" and "open dot" with effect easing
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js
index e9b7c52..4be66a6 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-list.js
@@ -1284,7 +1284,7 @@
   'text-emphasis-position': {
     // http://dev.w3.org/csswg/css-text-decor-3/#propdef-text-emphasis-position
     types: [
-      { type: 'discrete', options: [ [ 'over right', 'under left' ] ] }
+      { type: 'discrete', options: [ [ 'over', 'under left' ] ] }
     ]
   },
   'text-emphasis-style': {
diff --git a/third_party/blink/web_tests/fast/canvas/bug1280361.html b/third_party/blink/web_tests/fast/canvas/bug1280361.html
new file mode 100644
index 0000000..96fe8fe
--- /dev/null
+++ b/third_party/blink/web_tests/fast/canvas/bug1280361.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+
+<script>
+
+async_test((t) => {
+  // This test passes by not crashing.
+  const image = new Image();
+  image.src = '../canvas/resources/html5.png';
+  image.onload = () => {
+    canvas = document.createElement('canvas');
+    canvas.width = image.naturalWidth;
+    canvas.height = image.naturalHeight;
+    ctx = canvas.getContext('2d');
+    ctx.drawImage(image, 0, 0, image.naturalWidth, image.naturalHeight);
+    createImageBitmap(canvas).then((imageBitmap) => {
+      const worker = new Worker('resources/bug1280361-worker.js');
+      worker.addEventListener("message", (msg) => {
+        assert_equals(msg.data, 'Done');
+        worker.terminate();
+        ctx = null;
+        canvas = null;
+        // Trigger immediate destruction to force a thread check.
+        // Otherwise the crash might be delayed until the next test.
+        if (window.GCController)
+          window.GCController.collectAll();
+        t.done();
+      });
+      worker.postMessage(imageBitmap, [imageBitmap]);
+    });
+  };
+}, "Transfer ImageBitmap from canvas with drawImage");
+
+async_test((t) => {
+  // This test passes by not crashing.
+  const image = new Image();
+  image.src = '../canvas/resources/html5.png';
+  image.onload = () => {
+    canvas = document.createElement('canvas');
+    canvas.width = image.naturalWidth;
+    canvas.height = image.naturalHeight;
+    ctx = canvas.getContext('2d');
+    createImageBitmap(image).then((imageBitmap) => {
+      ctx.drawImage(imageBitmap, 0, 0);
+      const worker = new Worker('resources/bug1280361-worker.js');
+      worker.addEventListener("message", (msg) => {
+        assert_equals(msg.data, 'Done');
+        worker.terminate();
+        ctx = null;
+        canvas = null;
+        // Trigger immediate destruction to force a thread check.
+        // Otherwise the crash might be delayed until the next test.
+        if (window.GCController)
+          window.GCController.collectAll();
+        t.done();
+      });
+      worker.postMessage(imageBitmap, [imageBitmap]);
+    });
+  };
+}, "Transfer ImageBitmap after using as source for drawImage.");
+
+async_test((t) => {
+  // This test passes by not crashing.
+  const image = new Image();
+  image.src = '../canvas/resources/html5.png';
+  image.onload = () => {
+    canvas = document.createElement('canvas');
+    canvas.width = image.naturalWidth;
+    canvas.height = image.naturalHeight;
+    ctx = canvas.getContext('2d');
+    ctx.drawImage(image, 0, 0, image.naturalWidth, image.naturalHeight);
+    createImageBitmap(canvas).then((imageBitmap) => {
+      pattern = ctx.createPattern(imageBitmap, 'repeat');
+      const worker = new Worker('resources/bug1280361-worker.js');
+      worker.addEventListener("message", (msg) => {
+        assert_equals(msg.data, 'Done');
+        worker.terminate();
+        ctx = null;
+        canvas = null;
+        pattern = null;
+        // Trigger immediate destruction to force a thread check.
+        // Otherwise the crash might be delayed until the next test.
+        if (window.GCController)
+          window.GCController.collectAll();
+        t.done();
+      });
+      worker.postMessage(imageBitmap, [imageBitmap]);
+    });
+  };
+}, "Transfer ImageBitmap after using as source for createPattern");
+
+async_test((t) => {
+  // This test passes by not crashing.
+  const image = new Image();
+  image.src = '../canvas/resources/html5.png';
+  image.onload = () => {
+    canvas = document.createElement('canvas');
+    canvas.width = image.naturalWidth;
+    canvas.height = image.naturalHeight;
+    ctx = canvas.getContext('2d');
+    ctx.drawImage(image, 0, 0, image.naturalWidth, image.naturalHeight);
+    createImageBitmap(canvas).then((imageBitmap) => {
+      createImageBitmap(imageBitmap).then((imageBitmapCopy) => {
+        const worker = new Worker('resources/bug1280361-worker.js');
+        worker.addEventListener("message", (msg) => {
+          assert_equals(msg.data, 'Done');
+          worker.terminate();
+          ctx = null;
+          canvas = null;
+          imageBitmapCopy = null;
+          // Trigger immediate destruction to force a thread check.
+          // Otherwise the crash might be delayed until the next test.
+          if (window.GCController)
+            window.GCController.collectAll();
+          t.done();
+        });
+        worker.postMessage(imageBitmap, [imageBitmap]);
+      });
+    });
+  };
+}, "Transfer ImageBitmap after using as source for another ImageBitmap");
+</script>
diff --git a/third_party/blink/web_tests/fast/canvas/resources/bug1280361-worker.js b/third_party/blink/web_tests/fast/canvas/resources/bug1280361-worker.js
new file mode 100644
index 0000000..6e0c83e
--- /dev/null
+++ b/third_party/blink/web_tests/fast/canvas/resources/bug1280361-worker.js
@@ -0,0 +1,10 @@
+self.onmessage = e => {
+  const bitmap = e.data;
+  const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
+  const gl = canvas.getContext('webgl');
+  const texture = gl.createTexture();
+  gl.bindTexture(gl.TEXTURE_2D, texture);
+  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bitmap);
+  bitmap.close();
+  self.postMessage('Done');
+}
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
index 8f27e66..4e39f2f6 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
@@ -322,7 +322,7 @@
 text-decoration-skip-ink: auto
 text-decoration-style: solid
 text-emphasis-color: rgb(0, 0, 0)
-text-emphasis-position: over right
+text-emphasis-position: over
 text-emphasis-style: none
 text-indent: 0px
 text-justify: auto
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
index 9a0a14a1..1ddbcf4b 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
@@ -322,7 +322,7 @@
 text-decoration-skip-ink: auto
 text-decoration-style: solid
 text-emphasis-color: rgb(0, 0, 0)
-text-emphasis-position: over right
+text-emphasis-position: over
 text-emphasis-style: none
 text-indent: 0px
 text-justify: auto
diff --git a/third_party/blink/web_tests/http/tests/devtools/extensions/extensions-api-expected.txt b/third_party/blink/web_tests/http/tests/devtools/extensions/extensions-api-expected.txt
index 4fd51641..df21faa 100644
--- a/third_party/blink/web_tests/http/tests/devtools/extensions/extensions-api-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/extensions/extensions-api-expected.txt
@@ -55,6 +55,7 @@
         }
         openResource : <function>
         setOpenResourceHandler : <function>
+        setThemeChangeHandler : <function>
         sources : {
             createSidebarPane : <function>
             onSelectionChanged : {
diff --git a/third_party/blink/web_tests/inspector-protocol/timeline/mismatched-preload-expected.txt b/third_party/blink/web_tests/inspector-protocol/timeline/mismatched-preload-expected.txt
new file mode 100644
index 0000000..bd5970c4
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/timeline/mismatched-preload-expected.txt
@@ -0,0 +1,5 @@
+Tests reporting of mismatched preloads in traces.
+Recording started
+Tracing complete
+empty.js: 7
+
diff --git a/third_party/blink/web_tests/inspector-protocol/timeline/mismatched-preload.js b/third_party/blink/web_tests/inspector-protocol/timeline/mismatched-preload.js
new file mode 100644
index 0000000..551f113
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/timeline/mismatched-preload.js
@@ -0,0 +1,53 @@
+(async function(testRunner) {
+  const numberOfURLs = 2;
+
+  // Test traces
+  var {page, session, dp} = await testRunner.startHTML(`
+      <head></head>
+      <body>
+      </body>
+  `, 'Tests reporting of mismatched preloads in traces.');
+
+  var TracingHelper = await testRunner.loadScript('../resources/tracing-test.js');
+  var tracingHelper = new TracingHelper(testRunner, session);
+  await tracingHelper.startTracing("blink.resource");
+  dp.Network.enable();
+  session.evaluate(`
+    (function performActions() {
+      // Add a preload
+      const preload = document.createElement("link");
+      preload.href = "../resources/empty.js";
+      preload.rel = "preload";
+      preload.as = "script";
+      document.head.appendChild(preload);
+
+      // Try to use it, but with the wrong CORS mode
+      const script = document.createElement("script");
+      script.src = "../resources/empty.js";
+      script.crossOrigin = "anonymous";
+      document.head.appendChild(script);
+    })();
+  `);
+
+  // Wait for traces to show up.
+  for (let i = 0; i < numberOfURLs; ++i) {
+    await dp.Network.onceRequestWillBeSent();
+  }
+
+  const events = await tracingHelper.stopTracing(/blink.resource/);
+  const requestEvents = events.filter(e => e.name == "ResourceFetcher::PrintPreloadMismatch");
+
+  const resources = new Map();
+  for (let e of requestEvents) {
+    const url = e['args']['url'];
+    const status = e['args']['MatchStatus'];
+    const pathname = new URL(url).pathname;
+    const path_array = pathname.split('/');
+    resources.set(path_array[path_array.length - 1], status);
+  }
+  for (const resource of Array.from(resources.keys()).sort()) {
+    testRunner.log(`${resource}: ${resources.get(resource)}`);
+  }
+  testRunner.completeTest();
+})
+
diff --git a/third_party/blink/web_tests/inspector-protocol/timeline/unused-preload-expected.txt b/third_party/blink/web_tests/inspector-protocol/timeline/unused-preload-expected.txt
new file mode 100644
index 0000000..4ddc2129
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/timeline/unused-preload-expected.txt
@@ -0,0 +1,5 @@
+Tests reporting of unused preloads in traces.
+Recording started
+Tracing complete
+empty.js
+
diff --git a/third_party/blink/web_tests/inspector-protocol/timeline/unused-preload.js b/third_party/blink/web_tests/inspector-protocol/timeline/unused-preload.js
new file mode 100644
index 0000000..44af51c1
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/timeline/unused-preload.js
@@ -0,0 +1,48 @@
+(async function(testRunner) {
+  const numberOfURLs = 1;
+
+  // Test traces
+  var {page, session, dp} = await testRunner.startHTML(`
+      <head></head>
+      <body>
+      </body>
+  `, 'Tests reporting of unused preloads in traces.');
+
+  var TracingHelper = await testRunner.loadScript('../resources/tracing-test.js');
+  var tracingHelper = new TracingHelper(testRunner, session);
+  await tracingHelper.startTracing("blink.resource");
+  dp.Network.enable();
+  session.evaluate(`
+    (function performActions() {
+      // Add an unused preload
+      const preload = document.createElement("link");
+      preload.href = "../resources/empty.js";
+      preload.rel = "preload";
+      preload.as = "script";
+      document.head.appendChild(preload);
+    })();
+  `);
+
+  // Wait for traces to show up.
+  for (let i = 0; i < numberOfURLs; ++i) {
+    await dp.Network.onceRequestWillBeSent();
+  }
+
+  // Wait for 5 seconds for the trace to appear.
+  await new Promise(r => setTimeout(r, 3500));
+
+  const events = await tracingHelper.stopTracing(/blink.resource/);
+  const requestEvents = events.filter(e => e.name == "ResourceFetcher::WarnUnusedPreloads");
+
+  const resources = new Map();
+  for (let e of requestEvents) {
+    const url = e['args']['url'];
+    const pathname = new URL(url).pathname;
+    const path_array = pathname.split('/');
+    resources.set(path_array[path_array.length - 1], "");
+  }
+  for (const resource of Array.from(resources.keys()).sort()) {
+    testRunner.log(`${resource}`);
+  }
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-mandatory-getStats.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-mandatory-getStats.https-expected.txt
new file mode 100644
index 0000000..a0bf1d2
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-mandatory-getStats.https-expected.txt
@@ -0,0 +1,79 @@
+This is a testharness.js-based test.
+Found 75 tests; 71 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS getStats succeeds
+PASS Validating stats
+PASS RTCRtpStreamStats's ssrc
+PASS RTCRtpStreamStats's kind
+PASS RTCRtpStreamStats's transportId
+PASS RTCRtpStreamStats's codecId
+PASS RTCReceivedRtpStreamStats's packetsReceived
+PASS RTCReceivedRtpStreamStats's packetsLost
+PASS RTCReceivedRtpStreamStats's jitter
+PASS RTCReceivedRtpStreamStats's packetsDiscarded
+PASS RTCReceivedRtpStreamStats's framesDropped
+FAIL RTCInboundRtpStreamStats's receiverId assert_true: Is receiverId present expected true got false
+PASS RTCInboundRtpStreamStats's remoteId
+PASS RTCInboundRtpStreamStats's framesDecoded
+PASS RTCInboundRtpStreamStats's nackCount
+PASS RTCInboundRtpStreamStats's framesReceived
+PASS RTCInboundRtpStreamStats's bytesReceived
+PASS RTCInboundRtpStreamStats's totalAudioEnergy
+PASS RTCInboundRtpStreamStats's totalSamplesDuration
+PASS RTCRemoteInboundRtpStreamStats's localId
+PASS RTCRemoteInboundRtpStreamStats's roundTripTime
+PASS RTCSentRtpStreamStats's packetsSent
+PASS RTCSentRtpStreamStats's bytesSent
+FAIL RTCOutboundRtpStreamStats's senderId assert_true: Is senderId present expected true got false
+PASS RTCOutboundRtpStreamStats's remoteId
+PASS RTCOutboundRtpStreamStats's framesEncoded
+PASS RTCOutboundRtpStreamStats's nackCount
+PASS RTCOutboundRtpStreamStats's framesSent
+PASS RTCRemoteOutboundRtpStreamStats's localId
+PASS RTCRemoteOutboundRtpStreamStats's remoteTimestamp
+PASS RTCPeerConnectionStats's dataChannelsOpened
+PASS RTCPeerConnectionStats's dataChannelsClosed
+PASS RTCDataChannelStats's label
+PASS RTCDataChannelStats's protocol
+PASS RTCDataChannelStats's dataChannelIdentifier
+PASS RTCDataChannelStats's state
+PASS RTCDataChannelStats's messagesSent
+PASS RTCDataChannelStats's bytesSent
+PASS RTCDataChannelStats's messagesReceived
+PASS RTCDataChannelStats's bytesReceived
+PASS RTCMediaSourceStats's trackIdentifier
+PASS RTCMediaSourceStats's kind
+PASS RTCAudioSourceStats's totalAudioEnergy
+PASS RTCAudioSourceStats's totalSamplesDuration
+PASS RTCVideoSourceStats's width
+PASS RTCVideoSourceStats's height
+PASS RTCVideoSourceStats's framesPerSecond
+FAIL RTCMediaHandlerStats's trackIdentifier assert_true: Is trackIdentifier present expected true got false
+PASS RTCCodecStats's payloadType
+PASS RTCCodecStats's mimeType
+PASS RTCCodecStats's clockRate
+PASS RTCCodecStats's channels
+PASS RTCCodecStats's sdpFmtpLine
+PASS RTCTransportStats's bytesSent
+PASS RTCTransportStats's bytesReceived
+PASS RTCTransportStats's selectedCandidatePairId
+PASS RTCTransportStats's localCertificateId
+PASS RTCTransportStats's remoteCertificateId
+PASS RTCIceCandidatePairStats's transportId
+PASS RTCIceCandidatePairStats's localCandidateId
+PASS RTCIceCandidatePairStats's remoteCandidateId
+PASS RTCIceCandidatePairStats's state
+PASS RTCIceCandidatePairStats's nominated
+PASS RTCIceCandidatePairStats's bytesSent
+PASS RTCIceCandidatePairStats's bytesReceived
+PASS RTCIceCandidatePairStats's totalRoundTripTime
+PASS RTCIceCandidatePairStats's currentRoundTripTime
+PASS RTCIceCandidateStats's address
+PASS RTCIceCandidateStats's port
+PASS RTCIceCandidateStats's protocol
+PASS RTCIceCandidateStats's candidateType
+FAIL RTCIceCandidateStats's url assert_true: Is url present expected true got false
+PASS RTCCertificateStats's fingerprint
+PASS RTCCertificateStats's fingerprintAlgorithm
+PASS RTCCertificateStats's base64Certificate
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-mandatory-getStats.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-mandatory-getStats.https-expected.txt
new file mode 100644
index 0000000..c79cd16
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-mandatory-getStats.https-expected.txt
@@ -0,0 +1,79 @@
+This is a testharness.js-based test.
+Found 75 tests; 68 PASS, 7 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS getStats succeeds
+PASS Validating stats
+PASS RTCRtpStreamStats's ssrc
+PASS RTCRtpStreamStats's kind
+PASS RTCRtpStreamStats's transportId
+PASS RTCRtpStreamStats's codecId
+PASS RTCReceivedRtpStreamStats's packetsReceived
+PASS RTCReceivedRtpStreamStats's packetsLost
+PASS RTCReceivedRtpStreamStats's jitter
+PASS RTCReceivedRtpStreamStats's packetsDiscarded
+PASS RTCReceivedRtpStreamStats's framesDropped
+FAIL RTCInboundRtpStreamStats's receiverId assert_true: Is receiverId present expected true got false
+FAIL RTCInboundRtpStreamStats's remoteId assert_true: Is remoteId present expected true got false
+PASS RTCInboundRtpStreamStats's framesDecoded
+PASS RTCInboundRtpStreamStats's nackCount
+PASS RTCInboundRtpStreamStats's framesReceived
+PASS RTCInboundRtpStreamStats's bytesReceived
+PASS RTCInboundRtpStreamStats's totalAudioEnergy
+PASS RTCInboundRtpStreamStats's totalSamplesDuration
+PASS RTCRemoteInboundRtpStreamStats's localId
+PASS RTCRemoteInboundRtpStreamStats's roundTripTime
+PASS RTCSentRtpStreamStats's packetsSent
+PASS RTCSentRtpStreamStats's bytesSent
+FAIL RTCOutboundRtpStreamStats's senderId assert_true: Is senderId present expected true got false
+PASS RTCOutboundRtpStreamStats's remoteId
+PASS RTCOutboundRtpStreamStats's framesEncoded
+PASS RTCOutboundRtpStreamStats's nackCount
+PASS RTCOutboundRtpStreamStats's framesSent
+FAIL RTCRemoteOutboundRtpStreamStats's localId assert_true: Is localId present expected true got false
+FAIL RTCRemoteOutboundRtpStreamStats's remoteTimestamp assert_true: Is remoteTimestamp present expected true got false
+PASS RTCPeerConnectionStats's dataChannelsOpened
+PASS RTCPeerConnectionStats's dataChannelsClosed
+PASS RTCDataChannelStats's label
+PASS RTCDataChannelStats's protocol
+PASS RTCDataChannelStats's dataChannelIdentifier
+PASS RTCDataChannelStats's state
+PASS RTCDataChannelStats's messagesSent
+PASS RTCDataChannelStats's bytesSent
+PASS RTCDataChannelStats's messagesReceived
+PASS RTCDataChannelStats's bytesReceived
+PASS RTCMediaSourceStats's trackIdentifier
+PASS RTCMediaSourceStats's kind
+PASS RTCAudioSourceStats's totalAudioEnergy
+PASS RTCAudioSourceStats's totalSamplesDuration
+PASS RTCVideoSourceStats's width
+PASS RTCVideoSourceStats's height
+PASS RTCVideoSourceStats's framesPerSecond
+FAIL RTCMediaHandlerStats's trackIdentifier assert_true: Is trackIdentifier present expected true got false
+PASS RTCCodecStats's payloadType
+PASS RTCCodecStats's mimeType
+PASS RTCCodecStats's clockRate
+PASS RTCCodecStats's channels
+PASS RTCCodecStats's sdpFmtpLine
+PASS RTCTransportStats's bytesSent
+PASS RTCTransportStats's bytesReceived
+PASS RTCTransportStats's selectedCandidatePairId
+PASS RTCTransportStats's localCertificateId
+PASS RTCTransportStats's remoteCertificateId
+PASS RTCIceCandidatePairStats's transportId
+PASS RTCIceCandidatePairStats's localCandidateId
+PASS RTCIceCandidatePairStats's remoteCandidateId
+PASS RTCIceCandidatePairStats's state
+PASS RTCIceCandidatePairStats's nominated
+PASS RTCIceCandidatePairStats's bytesSent
+PASS RTCIceCandidatePairStats's bytesReceived
+PASS RTCIceCandidatePairStats's totalRoundTripTime
+PASS RTCIceCandidatePairStats's currentRoundTripTime
+PASS RTCIceCandidateStats's address
+PASS RTCIceCandidateStats's port
+PASS RTCIceCandidateStats's protocol
+PASS RTCIceCandidateStats's candidateType
+FAIL RTCIceCandidateStats's url assert_true: Is url present expected true got false
+PASS RTCCertificateStats's fingerprint
+PASS RTCCertificateStats's fingerprintAlgorithm
+PASS RTCCertificateStats's base64Certificate
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/printing/iframe-with-overflow-expected.html b/third_party/blink/web_tests/printing/iframe-with-overflow-expected.html
new file mode 100644
index 0000000..68fa60f
--- /dev/null
+++ b/third_party/blink/web_tests/printing/iframe-with-overflow-expected.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script>
+  if (window.testRunner)
+      testRunner.setPrinting();
+  if (window.internals)
+      internals.settings.setShouldPrintBackgrounds(true);
+</script>
+<div style="width: 200px; height: 200px; background: yellow; overflow: auto">
+  <div style="width: 400px; height: 500px"></div>
+</div>
+<div style="height: 300px; background: green"></div>
diff --git a/third_party/blink/web_tests/printing/iframe-with-overflow.html b/third_party/blink/web_tests/printing/iframe-with-overflow.html
new file mode 100644
index 0000000..eaa66b8
--- /dev/null
+++ b/third_party/blink/web_tests/printing/iframe-with-overflow.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+  if (window.testRunner)
+      testRunner.setPrinting();
+  if (window.internals)
+      internals.settings.setShouldPrintBackgrounds(true);
+</script>
+<iframe style="border: none; width: 200px; height: 200px; display: block" srcdoc="
+  <body style='background: yellow; margin: 0; width: 400px; height: 500px'>
+  </body>">
+</iframe>
+<div style="height: 300px; background: green"></div>
diff --git a/third_party/blink/web_tests/resources/testdriver-actions.js b/third_party/blink/web_tests/resources/testdriver-actions.js
index 4dafa0c..ef097961 100644
--- a/third_party/blink/web_tests/resources/testdriver-actions.js
+++ b/third_party/blink/web_tests/resources/testdriver-actions.js
@@ -2,9 +2,38 @@
   let sourceNameIdx = 0;
 
   /**
+   * @class
    * Builder for creating a sequence of actions
-   * The default tick duration is set to 16ms, which is one frame time based on
-   * 60Hz display.
+   *
+   *
+   * The actions are dispatched once
+   * :js:func:`test_driver.Actions.send` is called. This returns a
+   * promise which resolves once the actions are complete.
+   *
+   * The other methods on :js:class:`test_driver.Actions` object are
+   * used to build the sequence of actions that will be sent. These
+   * return the `Actions` object itself, so the actions sequence can
+   * be constructed by chaining method calls.
+   *
+   * Internally :js:func:`test_driver.Actions.send` invokes
+   * :js:func:`test_driver.action_sequence`.
+   *
+   * @example
+   * let text_box = document.getElementById("text");
+   *
+   * let actions = new test_driver.Actions()
+   *    .pointerMove(0, 0, {origin: text_box})
+   *    .pointerDown()
+   *    .pointerUp()
+   *    .addTick()
+   *    .keyDown("p")
+   *    .keyUp("p");
+   *
+   * actions.send();
+   *
+   * @param {number} [defaultTickDuration] - The default duration of a
+   * tick. Be default this is set ot 16ms, which is one frame time
+   * based on 60Hz display.
    */
   function Actions(defaultTickDuration=16) {
     this.sourceTypes = new Map([["key", KeySource],
diff --git a/third_party/blink/web_tests/resources/testdriver.js b/third_party/blink/web_tests/resources/testdriver.js
index f2df26c..7471372 100644
--- a/third_party/blink/web_tests/resources/testdriver.js
+++ b/third_party/blink/web_tests/resources/testdriver.js
@@ -46,7 +46,7 @@
 
 
     /**
-     * @namespace
+     * @namespace {test_driver}
      */
     window.test_driver = {
         /**
@@ -78,9 +78,17 @@
          * Trigger user interaction in order to grant additional privileges to
          * a provided function.
          *
-         * https://html.spec.whatwg.org/#triggered-by-user-activation
+         * See `triggered by user activation
+         * <https://html.spec.whatwg.org/#triggered-by-user-activation>`_.
          *
-         * @param {String} intent - a description of the action which much be
+         * @example
+         * var mediaElement = document.createElement('video');
+         *
+         * test_driver.bless('initiate media playback', function () {
+         *   mediaElement.play();
+         * });
+         *
+         * @param {String} intent - a description of the action which must be
          *                          triggered by user interaction
          * @param {Function} action - code requiring escalated privileges
          * @param {WindowProxy} context - Browsing context in which
@@ -118,9 +126,21 @@
         /**
          * Triggers a user-initiated click
          *
-         * This matches the behaviour of the {@link
-         * https://w3c.github.io/webdriver/#element-click|WebDriver
-         * Element Click command}.
+         * If ``element`` isn't inside the
+         * viewport, it will be scrolled into view before the click
+         * occurs.
+         *
+         * If ``element`` is from a different browsing context, the
+         * command will be run in that context.
+         *
+         * Matches the behaviour of the `Element Click
+         * <https://w3c.github.io/webdriver/#element-click>`_
+         * WebDriver command.
+         *
+         * **Note:** If the element to be clicked does not have a
+         * unique ID, the document must not have any DOM mutations
+         * made between the function being called and the promise
+         * settling.
          *
          * @param {Element} element - element to be clicked
          * @returns {Promise} fulfilled after click occurs, or rejected in
@@ -149,9 +169,9 @@
         /**
          * Deletes all cookies.
          *
-         * This matches the behaviour of the {@link
-         * https://w3c.github.io/webdriver/#delete-all-cookies|WebDriver
-         * Delete All Cookies command}.
+         * Matches the behaviour of the `Delete All Cookies
+         * <https://w3c.github.io/webdriver/#delete-all-cookies>`_
+         * WebDriver command.
          *
          * @param {WindowProxy} context - Browsing context in which
          *                                to run the call, or null for the current
@@ -165,11 +185,34 @@
         },
 
         /**
-         * Send keys to an element
+         * Send keys to an element.
          *
-         * This matches the behaviour of the {@link
-         * https://w3c.github.io/webdriver/#element-send-keys|WebDriver
-         * Send Keys command}.
+         * If ``element`` isn't inside the
+         * viewport, it will be scrolled into view before the click
+         * occurs.
+         *
+         * If ``element`` is from a different browsing context, the
+         * command will be run in that context.
+         *
+         * To send special keys, send the respective key's codepoint,
+         * as defined by `WebDriver
+         * <https://w3c.github.io/webdriver/#keyboard-actions>`_.  For
+         * example, the "tab" key is represented as "``\uE004``".
+         *
+         * **Note:** these special-key codepoints are not necessarily
+         * what you would expect. For example, <kbd>Esc</kbd> is the
+         * invalid Unicode character ``\uE00C``, not the ``\u001B`` Escape
+         * character from ASCII.
+         *
+         * This matches the behaviour of the
+         * `Send Keys
+         * <https://w3c.github.io/webdriver/#element-send-keys>`_
+         * WebDriver command.
+         *
+         * **Note:** If the element to be clicked does not have a
+         * unique ID, the document must not have any DOM mutations
+         * made between the function being called and the promise
+         * settling.
          *
          * @param {Element} element - element to send keys to
          * @param {String} keys - keys to send to the element
@@ -196,9 +239,8 @@
          * Freeze the current page
          *
          * The freeze function transitions the page from the HIDDEN state to
-         * the FROZEN state as described in {@link
-         * https://github.com/WICG/page-lifecycle/blob/master/README.md|Lifecycle API
-         * for Web Pages}
+         * the FROZEN state as described in `Lifecycle API for Web Pages
+         * <https://github.com/WICG/page-lifecycle/blob/master/README.md>`_.
          *
          * @param {WindowProxy} context - Browsing context in which
          *                                to run the call, or null for the current
@@ -212,26 +254,75 @@
         },
 
         /**
-         * Send a sequence of actions
+         * Minimizes the browser window.
          *
-         * This function sends a sequence of actions
-         * to perform. It is modeled after the behaviour of {@link
-         * https://w3c.github.io/webdriver/#actions|WebDriver Actions Command}
+         * Matches the the behaviour of the `Minimize
+         * <https://www.w3.org/TR/webdriver/#minimize-window>`_
+         * WebDriver command
          *
-         * @param {Array} actions - an array of actions. The format is the same as the actions
-         *                          property of the WebDriver command {@link
-         *                          https://w3c.github.io/webdriver/#perform-actions|Perform
-         *                          Actions} command. Each element is an object representing an
-         *                          input source and each input source itself has an actions
-         *                          property detailing the behaviour of that source at each timestep
-         *                          (or tick). Authors are not expected to construct the actions
-         *                          sequence by hand, but to use the builder api provided in
-         *                          testdriver-actions.js
          * @param {WindowProxy} context - Browsing context in which
          *                                to run the call, or null for the current
          *                                browsing context.
          *
-         * @returns {Promise} fufiled after the actions are performed, or rejected in
+         * @returns {Promise} fulfilled with the previous {@link
+         *                    https://www.w3.org/TR/webdriver/#dfn-windowrect-object|WindowRect}
+         *                      value, after the window is minimized.
+         */
+        minimize_window: function(context=null) {
+            return window.test_driver_internal.minimize_window(context);
+        },
+
+        /**
+         * Restore the window from minimized/maximized state to a given rect.
+         *
+         * Matches the behaviour of the `Set Window Rect
+         * <https://www.w3.org/TR/webdriver/#set-window-rect>`_
+         * WebDriver command
+         *
+         * @param {Object} rect - A {@link
+         *                           https://www.w3.org/TR/webdriver/#dfn-windowrect-object|WindowRect}
+         * @param {WindowProxy} context - Browsing context in which
+         *                                to run the call, or null for the current
+         *                                browsing context.
+         *
+         * @returns {Promise} fulfilled after the window is restored to the given rect.
+         */
+        set_window_rect: function(rect, context=null) {
+            return window.test_driver_internal.set_window_rect(rect, context);
+        },
+
+        /**
+         * Send a sequence of actions
+         *
+         * This function sends a sequence of actions to perform.
+         *
+         * Matches the behaviour of the `Actions
+         * <https://w3c.github.io/webdriver/#actions>`_ feature in
+         * WebDriver.
+         *
+         * Authors are encouraged to use the
+         * :js:class:`test_driver.Actions` builder rather than
+         * invoking this API directly.
+         *
+         * @param {Array} actions - an array of actions. The format is
+         *                          the same as the actions property
+         *                          of the `Perform Actions
+         *                          <https://w3c.github.io/webdriver/#perform-actions>`_
+         *                          WebDriver command. Each element is
+         *                          an object representing an input
+         *                          source and each input source
+         *                          itself has an actions property
+         *                          detailing the behaviour of that
+         *                          source at each timestep (or
+         *                          tick). Authors are not expected to
+         *                          construct the actions sequence by
+         *                          hand, but to use the builder api
+         *                          provided in testdriver-actions.js
+         * @param {WindowProxy} context - Browsing context in which
+         *                                to run the call, or null for the current
+         *                                browsing context.
+         *
+         * @returns {Promise} fulfilled after the actions are performed, or rejected in
          *                    the cases the WebDriver command errors
          */
         action_sequence: function(actions, context=null) {
@@ -241,9 +332,12 @@
         /**
          * Generates a test report on the current page
          *
-         * The generate_test_report function generates a report (to be observed
-         * by ReportingObserver) for testing purposes, as described in
-         * {@link https://w3c.github.io/reporting/#generate-test-report-command}
+         * The generate_test_report function generates a report (to be
+         * observed by ReportingObserver) for testing purposes.
+         *
+         * Matches the `Generate Test Report
+         * <https://w3c.github.io/reporting/#generate-test-report-command>`_
+         * WebDriver command.
          *
          * @param {WindowProxy} context - Browsing context in which
          *                                to run the call, or null for the current
@@ -259,21 +353,25 @@
         /**
          * Sets the state of a permission
          *
-         * This function simulates a user setting a permission into a particular state as described
-         * in {@link https://w3c.github.io/permissions/#set-permission-command}
+         * This function simulates a user setting a permission into a
+         * particular state.
          *
-         * @param {Object} descriptor - a [PermissionDescriptor]{@link
-         *                              https://w3c.github.io/permissions/#dictdef-permissiondescriptor}
+         * Matches the `Set Permission
+         * <https://w3c.github.io/permissions/#set-permission-command>`_
+         * WebDriver command.
+         *
+         * @example
+         * await test_driver.set_permission({ name: "background-fetch" }, "denied");
+         * await test_driver.set_permission({ name: "push", userVisibleOnly: true }, "granted", true);
+         *
+         * @param {Object} descriptor - a `PermissionDescriptor
+         *                              <https://w3c.github.io/permissions/#dictdef-permissiondescriptor>`_
          *                              object
          * @param {String} state - the state of the permission
          * @param {boolean} one_realm - Optional. Whether the permission applies to only one realm
          * @param {WindowProxy} context - Browsing context in which
          *                                to run the call, or null for the current
          *                                browsing context.
-         *
-         * The above params are used to create a [PermissionSetParameters]{@link
-         * https://w3c.github.io/permissions/#dictdef-permissionsetparameters} object
-         *
          * @returns {Promise} fulfilled after the permission is set, or rejected if setting the
          *                    permission fails
          */
@@ -289,12 +387,15 @@
         /**
          * Creates a virtual authenticator
          *
-         * This function creates a virtual authenticator for use with the U2F
-         * and WebAuthn APIs as described in {@link
-         * https://w3c.github.io/webauthn/#sctn-automation-add-virtual-authenticator}
+         * This function creates a virtual authenticator for use with
+         * the U2F and WebAuthn APIs.
          *
-         * @param {Object} config - an [Authenticator Configuration]{@link
-         *                          https://w3c.github.io/webauthn/#authenticator-configuration}
+         * Matches the `Add Virtual Authenticator
+         * <https://w3c.github.io/webauthn/#sctn-automation-add-virtual-authenticator>`_
+         * WebDriver command.
+         *
+         * @param {Object} config - an `Authenticator Configuration
+         *                          <https://w3c.github.io/webauthn/#authenticator-configuration>`_
          *                          object
          * @param {WindowProxy} context - Browsing context in which
          *                                to run the call, or null for the current
@@ -311,9 +412,12 @@
         /**
          * Removes a virtual authenticator
          *
-         * This function removes a virtual authenticator that has been created
-         * by add_virtual_authenticator
-         * https://w3c.github.io/webauthn/#sctn-automation-remove-virtual-authenticator
+         * This function removes a virtual authenticator that has been
+         * created by :js:func:`add_virtual_authenticator`.
+         *
+         * Matches the `Remove Virtual Authenticator
+         * <https://w3c.github.io/webauthn/#sctn-automation-remove-virtual-authenticator>`_
+         * WebDriver command.
          *
          * @param {String} authenticator_id - the ID of the authenticator to be
          *                                    removed.
@@ -332,11 +436,13 @@
         /**
          * Adds a credential to a virtual authenticator
          *
-         * https://w3c.github.io/webauthn/#sctn-automation-add-credential
+         * Matches the `Add Credential
+         * <https://w3c.github.io/webauthn/#sctn-automation-add-credential>`_
+         * WebDriver command.
          *
          * @param {String} authenticator_id - the ID of the authenticator
-         * @param {Object} credential - A [Credential Parameters]{@link
-         *                              https://w3c.github.io/webauthn/#credential-parameters}
+         * @param {Object} credential - A `Credential Parameters
+         *                              <https://w3c.github.io/webauthn/#credential-parameters>`_
          *                              object
          * @param {WindowProxy} context - Browsing context in which
          *                                to run the call, or null for the current
@@ -356,18 +462,21 @@
          * This function retrieves all the credentials (added via the U2F API,
          * WebAuthn, or the add_credential function) stored in a virtual
          * authenticator
-         * https://w3c.github.io/webauthn/#sctn-automation-get-credentials
+         *
+         * Matches the `Get Credentials
+         * <https://w3c.github.io/webauthn/#sctn-automation-get-credentials>`_
+         * WebDriver command.
          *
          * @param {String} authenticator_id - the ID of the authenticator
          * @param {WindowProxy} context - Browsing context in which
          *                                to run the call, or null for the current
          *                                browsing context.
          *
-         * @returns {Promise} fulfilled after the credentials are returned, or
-         *                    rejected in the cases the WebDriver command
-         *                    errors. Returns an array of [Credential
-         *                    Parameters]{@link
-         *                    https://w3c.github.io/webauthn/#credential-parameters}
+         * @returns {Promise} fulfilled after the credentials are
+         *                    returned, or rejected in the cases the
+         *                    WebDriver command errors. Returns an
+         *                    array of `Credential Parameters
+         *                    <https://w3c.github.io/webauthn/#credential-parameters>`_
          */
         get_credentials: function(authenticator_id, context=null) {
             return window.test_driver_internal.get_credentials(authenticator_id, context=null);
@@ -376,7 +485,9 @@
         /**
          * Remove a credential stored in an authenticator
          *
-         * https://w3c.github.io/webauthn/#sctn-automation-remove-credential
+         * Matches the `Remove Credential
+         * <https://w3c.github.io/webauthn/#sctn-automation-remove-credential>`_
+         * WebDriver command.
          *
          * @param {String} authenticator_id - the ID of the authenticator
          * @param {String} credential_id - the ID of the credential
@@ -395,7 +506,9 @@
         /**
          * Removes all the credentials stored in a virtual authenticator
          *
-         * https://w3c.github.io/webauthn/#sctn-automation-remove-all-credentials
+         * Matches the `Remoce All Credentials
+         * <https://w3c.github.io/webauthn/#sctn-automation-remove-all-credentials>`_
+         * WebDriver command.
          *
          * @param {String} authenticator_id - the ID of the authenticator
          * @param {WindowProxy} context - Browsing context in which
@@ -415,7 +528,10 @@
          *
          * Sets whether requests requiring user verification will succeed or
          * fail on a given virtual authenticator
-         * https://w3c.github.io/webauthn/#sctn-automation-set-user-verified
+         *
+         * Matches the `Set User Verified
+         * <https://w3c.github.io/webauthn/#sctn-automation-set-user-verified>`_
+         * WebDriver command.
          *
          * @param {String} authenticator_id - the ID of the authenticator
          * @param {boolean} uv - the User Verified flag
@@ -431,7 +547,9 @@
          * Sets the storage access rule for an origin when embedded
          * in a third-party context.
          *
-         * {@link https://privacycg.github.io/storage-access/#set-storage-access-command}
+         * Matches the `Set Storage Access
+         * <https://privacycg.github.io/storage-access/#set-storage-access-command>`_
+         * WebDriver command.
          *
          * @param {String} origin - A third-party origin to block or allow.
          *                          May be "*" to indicate all origins.
@@ -455,6 +573,45 @@
             const blocked = state === "blocked";
             return window.test_driver_internal.set_storage_access(origin, embedding_origin, blocked, context);
         },
+
+        /**
+         * Sets the current transaction automation mode for Secure Payment
+         * Confirmation.
+         *
+         * This function places `Secure Payment
+         * Confirmation <https://w3c.github.io/secure-payment-confirmation>`_ into
+         * an automated 'autoaccept' or 'autoreject' mode, to allow testing
+         * without user interaction with the transaction UX prompt.
+         *
+         * Matches the `Set SPC Transaction Mode
+         * <https://w3c.github.io/secure-payment-confirmation/#sctn-automation-set-spc-transaction-mode>`_
+         * WebDriver command.
+         *
+         * @example
+         * await test_driver.set_spc_transaction_mode("autoaccept");
+         * test.add_cleanup(() => {
+         *   return test_driver.set_spc_transaction_mode("none");
+         * });
+         *
+         * // Assumption: `request` is a PaymentRequest with a secure-payment-confirmation
+         * // payment method.
+         * const response = await request.show();
+         *
+         * @param {String} mode - The `transaction mode
+         *                        <https://w3c.github.io/secure-payment-confirmation/#enumdef-transactionautomationmode>`_
+         *                        to set. Must be one of "``none``",
+         *                        "``autoaccept``", or
+         *                        "``autoreject``".
+         * @param {WindowProxy} context - Browsing context in which
+         *                                to run the call, or null for the current
+         *                                browsing context.
+         *
+         * @returns {Promise} Fulfilled after the transaction mode has been set,
+         *                    or rejected if setting the mode fails.
+         */
+        set_spc_transaction_mode: function(mode, context=null) {
+          return window.test_driver_internal.set_spc_transaction_mode(mode, context);
+        },
     };
 
     window.test_driver_internal = {
@@ -516,6 +673,14 @@
             return Promise.reject(new Error("unimplemented"));
         },
 
+        minimize_window: function(context=null) {
+            return Promise.reject(new Error("unimplemented"));
+        },
+
+        set_window_rect: function(rect, context=null) {
+            return Promise.reject(new Error("unimplemented"));
+        },
+
         action_sequence: function(actions, context=null) {
             return Promise.reject(new Error("unimplemented"));
         },
@@ -560,5 +725,10 @@
         set_storage_access: function(origin, embedding_origin, blocked, context=null) {
             return Promise.reject(new Error("unimplemented"));
         },
+
+        set_spc_transaction_mode: function(mode, context=null) {
+            return Promise.reject(new Error("unimplemented"));
+        },
+
     };
 })();
diff --git a/third_party/blink/web_tests/resources/testharness.js b/third_party/blink/web_tests/resources/testharness.js
index f85b19f..5ebecca 100644
--- a/third_party/blink/web_tests/resources/testharness.js
+++ b/third_party/blink/web_tests/resources/testharness.js
@@ -542,8 +542,23 @@
         return test_environment.next_default_test_name();
     }
 
-    /*
-     * API functions
+    /**
+     * @callback TestFunction
+     * @param {Test} test - The test currnetly being run.
+     * @param {Any[]} args - Additional args to pass to function.
+     *
+     */
+
+    /**
+     * Create a synchronous test
+     *
+     * @param {TestFunction} func - Test function. This is executed
+     * immediately. If it returns without error, the test status is
+     * set to ``PASS``. If it throws an :js:class:`AssertionError`, or
+     * any other exception, the test status is set to ``FAIL``
+     * (typically from an `assert` function).
+     * @param {String} name - Test name. This must be unique in a
+     * given file and must be invariant between runs.
      */
     function test(func, name, properties)
     {
@@ -576,6 +591,17 @@
         }
     }
 
+    /**
+     * Create an asynchronous test
+     *
+     * @param {TestFunction|string} funcOrName - Initial step function
+     * to call immediately with the test name as an argument (if any),
+     * or name of the test.
+     * @param {String} name - Test name (if a test function was
+     * provided). This must be unique in a given file and must be
+     * invariant between runs.
+     * @returns {Test} An object representing the ongoing test.
+     */
     function async_test(func, name, properties)
     {
         if (tests.promise_setup_called) {
@@ -619,6 +645,19 @@
         return test_obj;
     }
 
+    /**
+     * Create a promise test.
+     *
+     * Promise tests are tests which are represeted by a promise
+     * object. If the promise is fulfilled the test passes, if it's
+     * rejected the test fails, otherwise the test passes.
+     *
+     * @param {TestFunction} func - Test function. This must return a
+     * promise. The test is automatically marked as complete once the
+     * promise settles.
+     * @param {String} name - Test name. This must be unique in a
+     * given file and must be invariant between runs.
+     */
     function promise_test(func, name, properties) {
         if (typeof func !== "function") {
             properties = name;
@@ -678,11 +717,12 @@
      *                          realm
      * @returns {Promise}
      *
-     * An arbitrary promise provided by the caller may have originated in
-     * another frame that have since navigated away, rendering the frame's
-     * document inactive. Such a promise cannot be used with `await` or
-     * Promise.resolve(), as microtasks associated with it may be prevented
-     * from being run. See https://github.com/whatwg/html/issues/5319 for a
+     * An arbitrary promise provided by the caller may have originated
+     * in another frame that have since navigated away, rendering the
+     * frame's document inactive. Such a promise cannot be used with
+     * `await` or Promise.resolve(), as microtasks associated with it
+     * may be prevented from being run. See `issue
+     * 5319<https://github.com/whatwg/html/issues/5319>`_ for a
      * particular case.
      *
      * In functions we define here, there is an expectation from the caller
@@ -695,6 +735,16 @@
         return new Promise(promise.then.bind(promise));
     }
 
+    /**
+     * Assert that a Promise is rejected with the right ECMAScript exception.
+     *
+     * @param {Test} test - the `Test` to use for the assertion.
+     * @param {Function} constructor - The expected exception constructor.
+     * @param {Promise} promise - The promise that's expected to
+     * reject with the given exception.
+     * @param {string} [description] Error message to add to assert in case of
+     *                               failure.
+     */
     function promise_rejects_js(test, constructor, promise, description) {
         return bring_promise_to_current_realm(promise)
             .then(test.unreached_func("Should have rejected: " + description))
@@ -707,9 +757,6 @@
     /**
      * Assert that a Promise is rejected with the right DOMException.
      *
-     * @param test the test argument passed to promise_test
-     * @param {number|string} type.  See documentation for assert_throws_dom.
-     *
      * For the remaining arguments, there are two ways of calling
      * promise_rejects_dom:
      *
@@ -721,8 +768,22 @@
      * third argument should be the DOMException constructor from that global,
      * the fourth argument the promise expected to reject, and the fifth,
      * optional, argument the assertion description.
+     *
+     * @param {Test} test - the `Test` to use for the assertion.
+     * @param {number|string} type - See documentation for
+     * `assert_throws_dom <#assert_throws_dom>`_.
+     * @param {Function} promiseOrConstructor - Either the constructor
+     * for the expected exception (if the exception comes from another
+     * global), or the promise that's expected to reject (if the
+     * exception comes from the current global).
+     * @param {Function|string} descriptionOrPromise - Either the
+     * promise that's expected to reject (if the exception comes from
+     * another global), or the optional description of the condition
+     * being tested (if the exception comes from the current global).
+     * @param {string} [description] - Description of the condition
+     * being tested (if the exception comes from another global).
+     *
      */
-
     function promise_rejects_dom(test, type, promiseOrConstructor, descriptionOrPromise, maybeDescription) {
         let constructor, promise, description;
         if (typeof promiseOrConstructor === "function" &&
@@ -745,6 +806,16 @@
             });
     }
 
+    /**
+     * Assert that a Promise is rejected with the provided value.
+     *
+     * @param {Test} test - the `Test` to use for the assertion.
+     * @param {Any} exception - The expected value of the rejected promise.
+     * @param {Promise} promise - The promise that's expected to
+     * reject.
+     * @param {string} [description] Error message to add to assert in case of
+     *                               failure.
+     */
     function promise_rejects_exactly(test, exception, promise, description) {
         return bring_promise_to_current_realm(promise)
             .then(test.unreached_func("Should have rejected: " + description))
@@ -755,9 +826,23 @@
     }
 
     /**
-     * This constructor helper allows DOM events to be handled using Promises,
-     * which can make it a lot easier to test a very specific series of events,
+     * Allow DOM events to be handled using Promises.
+     *
+     * This can make it a lot easier to test a very specific series of events,
      * including ensuring that unexpected events are not fired at any point.
+     *
+     * `EventWatcher` will assert if an event occurs while there is no `wait_for`
+     * created Promise waiting to be fulfilled, or if the event is of a different type
+     * to the type currently expected. This ensures that only the events that are
+     * expected occur, in the correct order, and with the correct timing.
+     *
+     * @constructor
+     * @param {Test} test - The `Test` to use for the assertion.
+     * @param {EventTarget} watchedNode - The target expected to receive the events.
+     * @param {string[]} eventTypes - List of events to watch for.
+     * @param {Promise} timeoutPromise - Promise that will cause the
+     * test to be set to `TIMEOUT` once fulfilled.
+     *
      */
     function EventWatcher(test, watchedNode, eventTypes, timeoutPromise)
     {
@@ -806,15 +891,13 @@
          * Returns a Promise that will resolve after the specified event or
          * series of events has occurred.
          *
-         * @param options An optional options object. If the 'record' property
-         *                on this object has the value 'all', when the Promise
-         *                returned by this function is resolved,  *all* Event
-         *                objects that were waited for will be returned as an
-         *                array.
+         * @param {Object} options An optional options object. If the 'record' property
+         *                 on this object has the value 'all', when the Promise
+         *                 returned by this function is resolved,  *all* Event
+         *                 objects that were waited for will be returned as an
+         *                 array.
          *
-         * For example,
-         *
-         * ```js
+         * @example
          * const watcher = new EventWatcher(t, div, [ 'animationstart',
          *                                            'animationiteration',
          *                                            'animationend' ]);
@@ -823,7 +906,6 @@
          *   assert_equals(evts[0].elapsedTime, 0.0);
          *   assert_equals(evts[1].elapsedTime, 2.0);
          * });
-         * ```
          */
         this.wait_for = function(types, options) {
             if (waitingFor) {
@@ -865,6 +947,9 @@
             });
         };
 
+        /**
+         * Stop listening for events
+         */
         function stop_watching() {
             for (var i = 0; i < eventTypes.length; i++) {
                 watchedNode.removeEventListener(eventTypes[i], eventHandler, false);
@@ -877,6 +962,46 @@
     }
     expose(EventWatcher, 'EventWatcher');
 
+    /**
+     * @typedef {Object} SettingsObject
+     * @property {bool} single_test - Use the single-page-test
+     * mode. In this mode the Document represents a single
+     * `async_test`. Asserts may be used directly without requiring
+     * `Test.step` or similar wrappers, and any exceptions set the
+     * status of the test rather than the status of the harness.
+     * @property {bool} allow_uncaught_exception - don't treat an
+     * uncaught exception as an error; needed when e.g. testing the
+     * `window.onerror` handler.
+     * @property {boolean} explicit_done - Wait for a call to `done()`
+     * before declaring all tests complete (this is always true for
+     * single-page tests).
+     * @property hide_test_state - hide the test state output while
+     * the test is running; This is helpful when the output of the test state
+     * may interfere the test results.
+     * @property {bool} explicit_timeout - disable file timeout; only
+     * stop waiting for results when the `timeout()` function is
+     * called This should typically only be set for manual tests, or
+     * by a test runner that providees its own timeout mechanism.
+     * @property {number} timeout_multiplier - Multiplier to apply to
+     * per-test timeouts. This should only be set by a test runner.
+     * @property {Document} output_document - The document to which
+     * results should be logged. By default this is the current
+     * document but could be an ancestor document in some cases e.g. a
+     * SVG test loaded in an HTML wrapper
+     *
+     */
+
+    /**
+     * Configure the harness
+     *
+     * @param {Function|SettingsObject} funcOrProperties - Either a
+     * setup function to run, or a set of properties. If this is a
+     * function that function is run synchronously. Any exception in
+     * the function will set the overall harness status to `ERROR`.
+     * @param {SettingsObject} maybeProperties - An object containing
+     * the settings to use, if the first argument is a function.
+     *
+     */
     function setup(func_or_properties, maybe_properties)
     {
         var func = null;
@@ -893,7 +1018,18 @@
         test_environment.on_new_harness_properties(properties);
     }
 
-    function promise_setup(func, maybe_properties)
+    /**
+     * Configure the harness, waiting for a promise to resolve
+     * before running any `promise_test` tests.
+     *
+     * @param {Function} func - Function returning a promise that's
+     * run synchronously. Promise tests are not run until after this
+     * function has resolved.
+     * @param {SettingsObject} [properties] - An object containing
+     * the harness settings to use.
+     *
+     */
+    function promise_setup(func, properties={})
     {
         if (typeof func !== "function") {
             tests.set_status(tests.status.ERROR,
@@ -910,7 +1046,6 @@
         tests.promise_tests = tests.promise_tests
             .then(function()
                   {
-                      var properties = maybe_properties || {};
                       var result;
 
                       tests.setup(null, properties);
@@ -931,6 +1066,17 @@
                    });
     }
 
+    /**
+     * Mark test loading as complete.
+     *
+     * Typically this function is called implicitly on page load; it's
+     * only necessary for users to call this when either the
+     * ``explict_done`` or ``single_page`` properties have been set
+     * via the :js:func:`setup` function.
+     *
+     * For single page tests this marks the test as complete and sets its status.
+     * For other tests, this marks test loading as complete, but doesn't affect ongoing tests.
+     */
     function done() {
         if (tests.tests.length === 0) {
             // `done` is invoked after handling uncaught exceptions, so if the
@@ -952,6 +1098,20 @@
         tests.end_wait();
     }
 
+    /**
+     * @deprecated generate a list of tests from a function and list of arguments
+     *
+     * This is deprecated because it runs all the tests outside of the test functions
+     * and as a result any test throwing an exception will result in no tests being
+     * run. In almost all cases, you should simply call test within the loop you would
+     * use to generate the parameter list array.
+     *
+     * @param {Function} func - The function that will be called for each generated tests.
+     * @param {Any[][]} args - An array of arrays. Each nested array
+     * has the structure `[testName, ...testArgs]`. For each of these nested arrays
+     * array, a test is generated with name `testName` and test function equivalent to
+     * `func(..testArgs)`.
+     */
     function generate_tests(func, args, properties) {
         forEach(args, function(x, i)
                 {
@@ -965,23 +1125,35 @@
                 });
     }
 
-    /*
-     * Register a function as a DOM event listener to the given object for the
-     * event bubbling phase.
+    /**
+     * @deprecated
      *
-     * This function was deprecated in November of 2019.
+     * Register a function as a DOM event listener to the
+     * given object for the event bubbling phase.
+     *
+     * @param {EventTarget} object - Event target
+     * @param {string} event - Event name
+     * @param {Function} callback - Event handler.
      */
     function on_event(object, event, callback)
     {
         object.addEventListener(event, callback, false);
     }
 
-    function step_timeout(f, t) {
+    /**
+     * Global version of :js:func:`Test.step_timeout` for use in single page tests.
+     *
+     * @param {Function} func - Function to run after the timeout
+     * @param {number} timeout - Time in ms to wait before running the
+     * test step. The actual wait time is ``timeout`` x
+     * ``timeout_multiplier``.
+     */
+    function step_timeout(func, timeout) {
         var outer_this = this;
         var args = Array.prototype.slice.call(arguments, 2);
         return setTimeout(function() {
-            f.apply(outer_this, args);
-        }, t * tests.timeout_multiplier);
+            func.apply(outer_this, args);
+        }, timeout * tests.timeout_multiplier);
     }
 
     expose(test, 'test');
@@ -1079,8 +1251,30 @@
         "0xffff": "uffff",
     };
 
-    /*
+    /**
      * Convert a value to a nice, human-readable string
+     *
+     * When many JavaScript Object values are coerced to a String, the
+     * resulting value will be ``"[object Object]"``. This obscures
+     * helpful information, making the coerced value unsuitable for
+     * use in assertion messages, test names, and debugging
+     * statements. `format_value` produces more distinctive string
+     * representations of many kinds of objects, including arrays and
+     * the more important DOM Node types. It also translates String
+     * values containing control characters to include human-readable
+     * representations.
+     *
+     * @example
+     * // "Document node with 2 children"
+     * format_value(document);
+     * @example
+     * // "\"foo\\uffffbar\""
+     * format_value("foo\uffffbar");
+     * @example
+     * // "[-0, Infinity]"
+     * format_value([-0, Infinity]);
+     * @param {Any} val - The value to convert to a string.
+     * @returns {string} - A string representation of ``val``, optimised for human readability.
      */
     function format_value(val, seen)
     {
@@ -1187,12 +1381,8 @@
                 status = Test.statuses.PASS;
                 return rv;
             } catch(e) {
-                if (e instanceof AssertionError) {
-                    status = Test.statuses.FAIL;
-                    stack = e.stack;
-                 } else {
-                    status = Test.statuses.ERROR;
-                 }
+                status = Test.statuses.FAIL;
+                stack = e.stack ? e.stack : null;
                 throw e;
             } finally {
                 if (tests.output && !stack) {
@@ -1206,6 +1396,12 @@
         expose(assert_wrapper, name);
     }
 
+    /**
+     * Assert that ``actual`` is strictly true
+     *
+     * @param {Any} actual - Value that is asserted to be true
+     * @param {string} [description] - Description of the condition being tested
+     */
     function assert_true(actual, description)
     {
         assert(actual === true, "assert_true", description,
@@ -1213,6 +1409,12 @@
     }
     expose_assert(assert_true, "assert_true");
 
+    /**
+     * Assert that ``actual`` is strictly false
+     *
+     * @param {Any} actual - Value that is asserted to be false
+     * @param {string} [description] - Description of the condition being tested
+     */
     function assert_false(actual, description)
     {
         assert(actual === false, "assert_false", description,
@@ -1232,6 +1434,17 @@
         return x === y;
     }
 
+    /**
+     * Assert that ``actual`` is the same value as ``expected``.
+     *
+     * For objects this compares by cobject identity; for primitives
+     * this distinguishes between 0 and -0, and has correct handling
+     * of NaN.
+     *
+     * @param {Any} actual - Test value.
+     * @param {Any} expected - Expected value.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_equals(actual, expected, description)
     {
          /*
@@ -1250,18 +1463,32 @@
     }
     expose_assert(assert_equals, "assert_equals");
 
+    /**
+     * Assert that ``actual`` is not the same value as ``expected``.
+     *
+     * Comparison is as for :js:func:`assert_equals`.
+     *
+     * @param {Any} actual - Test value.
+     * @param {Any} expected - The value ``actual`` is expected to be different to.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_not_equals(actual, expected, description)
     {
-         /*
-          * Test if two primitives are unequal or two objects
-          * are different objects
-          */
         assert(!same_value(actual, expected), "assert_not_equals", description,
                                               "got disallowed value ${actual}",
                                               {actual:actual});
     }
     expose_assert(assert_not_equals, "assert_not_equals");
 
+    /**
+     * Assert that ``expected`` is an array and ``actual`` is one of the members.
+     * This is implemented using ``indexOf``, so doesn't handle NaN or ±0 correctly.
+     *
+     * @param {Any} actual - Test value.
+     * @param {Array} expected - An array that ``actual`` is expected to
+     * be a member of.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_in_array(actual, expected, description)
     {
         assert(expected.indexOf(actual) != -1, "assert_in_array", description,
@@ -1272,6 +1499,18 @@
 
     // This function was deprecated in July of 2015.
     // See https://github.com/web-platform-tests/wpt/issues/2033
+    /**
+     * @deprecated
+     * Recursively compare two objects for equality.
+     *
+     * See `Issue 2033
+     * <https://github.com/web-platform-tests/wpt/issues/2033>`_ for
+     * more information.
+     *
+     * @param {Object} actual - Test value.
+     * @param {Object} expected - Expected value.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_object_equals(actual, expected, description)
     {
          assert(typeof actual === "object" && actual !== null, "assert_object_equals", description,
@@ -1308,6 +1547,14 @@
     }
     expose_assert(assert_object_equals, "assert_object_equals");
 
+    /**
+     * Assert that ``actual`` and ``expected`` are both arrays, and that the array properties of
+     * ``actual`` and ``expected`` are all the same value (as for :js:func:`assert_equals`).
+     *
+     * @param {Array} actual - Test array.
+     * @param {Array} expected - Array that is expected to contain the same values as ``actual``.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_array_equals(actual, expected, description)
     {
         const max_array_length = 20;
@@ -1365,6 +1612,16 @@
     }
     expose_assert(assert_array_equals, "assert_array_equals");
 
+    /**
+     * Assert that each array property in ``actual`` is a number within
+     * ± `epsilon` of the corresponding property in `expected`.
+     *
+     * @param {Array} actual - Array of test values.
+     * @param {Array} expected - Array of values expected to be close to the values in ``actual``.
+     * @param {number} epsilon - Magnitude of allowed difference
+     * between each value in ``actual`` and ``expected``.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_array_approx_equals(actual, expected, epsilon, description)
     {
         /*
@@ -1393,6 +1650,14 @@
     }
     expose_assert(assert_array_approx_equals, "assert_array_approx_equals");
 
+    /**
+     * Assert that ``actual`` is within ± ``epsilon`` of ``expected``.
+     *
+     * @param {number} actual - Test value.
+     * @param {number} expected - Value number is expected to be close to.
+     * @param {number} epsilon - Magnitude of allowed difference between ``actual`` and ``expected``.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_approx_equals(actual, expected, epsilon, description)
     {
         /*
@@ -1416,6 +1681,13 @@
     }
     expose_assert(assert_approx_equals, "assert_approx_equals");
 
+    /**
+     * Assert that ``actual`` is a number less than ``expected``.
+     *
+     * @param {number} actual - Test value.
+     * @param {number} expected - Number that ``actual`` must be less than.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_less_than(actual, expected, description)
     {
         /*
@@ -1433,6 +1705,13 @@
     }
     expose_assert(assert_less_than, "assert_less_than");
 
+    /**
+     * Assert that ``actual`` is a number greater than ``expected``.
+     *
+     * @param {number} actual - Test value.
+     * @param {number} expected - Number that ``actual`` must be greater than.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_greater_than(actual, expected, description)
     {
         /*
@@ -1450,6 +1729,15 @@
     }
     expose_assert(assert_greater_than, "assert_greater_than");
 
+    /**
+     * Assert that ``actual`` is a number greater than ``lower`` and less
+     * than ``upper`` but not equal to either.
+     *
+     * @param {number} actual - Test value.
+     * @param {number} lower - Number that ``actual`` must be greater than.
+     * @param {number} upper - Number that ``actual`` must be less than.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_between_exclusive(actual, lower, upper, description)
     {
         /*
@@ -1468,6 +1756,14 @@
     }
     expose_assert(assert_between_exclusive, "assert_between_exclusive");
 
+    /**
+     * Assert that ``actual`` is a number less than or equal to ``expected``.
+     *
+     * @param {number} actual - Test value.
+     * @param {number} expected - Number that ``actual`` must be less
+     * than or equal to.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_less_than_equal(actual, expected, description)
     {
         /*
@@ -1485,6 +1781,14 @@
     }
     expose_assert(assert_less_than_equal, "assert_less_than_equal");
 
+    /**
+     * Assert that ``actual`` is a number greater than or equal to ``expected``.
+     *
+     * @param {number} actual - Test value.
+     * @param {number} expected - Number that ``actual`` must be greater
+     * than or equal to.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_greater_than_equal(actual, expected, description)
     {
         /*
@@ -1502,6 +1806,15 @@
     }
     expose_assert(assert_greater_than_equal, "assert_greater_than_equal");
 
+    /**
+     * Assert that ``actual`` is a number greater than or equal to ``lower`` and less
+     * than or equal to ``upper``.
+     *
+     * @param {number} actual - Test value.
+     * @param {number} lower - Number that ``actual`` must be greater than or equal to.
+     * @param {number} upper - Number that ``actual`` must be less than or eqaul to.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_between_inclusive(actual, lower, upper, description)
     {
         /*
@@ -1520,6 +1833,13 @@
     }
     expose_assert(assert_between_inclusive, "assert_between_inclusive");
 
+    /**
+     * Assert that ``actual`` matches the RegExp ``expected``.
+     *
+     * @param {String} actual - Test string.
+     * @param {RegExp} expected - RegExp ``actual`` must match.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_regexp_match(actual, expected, description) {
         /*
          * Test if a string (actual) matches a regexp (expected)
@@ -1531,6 +1851,14 @@
     }
     expose_assert(assert_regexp_match, "assert_regexp_match");
 
+    /**
+     * Assert that the class string of ``object`` as returned in
+     * ``Object.prototype.toString`` is equal to ``class_name``.
+     *
+     * @param {Object} object - Object to stringify.
+     * @param {string} class_string - Expected class string for ``object``.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_class_string(object, class_string, description) {
         var actual = {}.toString.call(object);
         var expected = "[object " + class_string + "]";
@@ -1540,6 +1868,13 @@
     }
     expose_assert(assert_class_string, "assert_class_string");
 
+    /**
+     * Assert that ``object`` has an own property with name ``property_name``.
+     *
+     * @param {Object} object - Object that should have the given property.
+     * @param {string} property_name - Expected property name.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_own_property(object, property_name, description) {
         assert(object.hasOwnProperty(property_name),
                "assert_own_property", description,
@@ -1547,6 +1882,13 @@
     }
     expose_assert(assert_own_property, "assert_own_property");
 
+    /**
+     * Assert that ``object`` does not have an own property with name ``property_name``.
+     *
+     * @param {Object} object - Object that should not have the given property.
+     * @param {string} property_name - Property name to test.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_not_own_property(object, property_name, description) {
         assert(!object.hasOwnProperty(property_name),
                "assert_not_own_property", description,
@@ -1579,9 +1921,44 @@
                    {p:property_name});
         };
     }
-    expose_assert(_assert_inherits("assert_inherits"), "assert_inherits");
-    expose_assert(_assert_inherits("assert_idl_attribute"), "assert_idl_attribute");
 
+    /**
+     * Assert that ``object`` does not have an own property with name
+     * ``property_name``, but inherits one through the prototype chain.
+     *
+     * @param {Object} object - Object that should have the given property in its prototype chain.
+     * @param {string} property_name - Expected property name.
+     * @param {string} [description] - Description of the condition being tested.
+     */
+    function assert_inherits(object, property_name, description) {
+        return _assert_inherits("assert_inherits")(object, property_name, description);
+    }
+    expose_assert(assert_inherits, "assert_inherits");
+
+    /**
+     * Alias for :js:func:`insert_inherits`.
+     *
+     * @param {Object} object - Object that should have the given property in its prototype chain.
+     * @param {string} property_name - Expected property name.
+     * @param {string} [description] - Description of the condition being tested.
+     */
+    function assert_idl_attribute(object, property_name, description) {
+        return _assert_inherits("assert_idl_attribute")(object, property_name, description);
+    }
+    expose_assert(assert_idl_attribute, "assert_idl_attribute");
+
+
+    /**
+     * Assert that ``object`` has a property named ``property_name`` and that the property is readonly.
+     *
+     * Note: The implementation tries to update the named property, so
+     * any side effects of updating will be triggered. Users are
+     * encouraged to instead inspect the property descriptor of ``property_name`` on ``object``.
+     *
+     * @param {Object} object - Object that should have the given property in its prototype chain.
+     * @param {string} property_name - Expected property name.
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_readonly(object, property_name, description)
     {
          var initial_value = object[property_name];
@@ -1604,7 +1981,7 @@
      *
      * @param {object} constructor The expected exception constructor.
      * @param {Function} func Function which should throw.
-     * @param {string} description Error description for the case that the error is not thrown.
+     * @param {string} [description] Error description for the case that the error is not thrown.
      */
     function assert_throws_js(constructor, func, description)
     {
@@ -1669,20 +2046,13 @@
         }
     }
 
+    // TODO: Figure out how to document the overloads better.
+    // sphinx-js doesn't seem to handle @variation correctly,
+    // and only expects a single JSDoc entry per function.
     /**
      * Assert a DOMException with the expected type is thrown.
      *
-     * @param {number|string} type The expected exception name or code.  See the
-     *        table of names and codes at
-     *        https://heycam.github.io/webidl/#dfn-error-names-table
-     *        If a number is passed it should be one of the numeric code values
-     *        in that table (e.g. 3, 4, etc).  If a string is passed it can
-     *        either be an exception name (e.g. "HierarchyRequestError",
-     *        "WrongDocumentError") or the name of the corresponding error code
-     *        (e.g. "HIERARCHY_REQUEST_ERR", "WRONG_DOCUMENT_ERR").
-     *
-     * For the remaining arguments, there are two ways of calling
-     * promise_rejects_dom:
+     * There are two ways of calling assert_throws_dom:
      *
      * 1) If the DOMException is expected to come from the current global, the
      * second argument should be the function expected to throw and a third,
@@ -1692,6 +2062,22 @@
      * second argument should be the DOMException constructor from that global,
      * the third argument the function expected to throw, and the fourth, optional,
      * argument the assertion description.
+     *
+     * @param {number|string} type - The expected exception name or
+     * code.  See the `table of names and codes
+     * <https://webidl.spec.whatwg.org/#dfn-error-names-table>`_. If a
+     * number is passed it should be one of the numeric code values in
+     * that table (e.g. 3, 4, etc).  If a string is passed it can
+     * either be an exception name (e.g. "HierarchyRequestError",
+     * "WrongDocumentError") or the name of the corresponding error
+     * code (e.g. "``HIERARCHY_REQUEST_ERR``", "``WRONG_DOCUMENT_ERR``").
+     * @param {Function} descriptionOrFunc - The function expected to
+     * throw (if the exeception comes from another global), or the
+     * optional description of the condition being tested (if the
+     * exception comes from the current global).
+     * @param {string} [description] - Description of the condition
+     * being tested (if the exception comes from another global).
+     *
      */
     function assert_throws_dom(type, funcOrConstructor, descriptionOrFunc, maybeDescription)
     {
@@ -1865,7 +2251,7 @@
      *
      * @param {value} exception The expected exception.
      * @param {Function} func Function which should throw.
-     * @param {string} description Error description for the case that the error is not thrown.
+     * @param {string} [description] Error description for the case that the error is not thrown.
      */
     function assert_throws_exactly(exception, func, description)
     {
@@ -1896,15 +2282,43 @@
         }
     }
 
+    /**
+     * Asserts if called. Used to ensure that a specific codepath is
+     * not taken e.g. that an error event isn't fired.
+     *
+     * @param {string} [description] - Description of the condition being tested.
+     */
     function assert_unreached(description) {
          assert(false, "assert_unreached", description,
                 "Reached unreachable code");
     }
     expose_assert(assert_unreached, "assert_unreached");
 
-    function assert_any(assert_func, actual, expected_array)
+    /**
+     * @callback AssertFunc
+     * @param {Any} actual
+     * @param {Any} expected
+     * @param {Any[]} args
+     */
+
+    /**
+     * Asserts that ``actual`` matches at least one value of ``expected``
+     * according to a comparison defined by ``assert_func``.
+     *
+     * Note that tests with multiple allowed pass conditions are bad
+     * practice unless the spec specifically allows multiple
+     * behaviours. Test authors should not use this method simply to
+     * hide UA bugs.
+     *
+     * @param {AssertFunc} assert_func - Function to compare actual
+     * and expected. It must throw when the comparison fails and
+     * return when the comparison passes.
+     * @param {Any} actual - Test value.
+     * @param {Array} expected_array - Array of possible expected values.
+     * @param {Any[]} args - Additional arguments to pass to ``assert_func``.
+     */
+    function assert_any(assert_func, actual, expected_array, ...args)
     {
-        var args = [].slice.call(arguments, 3);
         var errors = [];
         var passed = false;
         forEach(expected_array,
@@ -1936,7 +2350,7 @@
      *     assert_implements(window.Foo, 'Foo is not supported');
      *
      * @param {object} condition The truthy value to test
-     * @param {string} description Error description for the case that the condition is not truthy.
+     * @param {string} [description] Error description for the case that the condition is not truthy.
      */
     function assert_implements(condition, description) {
         assert(!!condition, "assert_implements", description);
@@ -1954,25 +2368,37 @@
      *                                "webm video playback not supported");
      *
      * @param {object} condition The truthy value to test
-     * @param {string} description Error description for the case that the condition is not truthy.
+     * @param {string} [description] Error description for the case that the condition is not truthy.
      */
     function assert_implements_optional(condition, description) {
         if (!condition) {
             throw new OptionalFeatureUnsupportedError(description);
         }
     }
-    expose_assert(assert_implements_optional, "assert_implements_optional")
+    expose_assert(assert_implements_optional, "assert_implements_optional");
 
+    /**
+     * @class
+     *
+     * A single subtest. A Test is not constructed directly but via the
+     * :js:func:`test`, :js:func:`async_test` or :js:func:`promise_test` functions.
+     *
+     * @param {string} name - This must be unique in a given file and must be
+     * invariant between runs.
+     *
+     */
     function Test(name, properties)
     {
         if (tests.file_is_test && tests.tests.length) {
             throw new Error("Tried to create a test with file_is_test");
         }
+        /** The test name. */
         this.name = name;
 
         this.phase = (tests.is_aborted || tests.phase === tests.phases.COMPLETE) ?
             this.phases.COMPLETE : this.phases.INITIAL;
 
+        /** The test status code.*/
         this.status = this.NOTRUN;
         this.timeout_id = null;
         this.index = null;
@@ -1983,7 +2409,9 @@
             this.timeout_length *= tests.timeout_multiplier;
         }
 
+        /** A message indicating the reason for test failure. */
         this.message = null;
+        /** Stack trace in case of failure. */
         this.stack = null;
 
         this.steps = [];
@@ -2003,6 +2431,16 @@
         tests.push(this);
     }
 
+    /**
+     * Enum of possible test statuses.
+     *
+     * :values:
+     *   - ``PASS``
+     *   - ``FAIL``
+     *   - ``TIMEOUT``
+     *   - ``NOTRUN``
+     *   - ``PRECONDITION_FAILED``
+     */
     Test.statuses = {
         PASS:0,
         FAIL:1,
@@ -2052,6 +2490,15 @@
         return this._structured_clone;
     };
 
+    /**
+     * Run a single step of an ongoing test.
+     *
+     * @param {string} func - Callback function to run as a step. If
+     * this throws an :js:func:`AssertionError`, or any other
+     * exception, the :js:class:`Test` status is set to ``FAIL``.
+     * @param {Object} [this_obj] - The object to use as the this
+     * value when calling ``func``. Defaults to the  :js:class:`Test` object.
+     */
     Test.prototype.step = function(func, this_obj)
     {
         if (this.phase > this.phases.STARTED) {
@@ -2101,6 +2548,26 @@
         }
     };
 
+    /**
+     * Wrap a function so that it runs as a step of the current test.
+     *
+     * This allows creating a callback function that will run as a
+     * test step.
+     *
+     * @example
+     * let t = async_test("Example");
+     * onload = t.step_func(e => {
+     *   assert_equals(e.name, "load");
+     *   // Mark the test as complete.
+     *   t.done();
+     * })
+     *
+     * @param {string} func - Function to run as a step. If this
+     * throws an :js:func:`AssertionError`, or any other exception,
+     * the :js:class:`Test` status is set to ``FAIL``.
+     * @param {Object} [this_obj] - The object to use as the this
+     * value when calling ``func``. Defaults to the :js:class:`Test` object.
+     */
     Test.prototype.step_func = function(func, this_obj)
     {
         var test_this = this;
@@ -2116,6 +2583,18 @@
         };
     };
 
+    /**
+     * Wrap a function so that it runs as a step of the current test,
+     * and automatically marks the test as complete if the function
+     * returns without error.
+     *
+     * @param {string} func - Function to run as a step. If this
+     * throws an :js:func:`AssertionError`, or any other exception,
+     * the :js:class:`Test` status is set to ``FAIL``. If it returns
+     * without error the status is set to ``PASS``.
+     * @param {Object} [this_obj] - The object to use as the this
+     * value when calling `func`. Defaults to the :js:class:`Test` object.
+     */
     Test.prototype.step_func_done = function(func, this_obj)
     {
         var test_this = this;
@@ -2134,6 +2613,14 @@
         };
     };
 
+    /**
+     * Return a function that automatically sets the current test to
+     * ``FAIL`` if it's called.
+     *
+     * @param {string} [description] - Error message to add to assert
+     * in case of failure.
+     *
+     */
     Test.prototype.unreached_func = function(description)
     {
         return this.step_func(function() {
@@ -2141,37 +2628,68 @@
         });
     };
 
-    Test.prototype.step_timeout = function(f, timeout) {
+    /**
+     * Run a function as a step of the test after a given timeout.
+     *
+     * This multiplies the timeout by the global timeout multiplier to
+     * account for the expected execution speed of the current test
+     * environment. For example ``test.step_timeout(f, 2000)`` with a
+     * timeout multiplier of 2 will wait for 4000ms before calling ``f``.
+     *
+     * In general it's encouraged to use :js:func:`Test.step_wait` or
+     * :js:func:`step_wait_func` in preference to this function where possible,
+     * as they provide better test performance.
+     *
+     * @param {Function} func - Function to run as a test
+     * step.
+     * @param {number} timeout - Time in ms to wait before running the
+     * test step. The actual wait time is ``timeout`` x
+     * ``timeout_multiplier``.
+     *
+     */
+    Test.prototype.step_timeout = function(func, timeout) {
         var test_this = this;
         var args = Array.prototype.slice.call(arguments, 2);
         return setTimeout(this.step_func(function() {
-            return f.apply(test_this, args);
+            return func.apply(test_this, args);
         }), timeout * tests.timeout_multiplier);
     };
 
+    /**
+     * Poll for a function to return true, and call a callback
+     * function once it does, or assert if a timeout is
+     * reached. This is preferred over a simple step_timeout
+     * whenever possible since it allows the timeout to be longer
+     * to reduce intermittents without compromising test execution
+     * speed when the condition is quickly met.
+     *
+     * @example
+     * async_test(t => {
+     *  const popup = window.open("resources/coop-coep.py?coop=same-origin&coep=&navigate=about:blank");
+     *  t.add_cleanup(() => popup.close());
+     *  assert_equals(window, popup.opener);
+     *
+     *  popup.onload = t.step_func(() => {
+     *    assert_true(popup.location.href.endsWith("&navigate=about:blank"));
+     *    // Use step_wait_func_done as about:blank cannot message back.
+     *    t.step_wait_func_done(() => popup.location.href === "about:blank");
+     *  });
+     * }, "Navigating a popup to about:blank");
+     *
+     * @param {Function} cond A function taking no arguments and
+     *                        returning a boolean. The callback is called
+     *                        when this function returns true.
+     * @param {Function} func A function taking no arguments to call once
+     *                        the condition is met.
+     * @param {string} [description] Error message to add to assert in case of
+     *                               failure.
+     * @param {number} timeout Timeout in ms. This is multiplied by the global
+     *                         timeout_multiplier
+     * @param {number} interval Polling interval in ms
+     *
+     */
     Test.prototype.step_wait_func = function(cond, func, description,
                                              timeout=3000, interval=100) {
-        /**
-         * Poll for a function to return true, and call a callback
-         * function once it does, or assert if a timeout is
-         * reached. This is preferred over a simple step_timeout
-         * whenever possible since it allows the timeout to be longer
-         * to reduce intermittents without compromising test execution
-         * speed when the condition is quickly met.
-         *
-         * @param {Function} cond A function taking no arguments and
-         *                        returning a boolean. The callback is called
-         *                        when this function returns true.
-         * @param {Function} func A function taking no arguments to call once
-         *                        the condition is met.
-         * @param {string} description Error message to add to assert in case of
-         *                             failure.
-         * @param {number} timeout Timeout in ms. This is multiplied by the global
-         *                         timeout_multiplier
-         * @param {number} interval Polling interval in ms
-         *
-         **/
-
         var timeout_full = timeout * tests.timeout_multiplier;
         var remaining = Math.ceil(timeout_full / interval);
         var test_this = this;
@@ -2192,57 +2710,62 @@
         wait_for_inner();
     };
 
+    /**
+     * Poll for a function to return true, and invoke a callback
+     * followed by this.done() once it does, or assert if a timeout
+     * is reached. This is preferred over a simple step_timeout
+     * whenever possible since it allows the timeout to be longer
+     * to reduce intermittents without compromising test execution speed
+     * when the condition is quickly met.
+     *
+     * @param {Function} cond A function taking no arguments and
+     *                        returning a boolean. The callback is called
+     *                        when this function returns true.
+     * @param {Function} func A function taking no arguments to call once
+     *                        the condition is met.
+     * @param {string} [description] Error message to add to assert in case of
+     *                               failure.
+     * @param {number} timeout Timeout in ms. This is multiplied by the global
+     *                         timeout_multiplier
+     * @param {number} interval Polling interval in ms
+     *
+     */
     Test.prototype.step_wait_func_done = function(cond, func, description,
                                                   timeout=3000, interval=100) {
-        /**
-         * Poll for a function to return true, and invoke a callback
-         * followed by this.done() once it does, or assert if a timeout
-         * is reached. This is preferred over a simple step_timeout
-         * whenever possible since it allows the timeout to be longer
-         * to reduce intermittents without compromising test execution speed
-         * when the condition is quickly met.
-         *
-         * @param {Function} cond A function taking no arguments and
-         *                        returning a boolean. The callback is called
-         *                        when this function returns true.
-         * @param {Function} func A function taking no arguments to call once
-         *                        the condition is met.
-         * @param {string} description Error message to add to assert in case of
-         *                             failure.
-         * @param {number} timeout Timeout in ms. This is multiplied by the global
-         *                         timeout_multiplier
-         * @param {number} interval Polling interval in ms
-         *
-         **/
-
          this.step_wait_func(cond, () => {
             if (func) {
                 func();
             }
             this.done();
          }, description, timeout, interval);
-    }
+    };
 
+    /**
+     * Poll for a function to return true, and resolve a promise
+     * once it does, or assert if a timeout is reached. This is
+     * preferred over a simple step_timeout whenever possible
+     * since it allows the timeout to be longer to reduce
+     * intermittents without compromising test execution speed
+     * when the condition is quickly met.
+     *
+     * @example
+     * promise_test(async t => {
+     *  // …
+     * await t.step_wait(() => frame.contentDocument === null, "Frame navigated to a cross-origin document");
+     * // …
+     * }, "");
+     *
+     * @param {Function} cond A function taking no arguments and
+     *                        returning a boolean.
+     * @param {string} [description] Error message to add to assert in case of
+     *                              failure.
+     * @param {number} timeout Timeout in ms. This is multiplied by the global
+     *                         timeout_multiplier
+     * @param {number} interval Polling interval in ms
+     * @returns {Promise} Promise resolved once cond is met.
+     *
+     */
     Test.prototype.step_wait = function(cond, description, timeout=3000, interval=100) {
-        /**
-         * Poll for a function to return true, and resolve a promise
-         * once it does, or assert if a timeout is reached. This is
-         * preferred over a simple step_timeout whenever possible
-         * since it allows the timeout to be longer to reduce
-         * intermittents without compromising test execution speed
-         * when the condition is quickly met.
-         *
-         * @param {Function} cond A function taking no arguments and
-         *                        returning a boolean.
-         * @param {string} description Error message to add to assert in case of
-         *                             failure.
-         * @param {number} timeout Timeout in ms. This is multiplied by the global
-         *                         timeout_multiplier
-         * @param {number} interval Polling interval in ms
-         * @returns {Promise} Promise resolved once cond is met.
-         *
-         **/
-
         return new Promise(resolve => {
             this.step_wait_func(cond, resolve, description, timeout, interval);
         });
@@ -2258,11 +2781,16 @@
         this.cleanup_callbacks.push(callback);
     };
 
-    /*
+    /**
      * Schedule a function to be run after the test result is known, regardless
-     * of passing or failing state. The behavior of this function will not
+     * of passing or failing state.
+     *
+     * The behavior of this function will not
      * influence the result of the test, but if an exception is thrown, the
      * test harness will report an error.
+     *
+     * @param {Function} callback - The cleanup function to run. This
+     * is called with no arguments.
      */
     Test.prototype.add_cleanup = function(callback) {
         this._user_defined_cleanup_count += 1;
@@ -2287,6 +2815,9 @@
         this.stack = stack ? stack : null;
     };
 
+    /**
+     * Manually set the test status to ``TIMEOUT``.
+     */
     Test.prototype.timeout = function()
     {
         this.timeout_id = null;
@@ -2295,11 +2826,24 @@
         this.done();
     };
 
-    Test.prototype.force_timeout = Test.prototype.timeout;
+    /**
+     * Manually set the test status to ``TIMEOUT``.
+     *
+     * Alias for `Test.timeout <#Test.timeout>`_.
+     */
+    Test.prototype.force_timeout = function() {
+        return this.timeout();
+    };
 
     /**
-     * Update the test status, initiate "cleanup" functions, and signal test
-     * completion.
+     * Mark the test as complete.
+     *
+     * This sets the test status to ``PASS`` if no other status was
+     * already recorded. Any subsequent attempts to run additional
+     * test steps will be ignored.
+     *
+     * After setting the test status any test cleanup functions will
+     * be run.
      */
     Test.prototype.done = function()
     {
@@ -2318,7 +2862,7 @@
         if (settings.debug) {
             console.log("TEST DONE",
                         this.status,
-                        this.name,)
+                        this.name);
         }
 
         this.cleanup();
@@ -2340,10 +2884,10 @@
      * be cancelled.
      */
     Test.prototype.cleanup = function() {
-        var error_count = 0;
+        var errors = [];
         var bad_value_count = 0;
-        function on_error() {
-            error_count += 1;
+        function on_error(e) {
+            errors.push(e);
             // Abort tests immediately so that tests declared within subsequent
             // cleanup functions are not run.
             tests.abort();
@@ -2360,7 +2904,7 @@
                     try {
                         result = cleanup_callback();
                     } catch (e) {
-                        on_error();
+                        on_error(e);
                         return;
                     }
 
@@ -2375,7 +2919,7 @@
                 });
 
         if (!this._is_promise_test) {
-            cleanup_done(this_obj, error_count, bad_value_count);
+            cleanup_done(this_obj, errors, bad_value_count);
         } else {
             all_async(results,
                       function(result, done) {
@@ -2388,12 +2932,12 @@
                           }
                       },
                       function() {
-                          cleanup_done(this_obj, error_count, bad_value_count);
+                          cleanup_done(this_obj, errors, bad_value_count);
                       });
         }
     };
 
-    /**
+    /*
      * Determine if the return value of a cleanup function is valid for a given
      * test. Any test may return the value `undefined`. Tests created with
      * `promise_test` may alternatively return "thenable" object values.
@@ -2410,17 +2954,21 @@
         return false;
     }
 
-    function cleanup_done(test, error_count, bad_value_count) {
-        if (error_count || bad_value_count) {
+    function cleanup_done(test, errors, bad_value_count) {
+        if (errors.length || bad_value_count) {
             var total = test._user_defined_cleanup_count;
 
             tests.status.status = tests.status.ERROR;
+            tests.status.stack = null;
             tests.status.message = "Test named '" + test.name +
                 "' specified " + total +
                 " 'cleanup' function" + (total > 1 ? "s" : "");
 
-            if (error_count) {
-                tests.status.message += ", and " + error_count + " failed";
+            if (errors.length) {
+                tests.status.message += ", and " + errors.length + " failed";
+                tests.status.stack = ((typeof errors[0] === "object" &&
+                                       errors[0].hasOwnProperty("stack")) ?
+                                      errors[0].stack : null);
             }
 
             if (bad_value_count) {
@@ -2431,8 +2979,6 @@
             }
 
             tests.status.message += ".";
-
-            tests.status.stack = null;
         }
 
         test.phase = test.phases.COMPLETE;
@@ -2444,7 +2990,7 @@
         test._done_callbacks.length = 0;
     }
 
-    /*
+    /**
      * A RemoteTest object mirrors a Test object on a remote worker. The
      * associated RemoteWorker updates the RemoteTest object in response to
      * received events. In turn, the RemoteTest object replicates these events
@@ -2629,7 +3175,7 @@
     RemoteContext.prototype.remote_done = function(data) {
         if (tests.status.status === null &&
             data.status.status !== data.status.OK) {
-            tests.set_status(data.status.status, data.status.message, data.status.sack);
+            tests.set_status(data.status.status, data.status.message, data.status.stack);
         }
 
         for (let assert of data.asserts) {
@@ -2671,17 +3217,29 @@
         complete: RemoteContext.prototype.remote_done
     };
 
-    /*
-     * Harness
+    /**
+     * @class
+     * Status of the overall harness
      */
-
     function TestsStatus()
     {
+        /** The status code */
         this.status = null;
+        /** Message in case of failure */
         this.message = null;
+        /** Stack trace in case of an exception. */
         this.stack = null;
     }
 
+    /**
+     * Enum of possible harness statuses.
+     *
+     * :values:
+     *   - ``OK``
+     *   - ``ERROR``
+     *   - ``TIMEOUT``
+     *   - ``PRECONDITION_FAILED``
+     */
     TestsStatus.statuses = {
         OK:0,
         ERROR:1,
@@ -2696,8 +3254,7 @@
         1: "Error",
         2: "Timeout",
         3: "Optional Feature Unsupported"
-    }
-
+    };
 
     TestsStatus.prototype.structured_clone = function()
     {
@@ -2715,13 +3272,25 @@
 
     TestsStatus.prototype.format_status = function() {
         return this.formats[this.status];
-    }
+    };
 
+    /**
+     * @class
+     * Record of an assert that ran.
+     *
+     * @param {Test} test - The test which ran the assert.
+     * @param {string} assert_name - The function name of the assert.
+     * @param {Any} args - The arguments passed to the assert function.
+     */
     function AssertRecord(test, assert_name, args = []) {
+        /** Name of the assert that ran */
         this.assert_name = assert_name;
+        /** Test that ran the assert */
         this.test = test;
         // Avoid keeping complex objects alive
+        /** Stringification of the arguments that were passed to the assert function */
         this.args = args.map(x => format_value(x).replace(/\n/g, " "));
+        /** Status of the assert */
         this.status = null;
     }
 
@@ -2731,8 +3300,8 @@
             test: this.test ? this.test.structured_clone() : null,
             args: this.args,
             status: this.status,
-        }
-    }
+        };
+    };
 
     function Tests()
     {
@@ -2942,7 +3511,8 @@
     };
 
     Tests.prototype.all_done = function() {
-        return this.tests.length > 0 && test_environment.all_loaded &&
+        return (this.tests.length > 0 || this.pending_remotes.length > 0) &&
+                test_environment.all_loaded &&
                 (this.num_pending === 0 || this.is_aborted) && !this.wait_for_finish &&
                 !this.processing_callbacks &&
                 !this.pending_remotes.some(function(w) { return w.running; });
@@ -3178,6 +3748,14 @@
         return remoteContext.done;
     };
 
+    /**
+     * Get test results from a worker and include them in the current test.
+     *
+     * @param {Worker|SharedWorker|ServiceWorker|MessagePort} port -
+     * Either a worker object or a port connected to a worker which is
+     * running tests..
+     * @returns {Promise} - A promise that's resolved once all the remote tests are complete.
+     */
     function fetch_tests_from_worker(port) {
         return tests.fetch_tests_from_worker(port);
     }
@@ -3191,11 +3769,29 @@
         this.pending_remotes.push(this.create_remote_window(remote));
     };
 
+    /**
+     * Aggregate tests from separate windows or iframes
+     * into the current document as if they were all part of the same test file.
+     *
+     * The document of the second window (or iframe) should include
+     * ``testharness.js``, but not ``testharnessreport.js``, and use
+     * :js:func:`test`, :js:func:`async_test`, and :js:func:`promise_test` in
+     * the usual manner.
+     *
+     * @param {Window} window - The window to fetch tests from.
+     */
     function fetch_tests_from_window(window) {
         tests.fetch_tests_from_window(window);
     }
     expose(fetch_tests_from_window, 'fetch_tests_from_window');
 
+    /**
+     * Timeout the tests.
+     *
+     * This only has an effect when ``explict_timeout`` has been set
+     * in :js:func:`setup`. In other cases any call is a no-op.
+     *
+     */
     function timeout() {
         if (tests.timeout_length === null) {
             tests.timeout();
@@ -3203,18 +3799,49 @@
     }
     expose(timeout, 'timeout');
 
+    /**
+     * Add a callback that's triggered when the first :js:class:`Test` is created.
+     *
+     * @param {Function} callback - Callback function. This is called
+     * without arguments.
+     */
     function add_start_callback(callback) {
         tests.start_callbacks.push(callback);
     }
 
+    /**
+     * Add a callback that's triggered when a test state changes.
+     *
+     * @param {Function} callback - Callback function, called with the
+     * :js:class:`Test` as the only argument.
+     */
     function add_test_state_callback(callback) {
         tests.test_state_callbacks.push(callback);
     }
 
+    /**
+     * Add a callback that's triggered when a test result is received.
+     *
+     * @param {Function} callback - Callback function, called with the
+     * :js:class:`Test` as the only argument.
+     */
     function add_result_callback(callback) {
         tests.test_done_callbacks.push(callback);
     }
 
+    /**
+     * Add a callback that's triggered when all tests are complete.
+     *
+     * @param {Function} callback - Callback function, called with an
+     * array of :js:class:`Test` objects, a :js:class:`TestsStatus`
+     * object and an array of :js:class:`AssertRecord` objects. If the
+     * debug setting is ``false`` the final argument will be an empty
+     * array.
+     *
+     * For performance reasons asserts are only tracked when the debug
+     * setting is ``true``. In other cases the array of asserts will be
+     * empty.
+     */
     function add_completion_callback(callback) {
         tests.all_done_callbacks.push(callback);
     }
@@ -3413,7 +4040,12 @@
                                                 ["span", {"class":status_class(status)},
                                                  status
                                                 ],
-                                               ]
+                                               ],
+                                               ["button",
+                                                {"onclick": "let evt = new Event('__test_restart'); " +
+                                                 "let canceled = !window.dispatchEvent(evt);" +
+                                                 "if (!canceled) { location.reload() }"},
+                                                "Rerun"]
                                               ]];
 
                                     if (harness_status.status === harness_status.ERROR) {
@@ -3764,6 +4396,12 @@
         }
     }
 
+    /**
+     * @class
+     * Exception type that represents a failing assert.
+     *
+     * @param {string} message - Error message.
+     */
     function AssertionError(message)
     {
         if (typeof message == "string") {
@@ -3888,21 +4526,23 @@
      * invocations have signaled completion.
      *
      * If all callbacks complete synchronously (or if no callbacks are
-     * specified), the `done_callback` will be invoked synchronously. It is the
+     * specified), the ``done_callback`` will be invoked synchronously. It is the
      * responsibility of the caller to ensure asynchronicity in cases where
      * that is desired.
      *
      * @param {array} value Zero or more values to use in the invocation of
-     *                      `iter_callback`
-     * @param {function} iter_callback A function that will be invoked once for
-     *                                 each of the provided `values`. Two
-     *                                 arguments will be available in each
-     *                                 invocation: the value from `values` and
-     *                                 a function that must be invoked to
-     *                                 signal completion
+     *                      ``iter_callback``
+     * @param {function} iter_callback A function that will be invoked
+     *                                 once for each of the values min
+     *                                 ``value``. Two arguments will
+     *                                 be available in each
+     *                                 invocation: the value from
+     *                                 ``value`` and a function that
+     *                                 must be invoked to signal
+     *                                 completion
      * @param {function} done_callback A function that will be invoked after
      *                                 all operations initiated by the
-     *                                 `iter_callback` function have signaled
+     *                                 ``iter_callback`` function have signaled
      *                                 completion
      */
     function all_async(values, iter_callback, done_callback)
diff --git a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
index 5449a26..c5000461 100644
--- a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
+++ b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
@@ -322,7 +322,7 @@
 text-decoration-skip-ink: auto
 text-decoration-style: solid
 text-emphasis-color: rgb(0, 0, 0)
-text-emphasis-position: over right
+text-emphasis-position: over
 text-emphasis-style: none
 text-indent: 0px
 text-justify: auto
diff --git a/third_party/blink/web_tests/virtual/consume-code-cache-off-thread/README.md b/third_party/blink/web_tests/virtual/consume-code-cache-off-thread/README.md
deleted file mode 100644
index 57a943b..0000000
--- a/third_party/blink/web_tests/virtual/consume-code-cache-off-thread/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Virtual test suite for testing off-thread consumption of V8 code caches.
diff --git a/third_party/blink/web_tests/virtual/legacy-client-hints/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-subresource-without-hints.https.sub-expected.txt b/third_party/blink/web_tests/virtual/legacy-client-hints/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-subresource-without-hints.https.sub-expected.txt
new file mode 100644
index 0000000..1e7b5a1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/legacy-client-hints/external/wpt/client-hints/accept-ch-stickiness/meta-name-cross-origin-subresource-without-hints.https.sub-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL meta-name cross origin subresource without hints got client hints according to expectations. assert_true: expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/fetch/private-network-access/worker.https.window-expected.txt b/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/fetch/private-network-access/worker.https.window-expected.txt
index 6181be5..c7cd66da 100644
--- a/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/fetch/private-network-access/worker.https.window-expected.txt
+++ b/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/fetch/private-network-access/worker.https.window-expected.txt
@@ -1,8 +1,8 @@
 This is a testharness.js-based test.
-FAIL treat-as-public to local: failed preflight. assert_equals: response loaded expected false but got true
-FAIL treat-as-public to local: success. assert_equals: response loaded expected true but got false
-FAIL treat-as-public to private: failed preflight. assert_equals: response loaded expected false but got true
-FAIL treat-as-public to private: success. assert_equals: response loaded expected true but got false
+FAIL treat-as-public to local: failed preflight. assert_equals: worker error expected (string) "unknown error" but got (undefined) undefined
+FAIL treat-as-public to local: success. assert_equals: worker error expected (undefined) undefined but got (string) "unknown error"
+FAIL treat-as-public to private: failed preflight. assert_equals: worker error expected (string) "unknown error" but got (undefined) undefined
+FAIL treat-as-public to private: success. assert_equals: worker error expected (undefined) undefined but got (string) "unknown error"
 PASS public to public: success.
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/fetch/private-network-access/worker.window-expected.txt b/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/fetch/private-network-access/worker.window-expected.txt
index 5afa07e7..8718ffde 100644
--- a/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/fetch/private-network-access/worker.window-expected.txt
+++ b/third_party/blink/web_tests/virtual/plz-dedicated-worker/external/wpt/fetch/private-network-access/worker.window-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
-FAIL treat-as-public to local: failure. assert_equals: response loaded expected false but got true
-FAIL treat-as-public to private: failure. assert_equals: response loaded expected false but got true
+FAIL treat-as-public to local: failure. assert_equals: worker error expected (string) "unknown error" but got (undefined) undefined
+FAIL treat-as-public to private: failure. assert_equals: worker error expected (string) "unknown error" but got (undefined) undefined
 PASS public to public: success.
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/popup-disabled/popup-disabled-expected.txt b/third_party/blink/web_tests/virtual/popup-disabled/popup-disabled-expected.txt
index e3cba4d..832e08b 100644
--- a/third_party/blink/web_tests/virtual/popup-disabled/popup-disabled-expected.txt
+++ b/third_party/blink/web_tests/virtual/popup-disabled/popup-disabled-expected.txt
@@ -3,6 +3,8 @@
 
 Harness status: OK
 
+Rerun
+
 Found 1 tests
 
 1 Pass
diff --git a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
index 8b82ab6..b69b255 100644
--- a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
@@ -974,6 +974,7 @@
     property willValidate
 html element selectmenu
     property checkValidity
+    property disabled
     property form
     property labels
     property name
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index e5d329c9..0cc3d22 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -4249,6 +4249,7 @@
     setter value
 interface HTMLSelectMenuElement : HTMLElement
     attribute @@toStringTag
+    getter disabled
     getter form
     getter labels
     getter name
@@ -4264,6 +4265,7 @@
     method constructor
     method reportValidity
     method setCustomValidity
+    setter disabled
     setter name
     setter required
     setter value
diff --git a/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-author-style.manual.html b/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-author-style.manual.html
new file mode 100644
index 0000000..92790a5
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/document-transition/shared-transition-author-style.manual.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<html>
+<title>Shared transitions of different elements and shapes</title>
+<link rel="help" href="https://github.com/vmpstr/shared-element-transitions">
+<link rel="author" href="mailto:vmpstr@chromium.org">
+
+<style>
+body {
+  background: lightpink;
+  overflow: hidden;
+}
+
+input {
+  position: absolute;
+  left: 8px;
+  top: 8px;
+  z-index: 10;
+}
+
+.top {
+  top: 0px;
+  background: green;
+}
+.bottom {
+  bottom: 0px;
+  background: blue;
+}
+
+div {
+  position: absolute;
+  left: 0px;
+  right: 0px;
+  height: 40vh;
+  contain: paint;
+}
+</style>
+
+<input id=toggle type=button value="Toggle!"></input>
+<div id=target class=top>
+The div should alternate being at the bottom and at the top.
+The color at the top is green and bottom is blue.
+During the animation, the div has an (x, y) offset and its
+content is a blend of green and blue. There is also a grey
+background with a slight offset from the top.
+</div>
+
+<script>
+let classes = ["top", "bottom"]
+let i = 0;
+
+let transitionStyle =
+  `html::transition {
+    top: 50px;
+  }
+
+  html::transition-container(root) {
+    background-color: grey;
+  }
+
+  html::transition-container(shared-0) {
+    left: 50px;
+  }
+
+  html::transition-old-content(shared-0) {
+    opacity: 0.5;
+    animation-name: none;
+  }
+
+  html::transition-new-content(shared-0) {
+    opacity: 0.5;
+  }
+  `
+
+let pseudoStyle = document.createElement('style');
+pseudoStyle.appendChild(document.createTextNode(transitionStyle));
+
+async function runAnimation() {
+  await document.documentTransition.prepare({
+    rootTransition: "none",
+    sharedElements: [target]
+  });
+
+  target.classList.remove(classes[i]);
+  i = (i + 1) % classes.length;
+  target.classList.add(classes[i]);
+
+  document.head.appendChild(pseudoStyle);
+  await document.documentTransition.start({
+    sharedElements: [target]
+  });
+  document.head.removeChild(pseudoStyle);
+}
+
+function init() {
+  toggle.addEventListener("click", runAnimation);
+}
+onload = init;
+</script>
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index f464eb1..6de2cce1 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-11-1-26-ga25e85ed9
-Revision: a25e85ed95dc855e42e6bb55138e27d362c5ea1e
+Version: VER-2-11-1-37-ge838c37c2
+Revision: e838c37c2c1575eb12116ce6303ffacc72521ce8
 CPEPrefix: cpe:/a:freetype:freetype:2.11.1
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/third_party/freetype/include/freetype-custom/freetype/config/ftoption.h b/third_party/freetype/include/freetype-custom/freetype/config/ftoption.h
index 3e559a6..25bdf4d 100644
--- a/third_party/freetype/include/freetype-custom/freetype/config/ftoption.h
+++ b/third_party/freetype/include/freetype-custom/freetype/config/ftoption.h
@@ -4,7 +4,7 @@
  *
  *   User-selectable configuration macros (specification only).
  *
- * Copyright (C) 1996-2021 by
+ * Copyright (C) 1996-2022 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/third_party/freetype/include/freetype-custom/freetype/config/public-macros.h b/third_party/freetype/include/freetype-custom/freetype/config/public-macros.h
index 60ccb49..0103a4ca 100644
--- a/third_party/freetype/include/freetype-custom/freetype/config/public-macros.h
+++ b/third_party/freetype/include/freetype-custom/freetype/config/public-macros.h
@@ -4,7 +4,7 @@
  *
  *   Define a set of compiler macros used in public FreeType headers.
  *
- * Copyright (C) 2020-2021 by
+ * Copyright (C) 2020-2022 by
  * David Turner, Robert Wilhelm, and Werner Lemberg.
  *
  * This file is part of the FreeType project, and may only be used,
diff --git a/third_party/grpc/BUILD.gn b/third_party/grpc/BUILD.gn
index d08397c..764f96889 100644
--- a/third_party/grpc/BUILD.gn
+++ b/third_party/grpc/BUILD.gn
@@ -394,18 +394,14 @@
     "src/src/core/ext/filters/client_channel/local_subchannel_pool.h",
     "src/src/core/ext/filters/client_channel/proxy_mapper.h",
     "src/src/core/ext/filters/client_channel/proxy_mapper_registry.h",
-    "src/src/core/ext/filters/client_channel/resolver.h",
     "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h",
     "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h",
     "src/src/core/ext/filters/client_channel/resolver/dns/dns_resolver_selection.h",
     "src/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h",
-    "src/src/core/ext/filters/client_channel/resolver_factory.h",
-    "src/src/core/ext/filters/client_channel/resolver_registry.h",
     "src/src/core/ext/filters/client_channel/resolver_result_parsing.h",
     "src/src/core/ext/filters/client_channel/retry_filter.h",
     "src/src/core/ext/filters/client_channel/retry_service_config.h",
     "src/src/core/ext/filters/client_channel/retry_throttle.h",
-    "src/src/core/ext/filters/client_channel/server_address.h",
     "src/src/core/ext/filters/client_channel/subchannel.h",
     "src/src/core/ext/filters/client_channel/subchannel_interface.h",
     "src/src/core/ext/filters/client_channel/subchannel_pool_interface.h",
@@ -420,11 +416,11 @@
     "src/src/core/ext/filters/http/server/http_server_filter.h",
     "src/src/core/ext/filters/max_age/max_age_filter.h",
     "src/src/core/ext/filters/message_size/message_size_filter.h",
+
+    # "src/src/core/ext/filters/rbac/rbac_filter.h",
+    # "src/src/core/ext/filters/rbac/rbac_service_config_parser.h",
     "src/src/core/ext/filters/server_config_selector/server_config_selector.h",
     "src/src/core/ext/filters/server_config_selector/server_config_selector_filter.h",
-    "src/src/core/ext/service_config/service_config.h",
-    "src/src/core/ext/service_config/service_config_call_data.h",
-    "src/src/core/ext/service_config/service_config_parser.h",
     "src/src/core/ext/transport/binder/client/binder_connector.h",
     "src/src/core/ext/transport/binder/client/channel_create_impl.h",
     "src/src/core/ext/transport/binder/client/connection_id_generator.h",
@@ -462,15 +458,12 @@
     "src/src/core/ext/transport/chttp2/transport/frame_window_update.h",
     "src/src/core/ext/transport/chttp2/transport/hpack_constants.h",
     "src/src/core/ext/transport/chttp2/transport/hpack_encoder.h",
-    "src/src/core/ext/transport/chttp2/transport/hpack_encoder_index.h",
     "src/src/core/ext/transport/chttp2/transport/hpack_encoder_table.h",
     "src/src/core/ext/transport/chttp2/transport/hpack_parser.h",
     "src/src/core/ext/transport/chttp2/transport/hpack_parser_table.h",
-    "src/src/core/ext/transport/chttp2/transport/hpack_utils.h",
     "src/src/core/ext/transport/chttp2/transport/http2_settings.h",
     "src/src/core/ext/transport/chttp2/transport/huffsyms.h",
     "src/src/core/ext/transport/chttp2/transport/internal.h",
-    "src/src/core/ext/transport/chttp2/transport/popularity_count.h",
     "src/src/core/ext/transport/chttp2/transport/stream_map.h",
     "src/src/core/ext/transport/chttp2/transport/varint.h",
     "src/src/core/ext/transport/inproc/inproc_transport.h",
@@ -516,6 +509,7 @@
     "src/src/core/ext/upb-generated/envoy/extensions/clusters/aggregate/v3/cluster.upb.h",
     "src/src/core/ext/upb-generated/envoy/extensions/filters/common/fault/v3/fault.upb.h",
     "src/src/core/ext/upb-generated/envoy/extensions/filters/http/fault/v3/fault.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/rbac/v3/rbac.upb.h",
     "src/src/core/ext/upb-generated/envoy/extensions/filters/http/router/v3/router.upb.h",
     "src/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.h",
     "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/cert.upb.h",
@@ -615,6 +609,7 @@
     "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/udp_listener_config.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/config/metrics/v3/stats.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/config/overload/v3/overload.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/rbac/v3/rbac.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/route.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/route_components.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/scoped_route.upbdefs.h",
@@ -622,6 +617,7 @@
     "src/src/core/ext/upbdefs-generated/envoy/extensions/clusters/aggregate/v3/cluster.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/common/fault/v3/fault.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/fault/v3/fault.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/rbac/v3/rbac.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/router/v3/router.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/cert.upbdefs.h",
@@ -653,6 +649,11 @@
     "src/src/core/ext/upbdefs-generated/envoy/type/v3/range.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/type/v3/semantic_version.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/google/api/annotations.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/api/expr/v1alpha1/checked.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/api/expr/v1alpha1/eval.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/api/expr/v1alpha1/explain.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/api/expr/v1alpha1/syntax.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/google/api/expr/v1alpha1/value.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/google/api/http.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/google/protobuf/any.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/google/protobuf/duration.upbdefs.h",
@@ -693,13 +694,8 @@
     "src/src/core/lib/channel/handshaker_factory.h",
     "src/src/core/lib/channel/handshaker_registry.h",
     "src/src/core/lib/channel/status_util.h",
-    "src/src/core/lib/compression/algorithm_metadata.h",
-    "src/src/core/lib/compression/compression_args.h",
     "src/src/core/lib/compression/compression_internal.h",
     "src/src/core/lib/compression/message_compress.h",
-    "src/src/core/lib/compression/stream_compression.h",
-    "src/src/core/lib/compression/stream_compression_gzip.h",
-    "src/src/core/lib/compression/stream_compression_identity.h",
     "src/src/core/lib/config/core_configuration.h",
     "src/src/core/lib/debug/stats.h",
     "src/src/core/lib/debug/stats_data.h",
@@ -769,6 +765,7 @@
     "src/src/core/lib/iomgr/event_engine/pollset.h",
     "src/src/core/lib/iomgr/event_engine/promise.h",
     "src/src/core/lib/iomgr/event_engine/resolved_address_internal.h",
+    "src/src/core/lib/iomgr/event_engine/resolver.h",
     "src/src/core/lib/iomgr/exec_ctx.h",
     "src/src/core/lib/iomgr/executor.h",
     "src/src/core/lib/iomgr/executor/mpmcqueue.h",
@@ -795,6 +792,10 @@
     "src/src/core/lib/iomgr/python_util.h",
     "src/src/core/lib/iomgr/resolve_address.h",
     "src/src/core/lib/iomgr/resolve_address_custom.h",
+    "src/src/core/lib/iomgr/resolve_address_impl.h",
+    "src/src/core/lib/iomgr/resolve_address_posix.h",
+    "src/src/core/lib/iomgr/resolve_address_windows.h",
+    "src/src/core/lib/iomgr/resolved_address.h",
     "src/src/core/lib/iomgr/sockaddr.h",
     "src/src/core/lib/iomgr/sockaddr_posix.h",
     "src/src/core/lib/iomgr/sockaddr_windows.h",
@@ -839,6 +840,10 @@
     "src/src/core/lib/promise/poll.h",
     "src/src/core/lib/promise/race.h",
     "src/src/core/lib/promise/seq.h",
+    "src/src/core/lib/resolver/resolver.h",
+    "src/src/core/lib/resolver/resolver_factory.h",
+    "src/src/core/lib/resolver/resolver_registry.h",
+    "src/src/core/lib/resolver/server_address.h",
     "src/src/core/lib/resource_quota/api.h",
     "src/src/core/lib/resource_quota/arena.h",
     "src/src/core/lib/resource_quota/memory_quota.h",
@@ -848,6 +853,10 @@
     "src/src/core/lib/security/authorization/authorization_engine.h",
     "src/src/core/lib/security/authorization/authorization_policy_provider.h",
     "src/src/core/lib/security/authorization/evaluate_args.h",
+
+    # "src/src/core/lib/security/authorization/grpc_authorization_engine.h",
+    # "src/src/core/lib/security/authorization/matchers.h",
+    # "src/src/core/lib/security/authorization/rbac_policy.h",
     "src/src/core/lib/security/authorization/sdk_server_authz_filter.h",
     "src/src/core/lib/security/context/security_context.h",
     "src/src/core/lib/security/credentials/alts/alts_credentials.h",
@@ -894,6 +903,9 @@
     "src/src/core/lib/security/transport/security_handshaker.h",
     "src/src/core/lib/security/transport/tsi_error.h",
     "src/src/core/lib/security/util/json_util.h",
+    "src/src/core/lib/service_config/service_config.h",
+    "src/src/core/lib/service_config/service_config_call_data.h",
+    "src/src/core/lib/service_config/service_config_parser.h",
     "src/src/core/lib/slice/b64.h",
     "src/src/core/lib/slice/percent_encoding.h",
     "src/src/core/lib/slice/slice.h",
@@ -903,7 +915,6 @@
     "src/src/core/lib/slice/slice_split.h",
     "src/src/core/lib/slice/slice_string_helpers.h",
     "src/src/core/lib/slice/slice_utils.h",
-    "src/src/core/lib/slice/static_slice.h",
     "src/src/core/lib/surface/api_trace.h",
     "src/src/core/lib/surface/builtins.h",
     "src/src/core/lib/surface/call.h",
@@ -923,11 +934,9 @@
     "src/src/core/lib/transport/connectivity_state.h",
     "src/src/core/lib/transport/error_utils.h",
     "src/src/core/lib/transport/http2_errors.h",
-    "src/src/core/lib/transport/metadata.h",
     "src/src/core/lib/transport/metadata_batch.h",
     "src/src/core/lib/transport/parsed_metadata.h",
     "src/src/core/lib/transport/pid_controller.h",
-    "src/src/core/lib/transport/static_metadata.h",
     "src/src/core/lib/transport/status_conversion.h",
     "src/src/core/lib/transport/timeout_encoding.h",
     "src/src/core/lib/transport/transport.h",
@@ -1022,7 +1031,6 @@
     "src/src/core/ext/filters/client_channel/lb_policy_registry.cc",
     "src/src/core/ext/filters/client_channel/local_subchannel_pool.cc",
     "src/src/core/ext/filters/client_channel/proxy_mapper_registry.cc",
-    "src/src/core/ext/filters/client_channel/resolver.cc",
     "src/src/core/ext/filters/client_channel/resolver/binder/binder_resolver.cc",
     "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc",
     "src/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_event_engine.cc",
@@ -1038,12 +1046,10 @@
 
     # "src/src/core/ext/filters/client_channel/resolver/google_c2p/google_c2p_resolver.cc",
     "src/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc",
-    "src/src/core/ext/filters/client_channel/resolver_registry.cc",
     "src/src/core/ext/filters/client_channel/resolver_result_parsing.cc",
     "src/src/core/ext/filters/client_channel/retry_filter.cc",
     "src/src/core/ext/filters/client_channel/retry_service_config.cc",
     "src/src/core/ext/filters/client_channel/retry_throttle.cc",
-    "src/src/core/ext/filters/client_channel/server_address.cc",
     "src/src/core/ext/filters/client_channel/service_config_channel_arg_filter.cc",
     "src/src/core/ext/filters/client_channel/subchannel.cc",
     "src/src/core/ext/filters/client_channel/subchannel_pool_interface.cc",
@@ -1051,6 +1057,7 @@
     "src/src/core/ext/filters/client_idle/idle_filter_state.cc",
     "src/src/core/ext/filters/deadline/deadline_filter.cc",
     "src/src/core/ext/filters/fault_injection/fault_injection_filter.cc",
+    "src/src/core/ext/filters/fault_injection/service_config_parser.cc",
     "src/src/core/ext/filters/http/client/http_client_filter.cc",
     "src/src/core/ext/filters/http/client_authority_filter.cc",
     "src/src/core/ext/filters/http/http_filters_plugin.cc",
@@ -1059,10 +1066,11 @@
     "src/src/core/ext/filters/http/server/http_server_filter.cc",
     "src/src/core/ext/filters/max_age/max_age_filter.cc",
     "src/src/core/ext/filters/message_size/message_size_filter.cc",
+
+    # "src/src/core/ext/filters/rbac/rbac_filter.cc",
+    # "src/src/core/ext/filters/rbac/rbac_service_config_parser.cc",
     "src/src/core/ext/filters/server_config_selector/server_config_selector.cc",
     "src/src/core/ext/filters/server_config_selector/server_config_selector_filter.cc",
-    "src/src/core/ext/service_config/service_config.cc",
-    "src/src/core/ext/service_config/service_config_parser.cc",
     "src/src/core/ext/transport/binder/client/binder_connector.cc",
     "src/src/core/ext/transport/binder/client/channel_create.cc",
     "src/src/core/ext/transport/binder/client/channel_create_impl.cc",
@@ -1105,7 +1113,6 @@
     "src/src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc",
     "src/src/core/ext/transport/chttp2/transport/hpack_parser.cc",
     "src/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc",
-    "src/src/core/ext/transport/chttp2/transport/hpack_utils.cc",
     "src/src/core/ext/transport/chttp2/transport/http2_settings.cc",
     "src/src/core/ext/transport/chttp2/transport/huffsyms.cc",
     "src/src/core/ext/transport/chttp2/transport/parsing.cc",
@@ -1117,9 +1124,11 @@
     "src/src/core/ext/transport/inproc/inproc_transport.cc",
     "src/src/core/ext/upb-generated/envoy/admin/v3/config_dump.upb.c",
     "src/src/core/ext/upb-generated/envoy/annotations/deprecation.upb.c",
+    "src/src/core/ext/upb-generated/envoy/annotations/resource.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/accesslog/v3/accesslog.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/bootstrap/v3/bootstrap.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/cluster/v3/circuit_breaker.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/cluster/v3/cluster.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/cluster/v3/filter.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/cluster/v3/outlier_detection.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/core/v3/address.upb.c",
@@ -1152,7 +1161,6 @@
     "src/src/core/ext/upb-generated/envoy/config/route/v3/route_components.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/route/v3/scoped_route.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/trace/v3/http_tracer.upb.c",
-    "src/src/core/ext/upb-generated/envoy/extensions/clusters/aggregate/v3/cluster.upb.c",
     "src/src/core/ext/upb-generated/envoy/extensions/filters/http/fault/v3/fault.upb.c",
     "src/src/core/ext/upb-generated/envoy/extensions/filters/http/router/v3/router.upb.c",
     "src/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.c",
@@ -1170,13 +1178,13 @@
     "src/src/core/ext/upb-generated/envoy/service/route/v3/srds.upb.c",
     "src/src/core/ext/upb-generated/envoy/service/status/v3/csds.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/http/v3/path_transformation.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/metadata.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/node.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/number.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/path.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/regex.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/string.upb.c",
-    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/value.upb.c",
-    "src/src/core/ext/upb-generated/envoy/type/metadata/v3/metadata.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/struct.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/tracing/v3/custom_tag.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/v3/percent.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/v3/range.upb.c",
@@ -1186,11 +1194,11 @@
     "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/eval.upb.c",
     "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/explain.upb.c",
     "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/syntax.upb.c",
+    "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/value.upb.c",
     "src/src/core/ext/upb-generated/google/api/http.upb.c",
     "src/src/core/ext/upb-generated/google/protobuf/any.upb.c",
     "src/src/core/ext/upb-generated/google/protobuf/duration.upb.c",
     "src/src/core/ext/upb-generated/google/protobuf/empty.upb.c",
-    "src/src/core/ext/upb-generated/google/protobuf/struct.upb.c",
     "src/src/core/ext/upb-generated/google/protobuf/timestamp.upb.c",
     "src/src/core/ext/upb-generated/google/protobuf/wrappers.upb.c",
     "src/src/core/ext/upb-generated/src/proto/grpc/gcp/altscontext.upb.c",
@@ -1202,13 +1210,12 @@
     "src/src/core/ext/upb-generated/udpa/annotations/migrate.upb.c",
     "src/src/core/ext/upb-generated/udpa/annotations/security.upb.c",
     "src/src/core/ext/upb-generated/udpa/annotations/sensitive.upb.c",
+    "src/src/core/ext/upb-generated/udpa/annotations/status.upb.c",
     "src/src/core/ext/upb-generated/udpa/annotations/versioning.upb.c",
     "src/src/core/ext/upb-generated/validate/validate.upb.c",
-    "src/src/core/ext/upb-generated/xds/annotations/v3/status.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/authority.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/collection_entry.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/context_params.upb.c",
-    "src/src/core/ext/upb-generated/xds/core/v3/resource.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/resource_locator.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/resource_name.upb.c",
     "src/src/core/ext/upb-generated/xds/data/orca/v3/orca_load_report.upb.c",
@@ -1246,6 +1253,7 @@
     "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/udp_listener_config.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/metrics/v3/stats.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/overload/v3/overload.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/rbac/v3/rbac.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/route.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/route_components.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/scoped_route.upbdefs.c",
@@ -1267,31 +1275,35 @@
     "src/src/core/ext/upbdefs-generated/envoy/service/route/v3/srds.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/service/status/v3/csds.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/http/v3/path_transformation.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/metadata.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/node.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/number.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/path.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/regex.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/string.upbdefs.c",
-    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/struct.upbdefs.c",
-    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/value.upbdefs.c",
-    "src/src/core/ext/upbdefs-generated/envoy/type/metadata/v3/metadata.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/tracing/v3/custom_tag.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/v3/http.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/v3/percent.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/v3/range.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/v3/semantic_version.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/api/annotations.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/api/expr/v1alpha1/checked.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/api/expr/v1alpha1/eval.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/api/expr/v1alpha1/explain.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/api/expr/v1alpha1/syntax.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/api/expr/v1alpha1/value.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/protobuf/any.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/protobuf/duration.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/protobuf/empty.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/struct.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/protobuf/timestamp.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/protobuf/wrappers.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/rpc/status.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/udpa/annotations/migrate.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/udpa/annotations/security.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/udpa/annotations/sensitive.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/udpa/annotations/versioning.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/validate/validate.upbdefs.c",
-    "src/src/core/ext/upbdefs-generated/xds/annotations/v3/status.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/authority.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/collection_entry.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/context_params.upbdefs.c",
@@ -1314,15 +1326,12 @@
     "src/src/core/lib/channel/handshaker_registry.cc",
     "src/src/core/lib/channel/status_util.cc",
     "src/src/core/lib/compression/compression.cc",
-    "src/src/core/lib/compression/compression_args.cc",
     "src/src/core/lib/compression/compression_internal.cc",
     "src/src/core/lib/compression/message_compress.cc",
-    "src/src/core/lib/compression/stream_compression.cc",
-    "src/src/core/lib/compression/stream_compression_gzip.cc",
-    "src/src/core/lib/compression/stream_compression_identity.cc",
     "src/src/core/lib/config/core_configuration.cc",
     "src/src/core/lib/debug/stats.cc",
     "src/src/core/lib/debug/stats_data.cc",
+    "src/src/core/lib/debug/trace.cc",
     "src/src/core/lib/event_engine/channel_args_endpoint_config.cc",
     "src/src/core/lib/event_engine/event_engine.cc",
     "src/src/core/lib/event_engine/event_engine_factory.cc",
@@ -1401,6 +1410,7 @@
     "src/src/core/lib/iomgr/event_engine/pollset.cc",
     "src/src/core/lib/iomgr/event_engine/resolved_address_internal.cc",
     "src/src/core/lib/iomgr/event_engine/tcp.cc",
+    "src/src/core/lib/iomgr/event_engine/timer.cc",
     "src/src/core/lib/iomgr/exec_ctx.cc",
     "src/src/core/lib/iomgr/executor.cc",
     "src/src/core/lib/iomgr/executor/mpmcqueue.cc",
@@ -1455,7 +1465,6 @@
     "src/src/core/lib/iomgr/tcp_server_windows.cc",
     "src/src/core/lib/iomgr/tcp_windows.cc",
     "src/src/core/lib/iomgr/time_averaged_stats.cc",
-    "src/src/core/lib/iomgr/timer.cc",
     "src/src/core/lib/iomgr/timer_custom.cc",
     "src/src/core/lib/iomgr/timer_generic.cc",
     "src/src/core/lib/iomgr/timer_heap.cc",
@@ -1468,21 +1477,24 @@
     "src/src/core/lib/iomgr/wakeup_fd_posix.cc",
     "src/src/core/lib/iomgr/work_serializer.cc",
     "src/src/core/lib/json/json_reader.cc",
-    "src/src/core/lib/json/json_util.cc",
     "src/src/core/lib/json/json_writer.cc",
-
-    # "src/src/core/lib/matchers/matchers.cc",
     "src/src/core/lib/profiling/basic_timers.cc",
     "src/src/core/lib/profiling/stap_timers.cc",
     "src/src/core/lib/promise/activity.cc",
+    "src/src/core/lib/resolver/resolver.cc",
+    "src/src/core/lib/resolver/resolver_registry.cc",
+    "src/src/core/lib/resolver/server_address.cc",
     "src/src/core/lib/resource_quota/api.cc",
     "src/src/core/lib/resource_quota/arena.cc",
     "src/src/core/lib/resource_quota/memory_quota.cc",
     "src/src/core/lib/resource_quota/resource_quota.cc",
     "src/src/core/lib/resource_quota/thread_quota.cc",
-    "src/src/core/lib/resource_quota/trace.cc",
     "src/src/core/lib/security/authorization/authorization_policy_provider_vtable.cc",
     "src/src/core/lib/security/authorization/evaluate_args.cc",
+
+    # "src/src/core/lib/security/authorization/grpc_authorization_engine.cc",
+    # "src/src/core/lib/security/authorization/matchers.cc",
+    # "src/src/core/lib/security/authorization/rbac_policy.cc",
     "src/src/core/lib/security/authorization/sdk_server_authz_filter.cc",
     "src/src/core/lib/security/context/security_context.cc",
     "src/src/core/lib/security/credentials/alts/alts_credentials.cc",
@@ -1495,7 +1507,6 @@
     "src/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc",
     "src/src/core/lib/security/credentials/composite/composite_credentials.cc",
     "src/src/core/lib/security/credentials/credentials.cc",
-    "src/src/core/lib/security/credentials/credentials_metadata.cc",
     "src/src/core/lib/security/credentials/external/aws_external_account_credentials.cc",
     "src/src/core/lib/security/credentials/external/aws_request_signer.cc",
     "src/src/core/lib/security/credentials/external/external_account_credentials.cc",
@@ -1505,6 +1516,7 @@
     "src/src/core/lib/security/credentials/google_default/credentials_generic.cc",
     "src/src/core/lib/security/credentials/google_default/google_default_credentials.cc",
     "src/src/core/lib/security/credentials/iam/iam_credentials.cc",
+    "src/src/core/lib/security/credentials/insecure/insecure_credentials.cc",
     "src/src/core/lib/security/credentials/jwt/json_token.cc",
     "src/src/core/lib/security/credentials/jwt/jwt_credentials.cc",
     "src/src/core/lib/security/credentials/jwt/jwt_verifier.cc",
@@ -1518,8 +1530,6 @@
     "src/src/core/lib/security/credentials/tls/grpc_tls_credentials_options.cc",
     "src/src/core/lib/security/credentials/tls/tls_credentials.cc",
     "src/src/core/lib/security/credentials/tls/tls_utils.cc",
-
-    # "src/src/core/lib/security/credentials/xds/xds_credentials.cc",
     "src/src/core/lib/security/security_connector/alts/alts_security_connector.cc",
     "src/src/core/lib/security/security_connector/fake/fake_security_connector.cc",
     "src/src/core/lib/security/security_connector/insecure/insecure_security_connector.cc",
@@ -1536,6 +1546,8 @@
     "src/src/core/lib/security/transport/security_handshaker.cc",
     "src/src/core/lib/security/transport/server_auth_filter.cc",
     "src/src/core/lib/security/transport/tsi_error.cc",
+    "src/src/core/lib/security/util/json_util.cc",
+    "src/src/core/lib/service_config/service_config.cc",
     "src/src/core/lib/slice/b64.cc",
     "src/src/core/lib/slice/percent_encoding.cc",
     "src/src/core/lib/slice/slice.cc",
@@ -1545,7 +1557,6 @@
     "src/src/core/lib/slice/slice_refcount.cc",
     "src/src/core/lib/slice/slice_split.cc",
     "src/src/core/lib/slice/slice_string_helpers.cc",
-    "src/src/core/lib/slice/static_slice.cc",
     "src/src/core/lib/surface/api_trace.cc",
     "src/src/core/lib/surface/builtins.cc",
     "src/src/core/lib/surface/byte_buffer.cc",
@@ -1571,11 +1582,8 @@
     "src/src/core/lib/transport/byte_stream.cc",
     "src/src/core/lib/transport/connectivity_state.cc",
     "src/src/core/lib/transport/error_utils.cc",
-    "src/src/core/lib/transport/metadata.cc",
-    "src/src/core/lib/transport/metadata_batch.cc",
     "src/src/core/lib/transport/parsed_metadata.cc",
     "src/src/core/lib/transport/pid_controller.cc",
-    "src/src/core/lib/transport/static_metadata.cc",
     "src/src/core/lib/transport/status_conversion.cc",
     "src/src/core/lib/transport/timeout_encoding.cc",
     "src/src/core/lib/transport/transport.cc",
@@ -1616,8 +1624,9 @@
     "src/src/cpp/client/create_channel_internal.cc",
     "src/src/cpp/client/create_channel_posix.cc",
     "src/src/cpp/client/credentials_cc.cc",
-    "src/src/cpp/client/insecure_credentials.cc",
     "src/src/cpp/client/secure_credentials.cc",
+
+    # "src/src/cpp/client/xds_credentials.cc",
     "src/src/cpp/codegen/codegen_init.cc",
     "src/src/cpp/common/alarm.cc",
     "src/src/cpp/common/auth_property_iterator.cc",
@@ -1685,33 +1694,38 @@
 
 source_set("grpc++_repeated1") {
   sources = [
-    "src/src/core/ext/filters/fault_injection/service_config_parser.cc",
     "src/src/core/ext/transport/chttp2/client/insecure/channel_create.cc",
-    "src/src/core/ext/upb-generated/envoy/annotations/resource.upb.c",
-    "src/src/core/ext/upb-generated/envoy/config/cluster/v3/cluster.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/clusters/aggregate/v3/cluster.upb.c",
     "src/src/core/ext/upb-generated/envoy/extensions/filters/common/fault/v3/fault.upb.c",
-    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/metadata.upb.c",
-    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/struct.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/rbac/v3/rbac.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/value.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/metadata/v3/metadata.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/v3/http.upb.c",
-    "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/value.upb.c",
+    "src/src/core/ext/upb-generated/google/protobuf/struct.upb.c",
     "src/src/core/ext/upb-generated/google/rpc/status.upb.c",
+    "src/src/core/ext/upb-generated/xds/core/v3/resource.upb.c",
     "src/src/core/ext/upbdefs-generated/envoy/annotations/resource.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/clusters/aggregate/v3/cluster.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/fault/v3/fault.upbdefs.c",
-    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/metadata.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/rbac/v3/rbac.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/struct.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/value.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/metadata/v3/metadata.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/api/http.upbdefs.c",
-    "src/src/core/ext/upbdefs-generated/google/protobuf/struct.upbdefs.c",
-    "src/src/core/ext/upbdefs-generated/google/rpc/status.upbdefs.c",
-    "src/src/core/lib/debug/trace.cc",
+    "src/src/core/ext/upbdefs-generated/udpa/annotations/status.upbdefs.c",
     "src/src/core/lib/iomgr/event_engine/endpoint.cc",
     "src/src/core/lib/iomgr/event_engine/resolver.cc",
-    "src/src/core/lib/iomgr/event_engine/timer.cc",
     "src/src/core/lib/iomgr/iomgr.cc",
     "src/src/core/lib/iomgr/pollset.cc",
-    "src/src/core/lib/security/credentials/insecure/insecure_credentials.cc",
-    "src/src/core/lib/security/util/json_util.cc",
+    "src/src/core/lib/iomgr/timer.cc",
+    "src/src/core/lib/json/json_util.cc",
 
-    # "src/src/cpp/client/xds_credentials.cc",
+    # "src/src/core/lib/matchers/matchers.cc",
+    "src/src/core/lib/resource_quota/trace.cc",
+
+    # "src/src/core/lib/security/credentials/xds/xds_credentials.cc",
+    "src/src/core/lib/service_config/service_config_parser.cc",
+    "src/src/cpp/client/insecure_credentials.cc",
   ]
 
   deps = [
@@ -1734,8 +1748,8 @@
 }
 source_set("grpc++_repeated2") {
   sources = [
-    "src/src/core/ext/upb-generated/udpa/annotations/status.upb.c",
-    "src/src/core/ext/upbdefs-generated/udpa/annotations/status.upbdefs.c",
+    "src/src/core/ext/upb-generated/xds/annotations/v3/status.upb.c",
+    "src/src/core/ext/upbdefs-generated/xds/annotations/v3/status.upbdefs.c",
   ]
 
   deps = [
diff --git a/third_party/grpc/README.chromium b/third_party/grpc/README.chromium
index 9d999782..e2741daa 100644
--- a/third_party/grpc/README.chromium
+++ b/third_party/grpc/README.chromium
@@ -1,8 +1,8 @@
 Name: grpc
 URL: https://github.com/grpc/grpc
 License: Apache 2.0
-Version: v1.42.0+
-Revision: 47f58f5b8d338a6861febc8ee1a602c23a5af70a
+Version: v1.43.0+
+Revision: 754913545189b819829284b79ac5a4d31fddbdcc
 Security Critical: yes
 
 Please note that that the use of gRPC is not generally allowed within Chromium.
diff --git a/third_party/grpc/plugin_registry/grpc_plugin_registry.cc b/third_party/grpc/plugin_registry/grpc_plugin_registry.cc
index 2061555..1130cb3 100644
--- a/third_party/grpc/plugin_registry/grpc_plugin_registry.cc
+++ b/third_party/grpc/plugin_registry/grpc_plugin_registry.cc
@@ -31,8 +31,6 @@
 void grpc_chttp2_plugin_shutdown(void);
 void grpc_client_channel_init(void);
 void grpc_client_channel_shutdown(void);
-void grpc_inproc_plugin_init(void);
-void grpc_inproc_plugin_shutdown(void);
 void grpc_resolver_fake_init(void);
 void grpc_resolver_fake_shutdown(void);
 void grpc_lb_policy_grpclb_init(void);
@@ -68,6 +66,8 @@
 
 #ifndef GRPC_NO_XDS
 namespace grpc_core {
+void RbacFilterInit(void);
+void RbacFilterShutdown(void);
 void XdsClientGlobalInit();
 void XdsClientGlobalShutdown();
 }  // namespace grpc_core
@@ -103,7 +103,6 @@
   grpc_register_plugin(grpc_core::ServiceConfigParserInit,
                        grpc_core::ServiceConfigParserShutdown);
   grpc_register_plugin(grpc_client_channel_init, grpc_client_channel_shutdown);
-  grpc_register_plugin(grpc_inproc_plugin_init, grpc_inproc_plugin_shutdown);
   grpc_register_plugin(grpc_resolver_fake_init, grpc_resolver_fake_shutdown);
   // grpc_register_plugin(grpc_lb_policy_grpclb_init,
   // grpc_lb_policy_grpclb_shutdown);
@@ -132,6 +131,10 @@
   grpc_register_plugin(grpc_core::FaultInjectionFilterInit,
                        grpc_core::FaultInjectionFilterShutdown);
 #ifndef GRPC_NO_XDS
+  // rbac_filter is being guarded with GRPC_NO_XDS to avoid a dependency on the
+  // re2 library by default
+  grpc_register_plugin(grpc_core::RbacFilterInit,
+                       grpc_core::RbacFilterShutdown);
   grpc_register_plugin(grpc_core::XdsClientGlobalInit,
                        grpc_core::XdsClientGlobalShutdown);
   grpc_register_plugin(grpc_certificate_provider_registry_init,
diff --git a/third_party/grpc/template/BUILD.chromium.gn.template b/third_party/grpc/template/BUILD.chromium.gn.template
index 0ece963..29d6b7e4 100644
--- a/third_party/grpc/template/BUILD.chromium.gn.template
+++ b/third_party/grpc/template/BUILD.chromium.gn.template
@@ -181,6 +181,16 @@
         'src/src/cpp/server/xds_server_credentials.cc',
         'src/src/cpp/server/xds_server_credentials.cc',
         'src/src/cpp/server/xds_server_credentials.h',
+        'src/src/core/ext/filters/rbac/rbac_filter.cc',
+        'src/src/core/ext/filters/rbac/rbac_filter.h',
+        'src/src/core/ext/filters/rbac/rbac_service_config_parser.cc',
+        'src/src/core/ext/filters/rbac/rbac_service_config_parser.h',
+        'src/src/core/lib/security/authorization/grpc_authorization_engine.cc',
+        'src/src/core/lib/security/authorization/grpc_authorization_engine.h',
+        'src/src/core/lib/security/authorization/matchers.cc',
+        'src/src/core/lib/security/authorization/matchers.h',
+        'src/src/core/lib/security/authorization/rbac_policy.cc',
+        'src/src/core/lib/security/authorization/rbac_policy.h',
     ]
     return s in file_names
 
diff --git a/third_party/leveldatabase/env_chromium.h b/third_party/leveldatabase/env_chromium.h
index df86ffe..365596b 100644
--- a/third_party/leveldatabase/env_chromium.h
+++ b/third_party/leveldatabase/env_chromium.h
@@ -6,24 +6,22 @@
 #define THIRD_PARTY_LEVELDATABASE_ENV_CHROMIUM_H_
 
 #include <memory>
-#include <set>
 #include <string>
 #include <vector>
 
 #include "base/callback.h"
-#include "base/containers/circular_deque.h"
 #include "base/containers/linked_list.h"
+#include "base/containers/span.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/gtest_prod_util.h"
-#include "base/synchronization/condition_variable.h"
-#include "build/build_config.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
 #include "leveldb/cache.h"
 #include "leveldb/db.h"
 #include "leveldb/env.h"
 #include "leveldb/export.h"
 #include "port/port_chromium.h"
-#include "util/mutexlock.h"
 
 namespace base {
 namespace trace_event {
diff --git a/third_party/libaddressinput/chromium/address_input_strings.grd b/third_party/libaddressinput/chromium/address_input_strings.grd
index 16656ac..44b55c81 100644
--- a/third_party/libaddressinput/chromium/address_input_strings.grd
+++ b/third_party/libaddressinput/chromium/address_input_strings.grd
@@ -54,7 +54,9 @@
       <output filename="address_input_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="address_input_strings_af.pak" type="data_package" lang="af" />
       <output filename="address_input_strings_is.pak" type="data_package" lang="is" />
+      <output filename="address_input_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="address_input_strings_am.pak" type="data_package" lang="am" />
     <output filename="address_input_strings_ar.pak" type="data_package" lang="ar" />
diff --git a/third_party/liburlpattern/pattern.cc b/third_party/liburlpattern/pattern.cc
index d4a0e0d..cbab678 100644
--- a/third_party/liburlpattern/pattern.cc
+++ b/third_party/liburlpattern/pattern.cc
@@ -144,7 +144,7 @@
     //       output `{:foo}bar` and not `:foobar`.
     const Part* next_part =
         (i + 1) < part_list_.size() ? &part_list_[i + 1] : nullptr;
-    if (!needs_grouping && custom_name &&
+    if (!needs_grouping && part.prefix.empty() && custom_name &&
         part.type == PartType::kSegmentWildcard &&
         part.modifier == Modifier::kNone && next_part &&
         next_part->prefix.empty() && next_part->suffix.empty()) {
@@ -158,6 +158,18 @@
       }
     }
 
+    // 3. preceded by a fixed text part that ends with an implicit prefix
+    //    character (like `/`).  This occurs when the original pattern used
+    //    an escape or grouping to prevent the implicit prefix; e.g.
+    //    `\\/*` or `/{*}`.  In these cases we use a grouping to prevent the
+    //    implicit prefix in the generated string.
+    const Part* last_part = i > 0 ? &part_list_[i - 1] : nullptr;
+    if (!needs_grouping && part.prefix.empty() && last_part &&
+        last_part->type == PartType::kFixed) {
+      needs_grouping = options_.prefix_list.find(last_part->value.back()) !=
+                       std::string::npos;
+    }
+
     // This is a full featured part.  We must generate a string that looks
     // like:
     //
@@ -189,7 +201,6 @@
         result += ")";
       }
     } else if (part.type == PartType::kFullWildcard) {
-      const Part* last_part = i > 0 ? &part_list_[i - 1] : nullptr;
       // We can only use the `*` wildcard card if we meet a number
       // of conditions.  We must use an explicit `(.*)` group if:
       //
@@ -198,8 +209,11 @@
       //    `(foo)(.*)`.  In that case we cannot emit the `*` shorthand without
       //    it being mistakenly interpreted as the modifier for the previous
       //    group.
+      // 3. The current group is not enclosed in a `{ }` grouping.
+      // 4. The current group does not have an implicit prefix like `/`.
       if (!custom_name && (!last_part || last_part->type == PartType::kFixed ||
-                           last_part->modifier != Modifier::kNone)) {
+                           last_part->modifier != Modifier::kNone ||
+                           needs_grouping || !part.prefix.empty())) {
         result += "*";
       } else {
         result += "(";
diff --git a/third_party/liburlpattern/pattern_unittest.cc b/third_party/liburlpattern/pattern_unittest.cc
index 1c9146f..2484d3d 100644
--- a/third_party/liburlpattern/pattern_unittest.cc
+++ b/third_party/liburlpattern/pattern_unittest.cc
@@ -227,7 +227,7 @@
 }
 
 TEST(PatternStringTest, GroupWithRegexp) {
-  RunPatternStringTest("/foo/{(bar)}", "/foo/(bar)");
+  RunPatternStringTest("/foo/{(bar)}", "/foo/{(bar)}");
 }
 
 TEST(PatternStringTest, GroupWithPrefixAndRegexp) {
@@ -355,11 +355,11 @@
 }
 
 TEST(PatternStringTest, NamedGroupInGroupingFollowedByWildcardWithSuffix) {
-  RunPatternStringTest("{:foo}{(.*)bar}", ":foo{(.*)bar}");
+  RunPatternStringTest("{:foo}{(.*)bar}", ":foo{*bar}");
 }
 
 TEST(PatternStringTest, NamedGroupInGroupingFollowedByWildcardWithPrefix) {
-  RunPatternStringTest("{:foo}{bar(.*)}", ":foo{bar(.*)}");
+  RunPatternStringTest("{:foo}{bar(.*)}", ":foo{bar*}");
 }
 
 TEST(PatternStringTest, NamedGroupInGroupingFollowedByWildcardWithCustomName) {
@@ -412,6 +412,27 @@
   RunPatternStringTest("{:foo(baz)bar}", "{:foo(baz)bar}");
 }
 
+TEST(PatternStringTest, WildcardSlashAndWildcard) {
+  RunPatternStringTest("*/*", "*/*");
+}
+
+TEST(PatternStringTest, WildcardEscapedSlashAndWildcard) {
+  // The backslash in the original input forces the `/` to not be an
+  // implicit prefix for the second `*`.  The generated pattern string
+  // must similarly prevent the implicit prefix from occuring.  This
+  // is done using the `{}` grouping instead, however, as its a bit more
+  // readable than escape backslashes.
+  RunPatternStringTest("*\\/*", "*/{*}");
+}
+
+TEST(PatternStringTest, WildcardSlashAndWildcardInGrouping) {
+  RunPatternStringTest("*/{*}", "*/{*}");
+}
+
+TEST(PatternStringTest, WildcardSlashSlashAndWildcard) {
+  RunPatternStringTest("*//*", "*//*");
+}
+
 TEST(
     PatternStringTest,
     WildcardFollowedByEmptyGroupWithZeroOrMoreModifierAndWildcardWithOptionalModifier) {
diff --git a/third_party/libvpx/README.chromium b/third_party/libvpx/README.chromium
index a6f74501..a231ef2 100644
--- a/third_party/libvpx/README.chromium
+++ b/third_party/libvpx/README.chromium
@@ -6,9 +6,9 @@
 License File: source/libvpx/LICENSE
 Security Critical: yes
 
-Date: Tuesday December 07 2021
+Date: Tuesday January 11 2022
 Branch: main
-Commit: ab35ee100a38347433af24df05a5e1578172a2ae
+Commit: 51415c4076578d3cbc32fcd0d683161c3e887814
 
 Description:
 Contains the sources used to compile libvpx binaries used by Google Chrome and
diff --git a/third_party/libvpx/source/config/ios/arm-neon/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/ios/arm-neon/vpx_dsp_rtcd.h
index 9c314978..cba0a393 100644
--- a/third_party/libvpx/source/config/ios/arm-neon/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/ios/arm-neon/vpx_dsp_rtcd.h
@@ -812,11 +812,11 @@
 int16_t vpx_int_pro_col_neon(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_neon
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
-void vpx_int_pro_row_neon(int16_t* hbuf,
+void vpx_int_pro_row_neon(int16_t hbuf[16],
                           const uint8_t* ref,
                           const int ref_stride,
                           const int height);
diff --git a/third_party/libvpx/source/config/ios/arm64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/ios/arm64/vpx_dsp_rtcd.h
index 9c314978..cba0a393 100644
--- a/third_party/libvpx/source/config/ios/arm64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/ios/arm64/vpx_dsp_rtcd.h
@@ -812,11 +812,11 @@
 int16_t vpx_int_pro_col_neon(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_neon
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
-void vpx_int_pro_row_neon(int16_t* hbuf,
+void vpx_int_pro_row_neon(int16_t hbuf[16],
                           const uint8_t* ref,
                           const int ref_stride,
                           const int height);
diff --git a/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h
index 95df12a..93512df 100644
--- a/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm-neon-cpu-detect/vpx_dsp_rtcd.h
@@ -1042,15 +1042,15 @@
 int16_t vpx_int_pro_col_neon(const uint8_t* ref, const int width);
 RTCD_EXTERN int16_t (*vpx_int_pro_col)(const uint8_t* ref, const int width);
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
-void vpx_int_pro_row_neon(int16_t* hbuf,
+void vpx_int_pro_row_neon(int16_t hbuf[16],
                           const uint8_t* ref,
                           const int ref_stride,
                           const int height);
-RTCD_EXTERN void (*vpx_int_pro_row)(int16_t* hbuf,
+RTCD_EXTERN void (*vpx_int_pro_row)(int16_t hbuf[16],
                                     const uint8_t* ref,
                                     const int ref_stride,
                                     const int height);
diff --git a/third_party/libvpx/source/config/linux/arm-neon-highbd/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/arm-neon-highbd/vpx_dsp_rtcd.h
index 71eb333..bf1619d 100644
--- a/third_party/libvpx/source/config/linux/arm-neon-highbd/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm-neon-highbd/vpx_dsp_rtcd.h
@@ -3512,11 +3512,11 @@
 int16_t vpx_int_pro_col_neon(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_neon
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
-void vpx_int_pro_row_neon(int16_t* hbuf,
+void vpx_int_pro_row_neon(int16_t hbuf[16],
                           const uint8_t* ref,
                           const int ref_stride,
                           const int height);
diff --git a/third_party/libvpx/source/config/linux/arm-neon/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/arm-neon/vpx_dsp_rtcd.h
index 9c314978..cba0a393 100644
--- a/third_party/libvpx/source/config/linux/arm-neon/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm-neon/vpx_dsp_rtcd.h
@@ -812,11 +812,11 @@
 int16_t vpx_int_pro_col_neon(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_neon
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
-void vpx_int_pro_row_neon(int16_t* hbuf,
+void vpx_int_pro_row_neon(int16_t hbuf[16],
                           const uint8_t* ref,
                           const int ref_stride,
                           const int height);
diff --git a/third_party/libvpx/source/config/linux/arm/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/arm/vpx_dsp_rtcd.h
index 792d50f..ccb5eac4 100644
--- a/third_party/libvpx/source/config/linux/arm/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm/vpx_dsp_rtcd.h
@@ -535,7 +535,7 @@
 int16_t vpx_int_pro_col_c(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_c
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
diff --git a/third_party/libvpx/source/config/linux/arm64-highbd/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/arm64-highbd/vpx_dsp_rtcd.h
index 71eb333..bf1619d 100644
--- a/third_party/libvpx/source/config/linux/arm64-highbd/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm64-highbd/vpx_dsp_rtcd.h
@@ -3512,11 +3512,11 @@
 int16_t vpx_int_pro_col_neon(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_neon
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
-void vpx_int_pro_row_neon(int16_t* hbuf,
+void vpx_int_pro_row_neon(int16_t hbuf[16],
                           const uint8_t* ref,
                           const int ref_stride,
                           const int height);
diff --git a/third_party/libvpx/source/config/linux/arm64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/arm64/vpx_dsp_rtcd.h
index 9c314978..cba0a393 100644
--- a/third_party/libvpx/source/config/linux/arm64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/arm64/vpx_dsp_rtcd.h
@@ -812,11 +812,11 @@
 int16_t vpx_int_pro_col_neon(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_neon
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
-void vpx_int_pro_row_neon(int16_t* hbuf,
+void vpx_int_pro_row_neon(int16_t hbuf[16],
                           const uint8_t* ref,
                           const int ref_stride,
                           const int height);
diff --git a/third_party/libvpx/source/config/linux/generic/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/generic/vpx_dsp_rtcd.h
index 8ba4d880..382fc40 100644
--- a/third_party/libvpx/source/config/linux/generic/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/generic/vpx_dsp_rtcd.h
@@ -2820,7 +2820,7 @@
 int16_t vpx_int_pro_col_c(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_c
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
diff --git a/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h
index 096e3a86..7df6097 100644
--- a/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/ia32/vpx_dsp_rtcd.h
@@ -5110,11 +5110,11 @@
 int16_t vpx_int_pro_col_sse2(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_sse2
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
-void vpx_int_pro_row_sse2(int16_t* hbuf,
+void vpx_int_pro_row_sse2(int16_t hbuf[16],
                           const uint8_t* ref,
                           const int ref_stride,
                           const int height);
diff --git a/third_party/libvpx/source/config/linux/mips64el/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/mips64el/vpx_dsp_rtcd.h
index 63a0f75..5cd7f43 100644
--- a/third_party/libvpx/source/config/linux/mips64el/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/mips64el/vpx_dsp_rtcd.h
@@ -535,7 +535,7 @@
 int16_t vpx_int_pro_col_c(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_c
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
diff --git a/third_party/libvpx/source/config/linux/mipsel/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/mipsel/vpx_dsp_rtcd.h
index 63a0f75..5cd7f43 100644
--- a/third_party/libvpx/source/config/linux/mipsel/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/mipsel/vpx_dsp_rtcd.h
@@ -535,7 +535,7 @@
 int16_t vpx_int_pro_col_c(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_c
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
diff --git a/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h
index ed632331..b0a87f4f 100644
--- a/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h
@@ -5187,11 +5187,11 @@
 int16_t vpx_int_pro_col_sse2(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_sse2
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
-void vpx_int_pro_row_sse2(int16_t* hbuf,
+void vpx_int_pro_row_sse2(int16_t hbuf[16],
                           const uint8_t* ref,
                           const int ref_stride,
                           const int height);
diff --git a/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h
index 096e3a86..7df6097 100644
--- a/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/mac/ia32/vpx_dsp_rtcd.h
@@ -5110,11 +5110,11 @@
 int16_t vpx_int_pro_col_sse2(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_sse2
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
-void vpx_int_pro_row_sse2(int16_t* hbuf,
+void vpx_int_pro_row_sse2(int16_t hbuf[16],
                           const uint8_t* ref,
                           const int ref_stride,
                           const int height);
diff --git a/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h
index ed632331..b0a87f4f 100644
--- a/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/mac/x64/vpx_dsp_rtcd.h
@@ -5187,11 +5187,11 @@
 int16_t vpx_int_pro_col_sse2(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_sse2
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
-void vpx_int_pro_row_sse2(int16_t* hbuf,
+void vpx_int_pro_row_sse2(int16_t hbuf[16],
                           const uint8_t* ref,
                           const int ref_stride,
                           const int height);
diff --git a/third_party/libvpx/source/config/nacl/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/nacl/vpx_dsp_rtcd.h
index 8ba4d880..382fc40 100644
--- a/third_party/libvpx/source/config/nacl/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/nacl/vpx_dsp_rtcd.h
@@ -2820,7 +2820,7 @@
 int16_t vpx_int_pro_col_c(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_c
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
diff --git a/third_party/libvpx/source/config/vpx_version.h b/third_party/libvpx/source/config/vpx_version.h
index c78057a..fe0777a 100644
--- a/third_party/libvpx/source/config/vpx_version.h
+++ b/third_party/libvpx/source/config/vpx_version.h
@@ -2,8 +2,8 @@
 #define VERSION_MAJOR 1
 #define VERSION_MINOR 11
 #define VERSION_PATCH 0
-#define VERSION_EXTRA "53-gab35ee100"
+#define VERSION_EXTRA "74-g51415c407"
 #define VERSION_PACKED \
   ((VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | (VERSION_PATCH))
-#define VERSION_STRING_NOSP "v1.11.0-53-gab35ee100"
-#define VERSION_STRING " v1.11.0-53-gab35ee100"
+#define VERSION_STRING_NOSP "v1.11.0-74-g51415c407"
+#define VERSION_STRING " v1.11.0-74-g51415c407"
diff --git a/third_party/libvpx/source/config/win/arm64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/win/arm64/vpx_dsp_rtcd.h
index 71eb333..bf1619d 100644
--- a/third_party/libvpx/source/config/win/arm64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/win/arm64/vpx_dsp_rtcd.h
@@ -3512,11 +3512,11 @@
 int16_t vpx_int_pro_col_neon(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_neon
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
-void vpx_int_pro_row_neon(int16_t* hbuf,
+void vpx_int_pro_row_neon(int16_t hbuf[16],
                           const uint8_t* ref,
                           const int ref_stride,
                           const int height);
diff --git a/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h
index 096e3a86..7df6097 100644
--- a/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/win/ia32/vpx_dsp_rtcd.h
@@ -5110,11 +5110,11 @@
 int16_t vpx_int_pro_col_sse2(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_sse2
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
-void vpx_int_pro_row_sse2(int16_t* hbuf,
+void vpx_int_pro_row_sse2(int16_t hbuf[16],
                           const uint8_t* ref,
                           const int ref_stride,
                           const int height);
diff --git a/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h b/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h
index ed632331..b0a87f4f 100644
--- a/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h
+++ b/third_party/libvpx/source/config/win/x64/vpx_dsp_rtcd.h
@@ -5187,11 +5187,11 @@
 int16_t vpx_int_pro_col_sse2(const uint8_t* ref, const int width);
 #define vpx_int_pro_col vpx_int_pro_col_sse2
 
-void vpx_int_pro_row_c(int16_t* hbuf,
+void vpx_int_pro_row_c(int16_t hbuf[16],
                        const uint8_t* ref,
                        const int ref_stride,
                        const int height);
-void vpx_int_pro_row_sse2(int16_t* hbuf,
+void vpx_int_pro_row_sse2(int16_t hbuf[16],
                           const uint8_t* ref,
                           const int ref_stride,
                           const int height);
diff --git a/third_party/sqlite/README.chromium b/third_party/sqlite/README.chromium
index 4f0741a4..66f985d 100644
--- a/third_party/sqlite/README.chromium
+++ b/third_party/sqlite/README.chromium
@@ -1,7 +1,7 @@
 Name: sqlite
 URL: https://sqlite.org/
-Version: 3.36.0
-CPEPrefix: cpe:/a:sqlite:sqlite:3.36.0
+Version: 3.37.1
+CPEPrefix: cpe:/a:sqlite:sqlite:3.37.1
 Included In Release: Yes
 Security Critical: Yes
 License: Public domain
diff --git a/third_party/tflite/README.chromium b/third_party/tflite/README.chromium
index eb35b85..4b26763 100644
--- a/third_party/tflite/README.chromium
+++ b/third_party/tflite/README.chromium
@@ -1,8 +1,8 @@
 Name: TensorFlow Lite
 Short Name: tflite
 URL: https://github.com/tensorflow/tensorflow
-Version: a4289fcb7a4fc982886aa2d11c7b0d87e9815ed7
-Date: 2022/01/06
+Version: ceedf1794a703715d88605b5ac493517e8fa2499
+Date: 2022/01/10
 License: Apache 2.0
 License File: LICENSE
 Security Critical: Yes
diff --git a/third_party/webrtc_overrides/metronome_task_queue_factory.cc b/third_party/webrtc_overrides/metronome_task_queue_factory.cc
index 5174d81..f96a80a6d 100644
--- a/third_party/webrtc_overrides/metronome_task_queue_factory.cc
+++ b/third_party/webrtc_overrides/metronome_task_queue_factory.cc
@@ -31,8 +31,9 @@
 const base::FeatureParam<bool> kWebRtcMetronomeTaskQueueExcludePacer{
     &kWebRtcMetronomeTaskQueue, "exclude_pacer", /*default_value=*/true};
 
-const base::FeatureParam<bool> kWebRtcMetronomeTaskQueueExcludeDecoders{
-    &kWebRtcMetronomeTaskQueue, "exclude_decoders", /*default_value=*/true};
+const base::FeatureParam<bool> kWebRtcMetronomeTaskQueueExcludeEncodeDecode{
+    &kWebRtcMetronomeTaskQueue, "exclude_encode_decode",
+    /*default_value=*/true};
 
 const base::FeatureParam<bool> kWebRtcMetronomeTaskQueueExcludeMisc{
     &kWebRtcMetronomeTaskQueue, "exclude_misc", /*default_value=*/false};
@@ -191,7 +192,8 @@
       : metronome_source_(std::move(metronome_source)),
         high_priority_task_queue_factory_(CreateWebRtcTaskQueueFactory()),
         exclude_pacer_(kWebRtcMetronomeTaskQueueExcludePacer.Get()),
-        exclude_decoders_(kWebRtcMetronomeTaskQueueExcludeDecoders.Get()),
+        exclude_encode_decode_(
+            kWebRtcMetronomeTaskQueueExcludeEncodeDecode.Get()),
         exclude_misc_(kWebRtcMetronomeTaskQueueExcludeMisc.Get()) {}
 
   std::unique_ptr<webrtc::TaskQueueBase, webrtc::TaskQueueDeleter>
@@ -199,8 +201,9 @@
     bool use_metronome;
     if (name.compare("TaskQueuePacedSender") == 0) {
       use_metronome = !exclude_pacer_;
-    } else if (name.compare("DecodingQueue") == 0) {
-      use_metronome = !exclude_decoders_;
+    } else if (name.compare("DecodingQueue") == 0 ||
+               name.compare("EncoderQueue") == 0) {
+      use_metronome = !exclude_encode_decode_;
     } else if (priority == webrtc::TaskQueueFactory::Priority::HIGH) {
       use_metronome = false;
     } else {
@@ -220,7 +223,7 @@
   const std::unique_ptr<webrtc::TaskQueueFactory>
       high_priority_task_queue_factory_;
   const bool exclude_pacer_;
-  const bool exclude_decoders_;
+  const bool exclude_encode_decode_;
   const bool exclude_misc_;
 };
 
diff --git a/tools/android/test_health/test_health_extractor.py b/tools/android/test_health/test_health_extractor.py
new file mode 100644
index 0000000..4a31e98
--- /dev/null
+++ b/tools/android/test_health/test_health_extractor.py
@@ -0,0 +1,142 @@
+# Lint as: python3
+# Copyright 2022 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 dataclasses
+import datetime as dt
+import logging
+import os
+import pathlib
+import sys
+from typing import List, Optional, Set, Tuple, Union
+
+_TOOLS_ANDROID_PATH = pathlib.Path(__file__).resolve(strict=True).parents[1]
+if str(_TOOLS_ANDROID_PATH) not in sys.path:
+    sys.path.append(str(_TOOLS_ANDROID_PATH))
+from python_utils import git_metadata_utils
+
+import java_test_utils
+
+_CHROMIUM_SRC_PATH = git_metadata_utils.get_chromium_src_path()
+
+_IGNORED_DIRS = ('out', 'third_party', 'clank', 'build/linux', 'native_client',
+                 'tools/android/test_health/testdata')
+
+_IGNORED_FILES = {
+    # WebApkUpdateManagerUnitTest uses a method reference on an array, which
+    # is erroneously reported as a syntax error by javalang; ignoring for now.
+    'chrome/android/junit/src/org/chromium/chrome/browser/webapps/'
+    'WebApkUpdateManagerUnitTest.java'
+}
+
+
+@dataclasses.dataclass(frozen=True)
+class GitRepoInfo:
+    """Holder class for Git repository information."""
+
+    git_head: str
+    """The SHA1 hash of the Git repository's commit at HEAD."""
+
+    git_head_time: dt.datetime
+    """The datetime of the Git repository's commit at HEAD."""
+
+
+@dataclasses.dataclass(frozen=True)
+class TestHealthInfo:
+    """Holder class for test health information about a test class."""
+
+    test_name: str
+    """The name of the test, e.g., the class name of a Java test."""
+
+    test_dir: pathlib.Path
+    """The directory containing the test, relative to the Git repo root."""
+
+    test_filename: str
+    """The filename of the test, e.g., FooJavaTest.java."""
+
+    java_test_health: Optional[java_test_utils.JavaTestHealth]
+    """Java test health info and counters; this is None if not a Java test."""
+
+    git_repo_info: GitRepoInfo
+    """Information about the Git repository being sampled."""
+
+
+def get_repo_test_health(
+        git_repo: pathlib.Path = _CHROMIUM_SRC_PATH,
+        *,
+        test_dir: Union[str, pathlib.Path] = pathlib.Path('.'),
+        ignored_dirs: Tuple[str, ...] = _IGNORED_DIRS,
+        ignored_files: Set[str] = _IGNORED_FILES) -> List[TestHealthInfo]:
+    """Gets test health information and stats for a Git repository.
+
+    This function checks for Java tests annotated as disabled or flaky but could
+    be extended to check other metrics or languages in the future.
+
+    Args:
+        git_repo:
+            The path to the root of the Git repository being checked; defaults
+            to the Chromium repo.
+        test_dir:
+            The subdirectory, relative to the Git repo root, containing the
+            tests of interest; defaults to the root of the Git repo.
+        ignored_dirs:
+            A list of directories to skip (paths relative to `test_dir`);
+            defaults to a set of directories that should be ignored in the
+            Chromium Git repo.
+        ignored_files:
+            A set of file paths to skip (relative to `test_dir`); defaults to
+            files in the Chromium Git repo with unsupported Java syntax.
+    Returns:
+        A list of `TestHealthInfo` objects, one for each test file processed.
+    """
+    tests_root = (git_repo / test_dir).resolve(strict=True)
+    repo_info = _get_git_repo_info(git_repo)
+    test_health_infos: list[TestHealthInfo] = []
+
+    for dirpath, _, filenames in os.walk(tests_root):
+        if os.path.relpath(dirpath, tests_root).startswith(ignored_dirs):
+            continue
+
+        for filename in filenames:
+            if not filename.endswith('Test.java'):
+                continue
+
+            test_path = pathlib.Path(dirpath) / filename
+            if os.path.relpath(test_path, tests_root) in ignored_files:
+                continue
+
+            test_health_info = _get_test_health_info(git_repo, test_path,
+                                                     repo_info)
+            if test_health_info:
+                test_health_infos.append(test_health_info)
+
+    return test_health_infos
+
+
+def _get_test_health_info(repo_root: pathlib.Path, test_path: pathlib.Path,
+                          repo_info: GitRepoInfo) -> Optional[TestHealthInfo]:
+    test_file = test_path.relative_to(repo_root)
+    try:
+        test_health_stats = java_test_utils.get_java_test_health(test_path)
+    except java_test_utils.JavaSyntaxError:
+        # This can occur if the file uses syntax not supported by the underlying
+        # javalang python module used by java_test_utils. These files should be
+        # investigated manually.
+        logging.warning(f'Skipped file "{test_file}" due to'
+                        ' Java syntax error.')
+        return None
+
+    return TestHealthInfo(test_name=test_file.stem,
+                          test_dir=test_file.parent,
+                          test_filename=test_file.name,
+                          java_test_health=test_health_stats,
+                          git_repo_info=repo_info)
+
+
+def _get_git_repo_info(git_repo: pathlib.Path) -> GitRepoInfo:
+    return GitRepoInfo(
+        # TODO(crbug/3372331): Update git_metadata_utils API to accept both
+        #     str and pathlib.Path.
+        git_metadata_utils.get_head_commit_hash(str(git_repo)),
+        git_metadata_utils.get_head_commit_datetime(str(git_repo)))
diff --git a/tools/android/test_health/test_health_extractor_unittest.py b/tools/android/test_health/test_health_extractor_unittest.py
new file mode 100755
index 0000000..524cf93
--- /dev/null
+++ b/tools/android/test_health/test_health_extractor_unittest.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python3
+# Copyright 2022 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.
+"""Tests for test_health_extractor."""
+
+import logging
+import pathlib
+import sys
+import unittest
+
+import java_test_utils
+import test_health_extractor
+
+_TOOLS_ANDROID_PATH = pathlib.Path(__file__).resolve(strict=True).parents[1]
+if str(_TOOLS_ANDROID_PATH) not in sys.path:
+    sys.path.append(str(_TOOLS_ANDROID_PATH))
+from python_utils import git_metadata_utils
+
+_CHROMIUM_SRC_PATH = git_metadata_utils.get_chromium_src_path()
+_CHROMIUM_REPO_INFO = test_health_extractor.GitRepoInfo(
+    git_head=git_metadata_utils.get_head_commit_hash(),
+    git_head_time=git_metadata_utils.get_head_commit_datetime())
+
+_TEST_FILES_PATH = (_CHROMIUM_SRC_PATH / 'tools' / 'android' / 'test_health' /
+                    'testdata' / 'javatests' / 'org' / 'chromium' / 'chrome' /
+                    'browser' / 'test_health').relative_to(_CHROMIUM_SRC_PATH)
+_HEALTHY_TESTS_PATH = _TEST_FILES_PATH / 'healthy_tests'
+_UNHEALTHY_TESTS_PATH = _TEST_FILES_PATH / 'unhealthy_tests'
+_INVALID_SYNTAX_TEST_PATH = (
+    _UNHEALTHY_TESTS_PATH /
+    'InvalidSyntaxTest.java').relative_to(_TEST_FILES_PATH)
+
+_IGNORED_DIRS = ('disabled_tests', 'flaky_tests', 'unhealthy_tests')
+
+_BASE_JAVA_PACKAGE = 'org.chromium.chrome.browser.test_health'
+_JAVA_PACKAGE_HEALTHY_TESTS = _BASE_JAVA_PACKAGE + '.healthy_tests'
+_JAVA_PACKAGE_UNHEALTHY_TESTS = _BASE_JAVA_PACKAGE + '.unhealthy_tests'
+
+
+class GetRepoTestHealth(unittest.TestCase):
+    """Tests for the get_repo_test_health function."""
+
+    def test_get_test_health_healthy_test(self):
+        sample_test_path = _HEALTHY_TESTS_PATH / 'SampleTest.java'
+        expected_test_health_info = test_health_extractor.TestHealthInfo(
+            test_name=sample_test_path.stem,
+            test_dir=sample_test_path.parent,
+            test_filename=sample_test_path.name,
+            java_test_health=java_test_utils.JavaTestHealth(
+                java_package=_JAVA_PACKAGE_HEALTHY_TESTS,
+                disabled_tests_count=0,
+                disable_if_tests_count=0,
+                flaky_tests_count=0),
+            git_repo_info=_CHROMIUM_REPO_INFO)
+
+        test_health_infos = test_health_extractor.get_repo_test_health(
+            test_dir=_TEST_FILES_PATH,
+            ignored_dirs=_IGNORED_DIRS,
+            ignored_files={
+                str((_HEALTHY_TESTS_PATH /
+                     'SampleNoPackageTest.java').relative_to(_TEST_FILES_PATH))
+            })
+
+        self.assertEqual(expected_test_health_info, test_health_infos[0])
+
+    def test_get_test_health_unhealthy_test(self):
+        sample_test_path = _UNHEALTHY_TESTS_PATH / 'SampleTest.java'
+        expected_test_health_info = test_health_extractor.TestHealthInfo(
+            test_name=sample_test_path.stem,
+            test_dir=sample_test_path.parent,
+            test_filename=sample_test_path.name,
+            java_test_health=java_test_utils.JavaTestHealth(
+                java_package=_JAVA_PACKAGE_UNHEALTHY_TESTS,
+                disabled_tests_count=1,
+                disable_if_tests_count=1,
+                flaky_tests_count=1),
+            git_repo_info=_CHROMIUM_REPO_INFO)
+
+        test_health_infos = test_health_extractor.get_repo_test_health(
+            test_dir=_TEST_FILES_PATH,
+            ignored_dirs=('disabled_tests', 'flaky_tests', 'healthy_tests'),
+            ignored_files={str(_INVALID_SYNTAX_TEST_PATH)})
+
+        self.assertEqual(expected_test_health_info, test_health_infos[0])
+
+    def test_get_test_health_all_files_and_folders_ignored(self):
+        test_health_info = test_health_extractor.get_repo_test_health(
+            test_dir=_TEST_FILES_PATH,
+            ignored_dirs=_IGNORED_DIRS,
+            ignored_files={
+                str((_HEALTHY_TESTS_PATH /
+                     'SampleTest.java').relative_to(_TEST_FILES_PATH)),
+                str((_HEALTHY_TESTS_PATH /
+                     'SampleNoPackageTest.java').relative_to(_TEST_FILES_PATH))
+            })
+
+        self.assertListEqual([], test_health_info)
+
+    def test_get_test_health_invalid_syntax_test_skipped(self):
+        with self.assertLogs(level=logging.WARNING) as logging_cm:
+            test_health_infos = test_health_extractor.get_repo_test_health(
+                test_dir=_TEST_FILES_PATH,
+                ignored_dirs=('disabled_tests', 'flaky_tests',
+                              'healthy_tests'),
+                ignored_files={
+                    str((_UNHEALTHY_TESTS_PATH /
+                         'SampleTest.java').relative_to(_TEST_FILES_PATH))
+                })
+
+        self.assertListEqual([], test_health_infos)
+        self.assertIn(str(_INVALID_SYNTAX_TEST_PATH), logging_cm.output[0])
+
+    def test_get_test_health_checks_all_files(self):
+        all_files = [
+            f.relative_to(_CHROMIUM_SRC_PATH)
+            for f in (_CHROMIUM_SRC_PATH /
+                      _TEST_FILES_PATH).resolve(strict=True).rglob('*.java')
+            if f.name != 'InvalidSyntaxTest.java'
+        ]
+
+        test_health_infos = [
+            f.test_dir / f.test_filename
+            for f in test_health_extractor.get_repo_test_health(
+                test_dir=_TEST_FILES_PATH,
+                ignored_files={str(_INVALID_SYNTAX_TEST_PATH)})
+        ]
+
+        self.assertListEqual(all_files, test_health_infos)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tools/binary_size/libsupersize/archive.py b/tools/binary_size/libsupersize/archive.py
index ae46c450..392b16c 100644
--- a/tools/binary_size/libsupersize/archive.py
+++ b/tools/binary_size/libsupersize/archive.py
@@ -12,7 +12,6 @@
 import functools
 import gzip
 import itertools
-import json
 import logging
 import os
 import posixpath
@@ -34,6 +33,7 @@
 import dwarfdump
 import file_format
 import function_signature
+import json_config_parser
 import linker_map_parser
 import models
 import ninja_parser
@@ -90,6 +90,12 @@
   elf_path: str = None  # Unstripped .so path.
   linker_name: str = None  # Requires map_path. Either 'gold' or 'lld'.
   track_string_literals: bool = True
+  # component to use for all symbols.
+  component: str = None
+  # Regular expression that will match generated files.
+  gen_dir_regex: str = None
+  # source_path prefix to use for all symbols.
+  source_path_prefix: str = None
 
   @property
   def algorithm(self):
@@ -253,10 +259,18 @@
   return path
 
 
-def _NormalizeSourcePath(path):
+def _NormalizeSourcePath(path, gen_dir_pattern):
   """Returns (is_generated, normalized_path)"""
-  if path.startswith('$'):  # E.g. $APK, $GOOGLE3
+  # For apk other files, source_path is the path within the apk.
+  if path.startswith('$APK'):
     return False, path
+  if gen_dir_pattern:
+    # Non-chromium gen_dir logic.
+    m = gen_dir_pattern.match(path)
+    if m:
+      return True, path[m.end():]
+    return False, path
+
   if path.startswith('gen/'):
     # Convert gen/third_party/... -> third_party/...
     return True, path[4:]
@@ -300,15 +314,16 @@
       + 'FindSourceForTextAddress() likely has a bug.')
 
 
-def _NormalizePaths(raw_symbols):
+def _NormalizePaths(raw_symbols, gen_dir_regex=None):
   """Fills in the |source_path| attribute and normalizes |object_path|."""
   logging.info('Normalizing source and object paths')
+  gen_dir_pattern = re.compile(gen_dir_regex) if gen_dir_regex else None
   for symbol in raw_symbols:
     if symbol.object_path:
       symbol.object_path = _NormalizeObjectPath(symbol.object_path)
     if symbol.source_path:
       symbol.generated_source, symbol.source_path = _NormalizeSourcePath(
-          symbol.source_path)
+          symbol.source_path, gen_dir_pattern)
 
 
 def _ComputeAncestorPath(path_list, symbol_count):
@@ -994,7 +1009,7 @@
     ret = self._res_info.get(path)
     if ret:
       return ret
-    return None
+    return ''
 
 
 class _ResourcePathDeobfuscator:
@@ -1048,9 +1063,7 @@
   return 0
 
 
-def _AddUnattributedSectionSymbols(raw_symbols,
-                                   section_ranges,
-                                   source_path=None):
+def _AddUnattributedSectionSymbols(raw_symbols, section_ranges, source_path):
   # Create symbols for ELF sections not covered by existing symbols.
   logging.info('Searching for symbol gaps...')
   new_syms_by_section = collections.defaultdict(list)
@@ -1083,7 +1096,7 @@
   unsummed_sections, summed_sections = models.ClassifySections(
       section_ranges.keys())
   ret = []
-  other_elf_symbols = []
+  other_symbols = []
   # Sort keys to ensure consistent order (> 1 sections may have address = 0).
   for section_name, (_, section_size) in list(section_ranges.items()):
     if section_name in seen_sections:
@@ -1091,7 +1104,7 @@
     # Handle sections that don't appear in |raw_symbols|.
     if (section_name not in unsummed_sections
         and section_name not in summed_sections):
-      other_elf_symbols.append(
+      other_symbols.append(
           models.Symbol(models.SECTION_OTHER,
                         section_size,
                         full_name='** ELF Section: {}'.format(section_name),
@@ -1103,7 +1116,7 @@
                         section_size,
                         full_name='** ELF Section: {}'.format(section_name),
                         source_path=source_path))
-  other_elf_symbols.sort(key=lambda s: (s.address, s.full_name))
+  other_symbols.sort(key=lambda s: (s.address, s.full_name))
 
   # TODO(agrieve): It would probably simplify things to use a dict of
   #     section_name->raw_symbols while creating symbols.
@@ -1112,7 +1125,7 @@
       raw_symbols, lambda s: s.section_name):
     ret.extend(group)
     ret.extend(new_syms_by_section[section_name])
-  return ret, other_elf_symbols
+  return ret, other_symbols
 
 
 def _ParseNinjaFiles(output_directory, elf_path=None):
@@ -1238,16 +1251,15 @@
                                                   native_spec.tool_prefix)
       elf_overhead_size = _CalculateElfOverhead(section_ranges, f.name)
 
-  source_path = None
+  source_path = ''
   if native_spec.apk_so_path:
     # Put section symbols under $APK/lib/abi/libfoo.so.
     source_path = posixpath.join(models.APK_PREFIX_PATH,
                                  native_spec.apk_so_path)
 
-  raw_symbols, other_elf_symbols = _AddUnattributedSectionSymbols(
+  raw_symbols, other_symbols = _AddUnattributedSectionSymbols(
       raw_symbols, section_ranges, source_path)
 
-  other_symbols = other_elf_symbols
   if native_spec.elf_path:
     elf_overhead_symbol = models.Symbol(models.SECTION_OTHER,
                                         elf_overhead_size,
@@ -1268,7 +1280,7 @@
 
   # Path normalization must come before compacting aliases so that
   # ancestor paths do not mix generated and non-generated paths.
-  _NormalizePaths(raw_symbols)
+  _NormalizePaths(raw_symbols, native_spec.gen_dir_regex)
 
   logging.info('Converting excessive aliases into shared-path symbols')
   _CompactLargeAliasesIntoSharedSymbols(raw_symbols)
@@ -1401,7 +1413,7 @@
       resource_filename = resource_deobfuscator.MaybeRemapPath(
           zip_info.filename)
       source_path = res_source_mapper.FindSourceForPath(resource_filename)
-      if source_path is None:
+      if not source_path:
         source_path = posixpath.join(models.APK_PREFIX_PATH, resource_filename)
       raw_symbols.append(
           models.Symbol(
@@ -1440,8 +1452,13 @@
                            resources_pathmap_path, pak_id_map):
   raw_symbols = []
   section_sizes = {}
+  default_component = apk_spec.default_component if apk_spec else ''
 
-  def add_syms(section_ranges, new_raw_symbols):
+  def add_syms(section_ranges,
+               new_raw_symbols,
+               source_path_prefix=None,
+               component=None,
+               paths_already_normalized=False):
     new_section_sizes = {
         k: size
         for k, (address, size) in section_ranges.items()
@@ -1455,17 +1472,38 @@
         'Section collision: {}\n\n {}'.format(section_sizes, new_section_sizes))
     section_sizes.update(new_section_sizes)
 
-    # _CreateNativeSymbols() already calls _NormalizePaths().
-    if new_raw_symbols and not new_raw_symbols[0].IsNative():
+    # E.g.: _CreateNativeSymbols() already calls _NormalizePaths().
+    if not paths_already_normalized:
       _NormalizePaths(new_raw_symbols)
+
+    if source_path_prefix:
+      # Prefix the source_path for all symbols that have a source_path assigned,
+      # and that don't have it set to $APK or $GOOGLE3.
+      for s in new_raw_symbols:
+        if s.source_path and s.source_path[0] != '$':
+          s.source_path = source_path_prefix + s.source_path
+
+    if component is not None:
+      for s in new_raw_symbols:
+        s.component = component
+    else:
+      dir_metadata.PopulateComponents(new_raw_symbols,
+                                      source_directory,
+                                      default_component=default_component)
     raw_symbols.extend(new_raw_symbols)
 
   if native_spec:
-    add_syms(*_CreateNativeSymbols(metadata=metadata,
-                                   apk_spec=apk_spec,
-                                   native_spec=native_spec,
-                                   output_directory=output_directory,
-                                   pak_id_map=pak_id_map))
+    section_ranges, new_raw_symbols = _CreateNativeSymbols(
+        metadata=metadata,
+        apk_spec=apk_spec,
+        native_spec=native_spec,
+        output_directory=output_directory,
+        pak_id_map=pak_id_map)
+    add_syms(section_ranges,
+             new_raw_symbols,
+             source_path_prefix=native_spec.source_path_prefix,
+             component=native_spec.component,
+             paths_already_normalized=True)
 
   if pak_spec:
     add_syms(*_CreatePakSymbols(pak_spec=pak_spec,
@@ -1481,10 +1519,6 @@
                                 native_spec=native_spec,
                                 resources_pathmap_path=resources_pathmap_path))
 
-  default_component = apk_spec.default_component if apk_spec else ''
-  dir_metadata.PopulateComponents(raw_symbols,
-                                  source_directory,
-                                  default_component=default_component)
   container = models.Container(name=container_name,
                                metadata=metadata,
                                section_sizes=section_sizes)
@@ -1766,8 +1800,15 @@
   return sub_args_list
 
 
-def _MakeNativeSpec(tentative_output_dir, **kwargs):
+def _MakeNativeSpec(tentative_output_dir, json_config, **kwargs):
   native_spec = NativeSpec(**kwargs)
+  if native_spec.elf_path or native_spec.map_path:
+    basename = os.path.basename(native_spec.elf_path or native_spec.map_path)
+    native_spec.component = json_config.ComponentForNativeFile(basename)
+    native_spec.gen_dir_regex = json_config.GenDirRegexForNativeFile(basename)
+    native_spec.source_path_prefix = json_config.SourcePathPrefixForNativeFile(
+        basename)
+
   if not native_spec.map_path:
     # TODO(crbug.com/1193507): Implement string literal tracking without map
     #     files. nm emits some string literal symbols, but most are missing.
@@ -1801,7 +1842,7 @@
 def _CreateNativeSpecs(*, tentative_output_dir, apk_infolist, elf_path,
                        map_path, abi_filters, auto_abi_filters,
                        track_string_literals, ignore_linker_map, tool_prefix,
-                       on_config_error):
+                       json_config, on_config_error):
   if ignore_linker_map:
     map_path = None
   elif (map_path and not map_path.endswith('.map')
@@ -1816,6 +1857,7 @@
     if map_path or elf_path:
       ret.append(
           _MakeNativeSpec(tentative_output_dir,
+                          json_config,
                           tool_prefix=tool_prefix,
                           apk_so_path=None,
                           map_path=map_path,
@@ -1873,6 +1915,7 @@
 
     ret.append(
         _MakeNativeSpec(tentative_output_dir,
+                        json_config,
                         tool_prefix=tool_prefix,
                         apk_so_path=apk_so_path,
                         map_path=cur_map_path,
@@ -1976,12 +2019,9 @@
                                                'size-info',
                                                os.path.basename(apk_prefix))
     apk_spec.analyze_dex = bool(analyze_dex and apk_spec.size_info_prefix)
-    apk_spec.path_defaults = {
-        k: v['source_path']
-        for k, v in json_config.get('apk_files', {}).items()
-    }
-    apk_spec.default_component = json_config.get('apk_splits', {}).get(
-        split_name, {}).get('default_component', '')
+    apk_spec.default_component = json_config.DefaultComponentForSplit(
+        split_name)
+    apk_spec.path_defaults = json_config.ApkPathDefaults()
 
   pak_spec = None
   apk_pak_paths = None
@@ -2022,6 +2062,7 @@
         ignore_linker_map=(top_args.ignore_linker_map
                            or sub_args.ignore_linker_map),
         tool_prefix=tool_prefix_finder.Finalized(),
+        json_config=json_config,
         on_config_error=on_config_error)
 
     # For app bundles, use a consistent ABI for all splits.
@@ -2057,22 +2098,17 @@
   return on_demand
 
 
-def _ParseJsonConfig(path, on_config_error):
-  path = path or path_util.GetDefaultJsonConfigPath()
-  try:
-    with open(path) as f:
-      return json.load(f)
-  except Exception as e:
-    on_config_error(f'Error while parsing {path}: {e}')
-
-
 def _IterSubArgs(top_args, on_config_error):
   """Generates main paths (may be deduced) for each containers given by input.
 
   Yields:
     For each container, main paths and other info needed to create size_info.
   """
-  json_config = _ParseJsonConfig(top_args.json_config, on_config_error)
+  json_config_path = top_args.json_config
+  if not json_config_path:
+    json_config_path = path_util.GetDefaultJsonConfigPath()
+    logging.info('Using --json-config=%s', json_config_path)
+  json_config = json_config_parser.Parse(json_config_path, on_config_error)
 
   main_file = _IdentifyInputFile(top_args, on_config_error)
   if top_args.no_output_directory:
diff --git a/tools/binary_size/libsupersize/dwarfdump.py b/tools/binary_size/libsupersize/dwarfdump.py
index 99e91a1..1a98e7e 100755
--- a/tools/binary_size/libsupersize/dwarfdump.py
+++ b/tools/binary_size/libsupersize/dwarfdump.py
@@ -45,7 +45,7 @@
         return info[1]
 
     self._unmatched_queries_count += 1
-    return None
+    return ''
 
   def NumberOfPaths(self):
     return len(set(info[1] for info in self._range_info_list))
diff --git a/tools/binary_size/libsupersize/dwarfdump_test.py b/tools/binary_size/libsupersize/dwarfdump_test.py
index b2f8aa2..270969f4 100755
--- a/tools/binary_size/libsupersize/dwarfdump_test.py
+++ b/tools/binary_size/libsupersize/dwarfdump_test.py
@@ -167,7 +167,7 @@
     ]
     source_mapper = dwarfdump.CreateAddressSourceMapperForTest(lines)
     # Address is before first range.
-    self.assertIsNone(source_mapper.FindSourceForTextAddress(0x0))
+    self.assertEqual('', source_mapper.FindSourceForTextAddress(0x0))
     # Address matches start of first range.
     self.assertEqual('foo.cc', source_mapper.FindSourceForTextAddress(0x1))
     # Address is in the middle of middle range.
@@ -175,7 +175,7 @@
     # Address matches end of last range.
     self.assertEqual('baz.cc', source_mapper.FindSourceForTextAddress(0x4f))
     # Address is after lange range.
-    self.assertIsNone(source_mapper.FindSourceForTextAddress(0x50))
+    self.assertEqual('', source_mapper.FindSourceForTextAddress(0x50))
 
 
 if __name__ == '__main__':
diff --git a/tools/binary_size/libsupersize/json_config_parser.py b/tools/binary_size/libsupersize/json_config_parser.py
new file mode 100644
index 0000000..32d5ddf
--- /dev/null
+++ b/tools/binary_size/libsupersize/json_config_parser.py
@@ -0,0 +1,43 @@
+# Copyright 2022 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 interfacing with --json-config file."""
+
+import json
+
+
+class JsonConfig:
+  def __init__(self, json_obj):
+    self._json_obj = json_obj
+
+  def _ApkSplit(self, split_name):
+    return self._json_obj.get('apk_splits', {}).get(split_name, {})
+
+  def _NativeFile(self, basename):
+    return self._json_obj.get('native_files', {}).get(basename, {})
+
+  def DefaultComponentForSplit(self, split_name):
+    return self._ApkSplit(split_name).get('default_component', '')
+
+  def ApkPathDefaults(self):
+    return {
+        k: v['source_path']
+        for k, v in self._json_obj.get('apk_files', {}).items()
+    }
+
+  def ComponentForNativeFile(self, basename):
+    return self._NativeFile(basename).get('component')
+
+  def GenDirRegexForNativeFile(self, basename):
+    return self._NativeFile(basename).get('gen_dir_regex')
+
+  def SourcePathPrefixForNativeFile(self, basename):
+    return self._NativeFile(basename).get('source_path_prefix')
+
+
+def Parse(path, on_config_error):
+  try:
+    with open(path) as f:
+      return JsonConfig(json.load(f))
+  except Exception as e:
+    on_config_error(f'Error while parsing {path}: {e}')
diff --git a/tools/binary_size/libsupersize/testdata/supersize.json b/tools/binary_size/libsupersize/testdata/supersize.json
index c2d72cc..26ddeb9 100644
--- a/tools/binary_size/libsupersize/testdata/supersize.json
+++ b/tools/binary_size/libsupersize/testdata/supersize.json
@@ -8,6 +8,11 @@
     "on_demand": {
       "default_component": "DEFAULT"
     }
+  },
+  "native_files": {
+    "smalltest.so": {
+      "component": "SMALL"
+    }
   }
 }
 
diff --git a/tools/binary_size/supersize.json b/tools/binary_size/supersize.json
index 1492a06..35f2094f 100644
--- a/tools/binary_size/supersize.json
+++ b/tools/binary_size/supersize.json
@@ -4,46 +4,16 @@
       "source_path": "../../third_party/icu/android/icudtl.dat"
     },
     "assets/snapshot_blob_32.bin": {
-      "source_path": "../../v8/snapshot_blob_32.bin"
+      "source_path": "v8/snapshot_blob_32.bin"
     },
     "assets/snapshot_blob_64.bin": {
-      "source_path": "../../v8/snapshot_blob_64.bin"
+      "source_path": "v8/snapshot_blob_64.bin"
     },
     "assets/unwind_cfi_32": {
       "source_path": "../../base/trace_event/cfi_backtrace_android.cc"
     },
     "assets/webapk_dex_version.txt": {
       "source_path": "../../chrome/android/webapk/libs/runtime_library_version.gni"
-    },
-    "lib/armeabi-v7a/libarcore_sdk_c_minimal.so": {
-      "source_path": "../../third_party/arcore-android-sdk/BUILD.gn"
-    },
-    "lib/armeabi-v7a/libarcore_sdk_c.so": {
-      "source_path": "../../third_party/arcore-android-sdk/BUILD.gn"
-    },
-    "lib/armeabi-v7a/libcrashpad_handler_trampoline.so": {
-      "source_path": "../../third_party/crashpad/BUILD.gn"
-    },
-    "lib/armeabi-v7a/libyoga.so": {
-      "source_path": "../../chrome/android/feed/BUILD.gn"
-    },
-    "lib/armeabi-v7a/libelements.so": {
-      "source_path": "../../chrome/android/feed/BUILD.gn"
-    },
-    "lib/arm64-v8a/libarcore_sdk_c_minimal.so": {
-      "source_path": "../../third_party/arcore-android-sdk/BUILD.gn"
-    },
-    "lib/arm64-v8a/libarcore_sdk_c.so": {
-      "source_path": "../../third_party/arcore-android-sdk/BUILD.gn"
-    },
-    "lib/arm64-v8a/libcrashpad_handler_trampoline.so": {
-      "source_path": "../../third_party/crashpad/BUILD.gn"
-    },
-    "lib/arm64-v8a/libyoga.so": {
-      "source_path": "../../chrome/android/feed/BUILD.gn"
-    },
-    "lib/arm64-v8a/libelements.so": {
-      "source_path": "../../chrome/android/feed/BUILD.gn"
     }
   },
   "apk_splits": {
@@ -74,6 +44,32 @@
     "weblayer": {
       "default_component": "Internals>WebLayer"
     }
+  },
+  "native_files": {
+    "libarcore_sdk_c.so": {
+      "component": "Internals>XR>AR"
+    },
+    "libarcore_sdk_c_minimal.so": {
+      "component": "Internals>XR>AR"
+    },
+    "libcrashpad_handler_trampoline.so": {
+      "component": "Internals>CrashReporting"
+    },
+    "libelements.so": {
+      "component": "UI>Browser>ContentSuggestions>Feed",
+      "gen_dir_regex": "blaze-out/.*?/genfiles/",
+      "source_path_prefix": "$GOOGLE3/"
+    },
+    "libyoga.so": {
+      "component": "UI>Browser>ContentSuggestions>Feed",
+      "gen_dir_regex": "blaze-out/.*?/genfiles/",
+      "source_path_prefix": "$GOOGLE3/"
+    },
+    "libsketchology_native.so": {
+      "component": "UI>Browser>Sharing",
+      "gen_dir_regex": "blaze-out/.*?/genfiles/",
+      "source_path_prefix": "$GOOGLE3/"
+    }
   }
 }
 
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 8224d4a..3b4b414 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -35,8 +35,8 @@
 # https://chromium.googlesource.com/chromium/src/+/main/docs/updating_clang.md
 # Reverting problematic clang rolls is safe, though.
 # This is the output of `git describe` and is usable as a commit-ish.
-CLANG_REVISION = 'llvmorg-14-init-12719-gc4b45eeb'
-CLANG_SUB_REVISION = 3
+CLANG_REVISION = 'llvmorg-14-init-14241-ged3a4a49'
+CLANG_SUB_REVISION = 1
 
 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION)
 RELEASE_VERSION = '14.0.0'
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 9b9fe96..12fe343 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -857,6 +857,7 @@
       'gpu-fyi-try-android-p-pixel-2-32': 'gpu_tests_android_release_trybot',
       'gpu-fyi-try-android-p-pixel-2-skv-32': 'gpu_tests_android_release_trybot',
       'gpu-fyi-try-android-r-pixel-4-32': 'gpu_tests_android_release_trybot',
+      'gpu-fyi-try-android-pixel-6-64': 'gpu_tests_android_release_trybot_arm64',
       'gpu-try-android-m-nexus-5x-64': 'gpu_tests_android_release_trybot_arm64',
       'linux_android_dbg_ng': 'android_debug_bot',
       'try-nougat-phone-tester': 'android_debug_trybot_arm64',
@@ -2195,6 +2196,7 @@
 
     'fuchsia_workstation_bot': [
       'release_bot', 'fuchsia', 'fuchsia_include_workstation_image',
+      'fuchsia_chrome'
     ],
 
     'gpu_fyi_tests_debug_trybot': [
@@ -3503,6 +3505,10 @@
       'gn_args': 'target_os="fuchsia"',
     },
 
+    'fuchsia_chrome': {
+      'gn_args': 'fuchsia_browser_type="chrome"',
+    },
+
     'fuchsia_code_coverage': {
       'gn_args': 'fuchsia_code_coverage=true',
     },
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json
index d458782..4fb7bf9 100644
--- a/tools/mb/mb_config_expectations/chromium.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -551,6 +551,7 @@
       "fuchsia_additional_boot_images": [
         "//third_party/fuchsia-sdk/images/qemu-x64-release/"
       ],
+      "fuchsia_browser_type": "chrome",
       "is_component_build": false,
       "is_debug": false,
       "target_os": "fuchsia",
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.android.json b/tools/mb/mb_config_expectations/tryserver.chromium.android.json
index add2398..a5718eb7 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.android.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.android.json
@@ -1308,6 +1308,21 @@
       "use_static_angle": true
     }
   },
+  "gpu-fyi-try-android-pixel-6-64": {
+    "gn_args": {
+      "blink_enable_generated_code_formatting": false,
+      "dcheck_always_on": true,
+      "ffmpeg_branding": "Chrome",
+      "is_component_build": false,
+      "is_debug": false,
+      "proprietary_codecs": true,
+      "symbol_level": 1,
+      "target_cpu": "arm64",
+      "target_os": "android",
+      "use_goma": true,
+      "use_static_angle": true
+    }
+  },
   "gpu-fyi-try-android-r-pixel-4-32": {
     "gn_args": {
       "blink_enable_generated_code_formatting": false,
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index f854fa4..4c55f0c 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -1603,6 +1603,676 @@
   <description>A Chrome OS user manually starts Select To Speak.</description>
 </action>
 
+<action name="Accessibility.CrosSwitchAccess.MenuAction.ActionRecorder">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to open the
+    action recorder submenu.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.Copy">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to copy the
+    selected text.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.Cut">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to cut the
+    selected text.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.Decrement">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to decrement
+    the selected input.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.Dictation">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to dictate into
+    the focused input.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.DisplayBrightnessDown">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to turn down
+    brightness.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.DisplayBrightnessUp">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to turn up
+    brightness.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.DisplayMenu">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to open the
+    display submenu.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.DisplayMirror">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to mirror
+    display.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.DisplayRotate">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to rotate
+    screen.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.DisplayZoomIn">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to zoom in.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.DisplayZoomOut">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to zoom out.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.EndTextSelection">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to stop
+    changing the text selection.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.ExecuteMacro">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to execute the
+    recorded macro.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.Increment">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to increment
+    the selected input.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.ItemScan">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to begin
+    scanning through items on-screen.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.JumpToBeginningOfText">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to move the
+    cursor to the beginning of the text.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.JumpToEndOfText">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to move the
+    cursor to the end of the text.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.Keyboard">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to open the
+    onscreen keyboard.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.LeaveGroup">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to leave the
+    current group.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.LeftClick">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to left click
+    the location targetted by Point Scan.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.MediaFastforward">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to fastforward.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.MediaMenu">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to open the
+    media submenu.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.MediaMute">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to mute the
+    device's volume.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.MediaPlayPause">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to play or
+    pause media.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.MediaRewind">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to rewind
+    media.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.MediaVolumeDown">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to turn volume
+    down.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.MediaVolumeUp">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to turn volume
+    up.
+  </description>
+</action>
+
+<action
+    name="Accessibility.CrosSwitchAccess.MenuAction.MoveBackwardOneCharOfText">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to move the
+    cursor backward one character of text.
+  </description>
+</action>
+
+<action
+    name="Accessibility.CrosSwitchAccess.MenuAction.MoveBackwardOneWordOfText">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to move the
+    cursor backward one word of text.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.MoveCursor">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to start moving
+    the cursor.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.MoveDownOneLineOfText">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to move the
+    cursor down one line of text.
+  </description>
+</action>
+
+<action
+    name="Accessibility.CrosSwitchAccess.MenuAction.MoveForwardOneCharOfText">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to move the
+    cursor forward one character of text.
+  </description>
+</action>
+
+<action
+    name="Accessibility.CrosSwitchAccess.MenuAction.MoveForwardOneWordOfText">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to move the
+    cursor forward one word of text.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.MoveUpOneLineOfText">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to move the
+    cursor up one line of text.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.Paste">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to paste into
+    the text field.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.PointScan">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to start point
+    scanning.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.RightClick">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to right click
+    the location targetted by Point Scan.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.ScrollDown">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to scroll the
+    selected view down.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.ScrollLeft">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to scroll the
+    selected view left.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.ScrollRight">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to scroll the
+    selected view right.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.ScrollUp">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to scroll the
+    selected view up.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.Select">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to activate the
+    currently selected item.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.Settings">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to open the
+    Switch Access settings page.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.Shortcuts">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to open the
+    shortcuts submenu.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.StartRecording">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to start
+    recording a macro.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.StartTextSelection">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to start
+    selecting text.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.StopRecording">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to stop
+    recording a macro.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.SystemDiagnostics">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to open
+    diagnostics.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.SystemHelp">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to open the
+    system help page.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.SystemLauncher">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to jump to the
+    launcher.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.SystemMenu">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to open the
+    system submenu.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.SystemScreenshot">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to take a
+    screenshot.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.SystemStatusBar">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to jump to the
+    status area.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.SystemTaskManager">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to open task
+    manager.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.UserLock">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to locks the
+    device.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.UserMenu">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to open the
+    user submenu.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.UserNextUser">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to go to next
+    user.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.UserPreviousUser">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to go to
+    previous user.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.UserSignOut">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to sign out.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.WebBookmark">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to bookmark a
+    page.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.WebBottomOfPage">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to jump to
+    bottom of page.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.WebClearHistory">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to clear
+    history.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.WebDownloads">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to open
+    downloads.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.WebFindInPage">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to find in
+    page.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.WebMenu">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to open the web
+    submenu.
+  </description>
+</action>
+
+<action name="Accessibility.CrosSwitchAccess.MenuAction.WebTopOfPage">
+  <owner>anastasi@google.com</owner>
+  <owner>dtseng@chromium.org</owner>
+  <owner>ui/accessibility/OWNERS</owner>
+  <description>
+    Recorded when the user selects the Switch Access menu action to jump to top
+    of page.
+  </description>
+</action>
+
 <action name="Accessibility.NativeApi.DoDefault">
   <owner>aleventhal@chromium.org</owner>
   <owner>dtseng@chromium.org</owner>
@@ -2652,6 +3322,29 @@
   </description>
 </action>
 
+<action name="Android.MultiInstance.Enter">
+  <owner>jinsukkim@chromium.org</owner>
+  <owner>twellington@chromium.org</owner>
+  <description>
+    Recorded when multiple Chrome instances become visible on the screen at the
+    same time (e.g. two Chrome windows running side-by-side in split-screen
+    mode). Only applicable on Android S. For Android R and below, see
+    Android.MultiWindowMode.MultiInstance.Enter.
+  </description>
+</action>
+
+<action name="Android.MultiInstance.Exit">
+  <owner>jinsukkim@chromium.org</owner>
+  <owner>twellington@chromium.org</owner>
+  <description>
+    The number of visible, foreground Chrome instances got down below one, which
+    virtually ends the multi-instance 'session'. This can happen when one of the
+    visible instances or both of them in split-screen mode closed or went to the
+    background, or split-screen mode itself exited so that only a single
+    instance remains visible on screen.
+  </description>
+</action>
+
 <action name="Android.MultiWindowMode.Enter">
   <owner>jinsukkim@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
@@ -19568,6 +20261,7 @@
 </action>
 
 <action name="NewTabPage.PreinstalledApps.Clicked">
+  <obsolete>Removed from code in 2022-01.</obsolete>
   <owner>phillis@chromium.org</owner>
   <owner>desktop-pwas-team@google.com</owner>
   <description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 5bfb494..bbf57b4e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -9757,6 +9757,11 @@
   <int value="1" label="Expired"/>
 </enum>
 
+<enum name="BooleanFiltered">
+  <int value="0" label="Not Filtered"/>
+  <int value="1" label="Filtered"/>
+</enum>
+
 <enum name="BooleanFocused">
   <int value="0" label="Unfocused"/>
   <int value="1" label="Focused"/>
@@ -32220,6 +32225,11 @@
       label="Associate Account Dismissed By OS After Learn More Pressed"/>
 </enum>
 
+<enum name="FastPairVersion">
+  <int value="0" label="v1"/>
+  <int value="1" label="v2"/>
+</enum>
+
 <enum name="FaultTolerantHeap">
   <int value="0" label="FTH_OFF">FTH is completely off.</int>
   <int value="1" label="FTH_HKLM">FTH is enabled in HKLM.</int>
@@ -34831,9 +34841,9 @@
   <int value="2357" label="WebkitCrossFade"/>
   <int value="2358" label="DisablePictureInPictureAttribute"/>
   <int value="2359"
-      label="CertificateTransparencyNonCompliantSubresourceInMainFrame"/>
+      label="OBSOLETE_CertificateTransparencyNonCompliantSubresourceInMainFrame"/>
   <int value="2360"
-      label="CertificateTransparencyNonCompliantResourceInSubframe"/>
+      label="OBSOLETE_CertificateTransparencyNonCompliantResourceInSubframe"/>
   <int value="2361" label="V8AbortController_Constructor"/>
   <int value="2362" label="ReplaceCharsetInXHRIgnoringCase"/>
   <int value="2363" label="HTMLIFrameElementGestureMedia"/>
@@ -36687,6 +36697,7 @@
   <int value="4104" label="V8UDPSocket_Close_Method"/>
   <int value="4105" label="HTMLInputElementSimulatedClick"/>
   <int value="4106" label="RTCLocalSdpModificationIceUfragPwd"/>
+  <int value="4107" label="V8Navigator_DeprecatedURNToURL_Method"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -51763,6 +51774,7 @@
   <int value="-1308600417" label="NewNetErrorPageUI:disabled"/>
   <int value="-1308389775" label="PermissionChipGestureSensitive:enabled"/>
   <int value="-1308184869" label="MediaFeedsBackgroundFetching:enabled"/>
+  <int value="-1306581260" label="AutofillParsingPatternProvider:enabled"/>
   <int value="-1304957199" label="OfflinePagesShowAlternateDinoPage:enabled"/>
   <int value="-1304758527" label="SyncSendTabToSelf:disabled"/>
   <int value="-1304401930" label="CSSContainerQueries:enabled"/>
@@ -51848,6 +51860,7 @@
   <int value="-1251359563" label="SharingRenameDevices:disabled"/>
   <int value="-1250965985" label="MediaFeeds:enabled"/>
   <int value="-1250611337" label="ChromeVoxArcSupport:disabled"/>
+  <int value="-1250240035" label="NearbySharingOnePageOnboarding:enabled"/>
   <int value="-1250222445" label="WebMidi:enabled"/>
   <int value="-1248478422" label="enable-zip-archiver-packer"/>
   <int value="-1246840031" label="OptInImeMenu:disabled"/>
@@ -52587,6 +52600,7 @@
   <int value="-714543772" label="enable-gpu-service-logging"/>
   <int value="-714043324" label="OutOfBlinkCORS:enabled"/>
   <int value="-713705575" label="PhoneHubCallNotification:enabled"/>
+  <int value="-713136799" label="NearbySharingOnePageOnboarding:disabled"/>
   <int value="-713104676"
       label="DisablePeripheralDataAccessProtection:enabled"/>
   <int value="-711991950" label="SiteExplorationUi:enabled"/>
@@ -52759,6 +52773,7 @@
   <int value="-588669613"
       label="OmniboxClobberIsZeroSuggestEntrypoint:enabled"/>
   <int value="-585508682" label="DownloadRange:enabled"/>
+  <int value="-584866456" label="LacrosProfileMigrationForceOff:disabled"/>
   <int value="-582870536" label="BluetoothNextHandsfreeProfile:enabled"/>
   <int value="-581236612" label="ConnectivityDiagnosticsWebUi:disabled"/>
   <int value="-580897686" label="SharedHighlightingAmp:enabled"/>
@@ -52865,6 +52880,7 @@
   <int value="-499723386" label="EnableFilesAppCopyImage:disabled"/>
   <int value="-499186481"
       label="OmniboxGroupSuggestionsBySearchVsUrl:disabled"/>
+  <int value="-498883807" label="AutofillParsingPatternProvider:disabled"/>
   <int value="-498740735" label="ArcUsbHost:disabled"/>
   <int value="-498463128" label="MacSystemShareMenu:enabled"/>
   <int value="-498412005" label="EnableDrDc:disabled"/>
@@ -53902,6 +53918,7 @@
   <int value="293134455" label="AutofillSendBillingCustomerNumber:disabled"/>
   <int value="293996306" label="ArrayPrototypeValues:disabled"/>
   <int value="294150128" label="RelatedSearchesSimplifiedUx:enabled"/>
+  <int value="294194537" label="ChromeOSAmbientModeAnimation:disabled"/>
   <int value="295661985" label="video-capture-use-gpu-memory-buffer"/>
   <int value="296066418" label="CCTResizableForThirdParties:disabled"/>
   <int value="296215399" label="WindowsMixedReality:disabled"/>
@@ -54042,6 +54059,7 @@
   <int value="379326303" label="enable-add-to-shelf"/>
   <int value="379428799" label="security-chip-animation"/>
   <int value="380372760" label="V8Orinoco:disabled"/>
+  <int value="380378407" label="LacrosProfileMigrationForceOff:enabled"/>
   <int value="382478170" label="SemanticColorDebugOverride:disabled"/>
   <int value="384677240" label="AssistMultiWordLacrosSupport:enabled"/>
   <int value="385969127" label="disable-win32k-lockdown"/>
@@ -54328,6 +54346,7 @@
   <int value="602117675" label="NTPBookmarkSuggestions:enabled"/>
   <int value="602452004" label="WebBluetoothBondOnDemand:disabled"/>
   <int value="603326800" label="UsePasswordSeparatedSigninFlow:enabled"/>
+  <int value="603897763" label="AutofillPageLanguageDetection:disabled"/>
   <int value="603988014" label="NetworkService:enabled"/>
   <int value="604334859" label="Prerender2:enabled"/>
   <int value="605150752" label="WebUSB:disabled"/>
@@ -54370,6 +54389,7 @@
   <int value="630308195" label="SignInProfileCreation:enabled"/>
   <int value="630776247" label="USBGuard:disabled"/>
   <int value="630947363" label="touch-events"/>
+  <int value="631136125" label="EnableDefaultAppsMigration:enabled"/>
   <int value="631947048" label="WebRtcAnalogAgcClippingControl:disabled"/>
   <int value="632324382"
       label="ExperimentalAccessibilityDictationListening:disabled"/>
@@ -54532,6 +54552,7 @@
   <int value="734900932" label="RecurrentInterstitialFeature:enabled"/>
   <int value="735393448" label="InsertKeyToggleMode:disabled"/>
   <int value="736911267" label="ExperimentalCrostiniUI:disabled"/>
+  <int value="737195667" label="AutofillPageLanguageDetection:enabled"/>
   <int value="737421745" label="enable-accessibility-object-model"/>
   <int value="738868972" label="GdiTextPrinting:disabled"/>
   <int value="738962107" label="ExoOrdinalMotion:disabled"/>
@@ -55641,6 +55662,7 @@
   <int value="1579461102" label="MemoryCoordinator:disabled"/>
   <int value="1580606206" label="CameraAppDocumentManualCrop:disabled"/>
   <int value="1581002467" label="enable-explicit-dma-fences"/>
+  <int value="1582004845" label="ChromeOSAmbientModeAnimation:enabled"/>
   <int value="1582400283" label="VideoTutorials:enabled"/>
   <int value="1583543236" label="QuickSettingsNetworkRevamp:disabled"/>
   <int value="1584012382" label="UsbNotificationController:disabled"/>
@@ -56362,6 +56384,7 @@
   <int value="2089897928" label="enable-audio-focus"/>
   <int value="2089901626" label="QuickSettingsPWA:enabled"/>
   <int value="2091002949" label="RemoveNavigationHistory:disabled"/>
+  <int value="2091676363" label="EnableDefaultAppsMigration:disabled"/>
   <int value="2091943748"
       label="MigrateDefaultChromeAppToWebAppsGSuite:disabled"/>
   <int value="2092538671" label="UseStorkSmdsServerAddress:disabled"/>
@@ -58767,6 +58790,7 @@
   <int value="4" label="First by mDNS, then by DIAL"/>
   <int value="5" label="First by DIAL, then by mDNS"/>
   <int value="6" label="Connection retry on error"/>
+  <int value="7" label="Added by access code"/>
 </enum>
 
 <enum name="MediaRouterCreateRouteOutcome">
@@ -72108,6 +72132,12 @@
   <int value="1" label="The user chose to reload the full page."/>
 </enum>
 
+<enum name="PriceTrackingState">
+  <int value="0" label="Price tracking shown"/>
+  <int value="1" label="Price tracking enabled"/>
+  <int value="2" label="Price tracking disabled"/>
+</enum>
+
 <enum name="PrinterProtocol">
   <int value="0" label="Unknown"/>
   <int value="1" label="Universal Serial Bus (usb)"/>
@@ -84041,6 +84071,12 @@
   <int value="2" label="Reset migration"/>
 </enum>
 
+<enum name="StartupVisibility">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Background"/>
+  <int value="2" label="Foreground"/>
+</enum>
+
 <enum name="State">
   <int value="0" label="Canceled"/>
   <int value="1" label="Dropped"/>
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml
index 661a0365..b8d4a28 100644
--- a/tools/metrics/histograms/metadata/accessibility/histograms.xml
+++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -472,6 +472,13 @@
   <summary>Recorded whenever Dictation fails to perform a macro.</summary>
 </histogram>
 
+<histogram name="Accessibility.CrosDictation.MacroRecognized"
+    enum="CrosDictationMacroName" expires_after="2023-01-30">
+  <owner>akihiroota@chromium.org</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>Recorded whenever Dictation recognizes a macro.</summary>
+</histogram>
+
 <histogram name="Accessibility.CrosDictation.MacroSucceeded"
     enum="CrosDictationMacroName" expires_after="2023-01-30">
   <owner>akihiroota@chromium.org</owner>
@@ -805,6 +812,10 @@
 <histogram
     name="Accessibility.CrosSwitchAccess.MenuAction.{SwitchAccessMenuAction}"
     enum="BooleanUsage" expires_after="2022-04-17">
+  <obsolete>
+    No longer recording as histogram in M99. Reverted back to recording User
+    Action Accessibility.CrosSwitchAccess.MenuAction.*.
+  </obsolete>
   <owner>dtseng@chromium.org</owner>
   <owner>anastasi@google.com</owner>
   <owner>chrome-a11y-core@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index b0ded90b..1a4bd41 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -2115,6 +2115,18 @@
   </summary>
 </histogram>
 
+<histogram name="Android.MultiInstance.MaxInstanceCount" units="instances"
+    expires_after="2022-08-16">
+  <owner>jinsukkim@chromium.org</owner>
+  <owner>twellington@chromium.org</owner>
+  <owner>clank-app-team@google.com</owner>
+  <summary>
+    The maximum number of Chrome instances that ran at the same time in a
+    24-hour period. Checked whenever there is an activity state change to log
+    the stats once in 24 hours.
+  </summary>
+</histogram>
+
 <histogram name="Android.MultiInstance.NumActivities" units="activities"
     expires_after="2022-08-19">
   <owner>jinsukkim@chromium.org</owner>
@@ -2160,6 +2172,17 @@
   </summary>
 </histogram>
 
+<histogram name="Android.MultiWindowMode.TotalDuration" units="ms"
+    expires_after="2022-08-16">
+  <owner>jinsukkim@chromium.org</owner>
+  <owner>twellington@chromium.org</owner>
+  <owner>clank-app-team@google.com</owner>
+  <summary>
+    Total time spent in Android N+ multi-window (aka split-screen) mode.
+    Recorded when multi-window mode is exited.
+  </summary>
+</histogram>
+
 <histogram name="Android.NativeBackgroundTask.TaskFinished"
     enum="BackgroundTaskId" expires_after="2023-06-15">
   <owner>mheikal@chromium.org</owner>
@@ -3533,6 +3556,9 @@
 
 <histogram name="Android.WebView.AwDebugCall" enum="AwDebugCall"
     expires_after="2022-01-31">
+  <obsolete>
+    Removed in January 2022.
+  </obsolete>
   <owner>oksamyt@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -3713,7 +3739,7 @@
 </histogram>
 
 <histogram name="Android.WebView.DarkMode.ForceDarkBehavior"
-    enum="WebViewForceDarkBehavior" expires_after="2022-01-24">
+    enum="WebViewForceDarkBehavior" expires_after="2022-07-24">
   <owner>michaelbai@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -3723,7 +3749,7 @@
 </histogram>
 
 <histogram name="Android.WebView.DarkMode.ForceDarkMode"
-    enum="WebViewForceDarkMode2" expires_after="2022-01-24">
+    enum="WebViewForceDarkMode2" expires_after="2022-07-24">
   <owner>michaelbai@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -3733,7 +3759,7 @@
 </histogram>
 
 <histogram name="Android.WebView.DarkMode.InDarkMode" enum="Boolean"
-    expires_after="2022-01-24">
+    expires_after="2022-07-24">
   <owner>michaelbai@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -3743,7 +3769,7 @@
 </histogram>
 
 <histogram name="Android.WebView.DarkMode.InDarkModeVsLightTheme"
-    enum="WebViewInDarkModeVsLightTheme" expires_after="2022-01-24">
+    enum="WebViewInDarkModeVsLightTheme" expires_after="2022-07-24">
   <owner>michaelbai@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -3753,7 +3779,7 @@
 </histogram>
 
 <histogram name="Android.WebView.DarkMode.InDarkModeVsNightMode"
-    enum="WebViewInDarkModeVsNightMode" expires_after="2022-01-24">
+    enum="WebViewInDarkModeVsNightMode" expires_after="2022-07-24">
   <owner>michaelbai@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -3763,7 +3789,7 @@
 </histogram>
 
 <histogram name="Android.WebView.DarkMode.LightTheme" enum="LightTheme"
-    expires_after="2022-01-24">
+    expires_after="2022-07-24">
   <owner>michaelbai@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -3773,7 +3799,7 @@
 </histogram>
 
 <histogram name="Android.WebView.DarkMode.NightMode" enum="NightMode"
-    expires_after="2022-01-24">
+    expires_after="2022-07-24">
   <owner>michaelbai@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -3784,7 +3810,7 @@
 </histogram>
 
 <histogram name="Android.WebView.DarkMode.NightModeVsLightTheme"
-    enum="WebViewNightModeVsLightTheme" expires_after="2022-01-24">
+    enum="WebViewNightModeVsLightTheme" expires_after="2022-07-24">
   <owner>michaelbai@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -3817,7 +3843,7 @@
 </histogram>
 
 <histogram name="Android.WebView.DarkMode.PrimaryTextLuminanceVsLightTheme"
-    enum="WebViewPrimaryTextLuminanceVsLightTheme" expires_after="2022-01-24">
+    enum="WebViewPrimaryTextLuminanceVsLightTheme" expires_after="2022-07-24">
   <owner>michaelbai@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -3828,7 +3854,7 @@
 </histogram>
 
 <histogram name="Android.WebView.DarkMode.PrimaryTextLuminanceVsNightMode"
-    enum="WebViewPrimaryTextLuminanceVsNightMode" expires_after="2022-01-24">
+    enum="WebViewPrimaryTextLuminanceVsNightMode" expires_after="2022-07-24">
   <owner>michaelbai@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index fce06372..eaa42beb 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -1374,6 +1374,18 @@
   <summary>Records the number of tabs in a template when it is saved.</summary>
 </histogram>
 
+<histogram name="Ash.DeskTemplate.UnsupportedAppDialogShow" units="count"
+    expires_after="2023-01-04">
+  <owner>avynn@google.com</owner>
+  <owner>janetmac@chromium.org</owner>
+  <summary>
+    Records the number of times the unsupported Apps dialog shows. This event is
+    triggered when a user attempts to save a template that contains an
+    unsupported app type. Currently the desks templates feature only supports
+    PWAs, Extensions, and browser instances.
+  </summary>
+</histogram>
+
 <histogram name="Ash.DeskTemplate.UserTemplateCount" units="count"
     expires_after="2022-07-14">
   <owner>richui@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index 6fba7be..159c763 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -3660,7 +3660,7 @@
 </histogram>
 
 <histogram name="Blink.WebCodecs.ImageDecoder.Success" enum="BooleanSuccess"
-    expires_after="2022-06-12">
+    expires_after="M104">
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -3670,7 +3670,7 @@
 </histogram>
 
 <histogram name="Blink.WebCodecs.ImageDecoder.Type" enum="DecodedImageType"
-    expires_after="2022-06-12">
+    expires_after="M104">
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -3680,7 +3680,7 @@
 </histogram>
 
 <histogram name="Blink.WebCodecs.{AudioCodecApi}.Codec" enum="AudioCodec"
-    expires_after="2022-02-01">
+    expires_after="M104">
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -3694,7 +3694,7 @@
 </histogram>
 
 <histogram name="Blink.WebCodecs.{CodecApi}.FinalStatus" enum="MediaStatusCode"
-    expires_after="2022-02-01">
+    expires_after="M104">
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -3711,7 +3711,7 @@
 </histogram>
 
 <histogram name="Blink.WebCodecs.{VideoCodecApi}.Codec" enum="VideoCodec"
-    expires_after="2022-02-01">
+    expires_after="M104">
   <owner>dalecurtis@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/bluetooth/histograms.xml b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
index b83b702..bdc36948 100644
--- a/tools/metrics/histograms/metadata/bluetooth/histograms.xml
+++ b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
@@ -317,6 +317,32 @@
   </token>
 </histogram>
 
+<histogram name="Bluetooth.ChromeOS.FastPair.ConnectDevice.Result"
+    enum="BooleanSuccess" expires_after="2022-09-20">
+  <owner>shanefitz@google.com</owner>
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the success or failure of connecting to the device after a
+    successful key exchange. We use ConnectDevice to establish a connection to
+    the device when we have do not have a BluetoothDevice instance from the
+    adapter, and instead need to add ourselves as a PairingDelegate. Emitted
+    when we connect to the device to start the pairing procedure.
+  </summary>
+</histogram>
+
+<histogram name="Bluetooth.ChromeOS.FastPair.CreateScanFilter.Result"
+    enum="BooleanSuccess" expires_after="2022-09-20">
+  <owner>shanefitz@google.com</owner>
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the success or failure of creating a BLE scan filter, which we use
+    to scan for Fast Pair devices. Emitted when the scan filter is created.
+    Creating a scan filter can fail if the provided parameters are invalid.
+  </summary>
+</histogram>
+
 <histogram name="Bluetooth.ChromeOS.FastPair.DeviceMetadataFetcher.Result"
     enum="BooleanSuccess" expires_after="2022-09-20">
   <owner>shanefitz@google.com</owner>
@@ -328,6 +354,17 @@
   </summary>
 </histogram>
 
+<histogram name="Bluetooth.ChromeOS.FastPair.Discovered.Version"
+    enum="FastPairVersion" expires_after="2022-09-20">
+  <owner>shanefitz@google.com</owner>
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the FastPair version of the discovered device. Emitted when showing
+    the discovery notification to the user for a device.
+  </summary>
+</histogram>
+
 <histogram
     name="Bluetooth.ChromeOS.FastPair.EngagementFunnel.Steps.{FastPairPairingProtocol}"
     enum="FastPairEngagementFlowEvent" expires_after="2022-09-20">
@@ -640,6 +677,45 @@
   </summary>
 </histogram>
 
+<histogram name="Bluetooth.ChromeOS.FastPair.NavigateToSettings.Result"
+    enum="BooleanSuccess" expires_after="2022-09-20">
+  <owner>shanefitz@google.com</owner>
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the success or failure of navigating to Bluetooth settings from the
+    error notification. Emitted when the user clicks 'Settings' on the error
+    notification when there is a pair failure.
+  </summary>
+</histogram>
+
+<histogram name="Bluetooth.ChromeOS.FastPair.PairDevice.ErrorReason"
+    enum="BluetoothDeviceConnectErrorCode" expires_after="2022-09-20">
+  <owner>shanefitz@google.com</owner>
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the reason for failure to connect to the device after a successful
+    key exchange. We use PairDevice to establish a connection to the device when
+    we have a BluetoothDevice instance from the adapter. Emitted when we fail to
+    connect to the device to start the pairing procedure, and no metric is
+    emitted on success.
+  </summary>
+</histogram>
+
+<histogram name="Bluetooth.ChromeOS.FastPair.PairDevice.Result"
+    enum="BooleanSuccess" expires_after="2022-09-20">
+  <owner>shanefitz@google.com</owner>
+  <owner>julietlevesque@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records the success or failure of connecting to the device after a
+    successful key exchange. We use PairDevice to establish a connection to the
+    device when we have a BluetoothDevice instance from the adapter. Emitted
+    when we connect to the device to start the pairing procedure.
+  </summary>
+</histogram>
+
 <histogram
     name="Bluetooth.ChromeOS.FastPair.PairFailure.{FastPairPairingProtocol}"
     enum="FastPairPairFailure" expires_after="2022-09-20">
diff --git a/tools/metrics/histograms/metadata/crostini/histograms.xml b/tools/metrics/histograms/metadata/crostini/histograms.xml
index 93d89a92..7cfdc8d64 100644
--- a/tools/metrics/histograms/metadata/crostini/histograms.xml
+++ b/tools/metrics/histograms/metadata/crostini/histograms.xml
@@ -189,6 +189,10 @@
 
 <histogram name="Crostini.Crosvm.CpuPercentage" units="%"
     expires_after="2022-05-15">
+  <obsolete>
+    Removed in M99, no longer useful for Crostini as it measured all crosvm
+    processes.
+  </obsolete>
   <owner>clumptini@google.com</owner>
   <owner>laurentt@google.com</owner>
   <summary>
@@ -200,6 +204,10 @@
 
 <histogram name="Crostini.Crosvm.Processes.Count" units="processes"
     expires_after="2022-01-06">
+  <obsolete>
+    Removed in M99, no longer useful for Crostini as it measured all crosvm
+    processes.
+  </obsolete>
   <owner>clumptini@google.com</owner>
   <owner>laurentt@google.com</owner>
   <summary>
@@ -210,6 +218,10 @@
 
 <histogram name="Crostini.Crosvm.RssPercentage" units="%"
     expires_after="2022-05-15">
+  <obsolete>
+    Removed in M99, no longer useful for Crostini as it measured all crosvm
+    processes.
+  </obsolete>
   <owner>clumptini@google.com</owner>
   <owner>laurentt@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/extensions/histograms.xml b/tools/metrics/histograms/metadata/extensions/histograms.xml
index 36313687..8b118fc 100644
--- a/tools/metrics/histograms/metadata/extensions/histograms.xml
+++ b/tools/metrics/histograms/metadata/extensions/histograms.xml
@@ -37,19 +37,6 @@
   <variant name=".Tab" summary="A port opened to a tab context."/>
 </variants>
 
-<variants name="ExtensionWebUiPageType">
-  <variant name=".MD" summary="The Material Design chrome://extensions page.">
-    <obsolete>
-      Deprecated and removed from code as of 10/2020.
-    </obsolete>
-  </variant>
-  <variant name=".Uber" summary="The Uber chrome://extensions page.">
-    <obsolete>
-      Deprecated and removed from code as of 04/2018.
-    </obsolete>
-  </variant>
-</variants>
-
 <histogram name="Extensions.ActionSetIconFailureType"
     enum="ExtensionActionSetIconFailureType" expires_after="M88">
   <owner>rdevlin.cronin@chromium.org</owner>
@@ -80,42 +67,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.ApiBindingGenerationTime" units="ms"
-    expires_after="2021-06-01">
-  <obsolete>
-    Code removed 2021-11.
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The amount of time it takes to generate the JavaScript API bindings for a
-    particular extension API. This includes the time from when a JavaScript
-    context tries to access the API, triggering the lazy initializion, to the
-    time when the API object is returned. This is only recorded if the binding
-    is successfully generated. See also Extensions.DidCreateScriptContext_*.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.ApiBindingObjectGenerationTime"
-    units="microseconds" expires_after="2021-06-01">
-  <obsolete>
-    Code removed 2021-11.
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The amount of time it takes to create the JavaScript binding object using
-    the binding.js module. This is a strict subset of
-    Extensions.ApiBindingGenerationTime, which includes this work and more.
-
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.ApiTabUpdateJavascript" enum="Boolean"
     expires_after="M78">
   <owner>dbertoni@chromium.org</owner>
@@ -195,20 +146,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.AttemptedToDowngradeVersionType"
-    enum="ExtensionType" expires_after="2021-06-01">
-  <obsolete>
-    Code removed 2021-11.
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The extension item type of an extension that Chrome attempted to add, but
-    failed because it would downgrade the version. Tracking for
-    https://crbug.com/810799.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.BackgroundPageLoadTime2" units="ms"
     expires_after="2022-11-18">
   <owner>rdevlin.cronin@chromium.org</owner>
@@ -273,72 +210,6 @@
   </summary>
 </histogram>
 
-<histogram
-    name="Extensions.Bindings.UpdateBindingsForContextTime{ExtensionContextType}"
-    units="microseconds" expires_after="2021-01-31">
-  <obsolete>
-    Code removed 2021/06.
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The elapsed time to update the bindings for a new or existing v8::Context.
-    The suffix indicates which type of context the bindings are for.
-
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution. {ExtensionContextType}
-  </summary>
-  <token key="ExtensionContextType">
-    <variant name=""/>
-    <variant name=".BlessedExtensionContext"
-        summary="Blessed Extension Context">
-      <obsolete>
-        Removed 2021/06.
-      </obsolete>
-    </variant>
-    <variant name=".BlessedWebPageContext" summary="Blessed Web Page Context">
-      <obsolete>
-        Removed 2021/06.
-      </obsolete>
-    </variant>
-    <variant name=".ContentScriptContext" summary="Content Script Context">
-      <obsolete>
-        Removed 2021/06.
-      </obsolete>
-    </variant>
-    <variant name=".LockScreenExtensionContext"
-        summary="Lock Screen Extension Context">
-      <obsolete>
-        Removed 2021/06.
-      </obsolete>
-    </variant>
-    <variant name=".ServiceWorkerContext" summary="Service Worker Context">
-      <obsolete>
-        Removed 2021/06.
-      </obsolete>
-    </variant>
-    <variant name=".UnblessedExtensionContext"
-        summary="Unblessed Extension Context">
-      <obsolete>
-        Removed 2021/06.
-      </obsolete>
-    </variant>
-    <variant name=".WebPageContext" summary="(unblessed) Web Page Context">
-      <obsolete>
-        Removed 2021/06.
-      </obsolete>
-    </variant>
-    <variant name=".WebUIContext" summary="WebUI Context">
-      <obsolete>
-        Removed 2021/06.
-      </obsolete>
-    </variant>
-  </token>
-</histogram>
-
 <histogram name="Extensions.BookmarkAppLaunchContainer"
     enum="AppLaunchContainer" expires_after="2022-06-12">
   <owner>phillis@chromium.org</owner>
@@ -372,55 +243,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.Checkup.NtpPromoClicked"
-    enum="ExtensionCheckupMessage" expires_after="M85">
-  <obsolete>
-    Code removed 2021/07.
-  </obsolete>
-  <owner>archanasimha@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    Records every time the user clicks on the extensions checkup middle slot
-    promo link. The user is served the promo if they are part of the
-    ExtensionsCheckup experiment and have at least one non default installed
-    extension. The promo should appear every time the user opens the NTP. The
-    enum value represents the focus of the message displayed in the promo, which
-    is determined by the experiment group the user is in. This metric should be
-    compared with Extensions.Checkup.NtpPromoShown.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.Checkup.NtpPromoShown"
-    enum="ExtensionCheckupMessage" expires_after="M85">
-  <obsolete>
-    Code removed 2021/07.
-  </obsolete>
-  <owner>archanasimha@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    Records every time the user is served the extensions checkup middle slot
-    promo on the NTP. The user is served the promo if they are part of the
-    ExtensionsCheckup experiment and have at least one non default installed
-    extension. The promo should appear every time the user opens the NTP. The
-    enum value represents the focus of the message displayed in the promo, which
-    is determined by the experiment group the user is in. This metric should be
-    compared with Extensions.Checkup.NtpPromoClicked.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.Checkup.TimeSpent" units="ms" expires_after="M85">
-  <obsolete>
-    Code removed 2021/07.
-  </obsolete>
-  <owner>archanasimha@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The amount of time passed from when the chrome://extensions page has
-    finished loading till it is closed. We record the time elapsed once the
-    WebUI has been destroyed.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.ChromeExtensionsClientInitTime2"
     units="microseconds" expires_after="2019-12-01">
   <owner>dbertoni@chromium.org</owner>
@@ -794,21 +616,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.DeclarativeNetRequest.ManifestRulesCount2"
-    units="rules" expires_after="2022-03-31">
-  <obsolete>
-    Deprecated and removed from code as of 08/2021.
-  </obsolete>
-  <owner>karandeepb@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The number of indexed declarative rules provided by an extension manifest
-    for the Declarative Net Request API. This also includes rules from disabled
-    rulesets. This is emitted whenever a packaged extension with a declarative
-    ruleset is installed or updated.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.DeclarativeNetRequest.ReadDynamicRulesJSONStatus"
     enum="ReadDynamicRulesJSONStatus" expires_after="2022-06-01">
   <owner>rdevlin.cronin@chromium.org</owner>
@@ -1283,21 +1090,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.ExtensionAddDisabledRemotelyReason"
-    enum="ExtensionUpdateCheckDataKey" expires_after="2021-10-10">
-  <obsolete>
-    Removed in M92 and replaced with
-    Extensions.ExtensionAddDisabledRemotelyReason2.
-  </obsolete>
-  <owner>bdea@chromium.org</owner>
-  <owner>chrome-safebrowsing-alerts@google.com</owner>
-  <summary>
-    Recorded when a new violation is retrieved from Omaha for an extension. The
-    new violation reason is retrieved from the update service data key reasons
-    during an update check for extensions.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.ExtensionAddDisabledRemotelyReason2"
     enum="ExtensionUpdateCheckDataKey" expires_after="2022-10-10">
   <owner>drubery@chromium.org</owner>
@@ -1333,20 +1125,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.ExtensionDisabledRemotely"
-    enum="ExtensionUpdateCheckDataKey" expires_after="2021-10-10">
-  <obsolete>
-    Removed in M92 and replaced with Extensions.ExtensionDisabledRemotely2.
-  </obsolete>
-  <owner>bdea@chromium.org</owner>
-  <owner>chrome-safebrowsing-alerts@google.com</owner>
-  <summary>
-    Recorded when an extension that was previously enabled is disabled due to a
-    new violation retrieved from Omaha. This is recorded when the violation is
-    detected when Chrome checks for an update for the extension.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.ExtensionDisabledRemotely2"
     enum="ExtensionUpdateCheckDataKey" expires_after="2022-05-08">
   <owner>drubery@chromium.org</owner>
@@ -1414,36 +1192,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.ExtensionServiceInitTime" units="units"
-    expires_after="2021-12-01">
-  <obsolete>
-    Code removed 2021-11.
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    Time taken for the ExtensionService to initialize, including the time it
-    takes to load the extensions for the service's profile and parse their
-    manifests. This happens during startup and also any time a new profile is
-    loaded.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.ExtensionServiceNotifyReadyListenersTime"
-    units="units" expires_after="2021-06-01">
-  <obsolete>
-    Code removed 2021-11.
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    Time taken for the ExtensionService to notify all ready listeners that the
-    extension system is now ready. This happens as part of the total
-    initialization time of ExtensionService, measured in
-    Extensions.ExtensionServiceInitTime.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.ExtensionUninstalled" units="units"
     expires_after="2022-05-01">
   <owner>rdevlin.cronin@chromium.org</owner>
@@ -1662,23 +1410,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.ForceInstalledFailureSandboxUnpackFailureReason"
-    enum="ExtensionUnpackFailureReason" expires_after="2021-08-01">
-  <obsolete>
-    Deprecated as of 02/2021 in favour of
-    `Extensions.ForceInstalledFailureSandboxUnpackFailureReason2`.
-  </obsolete>
-  <owner>burunduk@chromium.org</owner>
-  <owner>swapnilgupta@google.com</owner>
-  <owner>managed-devices@google.com</owner>
-  <summary>
-    The reason why enterprise policy forced extensions had failed to unpack.
-    Recorded for each forced extension that failed to install after 5 minutes
-    with Extensions.ForceInstalledFailureReason3 equal to
-    CRX_INSTALL_ERROR_SANDBOXED_UNPACKER_FAILURE.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.ForceInstalledFailureSandboxUnpackFailureReason2"
     enum="ExtensionUnpackFailureReason" expires_after="2022-06-05">
   <owner>burunduk@chromium.org</owner>
@@ -1708,13 +1439,6 @@
   </summary>
   <token key="FailureReason">
     <variant name="" summary="Any type of failure"/>
-    <variant name=".ExtensionStuckInCreatedStage"
-        summary="Extensions stuck in CREATED stage">
-      <obsolete>
-        Replaced as of 03/2021 by
-        &quot;.ExtensionStuckInInitialCreationStage&quot; variant.
-      </obsolete>
-    </variant>
     <variant name=".ExtensionStuckInInitialCreationStage"
         summary="Extensions stuck in
                  NOTIFIED_FROM_MANAGEMENT_INITIAL_CREATION_FORCED stage.
@@ -1727,24 +1451,6 @@
 </histogram>
 
 <histogram
-    name="Extensions.ForceInstalledFailureStuckInCreatedStageAreExtensionsEnabled"
-    enum="BooleanEnabled" expires_after="2021-08-01">
-  <obsolete>
-    Replaced as of 03/2021 by
-    &quot;Extensions.ForceInstalledFailureStuckInInitialCreationStageAreExtensionsEnabled&quot;.
-  </obsolete>
-  <owner>swapnilgupta@google.com</owner>
-  <owner>burunduk@chromium.org</owner>
-  <owner>managed-devices@google.com</owner>
-  <summary>
-    Records whether the extensions are enabled or not. Recorded for each forced
-    extension that failed to install after 5 minutes with
-    Extensions.ForceInstalledCreationStage equal to
-    NOTIFIED_FROM_MANAGEMENT_INITIAL_CREATION_FORCED.
-  </summary>
-</histogram>
-
-<histogram
     name="Extensions.ForceInstalledFailureStuckInInitialCreationStageAreExtensionsEnabled"
     enum="BooleanEnabled" expires_after="2022-06-12">
   <owner>swapnilgupta@google.com</owner>
@@ -1895,11 +1601,6 @@
     {ExtensionInstallStages}
   </summary>
   <token key="ExtensionInstallStages">
-    <variant name="">
-      <obsolete>
-        Base histogram. Use suffixes of this histogram instead.
-      </obsolete>
-    </variant>
     <variant name=".CheckingExpectationsStartTo.FinalizingStart"
         summary="Time taken to perform the expectations checks to confirm
                  that the extension can be installed."/>
@@ -1979,20 +1680,6 @@
   </token>
 </histogram>
 
-<histogram name="Extensions.ForceToolbarPinnedCount" units="extensions"
-    expires_after="2022-04-24">
-  <obsolete>
-    Code removed 2021/10.
-  </obsolete>
-  <owner>nicolaso@chromium.org</owner>
-  <owner>chrome-enterprise-team-core@google.com</owner>
-  <summary>
-    Number of extensions that are &quot;force-pinned&quot; in the
-    ExtensionSettings policy. Recorded after policies are parsed, or refreshed.
-    Only recorded if non-zero.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.ForceToolbarPinnedCount2" units="extensions"
     expires_after="2022-04-24">
   <owner>nicolaso@chromium.org</owner>
@@ -2221,24 +1908,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.HasPermissions_AutoDisable3" enum="Boolean"
-    expires_after="2018-08-30">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    Whether there were any permissions present in an extension when it is
-    automatically disabled due to a permission increase (e.g., after an
-    extension upgrade). To find places where this histogram may be emitted, look
-    for calls to ExtensionService::RecordPermissionMessagesHistogram with the
-    argument AutoDisable. For Sync users, this may be reported for each device,
-    depending on whether the Sync update or the extension auto-update happen
-    first.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.HasPermissions_Install3" enum="Boolean"
     expires_after="never">
 <!-- expires-never: Monitoring extension usage. -->
@@ -2253,38 +1922,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.HasPermissions_InstallAbort3" enum="Boolean"
-    expires_after="2021-06-01">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    Whether there were any permissions present in an extension when installation
-    was aborted (e.g. because the parent window of the confirmation dialog went
-    away), not including installation errors and user cancels. To find places
-    where this histogram may be emitted, look for calls to
-    ExtensionService::RecordPermissionMessagesHistogram with the argument
-    InstallAbort.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.HasPermissions_InstallCancel3" enum="Boolean"
-    expires_after="2018-08-30">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    Whether there were any permissions present in an extension when installation
-    was canceled. To find places where this histogram may be emitted, look for
-    calls to ExtensionService::RecordPermissionMessagesHistogram with the
-    argument InstallCancel.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.HasPermissions_Load3" enum="Boolean"
     expires_after="never">
 <!-- expires-never: Monitoring extension usage. -->
@@ -2299,103 +1936,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.HasPermissions_ReEnable3" enum="Boolean"
-    expires_after="2018-08-30">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    Whether there were any permissions present in an extension when it was
-    re-enabled from a confirmation prompt. To find places where this histogram
-    may be emitted, look for calls to
-    ExtensionService::RecordPermissionMessagesHistogram with the argument
-    ReEnable.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.HasPermissions_ReEnableAbort3" enum="Boolean"
-    expires_after="2018-08-30">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    Whether there were any permissions present in an extension when the
-    re-enable prompt was aborted (e.g. because the parent window of the
-    confirmation dialog went away), not including installation errors and manual
-    user cancels. To find places where this histogram may be emitted, look for
-    calls to ExtensionService::RecordPermissionMessagesHistogram with the
-    argument ReEnableAbort.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.HasPermissions_ReEnableCancel3" enum="Boolean"
-    expires_after="2018-08-30">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    Whether there were any permissions present in an extension when the
-    re-enable was canceled from the confirmation prompt. To find places where
-    this histogram may be emitted, look for calls to
-    ExtensionService::RecordPermissionMessagesHistogram with the argument
-    ReEnableCancel.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.HasPermissions_Uninstall3" enum="Boolean"
-    expires_after="2018-08-30">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    Whether there were any permissions present in an extension when it was
-    uninstalled. To find places where this histogram may be emitted, look for
-    calls to ExtensionService::RecordPermissionMessagesHistogram with the
-    argument Uninstall. For Sync users, this is reported for each device.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.HasPermissions_WebStoreInstall3" enum="Boolean"
-    expires_after="2021-06-01">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    Whether there were any permissions present in an extension when it was
-    installed through the web store. To find places where this histogram may be
-    emitted, look for calls to
-    ExtensionService::RecordPermissionMessagesHistogram with the argument
-    WebStoreInstall. Contrary to the more-general HasPermissions_Install3
-    histogram, this one is NOT reported for each device.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.HasPermissions_WebStoreInstallCancel3"
-    enum="Boolean" expires_after="2021-06-01">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    Whether there were any permissions present in an extension when installation
-    from the web store was canceled. To find places where this histogram may be
-    emitted, look for calls to
-    ExtensionService::RecordPermissionMessagesHistogram with the argument
-    WebStoreInstallCancel.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.HomepageOverrides" units="units"
     expires_after="2022-11-18">
   <owner>rdevlin.cronin@chromium.org</owner>
@@ -2696,19 +2236,6 @@
   <summary>Installs grouped by Extension::HistogramType.</summary>
 </histogram>
 
-<histogram name="Extensions.InstanceID.GetToken.OptionsCount" units="options"
-    expires_after="M89">
-  <obsolete>
-    Deprecated as of 11/2020
-  </obsolete>
-  <owner>peter@chromium.org</owner>
-  <owner>knollr@chromium.org</owner>
-  <summary>
-    The number of options provided to the GetToken request. Recorded when the
-    extension has made a call to chrome.instanceID.getToken().
-  </summary>
-</histogram>
-
 <histogram name="Extensions.IsRenderedIconSufficientlyVisibleTime"
     units="microseconds" expires_after="2019-12-01">
   <owner>dbertoni@chromium.org</owner>
@@ -2907,19 +2434,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.LockedFullscreenStateRequest"
-    enum="LockedFullscreenState" expires_after="2020-01-01">
-  <obsolete>
-    2021.09.07 - Removed.
-  </obsolete>
-  <owner>isandrk@chromium.org</owner>
-  <owner>jhastings@chromium.org</owner>
-  <summary>
-    Records requests to enter or exit locked fullscreen mode through extension
-    APIs. Used only on Chrome OS.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.Management_Refresh" units="ms"
     expires_after="2022-06-26">
   <owner>jam@chromium.org</owner>
@@ -3073,25 +2587,6 @@
   </summary>
   <token key="ExtensionMessagingPortCreationTime">
     <variant name=""/>
-    <variant name=".InBeforeUnload"
-        summary="Created during an event handler for the 'beforeunload' event.">
-      <obsolete>
-        Deprecated and removed from code as of 05/2015.
-      </obsolete>
-    </variant>
-    <variant name=".InUnload"
-        summary="Created during an event handler for the 'unload' event.">
-      <obsolete>
-        Deprecated and removed from code as of 05/2015.
-      </obsolete>
-    </variant>
-    <variant name=".Normal"
-        summary="Created during any time other than the 'unload' or
-                 'beforeunload' handlers.">
-      <obsolete>
-        Deprecated and removed from code as of 05/2015.
-      </obsolete>
-    </variant>
     <variant name=".Total" summary="The total number of ports created."/>
   </token>
 </histogram>
@@ -3149,37 +2644,6 @@
   </token>
 </histogram>
 
-<histogram name="Extensions.MimeHandlerViewEvents" enum="MimeHandlerViewEvents"
-    expires_after="M81">
-  <obsolete>
-    2020.10.20 - Removed.
-  </obsolete>
-  <owner>ekaramad@chromium.org</owner>
-  <owner>wjmaclean@chromium.org</owner>
-  <summary>
-    Reports the creation of MimeHandlerViews and usage of postMessage for cross
-    and same origin resources.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.Navigation.Permissions"
-    enum="ExtensionNavigationPermissions" expires_after="M95">
-  <obsolete>
-    2021.08.30 - Removed.
-  </obsolete>
-  <owner>dtapuska@chromium.org</owner>
-  <owner>kouhei@chromium.org</owner>
-  <summary>
-    Reports a subset of the permissions that are granted to enabled extensions
-    running in the associated browser context at the time a navigation
-    completes. Upon the main frame navigation completing each unique permission
-    observed from all enabled extensions and a kTotal bucket will be recorded.
-    For example, the kWebNavigation bucket divided by kTotal will yield the
-    percentage of main frame navigations that had the kWebNavigation permission.
-    This will be used to evaluate potential impact for BFCache.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.NetworkDelay" units="ms" expires_after="2023-01-23">
   <owner>battre@chromium.org</owner>
   <owner>src/extensions/OWNERS</owner>
@@ -3269,23 +2733,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.Permissions_AutoDisable3"
-    enum="ExtensionPermission3" expires_after="2020-12-01">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The permissions present in an extension when it is automatically disabled
-    due to a permission increase (e.g., after an extension upgrade). To find
-    places where this histogram may be emitted, look for calls to
-    ExtensionService::RecordPermissionMessagesHistogram with the argument
-    AutoDisable. For Sync users, this may be reported for each device, depending
-    on whether the Sync update or the extension auto-update happen first.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.Permissions_Install3" enum="ExtensionPermission3"
     expires_after="never">
 <!-- expires-never: Monitoring extension usage. -->
@@ -3300,38 +2747,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.Permissions_InstallAbort3"
-    enum="ExtensionPermission3" expires_after="M85">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The permissions present in an extension when installation was aborted (e.g.
-    because the parent window of the confirmation dialog went away), not
-    including installation errors and user cancels. To find places where this
-    histogram may be emitted, look for calls to
-    ExtensionService::RecordPermissionMessagesHistogram with the argument
-    InstallAbort.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.Permissions_InstallCancel3"
-    enum="ExtensionPermission3" expires_after="2020-12-01">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The permissions present in an extension when installation was canceled. To
-    find places where this histogram may be emitted, look for calls to
-    ExtensionService::RecordPermissionMessagesHistogram with the argument
-    InstallCancel.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.Permissions_Load3" enum="ExtensionPermission3"
     expires_after="never">
 <!-- expires-never: Monitoring core extension usage. -->
@@ -3346,116 +2761,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.Permissions_ReEnable3" enum="ExtensionPermission3"
-    expires_after="2018-08-30">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The permissions present in an extension when it was re-enabled from a
-    confirmation prompt. To find places where this histogram may be emitted,
-    look for calls to ExtensionService::RecordPermissionMessagesHistogram with
-    the argument ReEnable.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.Permissions_ReEnableAbort3"
-    enum="ExtensionPermission3" expires_after="2018-08-30">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The permissions present in an extension when the re-enable prompt was
-    aborted (e.g. because the parent window of the confirmation dialog went
-    away), not including installation errors and manual user cancels. To find
-    places where this histogram may be emitted, look for calls to
-    ExtensionService::RecordPermissionMessagesHistogram with the argument
-    ReEnableAbort.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.Permissions_ReEnableCancel3"
-    enum="ExtensionPermission3" expires_after="2018-08-30">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The permissions present in an extension when the re-enable was canceled from
-    the confirmation prompt. To find places where this histogram may be emitted,
-    look for calls to ExtensionService::RecordPermissionMessagesHistogram with
-    the argument ReEnableCancel.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.Permissions_Uninstall3" enum="ExtensionPermission3"
-    expires_after="2021-06-01">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The permissions present in an extension when it was uninstalled. To find
-    places where this histogram may be emitted, look for calls to
-    ExtensionService::RecordPermissionMessagesHistogram with the argument
-    Uninstall. For Sync users, this is reported for each device.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.Permissions_WebStoreInstall3"
-    enum="ExtensionPermission3" expires_after="2021-06-01">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The permissions present in an extension when it was installed through the
-    web store. To find places where this histogram may be emitted, look for
-    calls to ExtensionService::RecordPermissionMessagesHistogram with the
-    argument WebStoreInstall. Contrary to the more-general Permissions_Install3
-    histogram, this one is NOT reported for each device.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.Permissions_WebStoreInstallAbort3"
-    enum="ExtensionPermission3" expires_after="2021-06-01">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The permissions present in an extension when installation from the web store
-    was aborted (e.g. because the parent window of the confirmation dialog went
-    away), not including installation errors and user cancels. To find places
-    where this histogram may be emitted, look for calls to
-    ExtensionService::RecordPermissionMessagesHistogram with the argument
-    WebStoreInstallAbort.
-  </summary>
-</histogram>
-
-<histogram name="Extensions.Permissions_WebStoreInstallCancel3"
-    enum="ExtensionPermission3" expires_after="2021-06-01">
-  <obsolete>
-    Deprecated as of 05/2021
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The permissions present in an extension when installation from the web store
-    was canceled. To find places where this histogram may be emitted, look for
-    calls to ExtensionService::RecordPermissionMessagesHistogram with the
-    argument WebStoreInstallCancel.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.PopupCreateTime" units="ms"
     expires_after="2022-06-30">
   <owner>rdevlin.cronin@chromium.org</owner>
@@ -3589,21 +2894,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.SandboxUnpackFailureReason"
-    enum="ExtensionUnpackFailureReason" expires_after="never">
-  <obsolete>
-    Deprecated as of 02/2021 in favour of
-    `Extensions.SandboxUnpackFailureReason2`.
-  </obsolete>
-<!-- expires-never: Monitors core extension installation flows. -->
-
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The reason an extension failed to unpack, recorded when a failure occurs.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.SandboxUnpackFailureReason2"
     enum="ExtensionUnpackFailureReason" expires_after="never">
 <!-- expires-never: Monitors core extension installation flows. -->
@@ -3828,20 +3118,6 @@
   </summary>
 </histogram>
 
-<histogram name="Extensions.UninstallDialogAction"
-    enum="ExtensionUninstallDialogAction" expires_after="2021-12-01">
-  <obsolete>
-    Code removed 2021-11.
-  </obsolete>
-  <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>extensions-core@chromium.org</owner>
-  <summary>
-    The action that was taken from an extension uninstall dialog. Recorded once
-    per dialog shown. Only recorded if all possible actions (including report
-    abuse) were available on the dialog.
-  </summary>
-</histogram>
-
 <histogram name="Extensions.UninstallSource" enum="ExtensionUninstallSource"
     expires_after="never">
 <!-- expires-never: Monitoring core extension usage. -->
@@ -4243,9 +3519,6 @@
     corresponds to the WebContentsObserver::DocumentLoadedInFrame method.
     {ExtensionWebUiPageType}
   </summary>
-  <token key="ExtensionWebUiPageType" variants="ExtensionWebUiPageType">
-    <variant name=""/>
-  </token>
 </histogram>
 
 <histogram
@@ -4260,9 +3533,6 @@
     WebContentsObserver::DocumentOnLoadCompletedInPrimaryMainFrame method.
     {ExtensionWebUiPageType}
   </summary>
-  <token key="ExtensionWebUiPageType" variants="ExtensionWebUiPageType">
-    <variant name=""/>
-  </token>
 </histogram>
 
 <histogram
@@ -4282,13 +3552,6 @@
     MISSING_UPDATE_CHECK_TAG would now be reported as BAD_APP_STATUS.
   </summary>
   <token key="ExtensionSource">
-    <variant name=""
-        summary="Aggregated detailed failure reason for any type of extension">
-      <obsolete>
-        Deprecated as of 04/2021 as this histogram is split into variants
-        `OffStore_` and `WebStore_`.
-      </obsolete>
-    </variant>
     <variant name="OffStore_"
         summary="Detailed failure reason for OffStore extensions"/>
     <variant name="WebStore_"
diff --git a/tools/metrics/histograms/metadata/file/histograms.xml b/tools/metrics/histograms/metadata/file/histograms.xml
index fece4e6..409c180 100644
--- a/tools/metrics/histograms/metadata/file/histograms.xml
+++ b/tools/metrics/histograms/metadata/file/histograms.xml
@@ -64,7 +64,7 @@
 </histogram>
 
 <histogram name="FileBrowser.DownloadDestination.IsGoogleDrive.Changed"
-    enum="BooleanEnabled" expires_after="M98">
+    enum="BooleanEnabled" expires_after="M108">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -790,7 +790,7 @@
 </histogram>
 
 <histogram name="FileBrowser.TeamDrivesCount" units="Team Drives"
-    expires_after="M98">
+    expires_after="M108">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -812,7 +812,7 @@
 </histogram>
 
 <histogram name="FileBrowser.ViewingFileType" enum="ViewFileType"
-    expires_after="M98">
+    expires_after="M108">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
index 7d074f5e..316ee46 100644
--- a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
@@ -496,26 +496,10 @@
   <suffix name="PercentAds"
       label="The percentage of bytes loaded from within ad frames."/>
   <affected-histogram name="PageLoad.Clients.Ads.Bytes.FullPage.Network"/>
-  <affected-histogram name="PageLoad.Clients.Ads.Bytes.FullPage.Total">
-    <obsolete>
-      Deprecated 09/2019. Replaced with PercentAds2 suffix.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram
       name="PageLoad.Clients.Ads.NonVisible.Bytes.FullPage.Network"/>
   <affected-histogram
-      name="PageLoad.Clients.Ads.NonVisible.Bytes.FullPage.Total">
-    <obsolete>
-      Deprecated 09/2019. Replaced with PercentAds2 suffix.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="PageLoad.Clients.Ads.Visible.Bytes.FullPage.Network"/>
-  <affected-histogram name="PageLoad.Clients.Ads.Visible.Bytes.FullPage.Total">
-    <obsolete>
-      Deprecated 09/2019. Replaced with PercentAds2 suffix.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="AdsPageLoadMetricsPercentAds2" separator=".">
@@ -1253,17 +1237,7 @@
   <suffix name="Added" label="Added"/>
   <suffix name="AddedOrRemoved" label="Added or removed"/>
   <suffix name="Removed" label="Removed"/>
-  <affected-histogram name="Autofill.WalletAddresses">
-    <obsolete>
-      Replaced with Autofill.WalletAddresses2 in 2018/12.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Autofill.WalletAddresses2"/>
-  <affected-histogram name="Autofill.WalletCards">
-    <obsolete>
-      Replaced with Autofill.WalletAddresses2 in 2018/12.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Autofill.WalletCards2"/>
 </histogram_suffixes>
 
@@ -1393,11 +1367,6 @@
   <suffix name="JPEG" label=""/>
   <suffix name="PNG" label=""/>
   <affected-histogram name="Blink.Canvas.ToBlob.CompleteEncodingDelay"/>
-  <affected-histogram name="Blink.Canvas.ToBlob.IdleEncodeDuration">
-    <obsolete>
-      Replaced with Blink.Canvas.ToBlob.CompleteEncodingDelay in 2017/12.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Blink.Canvas.ToBlob.InitiateEncodingDelay"/>
 </histogram_suffixes>
 
@@ -1424,11 +1393,6 @@
   <suffix name="LessThan1ms" label="Ratio when main frame shorter than 1ms."/>
   <suffix name="MoreThan5ms" label="Ratio when main frame longer than 5ms."/>
   <affected-histogram name="Blink.MainFrame.AnimateRatio"/>
-  <affected-histogram name="Blink.MainFrame.CompositingAssignmentsRatio">
-    <obsolete>
-      No longer recorded after M97 with the launch of CompositeAfterPaint.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Blink.MainFrame.CompositingCommitRatio"/>
   <affected-histogram name="Blink.MainFrame.CompositingInputsRatio"/>
   <affected-histogram name="Blink.MainFrame.CompositingRatio"/>
@@ -1455,11 +1419,6 @@
       name="Blink.AnchorElementMetricsIntersectionObserver.UpdateTime"/>
   <affected-histogram name="Blink.Animate.UpdateTime"/>
   <affected-histogram name="Blink.Compositing.UpdateTime"/>
-  <affected-histogram name="Blink.CompositingAssignments.UpdateTime">
-    <obsolete>
-      No longer recorded after M97 with the launch of CompositeAfterPaint.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Blink.CompositingCommit.UpdateTime"/>
   <affected-histogram name="Blink.CompositingInputs.UpdateTime"/>
   <affected-histogram name="Blink.ContentDocumentUpdate.UpdateTime"/>
@@ -1494,11 +1453,6 @@
       name="Blink.AnchorElementMetricsIntersectionObserver.UpdateTime"/>
   <affected-histogram name="Blink.Animate.UpdateTime"/>
   <affected-histogram name="Blink.Compositing.UpdateTime"/>
-  <affected-histogram name="Blink.CompositingAssignments.UpdateTime">
-    <obsolete>
-      No longer recorded after M97 with the launch of CompositeAfterPaint.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Blink.CompositingCommit.UpdateTime"/>
   <affected-histogram name="Blink.CompositingInputs.UpdateTime"/>
   <affected-histogram name="Blink.ContentDocumentUpdate.UpdateTime"/>
@@ -1533,11 +1487,6 @@
       name="Blink.AnchorElementMetricsIntersectionObserver.UpdateTime"/>
   <affected-histogram name="Blink.Animate.UpdateTime"/>
   <affected-histogram name="Blink.Compositing.UpdateTime"/>
-  <affected-histogram name="Blink.CompositingAssignments.UpdateTime">
-    <obsolete>
-      No longer recorded after M97 with the launch of CompositeAfterPaint.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Blink.CompositingCommit.UpdateTime"/>
   <affected-histogram name="Blink.CompositingInputs.UpdateTime"/>
   <affected-histogram name="Blink.ContentDocumentUpdate.UpdateTime"/>
@@ -1673,16 +1622,6 @@
   <suffix name="Video"
       label="Showing cache patterns only for video resources."/>
   <affected-histogram name="HttpCache.Pattern"/>
-  <affected-histogram name="HttpCache.StaleEntry.FreshnessPeriodsSinceLastUsed">
-    <obsolete>
-      Deprecated in M77.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="HttpCache.ValidationCause">
-    <obsolete>
-      Deprecated in M77.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="CacheInstance" separator="." ordering="prefix">
@@ -1731,21 +1670,6 @@
   <affected-histogram name="SimpleCache.Eviction.SizeWhenDone2"/>
   <affected-histogram name="SimpleCache.Eviction.TimeToDone"/>
   <affected-histogram name="SimpleCache.Eviction.TimeToSelectEntries"/>
-  <affected-histogram name="SimpleCache.FileDescriptorLimitHard">
-    <obsolete>
-      Removed January 2018, as the limit is independent of the backend type.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="SimpleCache.FileDescriptorLimitSoft">
-    <obsolete>
-      Removed January 2018, as the limit is independent of the backend type.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="SimpleCache.FileDescriptorLimitStatus">
-    <obsolete>
-      Removed January 2018, as the limit is independent of the backend type.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="SimpleCache.GlobalOpenEntryCount"/>
   <affected-histogram name="SimpleCache.HeaderSize"/>
   <affected-histogram name="SimpleCache.HeaderSizeChange"/>
@@ -2044,33 +1968,6 @@
   <suffix name="DocumentActivity"
       label="Activity launched in DOCUMENT mode (Tabs and apps together) on
              Android"/>
-  <affected-histogram name="MobileStartup.ToolbarFirstDrawTime">
-    <obsolete>
-      Removed and renamed to MobileStartup.ToolbarFirstDrawTime2 due to double
-      reporting bug (see https://crbug.com/857508).
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="MobileStartup.ToolbarFirstDrawTime2">
-    <obsolete>
-      No longer useful, deprecated in M82.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="MobileStartup.ToolbarFirstFocusStartupState">
-    <obsolete>
-      No longer useful, deprecated in M82 (see https://crbug.com/1053190).
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="MobileStartup.ToolbarFirstFocusTime">
-    <obsolete>
-      Removed and renamed to MobileStartup.ToolbarFirstFocusTime2 due to double
-      reporting bug (see https://crbug.com/857508).
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="MobileStartup.ToolbarFirstFocusTime2">
-    <obsolete>
-      No longer useful, deprecated in M82.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="MobileStartup.ToolbarInflationTime"/>
 </histogram_suffixes>
 
@@ -2088,11 +1985,6 @@
       label="Download failed without an OK HTTP response code."/>
   <suffix name="DownloadSuccess" label="Download succeeded."/>
   <suffix name="NetworkError" label="Download failed due to a network error."/>
-  <affected-histogram name="SoftwareReporter.Cleaner.NumberOfDownloadAttempts">
-    <obsolete>
-      Removed Jan 2018
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="SoftwareReporter.Cleaner.TimeToCompleteDownload"/>
 </histogram_suffixes>
 
@@ -2408,205 +2300,11 @@
       name="CompositorLatency.MissedDeadlineFrame.ScrollbarScroll"/>
   <affected-histogram name="CompositorLatency.MissedDeadlineFrame.TouchScroll"/>
   <affected-histogram name="CompositorLatency.MissedDeadlineFrame.WheelScroll"/>
-  <affected-histogram name="CompositorLatency.MissedFrame">
-    <obsolete>
-      Removed on 01/2020. MissedFrame changed to DroppedFrame for more clarity.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="CompositorLatency.MissedFrame.CompositorAnimation">
-    <obsolete>
-      Removed on 01/2020. MissedFrame changed to DroppedFrame for more clarity.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="CompositorLatency.MissedFrame.MainThreadAnimation">
-    <obsolete>
-      Removed on 01/2020. MissedFrame changed to DroppedFrame for more clarity.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="CompositorLatency.MissedFrame.PinchZoom">
-    <obsolete>
-      Removed on 01/2020. MissedFrame changed to DroppedFrame for more clarity.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="CompositorLatency.MissedFrame.RAF">
-    <obsolete>
-      Removed on 01/2020. MissedFrame changed to DroppedFrame for more clarity.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="CompositorLatency.MissedFrame.TouchScroll">
-    <obsolete>
-      Removed on 01/2020. MissedFrame changed to DroppedFrame for more clarity.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="CompositorLatency.MissedFrame.WheelScroll">
-    <obsolete>
-      Removed on 01/2020. MissedFrame changed to DroppedFrame for more clarity.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="CompositorLatency.MissedFrameLatencyIncrease">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="CompositorLatency.MissedFrameLatencyIncrease.CompositorAnimation">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="CompositorLatency.MissedFrameLatencyIncrease.MainThreadAnimation">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="CompositorLatency.MissedFrameLatencyIncrease.PinchZoom">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="CompositorLatency.MissedFrameLatencyIncrease.RAF">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="CompositorLatency.MissedFrameLatencyIncrease.TouchScroll">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="CompositorLatency.MissedFrameLatencyIncrease.WheelScroll">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="CompositorLatency.PinchZoom"/>
   <affected-histogram name="CompositorLatency.RAF"/>
   <affected-histogram name="CompositorLatency.ScrollbarScroll"/>
   <affected-histogram name="CompositorLatency.TouchScroll"/>
   <affected-histogram name="CompositorLatency.WheelScroll"/>
-  <affected-histogram name="SingleThreadedCompositorLatency">
-    <obsolete>
-      Removed on 9/2019: metric is not monitored for the UI compositor.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="SingleThreadedCompositorLatency.CompositorAnimation">
-    <obsolete>
-      Removed on 9/2019: metric is not monitored for the UI compositor.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="SingleThreadedCompositorLatency.MainThreadAnimation">
-    <obsolete>
-      Removed on 9/2019: metric is not monitored for the UI compositor.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="SingleThreadedCompositorLatency.MissedFrame">
-    <obsolete>
-      Removed on 9/2019: metric is not monitored for the UI compositor.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="SingleThreadedCompositorLatency.MissedFrame.CompositorAnimation">
-    <obsolete>
-      Removed on 9/2019: metric is not monitored for the UI compositor.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="SingleThreadedCompositorLatency.MissedFrame.MainThreadAnimation">
-    <obsolete>
-      Removed on 9/2019: metric is not monitored for the UI compositor.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="SingleThreadedCompositorLatency.MissedFrame.PinchZoom">
-    <obsolete>
-      Removed on 9/2019: metric is not monitored for the UI compositor.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="SingleThreadedCompositorLatency.MissedFrame.RAF">
-    <obsolete>
-      Removed on 9/2019: metric is not monitored for the UI compositor.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="SingleThreadedCompositorLatency.MissedFrame.TouchScroll">
-    <obsolete>
-      Removed on 9/2019: metric is not monitored for the UI compositor.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="SingleThreadedCompositorLatency.MissedFrame.WheelScroll">
-    <obsolete>
-      Removed on 9/2019: metric is not monitored for the UI compositor.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="SingleThreadedCompositorLatency.MissedFrameLatencyIncrease">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="SingleThreadedCompositorLatency.MissedFrameLatencyIncrease.CompositorAnimation">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="SingleThreadedCompositorLatency.MissedFrameLatencyIncrease.MainThreadAnimation">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="SingleThreadedCompositorLatency.MissedFrameLatencyIncrease.PinchZoom">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="SingleThreadedCompositorLatency.MissedFrameLatencyIncrease.RAF">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="SingleThreadedCompositorLatency.MissedFrameLatencyIncrease.TouchScroll">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="SingleThreadedCompositorLatency.MissedFrameLatencyIncrease.WheelScroll">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="SingleThreadedCompositorLatency.PinchZoom">
-    <obsolete>
-      Removed on 9/2019: metric is not monitored for the UI compositor.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="SingleThreadedCompositorLatency.RAF">
-    <obsolete>
-      Removed on 9/2019: metric is not monitored for the UI compositor.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="SingleThreadedCompositorLatency.TouchScroll">
-    <obsolete>
-      Removed on 9/2019: metric is not monitored for the UI compositor.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="SingleThreadedCompositorLatency.WheelScroll">
-    <obsolete>
-      Removed on 9/2019: metric is not monitored for the UI compositor.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="CompositorOnlyLatencyStages" separator=".">
@@ -2689,12 +2387,6 @@
   <suffix name="Browser" label=""/>
   <suffix name="Renderer" label=""/>
   <affected-histogram name="Scheduling.BeginImplFrameLatency2"/>
-  <affected-histogram name="Scheduling.BeginMainFrameQueueDurationCritical2">
-    <obsolete>
-      Replaced by SendBeginMainFrameToCommit.BeginMainSentToStarted of
-      CompositorLatency metrics.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Scheduling.BeginMainFrameStartToCommitDuration2"/>
   <affected-histogram name="Scheduling.CommitToReadyToActivateDuration2"/>
   <affected-histogram name="Scheduling.DrawDuration2"/>
@@ -3548,11 +3240,6 @@
   <suffix name="Success" label="Successful detached request"/>
   <affected-histogram name="CustomTabs.DetachedResourceRequest.Duration"/>
   <affected-histogram name="CustomTabs.DetachedResourceRequest.RedirectsCount"/>
-  <affected-histogram name="CustomTabs.ResourcePrefetch.Duration">
-    <obsolete>
-      No longer recorded since M73.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="DataReductionProxy" separator="_">
@@ -3662,36 +3349,6 @@
   <suffix name="None" label="With no detected network"/>
   <suffix name="Unknown" label="On Unknown network"/>
   <suffix name="WiFi" label="On WiFi network"/>
-  <affected-histogram name="DataReductionProxy.AutoLoFiAccuracy">
-    <obsolete>
-      Functionality removed in M77.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="DataReductionProxy.AutoLoFiRequestHeaderState">
-    <obsolete>
-      Functionality removed in M77.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="DataReductionProxy.LoFi.Accuracy.15">
-    <obsolete>
-      Functionality removed in M77.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="DataReductionProxy.LoFi.Accuracy.30">
-    <obsolete>
-      Functionality removed in M77.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="DataReductionProxy.LoFi.Accuracy.60">
-    <obsolete>
-      Functionality removed in M77.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="DataReductionProxy.LoFi.ImplicitOptOutAction">
-    <obsolete>
-      Functionality removed in M77.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="DataReductionProxy_TamperingFingerprints"
@@ -3881,12 +3538,6 @@
   <suffix name="Secure.NonCore"
       label="Records fetch attempts for the first non-core secure data
              reduction proxy"/>
-  <affected-histogram
-      name="DataReductionProxy.WarmupURL.FetchAttemptsBeforeSuccess">
-    <obsolete>
-      Obsoleted in March 2020.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="DataReductionProxy_WithValidOCL_LoFiOn" separator=".">
@@ -4009,16 +3660,6 @@
   <suffix name="DataSaverDisabled" label="Data Saver is disabled"/>
   <suffix name="DataSaverEnabled" label="Data Saver is enabled"/>
   <affected-histogram name="Previews.ContentLength"/>
-  <affected-histogram name="Previews.DataInflation">
-    <obsolete>
-      Obsolete as of 09/2020.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Previews.DataInflationPercent">
-    <obsolete>
-      Obsolete as of 09/2020.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Previews.DataSavings"/>
   <affected-histogram name="Previews.DataSavingsPercent"/>
   <affected-histogram name="Previews.OriginalContentLength"/>
@@ -4034,12 +3675,6 @@
       label="Over a secure, core data saver proxy."/>
   <suffix name="SecureProxy.NonCore"
       label="Over a secure, non-core data saver proxy."/>
-  <affected-histogram
-      name="DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch">
-    <obsolete>
-      Obsoleted in March 2020.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="DataUsageReportSubmissionBytes" separator=".">
@@ -4083,11 +3718,6 @@
       label="Data use was recorded with Chrome in background."/>
   <suffix name="Foreground"
       label="Data use was recorded with Chrome in foreground."/>
-  <affected-histogram name="DataUse.BytesReceived.OS">
-    <obsolete>
-      Replaced by DataUse.BytesReceived2.OS in October, 2020.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="DataUse.BytesReceived2.OS"/>
   <affected-histogram name="DataUse.BytesSent.OS"/>
 </histogram_suffixes>
@@ -5269,17 +4899,6 @@
       label="Triggered by entering overview by fading in from home"/>
   <suffix name="FadeOutOverview"
       label="Triggered by exiting overview by fading into home"/>
-  <affected-histogram name="Apps.StateTransition.AnimationSmoothness">
-    <obsolete>
-      Removed in 2019-11. Equivalent values are tracked by
-      Apps.HomeLauncherTransition.AnimationSmoothness.* histograms.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="KioskNextHome.StateTransition.AnimationSmoothness">
-    <obsolete>
-      Removed on 2019-07.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="Enterprise_ResourceCacheTiming" separator=".">
@@ -5327,21 +4946,6 @@
   <affected-histogram name="Event.Latency.OS2"/>
   <affected-histogram name="Event.Latency.OS_NO_VALIDATION.NEGATIVE"/>
   <affected-histogram name="Event.Latency.OS_NO_VALIDATION.POSITIVE"/>
-  <affected-histogram name="Event.Latency.OS_WIN.HIGH_RES">
-    <obsolete>
-      Removed 2021-04-22 - replaced by Event.Latency.OS2.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Event.Latency.OS_WIN.LOW_RES">
-    <obsolete>
-      Removed 2021-04-22 - replaced by Event.Latency.OS2.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Event.Latency.OS_WIN_IS_VALID">
-    <obsolete>
-      Removed 2021-04-22 - replaced by Event.Latency.OS2.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="ExitFunnels" separator=".">
@@ -5543,28 +5147,8 @@
   <suffix name="JournalStorage" label="Database for journal storage."/>
   <affected-histogram name="ContentSuggestions.Feed.CommitMutationCount"/>
   <affected-histogram name="ContentSuggestions.Feed.Count"/>
-  <affected-histogram name="ContentSuggestions.Feed.InitialSuccess">
-    <obsolete>
-      Removed 4/2019 in favor of ProtoDB.InitStatus.*.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="ContentSuggestions.Feed.LoadKeysSuccess">
-    <obsolete>
-      Removed 4/2019 in favor of ProtoDB.LoadKeysSuccess.*.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="ContentSuggestions.Feed.LoadKeysTime"/>
-  <affected-histogram name="ContentSuggestions.Feed.LoadSuccess">
-    <obsolete>
-      Removed 4/2019 in favor of ProtoDB.LoadEntriesSuccess.*.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="ContentSuggestions.Feed.LoadTime"/>
-  <affected-histogram name="ContentSuggestions.Feed.OperationCommitSuccess">
-    <obsolete>
-      Removed 4/2019 in favor of ProtoDB.UpdateSuccess.*.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="ContentSuggestions.Feed.OperationCommitTime"/>
 </histogram_suffixes>
 
@@ -6000,11 +5584,6 @@
   <suffix name="PowerResume" label="PowerResume"/>
   <affected-histogram name="GPU.WatchdogThread.ExtraThreadTime"/>
   <affected-histogram name="GPU.WatchdogThread.Timeout"/>
-  <affected-histogram name="GPU.WatchdogThread.WaitTime">
-    <obsolete>
-      Used for an experiment only. Removed 11/2020.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="GPUBlocklistPerFeature" separator=".">
@@ -7176,20 +6755,8 @@
       label="Recorded with between 20 and 39 live tabs open in the browser."/>
   <suffix name="ByLiveTabCount.40OrMoreTabs"
       label="Recorded with 40 or more live tabs open in the browser."/>
-  <affected-histogram
-      name="PageLoad.Experimental.PaintTiming.NavigationToFirstMeaningfulPaint">
-    <obsolete>
-      Deprecated 03/2021. FMP is being deprecated in favor of LCP.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="PageLoad.InteractiveTiming.FirstInputDelay"/>
   <affected-histogram name="PageLoad.InteractiveTiming.FirstInputDelay2"/>
-  <affected-histogram name="PageLoad.InteractiveTiming.FirstInputDelay3">
-    <obsolete>
-      Deprecated 12/2019. Recording suffixed version of
-      PageLoad.InteractiveTiming.FirstInputDelay4 instead.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="PageLoad.InteractiveTiming.FirstInputDelay4"/>
   <affected-histogram
       name="PageLoad.PaintTiming.NavigationToFirstContentfulPaint"/>
@@ -8128,13 +7695,6 @@
   <affected-histogram
       name="ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time"/>
   <affected-histogram
-      name="ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time_StartWorkerExistingProcess">
-    <obsolete>
-      Deprecated as of June 2017, in favor of
-      ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time_WorkerStartOccurred_NavigationPreloadEnabled.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time_WorkerStartOccurred"/>
   <affected-histogram
       name="ServiceWorker.ActivatedWorkerPreparationForMainFrame.Type"/>
@@ -9710,12 +9270,6 @@
       name="NewTabPage.BackgroundService.Collections.RequestLatency"/>
   <affected-histogram
       name="NewTabPage.BackgroundService.Images.RequestLatency"/>
-  <affected-histogram
-      name="NewTabPage.BackgroundService.NextImage.RequestLatency">
-    <obsolete>
-      Never recorded. Marked obsolete 01/2021.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="NewTabPage_OneGoogleBar_RequestLatency" separator=".">
@@ -9896,26 +9450,11 @@
   <suffix name="WebUI3PNTP" label="Loaded 3P WebUI NTP."/>
   <suffix name="WebUINTP" label="Loaded WebUI NTP."/>
   <affected-histogram name="NewTabPage.LoadTime"/>
-  <affected-histogram name="NewTabPage.TilesReceivedTime">
-    <obsolete>
-      Deprecated 06/2019.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="NewTabPageTimingsIsGoogle" separator=".">
   <suffix name="Google" label="Default search provider is Google."/>
   <suffix name="Other" label="Default search provider is not Google."/>
-  <affected-histogram name="NewTabPage.LoadTime.LocalNTP">
-    <obsolete>
-      Deprecated NTP replaced by WebUI NTP M91.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="NewTabPage.LoadTime.Web">
-    <obsolete>
-      Removed in 09 2021.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="NextTabState" separator="_">
@@ -10067,31 +9606,7 @@
   <affected-histogram
       name="NQE.Accuracy.EffectiveConnectionType.EstimatedObservedDiff.Negative.15"/>
   <affected-histogram
-      name="NQE.Accuracy.EffectiveConnectionType.EstimatedObservedDiff.Negative.30">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="NQE.Accuracy.EffectiveConnectionType.EstimatedObservedDiff.Negative.60">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="NQE.Accuracy.EffectiveConnectionType.EstimatedObservedDiff.Positive.15"/>
-  <affected-histogram
-      name="NQE.Accuracy.EffectiveConnectionType.EstimatedObservedDiff.Positive.30">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="NQE.Accuracy.EffectiveConnectionType.EstimatedObservedDiff.Positive.60">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="NQE_Accuracy_RTT_ObservedIntervals" separator=".">
@@ -10116,116 +9631,20 @@
   <affected-histogram
       name="NQE.Accuracy.DownstreamThroughputKbps.EstimatedObservedDiff.Negative.15"/>
   <affected-histogram
-      name="NQE.Accuracy.DownstreamThroughputKbps.EstimatedObservedDiff.Negative.30">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="NQE.Accuracy.DownstreamThroughputKbps.EstimatedObservedDiff.Negative.60">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="NQE.Accuracy.DownstreamThroughputKbps.EstimatedObservedDiff.Positive.15"/>
   <affected-histogram
-      name="NQE.Accuracy.DownstreamThroughputKbps.EstimatedObservedDiff.Positive.30">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="NQE.Accuracy.DownstreamThroughputKbps.EstimatedObservedDiff.Positive.60">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="NQE.Accuracy.HttpRTT.EstimatedObservedDiff.Negative.15"/>
   <affected-histogram
-      name="NQE.Accuracy.HttpRTT.EstimatedObservedDiff.Negative.30">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="NQE.Accuracy.HttpRTT.EstimatedObservedDiff.Negative.60">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="NQE.Accuracy.HttpRTT.EstimatedObservedDiff.Positive.15"/>
   <affected-histogram
-      name="NQE.Accuracy.HttpRTT.EstimatedObservedDiff.Positive.30">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="NQE.Accuracy.HttpRTT.EstimatedObservedDiff.Positive.60">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="NQE.Accuracy.TransportRTT.EstimatedObservedDiff.Negative.15"/>
   <affected-histogram
-      name="NQE.Accuracy.TransportRTT.EstimatedObservedDiff.Negative.30">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="NQE.Accuracy.TransportRTT.EstimatedObservedDiff.Negative.60">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="NQE.Accuracy.TransportRTT.EstimatedObservedDiff.Positive.15"/>
   <affected-histogram
-      name="NQE.Accuracy.TransportRTT.EstimatedObservedDiff.Positive.30">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="NQE.Accuracy.TransportRTT.EstimatedObservedDiff.Positive.60">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="NQE.ExternalEstimateProvider.RTT.Accuracy.EstimatedObservedDiff.Negative.15"/>
   <affected-histogram
-      name="NQE.ExternalEstimateProvider.RTT.Accuracy.EstimatedObservedDiff.Negative.30">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="NQE.ExternalEstimateProvider.RTT.Accuracy.EstimatedObservedDiff.Negative.60">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="NQE.ExternalEstimateProvider.RTT.Accuracy.EstimatedObservedDiff.Positive.15"/>
   <affected-histogram
-      name="NQE.ExternalEstimateProvider.RTT.Accuracy.EstimatedObservedDiff.Positive.30">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="NQE.ExternalEstimateProvider.RTT.Accuracy.EstimatedObservedDiff.Positive.60">
-    <obsolete>
-      Deprecated 01/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="NQE.UnweightedAverage.Accuracy.HttpRTT.EstimatedObservedDiff.Negative.15"/>
   <affected-histogram
       name="NQE.UnweightedAverage.Accuracy.HttpRTT.EstimatedObservedDiff.Positive.15"/>
@@ -10248,12 +9667,6 @@
   <suffix name="Level8" label="2000-4000"/>
   <suffix name="Level9" label="4000-8000"/>
   <suffix name="Level10" label="&gt;=8000"/>
-  <affected-histogram
-      name="NQE.CongestionAnalyzer.CountInflightRequestsForPeakQueueingDelay">
-    <obsolete>
-      Obsoleted in Apr 2020.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="NQE_DifferentPercentiles" separator=".">
@@ -10305,77 +9718,7 @@
   <suffix name="Unknown" label="On Unknown network"/>
   <suffix name="WiFi" label="On WiFi network"/>
   <affected-histogram name="NQE.FastestRTT"/>
-  <affected-histogram name="NQE.MainFrame.EffectiveConnectionType">
-    <obsolete>
-      Deprecated 08/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="NQE.MainFrame.Kbps.Percentile50">
-    <obsolete>
-      Deprecated 08/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="NQE.MainFrame.RTT.Percentile50">
-    <obsolete>
-      Deprecated 08/2017.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="NQE.MainFrame.TransportRTT.Percentile50">
-    <obsolete>
-      Deprecated 08/2017.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="NQE.PeakKbps"/>
-  <affected-histogram name="NQE.RTT.Percentile0">
-    <obsolete>
-      Deprecated 01/2018.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="NQE.RTT.Percentile10">
-    <obsolete>
-      Deprecated 01/2018.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="NQE.RTT.Percentile100">
-    <obsolete>
-      Deprecated 01/2018.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="NQE.RTT.Percentile50">
-    <obsolete>
-      Deprecated 01/2018.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="NQE.RTT.Percentile90">
-    <obsolete>
-      Deprecated 01/2018.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="NQE.TransportRTT.Percentile0">
-    <obsolete>
-      Deprecated 01/2018.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="NQE.TransportRTT.Percentile10">
-    <obsolete>
-      Deprecated 01/2018.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="NQE.TransportRTT.Percentile100">
-    <obsolete>
-      Deprecated 01/2018.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="NQE.TransportRTT.Percentile50">
-    <obsolete>
-      Deprecated 01/2018.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="NQE.TransportRTT.Percentile90">
-    <obsolete>
-      Deprecated 01/2018.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="NQE_ObservationSources" separator=".">
@@ -10823,12 +10166,6 @@
   <affected-histogram
       name="PageLoad.Clients.ServiceWorker.ParseTiming.NavigationToParseStart"/>
   <affected-histogram
-      name="PageLoad.Clients.ServiceWorker.Timing2.NavigationToFirstContentfulPaint">
-    <obsolete>
-      Deprecated in favor of PaintTiming equivalent.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="PageLoad.Clients.ServiceWorker2.PaintTiming.NavigationToFirstContentfulPaint"/>
   <affected-histogram
       name="PageLoad.Clients.ServiceWorker2.ParseTiming.NavigationToParseStart"/>
@@ -10896,11 +10233,6 @@
   <affected-histogram name="PageLoad.Experimental.AbortTiming.Other"/>
   <affected-histogram name="PageLoad.Experimental.AbortTiming.Reload"/>
   <affected-histogram name="PageLoad.Experimental.AbortTiming.Stop"/>
-  <affected-histogram name="PageLoad.Timing2.NavigationToFirstBackground">
-    <obsolete>
-      deprecated in favor of PageLoad.AbortTiming.Background.*
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="PageLoadMetricsAfterBuffering" separator=".">
@@ -11086,12 +10418,6 @@
   <suffix name="Proxied"
       label="Resources loaded through data reduction proxy."/>
   <affected-histogram
-      name="PageLoad.Clients.DataReductionProxy.Experimental.CompletedResources.Network">
-    <obsolete>
-      Deprecated 10/2018
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="PageLoad.Clients.DataReductionProxy.Experimental.CompletedResources.Network2"/>
 </histogram_suffixes>
 
@@ -11321,18 +10647,8 @@
              the NoScript Preview intervention."/>
   <affected-histogram
       name="PageLoad.DocumentTiming.NavigationToLoadEventFired"/>
-  <affected-histogram name="PageLoad.Experimental.Bytes.Network">
-    <obsolete>
-      Deprecated 12/2018.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram
       name="PageLoad.Experimental.Bytes.NetworkIncludingHeaders"/>
-  <affected-histogram name="PageLoad.Experimental.CompletedResources.Network">
-    <obsolete>
-      Functionality removed in M77.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram
       name="PageLoad.Experimental.PaintTiming.NavigationToFirstMeaningfulPaint"/>
   <affected-histogram
@@ -11350,18 +10666,6 @@
   <affected-histogram
       name="PageLoad.Clients.NoServiceWorker.DocumentTiming.NavigationToLoadEventFired"/>
   <affected-histogram
-      name="PageLoad.Clients.NoServiceWorker.Experimental.PaintTiming.NavigationToFirstMeaningfulPaint">
-    <obsolete>
-      Removed in December 2020.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="PageLoad.Clients.NoServiceWorker.Experimental.PaintTiming.ParseStartToFirstMeaningfulPaint">
-    <obsolete>
-      Removed in December 2020.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="PageLoad.Clients.NoServiceWorker.PaintTiming.NavigationToFirstContentfulPaint"/>
   <affected-histogram
       name="PageLoad.Clients.NoServiceWorker.PaintTiming.ParseStartToFirstContentfulPaint"/>
@@ -11438,18 +10742,8 @@
              ResourceLoadingHints to show a Preview version of the page."/>
   <affected-histogram
       name="PageLoad.DocumentTiming.NavigationToLoadEventFired"/>
-  <affected-histogram name="PageLoad.Experimental.Bytes.Network">
-    <obsolete>
-      Deprecated 12/2018.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram
       name="PageLoad.Experimental.Bytes.NetworkIncludingHeaders"/>
-  <affected-histogram name="PageLoad.Experimental.CompletedResources.Network">
-    <obsolete>
-      Functionality removed in M77.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram
       name="PageLoad.Experimental.PaintTiming.NavigationToFirstMeaningfulPaint"/>
   <affected-histogram
@@ -11533,18 +10827,6 @@
       name="PageLoad.DocumentTiming.NavigationToDOMContentLoadedEventFired"/>
   <affected-histogram
       name="PageLoad.DocumentTiming.NavigationToLoadEventFired"/>
-  <affected-histogram
-      name="PageLoad.Experimental.PaintTiming.NavigationToFirstMeaningfulPaint">
-    <obsolete>
-      Removed Dec 2020.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="PageLoad.Experimental.PaintTiming.ParseStartToFirstMeaningfulPaint">
-    <obsolete>
-      Removed Dec 2020.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="PageLoad.InteractiveTiming.FirstInputDelay"/>
   <affected-histogram name="PageLoad.InteractiveTiming.FirstInputDelay2"/>
   <affected-histogram name="PageLoad.InteractiveTiming.FirstInputDelay3"/>
@@ -11556,11 +10838,6 @@
   <affected-histogram
       name="PageLoad.PaintTiming.ParseStartToFirstContentfulPaint"/>
   <affected-histogram name="PageLoad.ParseTiming.NavigationToParseStart"/>
-  <affected-histogram name="PageLoad.Timing2.NavigationToFirstContentfulPaint">
-    <obsolete>
-      Deprecated in favor of PaintTiming equivalent.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="PageLoadMetricsClientsServiceWorkerSpecialApps"
@@ -11578,18 +10855,6 @@
   <affected-histogram
       name="PageLoad.Clients.ServiceWorker.DocumentTiming.NavigationToLoadEventFired"/>
   <affected-histogram
-      name="PageLoad.Clients.ServiceWorker.Experimental.PaintTiming.NavigationToFirstMeaningfulPaint">
-    <obsolete>
-      Removed in December 2020.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="PageLoad.Clients.ServiceWorker.Experimental.PaintTiming.ParseStartToFirstMeaningfulPaint">
-    <obsolete>
-      Removed in December 2020.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="PageLoad.Clients.ServiceWorker.PaintTiming.NavigationToFirstContentfulPaint"/>
   <affected-histogram
       name="PageLoad.Clients.ServiceWorker.PaintTiming.ParseStartToFirstContentfulPaint"/>
@@ -11600,18 +10865,6 @@
   <affected-histogram
       name="PageLoad.Clients.ServiceWorker2.DocumentTiming.NavigationToLoadEventFired"/>
   <affected-histogram
-      name="PageLoad.Clients.ServiceWorker2.Experimental.PaintTiming.NavigationToFirstMeaningfulPaint">
-    <obsolete>
-      Removed in December 2020.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="PageLoad.Clients.ServiceWorker2.Experimental.PaintTiming.ParseStartToFirstMeaningfulPaint">
-    <obsolete>
-      Removed in December 2020.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="PageLoad.Clients.ServiceWorker2.PaintTiming.NavigationToFirstContentfulPaint"/>
   <affected-histogram
       name="PageLoad.Clients.ServiceWorker2.PaintTiming.ParseStartToFirstContentfulPaint"/>
@@ -11810,13 +11063,6 @@
   <suffix name="UserGesture"
       label="Restricted to pages loaded via a user gesture."/>
   <affected-histogram
-      name="PageLoad.Clients.Reload.PaintTiming.NavigationToFirstContentfulPaint">
-    <obsolete>
-      Deprecated in favor of
-      PageLoad.PaintTiming.NavigationToFirstContentfulPaint.LoadType.Reload.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="PageLoad.PaintTiming.NavigationToFirstContentfulPaint.LoadType.Reload"/>
 </histogram_suffixes>
 
@@ -11829,24 +11075,6 @@
   <affected-histogram name="PageLoad.AbortTiming.NewNavigation.BeforeCommit"/>
   <affected-histogram name="PageLoad.AbortTiming.Reload.BeforeCommit"/>
   <affected-histogram
-      name="PageLoad.Experimental.AbortTiming.ForwardBackNavigation.BeforeCommit">
-    <obsolete>
-      Deprecated in favor of UserGesture/BrowserInitiated.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="PageLoad.Experimental.AbortTiming.NewNavigation.BeforeCommit">
-    <obsolete>
-      Deprecated in favor of UserGesture/BrowserInitiated.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="PageLoad.Experimental.AbortTiming.Reload.BeforeCommit">
-    <obsolete>
-      Deprecated in favor of UserGesture/BrowserInitiated.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="PageLoad.PaintTiming.NavigationToFirstContentfulPaint"/>
 </histogram_suffixes>
 
@@ -12343,16 +11571,6 @@
   <suffix name="UtilityProcess" label=""/>
   <suffix name="WorkerProcess" label=""/>
   <affected-histogram name="PerformanceMonitor.AverageCPU"/>
-  <affected-histogram name="PerformanceMonitor.AverageDisk">
-    <obsolete>
-      Removed 04/2021. Not needed for current investigations.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="PerformanceMonitor.HighCPU">
-    <obsolete>
-      Removed 04/2021.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="PermissionRequestGesture" separator=".">
@@ -12420,20 +11638,6 @@
   <affected-histogram name="ContentSettings.PermissionActionsInsecureOrigin"/>
   <affected-histogram name="ContentSettings.PermissionActionsSecureOrigin"/>
   <affected-histogram name="Permissions.Action"/>
-  <affected-histogram name="Permissions.Action.InsecureOrigin">
-    <obsolete>
-      Removed on 2020-06-19 as most permissions are no longer exposed to
-      insecure origins to begin with, and even those few that are recorded no
-      useful data any longer.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Permissions.Action.SecureOrigin">
-    <obsolete>
-      Removed on 2020-06-19 as most permissions are no longer exposed to
-      insecure origins to begin with, and even those few that are recorded no
-      useful data any longer.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Permissions.DSE.AutoPermissionRevertTransition"/>
   <affected-histogram name="Permissions.DSE.EffectiveSetting"/>
   <affected-histogram
@@ -13545,11 +12749,6 @@
              account"/>
   <suffix name="NonGAIA" label="Interaction was not initiated from GAIA"/>
   <affected-histogram name="Profile.AndroidAccountManagementMenu"/>
-  <affected-histogram name="Profile.DesktopMenu">
-    <obsolete>
-      Deprecated M81.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="ProfileOpenState" separator=".">
@@ -13741,11 +12940,6 @@
   <affected-histogram name="Net.QuicSession.ConnectRandomPort"/>
   <affected-histogram
       name="Net.QuicSession.ConnectRandomPortRequiringConfirmation"/>
-  <affected-histogram name="Net.QuicSession.ConnectSelectPort">
-    <obsolete>
-      Deprecated 04/2016.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Net.QuicSession.HandshakeRoundTrips"/>
 </histogram_suffixes>
 
@@ -14708,14 +13902,6 @@
   <suffix name="UrlUws" label=""/>
   <affected-histogram name="SafeBrowsing.V4Database.Size"/>
   <affected-histogram
-      name="SafeBrowsing.V4ProcessFullUpdate.AdditionsHashesCount">
-    <obsolete>
-      Removed in M92 in favor of
-      SafeBrowsing.V4ProcessFullUpdate.AdditionsHashesCount2, which has a larger
-      maximum count.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="SafeBrowsing.V4ProcessFullUpdate.AdditionsHashesCount2"/>
   <affected-histogram
       name="SafeBrowsing.V4ProcessFullUpdate.ApplyUpdate.Result"/>
@@ -16047,16 +15233,6 @@
   <suffix name="SMS_FETCH_REQUEST" label="SMS Fetch Request"/>
   <suffix name="UNKNOWN_MESSAGE" label="Unknown Message"/>
   <suffix name="WEB_RTC_SIGNALING_FRAME" label="Web RTC Signalling Message"/>
-  <affected-histogram name="Sharing.DeviceLastUpdatedAge">
-    <obsolete>
-      Removed in M89.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Sharing.MajorVersionComparison">
-    <obsolete>
-      Removed in M89.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Sharing.MessageAckTime"/>
   <affected-histogram name="Sharing.MessageAckTime.Android"/>
   <affected-histogram name="Sharing.MessageAckTime.ChromeOS"/>
@@ -16067,11 +15243,6 @@
   <affected-histogram name="Sharing.MessageAckTime.Unknown"/>
   <affected-histogram name="Sharing.MessageAckTime.Windows"/>
   <affected-histogram name="Sharing.MessageHandlerTime"/>
-  <affected-histogram name="Sharing.MessageReceivedType">
-    <obsolete>
-      Removed 2020-01.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Sharing.SendAckMessageResult"/>
   <affected-histogram name="Sharing.SendAckMessageResult.Android"/>
   <affected-histogram name="Sharing.SendAckMessageResult.ChromeOS"/>
@@ -16106,27 +15277,6 @@
   <affected-histogram name="Sharing.SendMessageResult.Windows"/>
 </histogram_suffixes>
 
-<histogram_suffixes name="SharingSendMessageResult" separator=".">
-  <suffix name="AckTimeout" label="Timeout waiting for ack"/>
-  <suffix name="CommitTimeout" label="Commit timeout"/>
-  <suffix name="DeviceNotFound" label="Device is not found"/>
-  <suffix name="EncryptionError" label="Encryption error"/>
-  <suffix name="InternalError" label="Other internal error"/>
-  <suffix name="NetworkError" label="Network error"/>
-  <suffix name="PayloadTooLarge" label="Payload is too large"/>
-  <suffix name="Successful" label="Successful"/>
-  <affected-histogram name="Sharing.DeviceLastUpdatedAgeWithResult">
-    <obsolete>
-      Removed in M89.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Sharing.SharedClipboardRetries">
-    <obsolete>
-      Removed in M89.
-    </obsolete>
-  </affected-histogram>
-</histogram_suffixes>
-
 <histogram_suffixes name="SharingWebRtcTimingEvent" separator=".">
   <obsolete>
     Removed 2020-09 as the WebRTC experiment is shut down.
@@ -16414,16 +15564,6 @@
   <affected-histogram name="CompositorLatency.CompositorOnlyFrame"/>
   <affected-histogram name="CompositorLatency.DroppedFrame"/>
   <affected-histogram name="CompositorLatency.MissedDeadlineFrame"/>
-  <affected-histogram name="CompositorLatency.MissedFrame">
-    <obsolete>
-      Removed on 01/2020. MissedFrame changed to DroppedFrame for more clarity.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="CompositorLatency.MissedFrameLatencyIncrease">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="CompositorLatency.Type"/>
   <affected-histogram name="Graphics.Smoothness.Checkerboarding"/>
   <affected-histogram name="Graphics.Smoothness.FrameSequenceLength"/>
@@ -16437,13 +15577,6 @@
   <affected-histogram
       name="Graphics.Smoothness.PercentDroppedFrames.ScrollingThread"/>
   <affected-histogram
-      name="Graphics.Smoothness.PercentDroppedFrames.SlowerThread">
-    <obsolete>
-      Removed on 9/2020. No longer needed after 'Universal' metric was
-      deprecated.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="Graphics.Smoothness.PercentMissedDeadlineFrames.CompositorThread"/>
   <affected-histogram
       name="Graphics.Smoothness.PercentMissedDeadlineFrames.MainThread"/>
@@ -16452,12 +15585,6 @@
   <affected-histogram name="Graphics.Smoothness.Stale"/>
   <affected-histogram name="SingleThreadedCompositorLatency"/>
   <affected-histogram name="SingleThreadedCompositorLatency.MissedFrame"/>
-  <affected-histogram
-      name="SingleThreadedCompositorLatency.MissedFrameLatencyIncrease">
-    <obsolete>
-      Removed on 9/2019. Did not provide enough information about latency.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="SmoothnessSequenceTypes_Universal" separator=".">
@@ -16474,13 +15601,6 @@
   <affected-histogram
       name="Graphics.Smoothness.PercentDroppedFrames.ScrollingThread"/>
   <affected-histogram
-      name="Graphics.Smoothness.PercentDroppedFrames.SlowerThread">
-    <obsolete>
-      Removed on 9/2020. No longer needed after 'Universal' metric was
-      deprecated.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="Graphics.Smoothness.PercentMissedDeadlineFrames.CompositorThread"/>
   <affected-histogram
       name="Graphics.Smoothness.PercentMissedDeadlineFrames.MainThread"/>
@@ -16781,28 +15901,11 @@
   <affected-histogram name="Startup.BrowserMessageLoopStartTimeFromMainEntry3"/>
   <affected-histogram name="Startup.BrowserOpenTabs"/>
   <affected-histogram name="Startup.BrowserWindow.FirstPaint"/>
-  <affected-histogram name="Startup.BrowserWindow.FirstPaint.CompositingEnded">
-    <obsolete>
-      Obsolete as of Feb 2021.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Startup.BrowserWindowDisplay"/>
   <affected-histogram name="Startup.FirstWebContents.MainFrameLoad"/>
   <affected-histogram name="Startup.FirstWebContents.MainFrameLoad2"/>
   <affected-histogram name="Startup.FirstWebContents.MainNavigationFinished"/>
   <affected-histogram name="Startup.FirstWebContents.MainNavigationStart"/>
-  <affected-histogram
-      name="Startup.FirstWebContents.MainNavigationStart.MultiTabs">
-    <obsolete>
-      Obsolete as of Jan 2020.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="Startup.FirstWebContents.MainNavigationStart.SingleTab">
-    <obsolete>
-      Obsolete as of Jan 2020.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Startup.FirstWebContents.NonEmptyPaint"/>
   <affected-histogram name="Startup.FirstWebContents.NonEmptyPaint2"/>
   <affected-histogram name="Startup.FirstWebContents.NonEmptyPaint3"/>
@@ -16957,12 +16060,6 @@
   <suffix name="SubresourceFilterOnly" label="subresource filter only pattern"/>
   <affected-histogram name="SubresourceFilter.PageLoad.FinalURLMatch"/>
   <affected-histogram name="SubresourceFilter.PageLoad.RedirectChainLength"/>
-  <affected-histogram
-      name="SubresourceFilter.PageLoad.RedirectChainMatchPattern">
-    <obsolete>
-      Obsolete as of April 2017
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="SweeperCompletionTypes" separator=".">
@@ -17147,92 +16244,6 @@
   <affected-histogram name="Apps.AppListSuggestedChipOpenType"/>
   <affected-histogram name="Apps.ContextMenuExecuteCommand.FromApp"/>
   <affected-histogram name="Apps.ContextMenuExecuteCommand.NotFromApp"/>
-  <affected-histogram name="Apps.ContextMenuShowSource.AppGrid">
-    <obsolete>
-      Obsoleted in October 2021. Replaced this suffix with variants in
-      histogram.xml
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Apps.ContextMenuShowSource.Desktop">
-    <obsolete>
-      Obsoleted in October 2021. Replaced this suffix with variants in
-      histogram.xml
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Apps.ContextMenuShowSource.SearchResult">
-    <obsolete>
-      Obsoleted in October 2021. Replaced this suffix with variants in
-      histogram.xml
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Apps.ContextMenuShowSource.Shelf">
-    <obsolete>
-      Obsoleted in October 2021. Replaced this suffix with variants in
-      histogram.xml
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Apps.ContextMenuShowSource.ShelfButton">
-    <obsolete>
-      Obsoleted in October 2021. Replaced this suffix with variants in
-      histogram.xml
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Apps.ContextMenuShowSource.SuggestedAppFullscreen">
-    <obsolete>
-      Obsoleted in October 2021. Replaced this suffix with variants in
-      histogram.xml
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Apps.ContextMenuShowSource.SuggestedAppPeeking">
-    <obsolete>
-      Obsoleted in October 2021. Replaced this suffix with variants in
-      histogram.xml
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Apps.ContextMenuUserJourneyTime.AppGrid">
-    <obsolete>
-      Obsoleted in October 2021. Replaced this suffix with variants in
-      histogram.xml
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Apps.ContextMenuUserJourneyTime.Desktop">
-    <obsolete>
-      Obsoleted in October 2021. Replaced this suffix with variants in
-      histogram.xml
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Apps.ContextMenuUserJourneyTime.SearchResult">
-    <obsolete>
-      Obsoleted in October 2021. Replaced this suffix with variants in
-      histogram.xml
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Apps.ContextMenuUserJourneyTime.Shelf">
-    <obsolete>
-      Obsoleted in October 2021. Replaced this suffix with variants in
-      histogram.xml
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Apps.ContextMenuUserJourneyTime.ShelfButton">
-    <obsolete>
-      Obsoleted in October 2021. Replaced this suffix with variants in
-      histogram.xml
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="Apps.ContextMenuUserJourneyTime.SuggestedAppFullscreen">
-    <obsolete>
-      Obsoleted in October 2021. Replaced this suffix with variants in
-      histogram.xml
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
-      name="Apps.ContextMenuUserJourneyTime.SuggestedAppPeeking">
-    <obsolete>
-      Obsoleted in October 2021. Replaced this suffix with variants in
-      histogram.xml
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Apps.PaginationTransition.AnimationSmoothness"/>
   <affected-histogram
       name="Apps.PaginationTransition.DragScroll.PresentationTime"/>
@@ -17274,21 +16285,6 @@
   <suffix name="MayBlock"
       label="Applies to tasks posted with MayBlock() or
              WithBaseSyncPrimitives()."/>
-  <affected-histogram name="TaskScheduler.TaskLatency.BackgroundTaskPriority">
-    <obsolete>
-      Deprecated 4/2017. Units changed from milliseconds to microseconds.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="TaskScheduler.TaskLatency.UserBlockingTaskPriority">
-    <obsolete>
-      Deprecated 4/2017. Units changed from milliseconds to microseconds.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="TaskScheduler.TaskLatency.UserVisibleTaskPriority">
-    <obsolete>
-      Deprecated 4/2017. Units changed from milliseconds to microseconds.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram
       name="TaskScheduler.TaskLatencyMicroseconds.BackgroundTaskPriority"/>
   <affected-histogram
@@ -17329,31 +16325,6 @@
       label="Applies to tasks posted with a USER_BLOCKING priority."/>
   <suffix name="UserVisibleTaskPriority"
       label="Applies to tasks posted with a USER_VISIBLE priority."/>
-  <affected-histogram name="TaskScheduler.TaskLatency">
-    <obsolete>
-      Deprecated 4/2017. Units changed from milliseconds to microseconds.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="TaskScheduler.TaskLatency.BackgroundFileIOPool">
-    <obsolete>
-      Deprecated 12/2016. Pool name removed from task latency histogram name.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="TaskScheduler.TaskLatency.BackgroundPool">
-    <obsolete>
-      Deprecated 12/2016. Pool name removed from task latency histogram name.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="TaskScheduler.TaskLatency.ForegroundFileIOPool">
-    <obsolete>
-      Deprecated 12/2016. Pool name removed from task latency histogram name.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="TaskScheduler.TaskLatency.ForegroundPool">
-    <obsolete>
-      Deprecated 12/2016. Pool name removed from task latency histogram name.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="TaskScheduler.TaskLatencyMicroseconds"/>
 </histogram_suffixes>
 
@@ -17449,41 +16420,21 @@
       Deprecated 01/2018. In favor of TaskSchedulerName suffix.
     </obsolete>
   </suffix>
-  <affected-histogram name="TaskScheduler.DetachDuration">
-    <obsolete>
-      Deprecated 01/2018. In favor of explicit .Browser suffix.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="TaskScheduler.DetachDuration.Browser"/>
   <affected-histogram name="TaskScheduler.DetachDuration.ContentChild"/>
   <affected-histogram name="TaskScheduler.DetachDuration.Renderer"/>
   <affected-histogram name="TaskScheduler.NumActiveWorkers.Browser"/>
   <affected-histogram name="TaskScheduler.NumActiveWorkers.ContentChild"/>
   <affected-histogram name="TaskScheduler.NumActiveWorkers.Renderer"/>
-  <affected-histogram name="TaskScheduler.NumTasksBeforeDetach">
-    <obsolete>
-      Deprecated 01/2018. In favor of explicit .Browser suffix.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="TaskScheduler.NumTasksBeforeDetach.Browser"/>
   <affected-histogram name="TaskScheduler.NumTasksBeforeDetach.ContentChild"/>
   <affected-histogram name="TaskScheduler.NumTasksBeforeDetach.Renderer"/>
-  <affected-histogram name="TaskScheduler.NumTasksBetweenWaits">
-    <obsolete>
-      Deprecated 01/2018. In favor of explicit .Browser suffix.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="TaskScheduler.NumTasksBetweenWaits.Browser"/>
   <affected-histogram name="TaskScheduler.NumTasksBetweenWaits.ContentChild"/>
   <affected-histogram name="TaskScheduler.NumTasksBetweenWaits.Renderer"/>
   <affected-histogram name="TaskScheduler.NumWorkers.Browser"/>
   <affected-histogram name="TaskScheduler.NumWorkers.ContentChild"/>
   <affected-histogram name="TaskScheduler.NumWorkers.Renderer"/>
-  <affected-histogram name="TaskScheduler.TaskLatency">
-    <obsolete>
-      Deprecated 12/2016. Pool name removed from task latency histogram name.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="TeamDrivesSupport" separator=".">
@@ -17647,11 +16598,6 @@
   <suffix name="Secure.Quic"
       label="Recorded for Google servers (https) only when QUIC is used."/>
   <affected-histogram name="Net.HttpJob.TotalTime"/>
-  <affected-histogram name="Net.HttpJob.TotalTimeCached">
-    <obsolete>
-      Deprecated 10/2016, no longer used.
-    </obsolete>
-  </affected-histogram>
   <affected-histogram name="Net.HttpJob.TotalTimeNotCached"/>
 </histogram_suffixes>
 
@@ -17771,14 +16717,6 @@
   <affected-histogram
       name="NewTabPage.ContentSuggestions.TimeUntilFirstShownTrigger"/>
   <affected-histogram
-      name="NewTabPage.ContentSuggestions.TimeUntilFirstSoftTrigger">
-    <obsolete>
-      Deprecated as of July 2017, in favor of
-      NewTabPage.ContentSuggestions.TimeUntilFirstShownTrigger and
-      NewTabPage.ContentSuggestions.TimeUntilFirstStartupTrigger.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram
       name="NewTabPage.ContentSuggestions.TimeUntilFirstStartupTrigger"/>
   <affected-histogram
       name="NewTabPage.ContentSuggestions.TimeUntilPersistentFetch"/>
@@ -18035,17 +16973,6 @@
   <suffix name="Primary" label="Regular user"/>
   <suffix name="SigninOrLockScreen" label="Sign in or lockscreen"/>
   <affected-histogram name="Webapp.InstallResult.System.Profiles"/>
-  <affected-histogram
-      name="Webapp.InstallResultExtensionDisabledReason.System.Profiles">
-    <obsolete>
-      Deprecated 11/2020. The affected histogram is deprecated.
-    </obsolete>
-  </affected-histogram>
-  <affected-histogram name="Webapp.InstallResultExtensionError.System.Profiles">
-    <obsolete>
-      Deprecated 11/2020. The affected histogram is deprecated.
-    </obsolete>
-  </affected-histogram>
 </histogram_suffixes>
 
 <histogram_suffixes name="WebappType" separator=".">
diff --git a/tools/metrics/histograms/metadata/history/histograms.xml b/tools/metrics/histograms/metadata/history/histograms.xml
index e1e92f5..eb4fbc7 100644
--- a/tools/metrics/histograms/metadata/history/histograms.xml
+++ b/tools/metrics/histograms/metadata/history/histograms.xml
@@ -384,7 +384,7 @@
   </summary>
 </histogram>
 
-<histogram name="History.Clusters.Actions.DidMakeQuery" units="BooleanQueried"
+<histogram name="History.Clusters.Actions.DidMakeQuery" enum="BooleanQueried"
     expires_after="2022-06-12">
   <owner>mcrouse@chromium.org</owner>
   <owner>chrome-memories@google.com</owner>
@@ -396,7 +396,7 @@
 </histogram>
 
 <histogram name="History.Clusters.Actions.FinalState"
-    units="HistoryClustersFinalState" expires_after="2022-05-15">
+    enum="HistoryClustersFinalState" expires_after="2022-05-15">
   <owner>mcrouse@chromium.org</owner>
   <owner>chrome-memories@google.com</owner>
   <summary>
@@ -408,7 +408,7 @@
 </histogram>
 
 <histogram name="History.Clusters.Actions.InitialState"
-    units="HistoryClustersInitialState" expires_after="2022-05-15">
+    enum="HistoryClustersInitialState" expires_after="2022-05-15">
   <owner>mcrouse@chromium.org</owner>
   <owner>chrome-memories@google.com</owner>
   <summary>
@@ -600,15 +600,21 @@
   </summary>
 </histogram>
 
-<histogram name="History.Clusters.PercentClustersFilteredByQuery"
-    units="percent clusters filtered" expires_after="2022-05-15">
+<histogram
+    name="History.Clusters.Backend.WasClusterFiltered.{ClusterFilterReason}"
+    enum="BooleanFiltered" expires_after="2022-05-15">
   <owner>mcrouse@chromium.org</owner>
   <owner>chrome-memories@google.com</owner>
   <summary>
-    Logs the percentage of clusters that were filtered by the specified queried.
-    Logged when any request to cluster visits is made and a query has been
-    specified in the UI.
+    Logs whether a cluster was filtered for having {ClusterFilterReason}. Logged
+    when finalizing each individual cluster which occurs at the end of every
+    clustering backend request.
   </summary>
+  <token key="ClusterFilterReason">
+    <variant name="NoisyCluster" summary="all noisy visits"/>
+    <variant name="SingleVisit" summary="only a single visit"/>
+    <variant name="VisibilityScore" summary="low visibility score"/>
+  </token>
 </histogram>
 
 <histogram name="History.Clusters.ProcessClustersDuration" units="ms"
@@ -690,7 +696,7 @@
 </histogram>
 
 <histogram name="History.Clusters.UIActions.ToggledVisibility"
-    units="BooleanVisible" expires_after="2022-02-25">
+    enum="BooleanVisible" expires_after="2022-02-25">
   <owner>mahmadi@chromium.org</owner>
   <owner>chrome-memories@google.com</owner>
   <summary>
@@ -771,6 +777,10 @@
 
 <histogram name="History.DomainCount1Day" units="domains"
     expires_after="2022-06-19">
+  <obsolete>
+    Replaced in M99 by History.DomainCount1Day_V2, which limits the metric
+    recording to regular profiles.
+  </obsolete>
   <owner>mpearson@chromium.org</owner>
   <owner>mjzhang@chromium.org</owner>
   <owner>chrome-analysis-team@google.com</owner>
@@ -807,8 +817,48 @@
   </summary>
 </histogram>
 
+<histogram name="History.DomainCount1Day_V2" units="domains"
+    expires_after="2022-06-19">
+  <owner>mpearson@chromium.org</owner>
+  <owner>mjzhang@chromium.org</owner>
+  <owner>chrome-analysis-team@google.com</owner>
+  <summary>
+    Number of unique domains visited by the user within a calendar day in user's
+    local timezone. For each regular profile (not incognito, guest, system,
+    etc.), reported at profile open (which usually happens at startup) for each
+    unreported day (up to 7 days) prior to the current date, and also reported
+    once every 24 hours thereafter for the latest unreported day, while the
+    profile remains open. If no domains are visited in a given day, a count of 0
+    will be reported for that day.
+
+    Warning about delayed data: Chrome may upload logs on a given day without
+    uploading this histogram. This can happen because Chrome uploads logs
+    initially upon startup. This histogram is emitted shortly _after_ startup.
+    In the case of short sessions, it's possible the log with this histogram did
+    not have time to be uploaded. Generally the log will be cached and uploaded
+    the next time the user starts Chrome. We should still get one count per
+    calendar day; it simply may not be on the day the metric was computed.
+    (Exception: on Android before M-91, sometimes these emitted histograms were
+    lost due to lack of robust &quot;background logging&quot;.)
+
+    Note: for users syncing between multiple devices, this count may include
+    some URLs/domains that weren't visited on this device. In other words, some
+    domains may be counted for multiple client_ids even though they were only
+    visited once, on one device. However, at this time (Feb 2020), only URLs
+    navigated to via the omnibox (&quot;typed URLs&quot;) are synced, not all
+    visited URLs. There are no plans to change this behavior. As such, because
+    the mis-counting only occurs for users who are syncing across multiple
+    devices and only for a subset of visited URLs, the effect of double-counting
+    is likely to be small.
+  </summary>
+</histogram>
+
 <histogram name="History.DomainCount28Day" units="domains"
     expires_after="2022-06-19">
+  <obsolete>
+    Replaced in M99 by History.DomainCount28Day_V2, which limits the metric
+    recording to regular profiles.
+  </obsolete>
   <owner>mpearson@chromium.org</owner>
   <owner>mjzhang@chromium.org</owner>
   <owner>chrome-analysis-team@google.com</owner>
@@ -846,8 +896,49 @@
   </summary>
 </histogram>
 
+<histogram name="History.DomainCount28Day_V2" units="domains"
+    expires_after="2022-06-19">
+  <owner>mpearson@chromium.org</owner>
+  <owner>mjzhang@chromium.org</owner>
+  <owner>chrome-analysis-team@google.com</owner>
+  <summary>
+    Number of unique domains visited by the user within a 28 calendar day period
+    in user's local timezone. For each regular profile (not incognito, guest,
+    system, etc.), reported at profile open (which usually happens at startup)
+    for each unreported 28-day period (up to 7 periods) ending on the current
+    day, current day - 1, current day - 2, etc. Also reported once every 24
+    hours thereafter for the latest unreported period, while the profile remains
+    open. If no domains are visited during a 28-day period, a count of 0 will be
+    reported for that period.
+
+    Warning about delayed data: Chrome may upload logs on a given day without
+    uploading this histogram. This can happen because Chrome uploads logs
+    initially upon startup. This histogram is emitted shortly _after_ startup.
+    In the case of short sessions, it's possible the log with this histogram did
+    not have time to be uploaded. Generally the log will be cached and uploaded
+    the next time the user starts Chrome. We should still get one count per
+    calendar day; it simply may not be on the day the metric was computed.
+    (Exception: on Android before M-91, sometimes these emitted histograms were
+    lost due to lack of robust &quot;background logging&quot;.)
+
+    Note: for users syncing between multiple devices, this count may include
+    some URLs/domains that weren't visited on this device. In other words, some
+    domains may be counted for multiple client_ids even though they were only
+    visited once, on one device. However, at this time (Feb 2020), only URLs
+    navigated to via the omnibox (&quot;typed URLs&quot;) are synced, not all
+    visited URLs. There are no plans to change this behavior. As such, because
+    the mis-counting only occurs for users who are syncing across multiple
+    devices and only for a subset of visited URLs, the effect of double-counting
+    is likely to be small.
+  </summary>
+</histogram>
+
 <histogram name="History.DomainCount7Day" units="domains"
     expires_after="2022-06-19">
+  <obsolete>
+    Replaced in M99 by History.DomainCount7Day_V2, which limits the metric
+    recording to regular profiles.
+  </obsolete>
   <owner>mpearson@chromium.org</owner>
   <owner>mjzhang@chromium.org</owner>
   <owner>chrome-analysis-team@google.com</owner>
@@ -885,8 +976,49 @@
   </summary>
 </histogram>
 
+<histogram name="History.DomainCount7Day_V2" units="domains"
+    expires_after="2022-06-19">
+  <owner>mpearson@chromium.org</owner>
+  <owner>mjzhang@chromium.org</owner>
+  <owner>chrome-analysis-team@google.com</owner>
+  <summary>
+    Number of unique domains visited by the user within a 7 calendar day period
+    in user's local timezone. For each regular profile (not incognito, guest,
+    system, etc.), reported at profile open (which usually happens at startup)
+    for each unreported 7-day period (up to 7 periods) ending on the current
+    day, current day - 1, current day - 2, etc. Also reported once every 24
+    hours thereafter for the latest unreported period, while the profile remains
+    open. If no domains are visited during a 7-day period, a count 0 will be
+    reported for that period.
+
+    Warning about delayed data: Chrome may upload logs on a given day without
+    uploading this histogram. This can happen because Chrome uploads logs
+    initially upon startup. This histogram is emitted shortly _after_ startup.
+    In the case of short sessions, it's possible the log with this histogram did
+    not have time to be uploaded. Generally the log will be cached and uploaded
+    the next time the user starts Chrome. We should still get one count per
+    calendar day; it simply may not be on the day the metric was computed.
+    (Exception: on Android before M-91, sometimes these emitted histograms were
+    lost due to lack of robust &quot;background logging&quot;.)
+
+    Note: for users syncing between multiple devices, this count may include
+    some URLs/domains that weren't visited on this device. In other words, some
+    domains may be counted for multiple client_ids even though they were only
+    visited once, on one device. However, at this time (Feb 2020), only URLs
+    navigated to via the omnibox (&quot;typed URLs&quot;) are synced, not all
+    visited URLs. There are no plans to change this behavior. As such, because
+    the mis-counting only occurs for users who are syncing across multiple
+    devices and only for a subset of visited URLs, the effect of double-counting
+    is likely to be small.
+  </summary>
+</histogram>
+
 <histogram name="History.DomainCountQueryTime" units="ms"
     expires_after="2022-02-20">
+  <obsolete>
+    Replaced in M99 by History.DomainCountQueryTime_V2, which limits the metric
+    recording to regular profiles.
+  </obsolete>
   <owner>mpearson@chromium.org</owner>
   <owner>mjzhang@chromium.org</owner>
   <owner>chrome-analysis-team@google.com</owner>
@@ -896,7 +1028,24 @@
     where each set includes 3 results, for a 1-day, 7-day and 28-day period
     respectively. Note that for each computation task, only one query time is
     reported which covers the entire duration of the task. A task is scheduled
-    once at startup and every 24 hours thereafter while the browser remains
+    once per profile at startup and every 24 hours per profile thereafter while
+    the profile remains open.
+  </summary>
+</histogram>
+
+<histogram name="History.DomainCountQueryTime_V2" units="ms"
+    expires_after="2022-06-19">
+  <owner>mpearson@chromium.org</owner>
+  <owner>mjzhang@chromium.org</owner>
+  <owner>chrome-analysis-team@google.com</owner>
+  <summary>
+    Time spent on a scheduled computation task of domain visit counts. Each such
+    task computes a number of (at least 1 and up to 7) sets of domain counts,
+    where each set includes 3 results, for a 1-day, 7-day and 28-day period
+    respectively. Note that for each computation task, only one query time is
+    reported which covers the entire duration of the task. A task is scheduled
+    once per regular profile (not Incognito, Guest, System, etc. profiles) at
+    startup and every 24 hours per profile thereafter while the profile remains
     open.
   </summary>
 </histogram>
diff --git a/tools/metrics/histograms/metadata/input/histograms.xml b/tools/metrics/histograms/metadata/input/histograms.xml
index d71e3542..87bedc4d 100644
--- a/tools/metrics/histograms/metadata/input/histograms.xml
+++ b/tools/metrics/histograms/metadata/input/histograms.xml
@@ -1190,6 +1190,47 @@
   </summary>
 </histogram>
 
+<!--
+These viewport metrics are in input/ since they're related to pinch-zoom, which
+is owned by the Blink input team.
+-->
+
+<histogram name="Viewport.DidScalePage" enum="BooleanDidScalePage"
+    expires_after="M108">
+  <owner>bokan@chromium.org</owner>
+  <owner>input-dev@chromium.org</owner>
+  <summary>
+    Tracks the proportion of non-mobile optimized (i.e. zoom disabled or layout
+    width matches viewport) page views that had a user-initiated page scale
+    (e.g. pinch-zoom, double-tap). Recorded on navigation to a new page - on
+    Android only.
+  </summary>
+</histogram>
+
+<histogram name="Viewport.MaxPageScale" enum="PageScaleFactorRange"
+    expires_after="M108">
+  <owner>bokan@chromium.org</owner>
+  <owner>input-dev@chromium.org</owner>
+  <summary>
+    Tracks the maximum scale factor that a user has scaled to over the lifetime
+    of the page. The scale is counted at pinch end (e.g. zooming to 300% and
+    back out to 150% in one gesture would count as 150%). Reported only on
+    non-mobile optimized pages (i.e. zoom disabled or layout width matches
+    viewport) which have had a page scale changing gesture. Recorded on
+    navigation to a new page - on Android only.
+  </summary>
+</histogram>
+
+<histogram name="Viewport.MetaTagType" enum="MetaTagTypeEnum"
+    expires_after="M108">
+  <owner>bokan@chromium.org</owner>
+  <owner>input-dev@chromium.org</owner>
+  <summary>
+    The viewport meta tag type seen on each page load. Only recorded on Android.
+    Recorded when the page finishes loading.
+  </summary>
+</histogram>
+
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index 89397d73..c2eb330 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -1311,7 +1311,7 @@
 <histogram name="IOS.SiriShortcuts.Count" units="shortcuts"
     expires_after="2023-02-06">
   <owner>gujen@google.com</owner>
-  <owner>sebsg@chromium.org</owner>
+  <owner>ios-google-eng-ios@google.com</owner>
   <summary>
     Counts the number of Chrome Siri Shortcuts that the user has created in the
     Siri Shortcuts app. This is recorded once during startup. The histogram caps
@@ -1320,7 +1320,8 @@
     isn't a Chrome-provided one. For example, a shortcut that opens URLs in
     Chrome and then opens URLs in another app won't be counted. As such, this
     metric undercounts the true number of Chrome shortcuts. This is a
-    restriction of the native Shortcuts API.
+    restriction of the native Shortcuts API. This histogram is actively being
+    monitored by the iOS at Google team.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/navigation/histograms.xml b/tools/metrics/histograms/metadata/navigation/histograms.xml
index 59bfbbb..531e8d86 100644
--- a/tools/metrics/histograms/metadata/navigation/histograms.xml
+++ b/tools/metrics/histograms/metadata/navigation/histograms.xml
@@ -478,7 +478,7 @@
 
 <histogram name="BackForwardCache.ReloadsAfterHistoryNavigation"
     enum="BackForwardCacheReloadsAfterHistoryNavigation"
-    expires_after="2022-02-06">
+    expires_after="2022-06-26">
   <owner>sreejakshetty@chromium.org</owner>
   <owner>altimin@chromium.org</owner>
   <owner>bfcache-dev@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
index f455d43..c5be4bdb 100644
--- a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
+++ b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
@@ -1253,6 +1253,9 @@
 
 <histogram name="NewTabPage.NumberOfPreinstalledApps" units="count"
     expires_after="M97">
+  <obsolete>
+    Removed from code in 2022-01.
+  </obsolete>
   <owner>phillis@chromium.org</owner>
   <owner>desktop-pwas-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/obsolete_histograms.xml b/tools/metrics/histograms/metadata/obsolete_histograms.xml
index cb8ca48..2ece08d8 100644
--- a/tools/metrics/histograms/metadata/obsolete_histograms.xml
+++ b/tools/metrics/histograms/metadata/obsolete_histograms.xml
@@ -56154,6 +56154,53 @@
   </summary>
 </histogram>
 
+<histogram name="PerformanceManager.AgentsByTime" units="units"
+    expires_after="2020-12-04">
+  <obsolete>
+    Removed 2020-11.
+  </obsolete>
+  <owner>bokan@chromium.org</owner>
+  <owner>kouhei@chromium.org</owner>
+  <owner>platform-architecture-dev@chromium.org</owner>
+  <summary>
+    Tracks the total number of agents hosted by the browser by time. An entry in
+    bucket N corresponds to N agents being hosted across all live renderer
+    process for 1 second. Recorded on state changes and every 5 minutes.
+  </summary>
+</histogram>
+
+<histogram name="PerformanceManager.AgentsPerRendererByTime" units="units"
+    expires_after="2020-12-04">
+  <obsolete>
+    Removed 2020-11.
+  </obsolete>
+  <owner>bokan@chromium.org</owner>
+  <owner>kouhei@chromium.org</owner>
+  <owner>platform-architecture-dev@chromium.org</owner>
+  <summary>
+    Tracks the number of agents hosted per renderer by time. An entry in bucket
+    N corresponds to N agents being hosted by a renderer process for 1 second.
+    Recorded on state changes and every 5 minutes.
+  </summary>
+</histogram>
+
+<histogram name="PerformanceManager.AgentsUniqueByTime" units="units"
+    expires_after="2020-12-04">
+  <obsolete>
+    Removed 2020-11.
+  </obsolete>
+  <owner>bokan@chromium.org</owner>
+  <owner>kouhei@chromium.org</owner>
+  <owner>platform-architecture-dev@chromium.org</owner>
+  <summary>
+    Tracks the number of unique agents hosted by the browser by time. An entry
+    in bucket N corresponds to N unique agents being hosted across all live
+    renderer process for 1 second. Unique means that the agent count would
+    remain the same if they were in one process (i.e. they have different
+    protocol+site). Recorded on state changes and every 5 minutes.
+  </summary>
+</histogram>
+
 <histogram
     name="PerformanceManager.BrowsingInstancePluralityVisibilityState.ByPageTime"
     enum="BrowsingInstancePluralityVisibilityState" expires_after="2019-09-30">
@@ -86896,6 +86943,19 @@
   </summary>
 </histogram>
 
+<histogram name="Viewport.OverviewZoom" units="%" expires_after="M85">
+  <obsolete>
+    Expired in M85.
+  </obsolete>
+  <owner>bokan@chromium.org</owner>
+  <owner>input-dev@chromium.org</owner>
+  <summary>
+    The screen width as a percentage of viewport width (i.e. zoom at which we
+    can see the whole page). Only recorded on Android and for viewport meta tags
+    with constant width.
+  </summary>
+</histogram>
+
 <histogram name="VirtualKeyboard.ControllerStateTransitionIsValid"
     enum="BooleanValid" expires_after="2019-06-12">
   <obsolete>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 4ccba11..58733b41 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -3937,6 +3937,21 @@
   </summary>
 </histogram>
 
+<histogram name="Conversion.ReportSendOutcome"
+    enum="ConversionReportSendOutcome" expires_after="M105">
+  <obsolete>
+    Replaced with Conversions.ReportSendOutcome, 01/2022.
+  </obsolete>
+  <owner>linnan@chromium.org</owner>
+  <owner>johnidel@chromium.org</owner>
+  <owner>measurement-api-dev+metrics@google.com</owner>
+  <summary>
+    Records the high level request status of a conversion report. Recorded once
+    per conversion report. For conversion report that has been retried, only the
+    last retry will be counted.
+  </summary>
+</histogram>
+
 <histogram name="Conversions.AppToWeb.AttributionEvents"
     enum="AttributionEvent" expires_after="2022-12-08">
   <owner>johnidel@chromium.org</owner>
@@ -10550,53 +10565,6 @@
   </summary>
 </histogram>
 
-<histogram name="PerformanceManager.AgentsByTime" units="units"
-    expires_after="2020-12-04">
-  <obsolete>
-    Removed 2020-11.
-  </obsolete>
-  <owner>bokan@chromium.org</owner>
-  <owner>kouhei@chromium.org</owner>
-  <owner>platform-architecture-dev@chromium.org</owner>
-  <summary>
-    Tracks the total number of agents hosted by the browser by time. An entry in
-    bucket N corresponds to N agents being hosted across all live renderer
-    process for 1 second. Recorded on state changes and every 5 minutes.
-  </summary>
-</histogram>
-
-<histogram name="PerformanceManager.AgentsPerRendererByTime" units="units"
-    expires_after="2020-12-04">
-  <obsolete>
-    Removed 2020-11.
-  </obsolete>
-  <owner>bokan@chromium.org</owner>
-  <owner>kouhei@chromium.org</owner>
-  <owner>platform-architecture-dev@chromium.org</owner>
-  <summary>
-    Tracks the number of agents hosted per renderer by time. An entry in bucket
-    N corresponds to N agents being hosted by a renderer process for 1 second.
-    Recorded on state changes and every 5 minutes.
-  </summary>
-</histogram>
-
-<histogram name="PerformanceManager.AgentsUniqueByTime" units="units"
-    expires_after="2020-12-04">
-  <obsolete>
-    Removed 2020-11.
-  </obsolete>
-  <owner>bokan@chromium.org</owner>
-  <owner>kouhei@chromium.org</owner>
-  <owner>platform-architecture-dev@chromium.org</owner>
-  <summary>
-    Tracks the number of unique agents hosted by the browser by time. An entry
-    in bucket N corresponds to N unique agents being hosted across all live
-    renderer process for 1 second. Unique means that the agent count would
-    remain the same if they were in one process (i.e. they have different
-    protocol+site). Recorded on state changes and every 5 minutes.
-  </summary>
-</histogram>
-
 <histogram
     name="PerformanceManager.FrameSiteInstanceProcessRelationship.ByProcess2"
     enum="FrameSiteInstanceProcessRelationship" expires_after="2020-12-31">
@@ -11403,6 +11371,26 @@
   </summary>
 </histogram>
 
+<histogram name="PowerBookmarks.BookmarkManager.PriceTrackingEnabled"
+    enum="PriceTrackingState" expires_after="2023-01-24">
+  <owner>wylieb@chromium.org</owner>
+  <owner>chrome-collections@google.com</owner>
+  <summary>
+    Tracks the interactions with the price-tracking toggle present when viewing
+    previously saved products.
+  </summary>
+</histogram>
+
+<histogram name="PowerBookmarks.BookmarkSaveFlow.PriceTrackingEnabled"
+    enum="PriceTrackingState" expires_after="2023-01-24">
+  <owner>wylieb@chromium.org</owner>
+  <owner>chrome-collections@google.com</owner>
+  <summary>
+    Tracks the interactions with the price-tracking toggle present when
+    initially saving products.
+  </summary>
+</histogram>
+
 <histogram name="PrefetchedSignedExchangeCache.BodySize" units="bytes"
     expires_after="2022-04-24">
   <owner>horo@chromium.org</owner>
@@ -12494,6 +12482,9 @@
 
 <histogram name="RenderFrameHostImpl.DroppedInterfaceRequestName"
     enum="RenderFrameHostImpl.InterfaceNames" expires_after="M85">
+  <obsolete>
+    Removed in January 2022.
+  </obsolete>
   <owner>engedy@chromium.org</owner>
   <summary>
     For each load and dropped Mojo interface request in a frame, records a
@@ -12513,6 +12504,9 @@
 
 <histogram name="RenderFrameHostImpl.DroppedInterfaceRequests" units="count"
     expires_after="M85">
+  <obsolete>
+    Removed in January 2022.
+  </obsolete>
   <owner>engedy@chromium.org</owner>
   <summary>
     For each load in a frame, records the number of interface requests to
@@ -14283,7 +14277,7 @@
 </histogram>
 
 <histogram base="true" name="Spellcheck.Windows.SuggestionGatheringDuration"
-    units="ms" expires_after="2022-02-20">
+    units="ms" expires_after="2022-08-20">
   <owner>gujen@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
@@ -16381,55 +16375,6 @@
   </summary>
 </histogram>
 
-<histogram name="Viewport.DidScalePage" enum="BooleanDidScalePage"
-    expires_after="M108">
-  <owner>bokan@chromium.org</owner>
-  <owner>input-dev@chromium.org</owner>
-  <summary>
-    Tracks the proportion of non-mobile optimized (i.e. zoom disabled or layout
-    width matches viewport) page views that had a user-initiated page scale
-    (e.g. pinch-zoom, double-tap). Recorded on navigation to a new page - on
-    Android only.
-  </summary>
-</histogram>
-
-<histogram name="Viewport.MaxPageScale" enum="PageScaleFactorRange"
-    expires_after="M108">
-  <owner>bokan@chromium.org</owner>
-  <owner>input-dev@chromium.org</owner>
-  <summary>
-    Tracks the maximum scale factor that a user has scaled to over the lifetime
-    of the page. The scale is counted at pinch end (e.g. zooming to 300% and
-    back out to 150% in one gesture would count as 150%). Reported only on
-    non-mobile optimized pages (i.e. zoom disabled or layout width matches
-    viewport) which have had a page scale changing gesture. Recorded on
-    navigation to a new page - on Android only.
-  </summary>
-</histogram>
-
-<histogram name="Viewport.MetaTagType" enum="MetaTagTypeEnum"
-    expires_after="M108">
-  <owner>bokan@chromium.org</owner>
-  <owner>input-dev@chromium.org</owner>
-  <summary>
-    The viewport meta tag type seen on each page load. Only recorded on Android.
-    Recorded when the page finishes loading.
-  </summary>
-</histogram>
-
-<histogram name="Viewport.OverviewZoom" units="%" expires_after="M85">
-  <obsolete>
-    Expired in M85.
-  </obsolete>
-  <owner>bokan@chromium.org</owner>
-  <owner>input-dev@chromium.org</owner>
-  <summary>
-    The screen width as a percentage of viewport width (i.e. zoom at which we
-    can see the whole page). Only recorded on Android and for viewport meta tags
-    with constant width.
-  </summary>
-</histogram>
-
 <histogram name="ViewResourceAdapter.GetBitmapInterval" units="ms"
     expires_after="M82">
   <owner>wychen@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/page/histograms.xml b/tools/metrics/histograms/metadata/page/histograms.xml
index cd1deb71..6a20022 100644
--- a/tools/metrics/histograms/metadata/page/histograms.xml
+++ b/tools/metrics/histograms/metadata/page/histograms.xml
@@ -2364,6 +2364,9 @@
 
 <histogram name="PageLoad.Internal.ProvisionalAbortChainSize.ForwardBack"
     units="length" expires_after="2018-08-30">
+  <obsolete>
+    Removed 01/2022.
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     This histogram counts the number of provisional loads aborted by other
@@ -2373,6 +2376,9 @@
 
 <histogram name="PageLoad.Internal.ProvisionalAbortChainSize.NewNavigation"
     units="length" expires_after="M77">
+  <obsolete>
+    Removed 01/2022.
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     This histogram counts the number of provisional loads aborted by new
@@ -2382,6 +2388,9 @@
 
 <histogram name="PageLoad.Internal.ProvisionalAbortChainSize.NoCommit"
     units="length" expires_after="2018-08-30">
+  <obsolete>
+    Removed 01/2022.
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     This histogram counts the number of provisional loads aborted by new
@@ -2392,6 +2401,9 @@
 
 <histogram name="PageLoad.Internal.ProvisionalAbortChainSize.Reload"
     units="length" expires_after="2018-08-30">
+  <obsolete>
+    Removed 01/2022.
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     This histogram counts the number of provisional loads aborted by other
@@ -2401,6 +2413,9 @@
 
 <histogram name="PageLoad.Internal.ProvisionalAbortChainSize.SameURL"
     units="length" expires_after="2018-08-30">
+  <obsolete>
+    Removed 01/2022.
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     This histogram counts the number of consecutive provisional loads aborted by
diff --git a/tools/metrics/histograms/metadata/platform/histograms.xml b/tools/metrics/histograms/metadata/platform/histograms.xml
index d262006..b5263e1 100644
--- a/tools/metrics/histograms/metadata/platform/histograms.xml
+++ b/tools/metrics/histograms/metadata/platform/histograms.xml
@@ -600,14 +600,15 @@
   </summary>
 </histogram>
 
-<histogram name="Platform.Mem" units="%" expires_after="2021-07-02">
+<histogram name="Platform.Mem" units="%" expires_after="2022-07-02">
   <owner>hajimehoshi@chromium.org</owner>
   <owner>kouhei@chromium.org</owner>
   <owner>sonnyrao@chromium.org</owner>
   <owner>chromeos-memory@google.com</owner>
   <summary>
     Various memory usage % of total memory on Chrome OS devices (snapshotted
-    every 30s).
+    every 30s). Warning: this histogram was expired from 2021-07-02 to 2022-01;
+    data may be missing.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index c20814ae..05ea546 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -223,7 +223,8 @@
   </summary>
 </histogram>
 
-<histogram name="SafeBrowsing.CookieAgeHours" units="hours" expires_after="M98">
+<histogram name="SafeBrowsing.CookieAgeHours" units="hours"
+    expires_after="2022-07-07">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1122,7 +1123,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.HasCookieAtStartup" enum="Boolean"
-    expires_after="M98">
+    expires_after="2022-07-07">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/subresource/histograms.xml b/tools/metrics/histograms/metadata/subresource/histograms.xml
index fa0a0ca..fc04b6a9 100644
--- a/tools/metrics/histograms/metadata/subresource/histograms.xml
+++ b/tools/metrics/histograms/metadata/subresource/histograms.xml
@@ -752,6 +752,9 @@
 
 <histogram name="SubresourceRedirect.Blink.Ineligibility"
     enum="BlinkSubresourceRedirectIneligibility" expires_after="2022-05-01">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -762,6 +765,9 @@
 
 <histogram name="SubresourceRedirect.BypassDuration" units="ms"
     expires_after="M96">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -775,6 +781,9 @@
 
 <histogram name="SubresourceRedirect.CompressionAttempt.ResponseCode"
     enum="HttpResponseCode" expires_after="2022-06-19">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>robertogden@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -787,6 +796,9 @@
 
 <histogram name="SubresourceRedirect.CompressionAttempt.ServerResponded"
     enum="Boolean" expires_after="M96">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>robertogden@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -798,6 +810,9 @@
 
 <histogram name="SubresourceRedirect.CompressionFetchTimeout" enum="Boolean"
     expires_after="M96">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -808,6 +823,9 @@
 
 <histogram name="SubresourceRedirect.DidCompress.BytesSaved" units="bytes"
     expires_after="2022-04-17">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>robertogden@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -818,6 +836,9 @@
 
 <histogram name="SubresourceRedirect.DidCompress.CompressionPercent" units="%"
     expires_after="2022-04-17">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>robertogden@chromium.org</owner>
   <owner>tbansal@chromium.org</owner>
   <summary>
@@ -828,6 +849,9 @@
 
 <histogram name="SubresourceRedirect.ImageCompressionNotificationInfoBar"
     enum="HttpsImageCompressionInfoBarAction" expires_after="M96">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>rajendrant@chromium.org</owner>
   <owner>src/components/data_reduction_proxy/OWNERS</owner>
   <summary>
@@ -838,6 +862,9 @@
 
 <histogram name="SubresourceRedirect.LitePagesService.BypassResult"
     enum="Boolean" expires_after="M96">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -849,6 +876,9 @@
 
 <histogram name="SubresourceRedirect.LoginRobotsDeciderAgent.RedirectResult"
     enum="SubresourceRedirectRedirectResult" expires_after="M96">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -873,6 +903,9 @@
 
 <histogram name="SubresourceRedirect.RobotRulesDecider.ApplyDuration"
     units="ms" expires_after="M96">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -885,6 +918,9 @@
 
 <histogram name="SubresourceRedirect.RobotRulesDecider.Count" units="count"
     expires_after="M96">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -896,6 +932,9 @@
 
 <histogram name="SubresourceRedirect.RobotRulesDecider.ReceiveResult"
     enum="SubresourceRedirectRobotsRulesReceiveResult" expires_after="M96">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -907,6 +946,9 @@
 
 <histogram name="SubresourceRedirect.RobotsRules.Browser.InMemoryCacheHit"
     units="BooleanCacheHit" expires_after="M96">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -918,6 +960,9 @@
 
 <histogram name="SubresourceRedirect.RobotsRulesFetcher.CacheHit" units="ms"
     expires_after="M96">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -928,6 +973,9 @@
 
 <histogram name="SubresourceRedirect.RobotsRulesFetcher.NetErrorCode"
     enum="NetErrorCodes" expires_after="M96">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -938,6 +986,9 @@
 
 <histogram name="SubresourceRedirect.RobotsRulesFetcher.ResponseCode"
     units="HttpResponseCode" expires_after="M96">
+  <obsolete>
+    Obsoleted 01/2022.
+  </obsolete>
   <owner>rajendrant@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/tab/histograms.xml b/tools/metrics/histograms/metadata/tab/histograms.xml
index b533c04..dd86f08 100644
--- a/tools/metrics/histograms/metadata/tab/histograms.xml
+++ b/tools/metrics/histograms/metadata/tab/histograms.xml
@@ -266,7 +266,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.MemoryUsage.CompressedData.PerThumbnailKiB"
-    units="KB" expires_after="2022-02-13">
+    units="KB" expires_after="2022-07-01">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
@@ -276,7 +276,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.MemoryUsage.CompressedData.TotalKiB" units="KB"
-    expires_after="2022-02-13">
+    expires_after="2022-07-01">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
@@ -334,7 +334,7 @@
 </histogram>
 
 <histogram name="Tab.Preview.VideoCaptureDuration" units="ms"
-    expires_after="2022-02-13">
+    expires_after="2022-07-01">
   <owner>dfried@chromium.org</owner>
   <owner>collinbaker@chromium.org</owner>
   <summary>
@@ -750,7 +750,7 @@
 </histogram>
 
 <histogram name="TabHoverCards.LastTabHoverCardPreviewTime{TabCountMetrics}"
-    units="units" expires_after="2022-02-13">
+    units="ms" expires_after="2022-07-01">
   <owner>dfried@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -779,7 +779,7 @@
 </histogram>
 
 <histogram name="TabHoverCards.LastTabHoverCardViewedTime{TabCountMetrics}"
-    units="units" expires_after="2022-02-13">
+    units="ms" expires_after="2022-07-01">
   <owner>dfried@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -806,7 +806,7 @@
 </histogram>
 
 <histogram name="TabHoverCards.TabHoverCardPreviewTime{TabCountMetrics}"
-    units="units" expires_after="2022-02-13">
+    units="ms" expires_after="2022-07-01">
   <owner>dfried@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -835,7 +835,7 @@
 
 <histogram
     name="TabHoverCards.TabHoverCardsSeenBeforeTabSelection{TabCountMetrics}"
-    units="units" expires_after="2022-02-13">
+    units="hover cards" expires_after="2022-07-01">
   <owner>dfried@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -860,7 +860,7 @@
 </histogram>
 
 <histogram name="TabHoverCards.TabHoverCardViewedTime{TabCountMetrics}"
-    units="units" expires_after="2022-02-13">
+    units="ms" expires_after="2022-07-01">
   <owner>dfried@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -888,7 +888,7 @@
 
 <histogram
     name="TabHoverCards.TabPreviewsSeenBeforeTabSelection{TabCountMetrics}"
-    units="units" expires_after="2022-02-13">
+    units="previews" expires_after="2022-07-01">
   <owner>dfried@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -914,7 +914,7 @@
 </histogram>
 
 <histogram name="TabHoverCards.TimeSinceLastVisible" units="ms"
-    expires_after="2022-02-13">
+    expires_after="2022-07-01">
   <owner>corising@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -1951,7 +1951,7 @@
 </histogram>
 
 <histogram name="Tabs.ScrubbedInInterval.KeyPress" units="tabs"
-    expires_after="2022-02-13">
+    expires_after="2022-07-01">
   <owner>corising@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/uma/histograms.xml b/tools/metrics/histograms/metadata/uma/histograms.xml
index 7105f3c..7ee2e667 100644
--- a/tools/metrics/histograms/metadata/uma/histograms.xml
+++ b/tools/metrics/histograms/metadata/uma/histograms.xml
@@ -791,6 +791,16 @@
   </summary>
 </histogram>
 
+<histogram name="UMA.Startup.Visibility" enum="StartupVisibility"
+    expires_after="2022-11-01">
+  <owner>caitlinfischer@google.com</owner>
+  <owner>src/base/metrics/OWNERS</owner>
+  <summary>
+    Whether the session is a background or foreground session. Recorded just
+    before the MetricsStateManager is created. Android Chrome only.
+  </summary>
+</histogram>
+
 <histogram name="UMA.StructuredMetrics.ClientInitializationSuccessful"
     enum="BooleanSuccess" expires_after="M97">
   <obsolete>
diff --git a/tools/metrics/histograms/metadata/variations/histograms.xml b/tools/metrics/histograms/metadata/variations/histograms.xml
index c0a61ea..692dd492 100644
--- a/tools/metrics/histograms/metadata/variations/histograms.xml
+++ b/tools/metrics/histograms/metadata/variations/histograms.xml
@@ -541,6 +541,11 @@
     seeds. Second, the metric was not previously recorded for regular seeds that
     were older than 30 days, but as of M93, the freshness is recorded for seeds
     that are older than 30 days and newer than the binary build time.
+
+    Prior to M99, this histogram would not be logged for first run seeds (where
+    they were not fetched by Chrome, but provided externally, such as part of
+    Chrome installation via Google Update). In M99+, this histogram will be
+    logged with a value of 0 minutes, so that the seed is considered fresh.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/webauthn/histograms.xml b/tools/metrics/histograms/metadata/webauthn/histograms.xml
index 54dc324f..7d3b83a 100644
--- a/tools/metrics/histograms/metadata/webauthn/histograms.xml
+++ b/tools/metrics/histograms/metadata/webauthn/histograms.xml
@@ -135,7 +135,7 @@
 
 <histogram name="WebAuthentication.ChromeOS.MakeCredentialStatus"
     enum="WebAuthenticationChromeOSMakeCredentialResult"
-    expires_after="2022-01-23">
+    expires_after="2022-12-31">
   <owner>martinkr@google.com</owner>
   <owner>chrome-webauthn@google.com</owner>
   <summary>Records the outcome of MakeCredential on Chrome OS.</summary>
@@ -159,7 +159,7 @@
 </histogram>
 
 <histogram name="WebAuthentication.ChromeOS.U2FClient.IsU2fEnabledStatus"
-    enum="WebAuthenticationU2FClientStatus" expires_after="2022-01-23">
+    enum="WebAuthenticationU2FClientStatus" expires_after="2022-12-31">
   <owner>martinkr@google.com</owner>
   <owner>chrome-webauthn@google.com</owner>
   <summary>
@@ -176,7 +176,7 @@
 </histogram>
 
 <histogram name="WebAuthentication.ChromeOS.U2FClient.IsUvpaaStatus"
-    enum="WebAuthenticationU2FClientStatus" expires_after="2022-01-23">
+    enum="WebAuthenticationU2FClientStatus" expires_after="2022-12-31">
   <owner>martinkr@google.com</owner>
   <owner>chrome-webauthn@google.com</owner>
   <summary>
@@ -226,7 +226,7 @@
 </histogram>
 
 <histogram name="WebAuthentication.GetAssertionResponseTransport"
-    enum="WebAuthenticationFidoTransport" expires_after="2022-01-30">
+    enum="WebAuthenticationFidoTransport" expires_after="2022-12-31">
   <owner>kenrb@chromium.org</owner>
   <owner>martinkr@google.com</owner>
   <summary>
diff --git a/tools/traffic_annotation/auditor/safe_list.txt b/tools/traffic_annotation/auditor/safe_list.txt
index 2a40844da..38b49ec1 100644
--- a/tools/traffic_annotation/auditor/safe_list.txt
+++ b/tools/traffic_annotation/auditor/safe_list.txt
@@ -20,7 +20,6 @@
 missing,chrome/android/java/src/org/chromium/chrome/browser/feedback/ConnectivityChecker.java
 missing,chrome/android/java/src/org/chromium/chrome/browser/net/connectivitydetector/ConnectivityDetector.java
 missing,chrome/android/java/src/org/chromium/chrome/browser/offlinepages/measurements/OfflineMeasurementsBackgroundTask.java
-missing,chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
 missing,chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc
 missing,chrome/browser/android/feedback/connectivity_checker.cc
 missing,chrome/browser/android/webapk/webapk_installer.cc
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index d44bde8..c909421 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -135,7 +135,6 @@
  <item id="intranet_redirect_detector" added_in_milestone="62" content_hash_code="03b26f7b" os_list="linux,windows,chromeos" file_path="chrome/browser/intranet_redirect_detector.cc" />
  <item id="javascript_report_error" added_in_milestone="87" content_hash_code="006e4e54" os_list="linux" file_path="chrome/browser/error_reporting/chrome_js_error_report_processor_nonchromeos.cc" />
  <item id="lib_address_input" added_in_milestone="62" content_hash_code="0374aae8" os_list="linux,windows,chromeos,android" file_path="third_party/libaddressinput/chromium/chrome_metadata_source.cc" />
- <item id="litepages_robots_rules" added_in_milestone="89" content_hash_code="04534928" os_list="linux,windows,chromeos,android" file_path="chrome/browser/subresource_redirect/origin_robots_rules.cc" />
  <item id="load_autofill_gstatic_data" added_in_milestone="78" content_hash_code="07a1e952" os_list="linux,windows,chromeos,android" file_path="chrome/browser/autofill/autofill_gstatic_reader.cc" />
  <item id="logo_service" added_in_milestone="73" content_hash_code="013550c3" os_list="linux,windows,chromeos,android" file_path="components/search_provider_logos/logo_service_impl.cc" />
  <item id="lookup_single_password_leak" added_in_milestone="78" content_hash_code="00b98558" os_list="linux,windows,chromeos,android" file_path="components/password_manager/core/browser/leak_detection/leak_detection_request.cc" />
@@ -158,6 +157,7 @@
  <item id="oauth2_api_call_flow" added_in_milestone="65" type="completing" content_hash_code="067ca204" os_list="linux,windows,chromeos,android" policy_fields="-1" file_path="google_apis/gaia/oauth2_api_call_flow.cc" />
  <item id="oauth2_mint_token_flow" added_in_milestone="62" type="partial" second_id="oauth2_api_call_flow" content_hash_code="05756bf8" os_list="linux,windows,chromeos,android" semantics_fields="1,2,3,4,5" policy_fields="3,4" file_path="google_apis/gaia/oauth2_mint_token_flow.cc" />
  <item id="offline_prefetch" added_in_milestone="62" content_hash_code="06ad9616" os_list="linux,windows,chromeos,android" file_path="components/offline_pages/core/prefetch/prefetch_request_fetcher.cc" />
+ <item id="omaha_client_android_uc" added_in_milestone="99" content_hash_code="0757ca45" os_list="android" file_path="chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java" />
  <item id="omnibox_documentsuggest" added_in_milestone="69" content_hash_code="07917541" os_list="linux,windows,chromeos,android" file_path="components/omnibox/browser/document_suggestions_service.cc" />
  <item id="omnibox_navigation_observer" added_in_milestone="62" content_hash_code="043a7a2f" os_list="linux,windows,chromeos" file_path="chrome/browser/ui/omnibox/chrome_omnibox_navigation_observer.cc" />
  <item id="omnibox_result_change" added_in_milestone="62" content_hash_code="017a7557" os_list="linux,windows,chromeos,android" file_path="chrome/browser/bitmap_fetcher/bitmap_fetcher_service.cc" />
@@ -323,7 +323,8 @@
  <item id="management_ui_customer_logo" added_in_milestone="98" content_hash_code="01a17a58" os_list="chromeos" file_path="chrome/browser/ui/webui/management/management_ui_handler_chromeos.cc" />
  <item id="gaia_reauth_token_fetcher" added_in_milestone="98" content_hash_code="04ff463d" os_list="chromeos" file_path="chrome/browser/ash/login/gaia_reauth_token_fetcher.cc" />
  <item id="partner_bookmarks_reader_get_favicon" added_in_milestone="98" content_hash_code="0186a55e" os_list="android" file_path="chrome/browser/android/bookmarks/partner_bookmarks_reader.cc" />
- <item id="contextual_search_resolve" added_in_milestone="98" content_hash_code="07e8053b" os_list="android" file_path="chrome/browser/android/contextualsearch/contextual_search_delegate.cc" />
+ <item id="contextual_search_resolve" added_in_milestone="98" content_hash_code="01a5751f" os_list="android" file_path="chrome/browser/android/contextualsearch/contextual_search_delegate.cc" />
+ <item id="contextual_search_thumbnail" added_in_milestone="99" content_hash_code="06c5c621" os_list="android" file_path="chrome/browser/android/compositor/scene_layer/contextual_search_scene_layer.cc" />
  <item id="customtabs_parallel_request" added_in_milestone="98" content_hash_code="02c6bf11" os_list="android" file_path="chrome/browser/android/customtabs/detached_resource_request.cc" />
  <item id="explore_sites_image_fetcher" added_in_milestone="98" content_hash_code="031c2b7a" os_list="android" file_path="chrome/browser/android/explore_sites/explore_sites_bridge_experimental.cc" />
  <item id="explore_sites" added_in_milestone="98" content_hash_code="05256b51" os_list="android" file_path="chrome/browser/android/explore_sites/explore_sites_fetcher.cc" />
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml
index 2b3f9adc0..8f924d77 100644
--- a/tools/traffic_annotation/summary/grouping.xml
+++ b/tools/traffic_annotation/summary/grouping.xml
@@ -24,7 +24,6 @@
       <traffic_annotation unique_id="chime_sdk"/>
       <traffic_annotation unique_id="chrome_android_hats"/>
       <traffic_annotation unique_id="chrome_variations_android"/>
-      <traffic_annotation unique_id="contextual_search_resolve"/>
       <traffic_annotation unique_id="customtabs_parallel_request"/>
       <traffic_annotation unique_id="download_bitmap"/>
       <traffic_annotation unique_id="download_manager_service_retry"/>
@@ -362,7 +361,6 @@
       <traffic_annotation unique_id="hintsfetcher_gethintsrequest"/>
       <traffic_annotation unique_id="optimization_guide_model"/>
       <traffic_annotation unique_id="optimization_guide_model_download"/>
-      <traffic_annotation unique_id="litepages_robots_rules"/>
     </sender>
     <sender name="Network">
       <traffic_annotation unique_id="network_time_component"/>
@@ -393,12 +391,17 @@
       <traffic_annotation unique_id="one_google_bar_service"/>
       <traffic_annotation unique_id="search_prefetch_service"/>
     </sender>
+    <sender name="Contextual Search">
+      <traffic_annotation unique_id="contextual_search_resolve"/>
+      <traffic_annotation unique_id="contextual_search_thumbnail"/>
+    </sender>
   </group>
   <group name="Updates, Recovery, and Crash Reports">
     <sender name="Chrome Variations Service">
       <traffic_annotation unique_id="chrome_variations_service"/>
     </sender>
     <sender name="Updates">
+      <traffic_annotation unique_id="omaha_client_android_uc"/>
       <traffic_annotation unique_id="update_client"/>
     </sender>
     <sender name="Crash Reports">
diff --git a/tools/typescript/tests/project4/exclude.ts b/tools/typescript/tests/project4/exclude.ts
new file mode 100644
index 0000000..4265fe3
--- /dev/null
+++ b/tools/typescript/tests/project4/exclude.ts
@@ -0,0 +1,11 @@
+// Copyright 2022 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 {hello} from './include.js';
+
+function sayHello(): void {
+  console.log(hello());
+}
+
+sayHello();
diff --git a/tools/typescript/tests/project4/include.ts b/tools/typescript/tests/project4/include.ts
new file mode 100644
index 0000000..3454ad8
--- /dev/null
+++ b/tools/typescript/tests/project4/include.ts
@@ -0,0 +1,8 @@
+// Copyright 2022 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.
+
+export function hello(): string {
+  return 'hello';
+}
+
diff --git a/tools/typescript/ts_library.gni b/tools/typescript/ts_library.gni
index ae22c97..f30fc51b 100644
--- a/tools/typescript/ts_library.gni
+++ b/tools/typescript/ts_library.gni
@@ -14,6 +14,7 @@
                              "extra_deps",
                              "in_files",
                              "tsconfig_base",
+                             "manifest_excludes",
                            ])
 
     inputs = [ "//tools/typescript/tsconfig_base.json" ]
@@ -64,6 +65,11 @@
       args += [ "--in_files" ] + in_files
     }
 
+    if (defined(manifest_excludes)) {
+      assert(defined(in_files))
+      args += [ "--manifest_excludes" ] + manifest_excludes
+    }
+
     if (defined(invoker.definitions)) {
       inputs += invoker.definitions
       definitions = []
diff --git a/tools/typescript/ts_library.py b/tools/typescript/ts_library.py
index 1b39280..fb67440 100644
--- a/tools/typescript/ts_library.py
+++ b/tools/typescript/ts_library.py
@@ -36,6 +36,7 @@
   parser.add_argument('--out_dir', required=True)
   parser.add_argument('--tsconfig_base')
   parser.add_argument('--in_files', nargs='*')
+  parser.add_argument('--manifest_excludes', nargs='*')
   parser.add_argument('--definitions', nargs='*')
   parser.add_argument('--composite', action='store_true')
   args = parser.parse_args(argv)
@@ -120,8 +121,12 @@
         as manifest_file:
       manifest_data = {}
       manifest_data['base_dir'] = args.out_dir
+      manifest_files = args.in_files
+      if args.manifest_excludes is not None:
+        manifest_files = filter(lambda f: f not in args.manifest_excludes,
+                                args.in_files)
       manifest_data['files'] = \
-          [re.sub(r'\.ts$', '.js', f) for f in args.in_files]
+          [re.sub(r'\.ts$', '.js', f) for f in manifest_files]
       json.dump(manifest_data, manifest_file)
 
 
diff --git a/tools/typescript/ts_library_test.py b/tools/typescript/ts_library_test.py
index 65dd9bb..4a9314f 100755
--- a/tools/typescript/ts_library_test.py
+++ b/tools/typescript/ts_library_test.py
@@ -3,6 +3,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import json
 import ts_library
 import ts_definitions
 import os
@@ -136,6 +137,44 @@
         os.path.exists(os.path.join(gen_dir, 'tsconfig.tsbuildinfo')))
     self.assertFalse(os.path.exists(os.path.join(gen_dir, 'tsconfig.manifest')))
 
+  def _build_project4(self):
+    gen_dir = os.path.join(self._out_folder, 'project4')
+
+    # Build project4, which includes multiple TS files, only one of which should
+    # be included in the manifest.
+    ts_library.main([
+        '--root_dir',
+        os.path.join(_HERE_DIR, 'tests', 'project4'),
+        '--gen_dir',
+        gen_dir,
+        '--out_dir',
+        gen_dir,
+        '--in_files',
+        'include.ts',
+        'exclude.ts',
+        '--manifest_excludes',
+        'exclude.ts',
+    ])
+    return gen_dir
+
+  def _assert_project4_output(self, gen_dir):
+    files = [
+        'include.js',
+        'exclude.js',
+        'tsconfig.json',
+        'tsconfig.manifest',
+    ]
+    for f in files:
+      self.assertTrue(os.path.exists(os.path.join(gen_dir, f)), f)
+
+    # Check that the generated manifest file doesn't include exclude.js.
+    manifest = 'tsconfig.manifest'
+    with open(os.path.join(gen_dir, manifest), 'r') as f:
+      data = json.load(f)
+      self.assertEqual(len(data['files']), 1)
+      self.assertEqual(data['files'][0], 'include.js')
+
+
   # Test success case where both project1 and project2 are compiled successfully
   # and no errors are thrown.
   def testSuccess(self):
@@ -149,6 +188,9 @@
     project2_gen_dir = self._build_project2(project1_gen_dir, project3_gen_dir)
     self._assert_project2_output(project2_gen_dir)
 
+    project4_gen_dir = self._build_project4()
+    self._assert_project4_output(project4_gen_dir)
+
   # Test error case where a type violation exists, ensure that an error is
   # thrown.
   def testError(self):
@@ -167,7 +209,7 @@
       ])
     except RuntimeError as err:
       self.assertTrue('Type \'number\' is not assignable to type \'string\'' \
-          in err.message)
+                      in str(err))
     else:
       self.fail('Failed to detect type error')
 
diff --git a/ui/accessibility/BUILD.gn b/ui/accessibility/BUILD.gn
index f146bae..49abb09 100644
--- a/ui/accessibility/BUILD.gn
+++ b/ui/accessibility/BUILD.gn
@@ -286,7 +286,7 @@
   ]
 
   if (is_fuchsia) {
-    sources += [ "platform/fuchsia/semantic_provider_test.cc" ]
+    sources += [ "platform/fuchsia/semantic_provider_unittest.cc" ]
 
     deps += [
       "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics",
diff --git a/ui/accessibility/ax_computed_node_data.cc b/ui/accessibility/ax_computed_node_data.cc
index e175016..6e15815a 100644
--- a/ui/accessibility/ax_computed_node_data.cc
+++ b/ui/accessibility/ax_computed_node_data.cc
@@ -280,13 +280,9 @@
 
   sentence_starts_ = std::vector<int32_t>();
   sentence_ends_ = std::vector<int32_t>();
-  if (owner_->IsLineBreak())
-    return;
   const std::u16string& text_content = GetOrComputeTextContentUTF16();
-  if (text_content.empty() ||
-      base::ContainsOnlyChars(text_content, base::kWhitespaceUTF16)) {
+  if (text_content.empty())
     return;
-  }
 
   // Unlike in ICU, a sentence boundary is not valid in Blink if it falls within
   // some whitespace that is used to separate sentences. We therefore need to
diff --git a/ui/accessibility/ax_node.h b/ui/accessibility/ax_node.h
index 3db59f6..fc09e38 100644
--- a/ui/accessibility/ax_node.h
+++ b/ui/accessibility/ax_node.h
@@ -819,10 +819,10 @@
   // increment the iterator past the end, we remain at the past-the-end iterator
   // condition.
   if (child_ && parent_) {
-    if (child_ == (parent_->*LastChild)())
+    if (child_ == (parent_.get()->*LastChild)())
       child_ = nullptr;
     else
-      child_ = (child_->*NextSibling)();
+      child_ = (child_.get()->*NextSibling)();
   }
 
   return *this;
@@ -847,12 +847,12 @@
     // If the iterator is past the end, |child_=nullptr|, decrement the iterator
     // gives us the last iterator element.
     if (!child_)
-      child_ = (parent_->*LastChild)();
+      child_ = (parent_.get()->*LastChild)();
     // Decrement the iterator gives us the previous element, except when the
     // iterator is at the beginning; in which case, decrementing the iterator
     // remains at the beginning.
-    else if (child_ != (parent_->*FirstChild)())
-      child_ = (child_->*PreviousSibling)();
+    else if (child_ != (parent_.get()->*FirstChild)())
+      child_ = (child_.get()->*PreviousSibling)();
   }
 
   return *this;
diff --git a/ui/accessibility/ax_node_position_unittest.cc b/ui/accessibility/ax_node_position_unittest.cc
index 2876598..a7368e02 100644
--- a/ui/accessibility/ax_node_position_unittest.cc
+++ b/ui/accessibility/ax_node_position_unittest.cc
@@ -388,10 +388,6 @@
   inline_box1_.role = ax::mojom::Role::kInlineTextBox;
   inline_box1_.AddState(ax::mojom::State::kEditable);
   inline_box1_.SetName("Line 1");
-  inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kSentenceStarts,
-                                   {0});
-  inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kSentenceEnds,
-                                   {6});
   inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
                                    std::vector<int32_t>{0, 5});
   inline_box1_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
@@ -416,10 +412,6 @@
   inline_box2_.role = ax::mojom::Role::kInlineTextBox;
   inline_box2_.AddState(ax::mojom::State::kEditable);
   inline_box2_.SetName("Line 2");
-  inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kSentenceStarts,
-                                   {0});
-  inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kSentenceEnds,
-                                   {6});
   inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
                                    std::vector<int32_t>{0, 5});
   inline_box2_.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
@@ -3230,19 +3222,19 @@
                                          ax::mojom::TextAffinity::kDownstream);
   paragraph_start_position =
       paragraph_start_position->CreateNextParagraphStartPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   ASSERT_TRUE(paragraph_start_position->IsTextPosition());
   EXPECT_EQ(inline_text_data_b_1.id, paragraph_start_position->anchor_id());
   EXPECT_EQ(0, paragraph_start_position->text_offset());
   paragraph_start_position =
       paragraph_start_position->CreateNextParagraphStartPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   ASSERT_TRUE(paragraph_start_position->IsTextPosition());
   EXPECT_EQ(inline_text_data_c.id, paragraph_start_position->anchor_id());
   EXPECT_EQ(0, paragraph_start_position->text_offset());
   paragraph_start_position =
       paragraph_start_position->CreateNextParagraphStartPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   EXPECT_TRUE(paragraph_start_position->IsNullPosition());
 
   paragraph_start_position = AXNodePosition::CreateTextPosition(
@@ -3250,25 +3242,25 @@
       ax::mojom::TextAffinity::kDownstream);
   paragraph_start_position =
       paragraph_start_position->CreatePreviousParagraphStartPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   ASSERT_TRUE(paragraph_start_position->IsTextPosition());
   EXPECT_EQ(inline_text_data_c.id, paragraph_start_position->anchor_id());
   EXPECT_EQ(0, paragraph_start_position->text_offset());
   paragraph_start_position =
       paragraph_start_position->CreatePreviousParagraphStartPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   ASSERT_TRUE(paragraph_start_position->IsTextPosition());
   EXPECT_EQ(inline_text_data_b_1.id, paragraph_start_position->anchor_id());
   EXPECT_EQ(0, paragraph_start_position->text_offset());
   paragraph_start_position =
       paragraph_start_position->CreatePreviousParagraphStartPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   ASSERT_TRUE(paragraph_start_position->IsTextPosition());
   EXPECT_EQ(inline_text_data_a.id, paragraph_start_position->anchor_id());
   EXPECT_EQ(0, paragraph_start_position->text_offset());
   paragraph_start_position =
       paragraph_start_position->CreatePreviousParagraphStartPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   EXPECT_TRUE(paragraph_start_position->IsNullPosition());
 
   TestPositionType paragraph_end_position = AXNodePosition::CreateTextPosition(
@@ -3276,28 +3268,28 @@
       ax::mojom::TextAffinity::kDownstream);
   paragraph_end_position =
       paragraph_end_position->CreateNextParagraphEndPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   ASSERT_TRUE(paragraph_end_position->IsTextPosition());
   EXPECT_EQ(inline_text_data_a.id, paragraph_end_position->anchor_id());
   // "First paragraph<>".
   EXPECT_EQ(15, paragraph_end_position->text_offset());
   paragraph_end_position =
       paragraph_end_position->CreateNextParagraphEndPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   ASSERT_TRUE(paragraph_end_position->IsTextPosition());
   EXPECT_EQ(inline_text_data_b_3.id, paragraph_end_position->anchor_id());
   // "paragraph<>".
   EXPECT_EQ(9, paragraph_end_position->text_offset());
   paragraph_end_position =
       paragraph_end_position->CreateNextParagraphEndPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   ASSERT_TRUE(paragraph_end_position->IsTextPosition());
   EXPECT_EQ(inline_text_data_c.id, paragraph_end_position->anchor_id());
   // "Third paragraph<>".
   EXPECT_EQ(15, paragraph_end_position->text_offset());
   paragraph_end_position =
       paragraph_end_position->CreateNextParagraphEndPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   EXPECT_TRUE(paragraph_end_position->IsNullPosition());
 
   paragraph_end_position = AXNodePosition::CreateTextPosition(
@@ -3305,21 +3297,21 @@
       ax::mojom::TextAffinity::kDownstream);
   paragraph_end_position =
       paragraph_end_position->CreatePreviousParagraphEndPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   ASSERT_TRUE(paragraph_end_position->IsTextPosition());
   EXPECT_EQ(inline_text_data_b_3.id, paragraph_end_position->anchor_id());
   // "paragraph<>".
   EXPECT_EQ(9, paragraph_end_position->text_offset());
   paragraph_end_position =
       paragraph_end_position->CreatePreviousParagraphEndPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   ASSERT_TRUE(paragraph_end_position->IsTextPosition());
   EXPECT_EQ(inline_text_data_a.id, paragraph_end_position->anchor_id());
   // "First paragraph<>".
   EXPECT_EQ(15, paragraph_end_position->text_offset());
   paragraph_end_position =
       paragraph_end_position->CreatePreviousParagraphEndPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   EXPECT_TRUE(paragraph_end_position->IsNullPosition());
 }
 
@@ -3371,7 +3363,7 @@
       ax::mojom::TextAffinity::kDownstream);
 
   test_position = test_position->CreatePreviousParagraphEndPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundary);
+      AXBoundaryBehavior::StopAtAnchorBoundary);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(root_data.id, test_position->anchor_id());
   EXPECT_EQ(5, test_position->text_offset());
@@ -4524,9 +4516,9 @@
   inline_box_data_1.SetName("One");
   inline_box_data_1.AddState(ax::mojom::State::kIgnored);
   inline_box_data_1.AddIntListAttribute(
-      ax::mojom::IntListAttribute::kWordStarts, {0});
+      ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
   inline_box_data_1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
-                                        {3});
+                                        std::vector<int32_t>{3});
   inline_box_data_1.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
                                     kInlineBox2Id);
 
@@ -4540,9 +4532,9 @@
   inline_box_data_2.role = ax::mojom::Role::kInlineTextBox;
   inline_box_data_2.SetName("Two");
   inline_box_data_2.AddIntListAttribute(
-      ax::mojom::IntListAttribute::kWordStarts, {0});
+      ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
   inline_box_data_2.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
-                                        {3});
+                                        std::vector<int32_t>{3});
   inline_box_data_2.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
                                     kInlineBox1Id);
   inline_box_data_2.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
@@ -4558,9 +4550,9 @@
   inline_box_data_3.role = ax::mojom::Role::kInlineTextBox;
   inline_box_data_3.SetName("Three");
   inline_box_data_3.AddIntListAttribute(
-      ax::mojom::IntListAttribute::kWordStarts, {0});
+      ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
   inline_box_data_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
-                                        {5});
+                                        std::vector<int32_t>{5});
   inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
                                     kInlineBox2Id);
   inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kNextOnLineId,
@@ -4577,9 +4569,9 @@
   inline_box_data_4.SetName("Four");
   inline_box_data_4.AddState(ax::mojom::State::kIgnored);
   inline_box_data_3.AddIntListAttribute(
-      ax::mojom::IntListAttribute::kWordStarts, {0});
+      ax::mojom::IntListAttribute::kWordStarts, std::vector<int32_t>{0});
   inline_box_data_3.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
-                                        {4});
+                                        std::vector<int32_t>{4});
   inline_box_data_3.AddIntAttribute(ax::mojom::IntAttribute::kPreviousOnLineId,
                                     kInlineBox3Id);
 
@@ -4601,7 +4593,7 @@
   ASSERT_FALSE(text_position->IsIgnored());
   TestPositionType test_position = text_position->CreatePositionAtTextBoundary(
       ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kForward,
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
@@ -4609,7 +4601,7 @@
   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
   test_position = text_position->CreatePositionAtTextBoundary(
       ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kBackward,
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box_data_2.id, test_position->anchor_id());
@@ -4622,7 +4614,7 @@
   ASSERT_FALSE(text_position->IsIgnored());
   test_position = text_position->CreatePositionAtTextBoundary(
       ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kForward,
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box_data_3.id, test_position->anchor_id());
@@ -4630,7 +4622,7 @@
   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
   test_position = text_position->CreatePositionAtTextBoundary(
       ax::mojom::TextBoundary::kWordStart, ax::mojom::MoveDirection::kBackward,
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box_data_2.id, test_position->anchor_id());
@@ -4789,15 +4781,15 @@
   ASSERT_NE(nullptr, null_position);
   TestPositionType test_position =
       null_position->CreatePreviousFormatStartPosition(
-          AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+          AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
   test_position = null_position->CreatePreviousFormatStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
   test_position = null_position->CreatePreviousFormatStartPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundary);
+      AXBoundaryBehavior::StopAtAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 }
@@ -4812,46 +4804,45 @@
 
   TestPositionType test_position =
       tree_position->CreatePreviousFormatStartPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(static_text1_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->child_index());
 
   test_position = test_position->CreatePreviousFormatStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(check_box_.id, test_position->anchor_id());
   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
 
   test_position = test_position->CreatePreviousFormatStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(button_.id, test_position->anchor_id());
   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
 
-  // kStopAtAnchorBoundaryOrIfAlreadyAtBoundary shouldn't move, since it's
-  // already at a boundary.
+  // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
   test_position = test_position->CreatePreviousFormatStartPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(button_.id, test_position->anchor_id());
   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
 
-  // kStopAtLastAnchorBoundary should stop at the start of the whole content
-  // while kCrossBoundary should return a null position when crossing it.
+  // StopAtLastAnchorBoundary should stop at the start of the whole content
+  // while CrossBoundary should return a null position when crossing it.
   test_position = test_position->CreatePreviousFormatStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(button_.id, test_position->anchor_id());
   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
 
   test_position = test_position->CreatePreviousFormatStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 }
@@ -4867,7 +4858,7 @@
 
   TestPositionType test_position =
       text_position->CreatePreviousFormatStartPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
@@ -4875,39 +4866,38 @@
   EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, test_position->affinity());
 
   test_position = test_position->CreatePreviousFormatStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(check_box_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
 
   test_position = test_position->CreatePreviousFormatStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(button_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
 
-  // kStopAtAnchorBoundaryOrIfAlreadyAtBoundary shouldn't move, since it's
-  // already at a boundary.
+  // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
   test_position = test_position->CreatePreviousFormatStartPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(button_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
 
-  // kStopAtLastAnchorBoundary should stop at the start of the whole content
-  // while kCrossBoundary should return a null position when crossing it.
+  // StopAtLastAnchorBoundary should stop at the start of the whole content
+  // while CrossBoundary should return a null position when crossing it.
   test_position = test_position->CreatePreviousFormatStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(button_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
 
   test_position = test_position->CreatePreviousFormatStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 }
@@ -4916,11 +4906,11 @@
   TestPositionType null_position = AXNodePosition::CreateNullPosition();
   ASSERT_NE(nullptr, null_position);
   TestPositionType test_position = null_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
   test_position = null_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 }
@@ -4934,53 +4924,52 @@
   ASSERT_TRUE(tree_position->IsTreePosition());
 
   TestPositionType test_position = tree_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(check_box_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->child_index());
 
   test_position = test_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->child_index());
 
   test_position = test_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(line_break_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->child_index());
 
   test_position = test_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->child_index());
 
-  // kStopAtAnchorBoundaryOrIfAlreadyAtBoundary shouldn't move, since it's
-  // already at a boundary.
+  // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
   test_position = test_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->child_index());
 
-  // kStopAtLastAnchorBoundary should stop at the end of the whole content while
-  // kCrossBoundary should return a null position when crossing it.
+  // StopAtLastAnchorBoundary should stop at the end of the whole content while
+  // CrossBoundary should return a null position when crossing it.
   test_position = test_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->child_index());
 
   test_position = test_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 }
@@ -4995,60 +4984,59 @@
   ASSERT_TRUE(text_position->IsTextPosition());
 
   TestPositionType test_position = text_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(button_.id, test_position->anchor_id());
   EXPECT_EQ(1, test_position->text_offset());
 
   test_position = test_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(check_box_.id, test_position->anchor_id());
   EXPECT_EQ(1, test_position->text_offset());
 
   test_position = test_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
   EXPECT_EQ(6, test_position->text_offset());
 
   test_position = test_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(line_break_.id, test_position->anchor_id());
   EXPECT_EQ(1, test_position->text_offset());
 
   test_position = test_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(6, test_position->text_offset());
 
-  // kStopAtAnchorBoundaryOrIfAlreadyAtBoundary shouldn't move, since it's
-  // already at a boundary.
+  // StopIfAlreadyAtBoundary shouldn't move, since it's already at a boundary.
   test_position = test_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(6, test_position->text_offset());
 
-  // kStopAtLastAnchorBoundary should stop at the end of the whole content while
-  // kCrossBoundary should return a null position when crossing it.
+  // StopAtLastAnchorBoundary should stop at the end of the whole content while
+  // CrossBoundary should return a null position when crossing it.
   test_position = test_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(6, test_position->text_offset());
 
   test_position = test_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 }
@@ -5206,7 +5194,7 @@
   // is at the end of "heading 1".
   TestPositionType format_end_position =
       text_position->CreateNextFormatEndPosition(
-          AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+          AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, format_end_position);
   EXPECT_TRUE(format_end_position->IsTextPosition());
   EXPECT_EQ(inline_text_4.id, format_end_position->anchor_id());
@@ -5217,7 +5205,7 @@
   // Move position to end of format at "heading 1|select, option 1|<>...", which
   // is at the end of embedded object <select, option 1> (popup_button_5).
   format_end_position = format_end_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, format_end_position);
   EXPECT_TRUE(format_end_position->IsTextPosition());
   EXPECT_EQ(popup_button_5.id, format_end_position->anchor_id());
@@ -5228,7 +5216,7 @@
   // Move position to end of format at "...|select, option 1|heading 2<>...",
   // which is at the end of "heading 2".
   format_end_position = format_end_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, format_end_position);
   EXPECT_TRUE(format_end_position->IsTextPosition());
   EXPECT_EQ(inline_text_10.id, format_end_position->anchor_id());
@@ -5240,7 +5228,7 @@
   // "...heading 2|select, option 2|<>|select, option 3|...", which is at the
   // end of embedded object <select, option 2> (popup_button_11).
   format_end_position = format_end_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, format_end_position);
   EXPECT_TRUE(format_end_position->IsTextPosition());
   EXPECT_EQ(popup_button_11.id, format_end_position->anchor_id());
@@ -5252,7 +5240,7 @@
   // "...heading 2|select, option 2||select, option 3|<>...", which is at the
   // end of embedded object <select, option 3> (popup_button_14).
   format_end_position = format_end_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, format_end_position);
   EXPECT_TRUE(format_end_position->IsTextPosition());
   EXPECT_EQ(popup_button_14.id, format_end_position->anchor_id());
@@ -5263,7 +5251,7 @@
   // Move position to end of format at "...|select, option 3|more text<>", which
   // is at the end of "more text".
   format_end_position = format_end_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, format_end_position);
   EXPECT_TRUE(format_end_position->IsTextPosition());
   EXPECT_EQ(inline_text_18.id, format_end_position->anchor_id());
@@ -5301,7 +5289,7 @@
   ASSERT_NE(nullptr, text_position);
   TestPositionType test_position =
       text_position->CreatePreviousFormatStartPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(text_data.id, test_position->anchor_id());
@@ -5313,7 +5301,7 @@
       ax::mojom::TextAffinity::kDownstream);
   ASSERT_NE(nullptr, text_position);
   test_position = text_position->CreateNextFormatEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(more_text_data.id, test_position->anchor_id());
@@ -5528,7 +5516,7 @@
     EXPECT_EQ(6, text_position->text_offset());
 
     text_position = text_position->CreateNextFormatEndPosition(
-        AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+        AXBoundaryBehavior::StopAtLastAnchorBoundary);
     ASSERT_NE(nullptr, text_position);
     EXPECT_TRUE(text_position->IsTextPosition());
     EXPECT_EQ(inline_box_11.id, text_position->anchor_id());
@@ -5544,7 +5532,7 @@
     EXPECT_EQ(0, text_position->text_offset());
 
     text_position = text_position->CreatePreviousFormatStartPosition(
-        AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+        AXBoundaryBehavior::StopAtLastAnchorBoundary);
     ASSERT_NE(nullptr, text_position);
     EXPECT_TRUE(text_position->IsTextPosition());
     EXPECT_EQ(inline_box_5.id, text_position->anchor_id());
@@ -5564,7 +5552,7 @@
     EXPECT_EQ(7, text_position->text_offset());
 
     text_position = text_position->CreateNextFormatEndPosition(
-        AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+        AXBoundaryBehavior::StopAtLastAnchorBoundary);
     ASSERT_NE(nullptr, text_position);
     EXPECT_TRUE(text_position->IsTextPosition());
     EXPECT_EQ(inline_box_13.id, text_position->anchor_id());
@@ -5580,7 +5568,7 @@
     EXPECT_EQ(0, text_position->text_offset());
 
     text_position = text_position->CreatePreviousFormatStartPosition(
-        AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+        AXBoundaryBehavior::StopAtLastAnchorBoundary);
     ASSERT_NE(nullptr, text_position);
     EXPECT_TRUE(text_position->IsTextPosition());
     EXPECT_EQ(inline_box_22.id, text_position->anchor_id());
@@ -5593,22 +5581,22 @@
   ASSERT_NE(nullptr, null_position);
   TestPositionType test_position =
       null_position->CreatePreviousPageStartPosition(
-          AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+          AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 
   test_position = null_position->CreateNextPageStartPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 
   test_position = null_position->CreatePreviousPageEndPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 
   test_position = null_position->CreatePreviousPageStartPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 }
@@ -5626,24 +5614,23 @@
   ASSERT_NE(nullptr, tree_position);
   ASSERT_TRUE(tree_position->IsTreePosition());
 
-  // kStopAtAnchorBoundaryOrIfAlreadyAtBoundary shouldn't move at all since it's
-  // at a boundary.
+  // StopIfAlreadyAtBoundary shouldn't move at all since it's at a boundary.
   TestPositionType test_position = tree_position->CreateNextPageStartPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(page_1_data.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->child_index());
 
   test_position = tree_position->CreateNextPageStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
 
   test_position = tree_position->CreateNextPageStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
@@ -5651,36 +5638,36 @@
 
   // Test CreateNextPageEndPosition until the end of content is reached.
   test_position = tree_position->CreateNextPageEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(page_1_data.id, test_position->anchor_id());
   EXPECT_EQ(1, test_position->child_index());
 
   test_position = test_position->CreateNextPageEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->child_index());
 
   test_position = test_position->CreateNextPageEndPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->child_index());
 
-  // kStopAtLastAnchorBoundary shouldn't move past the end of the whole content.
+  // StopAtLastAnchorBoundary shouldn't move past the end of the whole content.
   test_position = test_position->CreateNextPageStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->child_index());
 
   test_position = test_position->CreateNextPageEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
@@ -5688,69 +5675,69 @@
 
   // Moving forward past the end should return a null position.
   TestPositionType null_position = test_position->CreateNextPageStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, null_position);
   EXPECT_TRUE(null_position->IsNullPosition());
 
   null_position = test_position->CreateNextPageEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, null_position);
   EXPECT_TRUE(null_position->IsNullPosition());
 
   // Now move backward through the accessibility tree.
   tree_position = test_position->CreatePreviousPageEndPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, tree_position);
   EXPECT_TRUE(tree_position->IsTreePosition());
   EXPECT_EQ(page_3_text_data.id, tree_position->anchor_id());
   EXPECT_EQ(0, tree_position->child_index());
 
   test_position = tree_position->CreatePreviousPageEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->child_index());
 
   test_position = tree_position->CreatePreviousPageEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->child_index());
 
   test_position = test_position->CreatePreviousPageStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
 
   test_position = test_position->CreatePreviousPageStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
 
   test_position = test_position->CreatePreviousPageStartPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
 
-  // kStopAtLastAnchorBoundary shouldn't move past the start of the whole
+  // StopAtLastAnchorBoundary shouldn't move past the start of the whole
   // content.
   test_position = test_position->CreatePreviousPageStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
   EXPECT_EQ(AXNodePosition::BEFORE_TEXT, test_position->child_index());
 
   test_position = test_position->CreatePreviousPageEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
@@ -5758,12 +5745,12 @@
 
   // Moving before the start should return a null position.
   null_position = test_position->CreatePreviousPageStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, null_position);
   EXPECT_TRUE(null_position->IsNullPosition());
 
   null_position = test_position->CreatePreviousPageEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, null_position);
   EXPECT_TRUE(null_position->IsNullPosition());
 }
@@ -5782,24 +5769,23 @@
   ASSERT_NE(nullptr, text_position);
   ASSERT_TRUE(text_position->IsTextPosition());
 
-  // kStopAtAnchorBoundaryOrIfAlreadyAtBoundary shouldn't move at all since it's
-  // at a boundary.
+  // StopIfAlreadyAtBoundary shouldn't move at all since it's at a boundary.
   TestPositionType test_position = text_position->CreateNextPageStartPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
 
   test_position = text_position->CreateNextPageStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
 
   test_position = text_position->CreateNextPageStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
@@ -5807,36 +5793,36 @@
 
   // Test CreateNextPageEndPosition until the end of content is reached.
   test_position = test_position->CreateNextPageEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
   EXPECT_EQ(19, test_position->text_offset());
 
   test_position = test_position->CreateNextPageEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
   EXPECT_EQ(24, test_position->text_offset());
 
   test_position = test_position->CreateNextPageEndPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
   EXPECT_EQ(24, test_position->text_offset());
 
-  // kStopAtLastAnchorBoundary shouldn't move past the end of the whole content.
+  // StopAtLastAnchorBoundary shouldn't move past the end of the whole content.
   test_position = test_position->CreateNextPageStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
   EXPECT_EQ(24, test_position->text_offset());
 
   test_position = test_position->CreateNextPageEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(page_3_text_data.id, test_position->anchor_id());
@@ -5844,69 +5830,69 @@
 
   // Moving forward past the end should return a null position.
   TestPositionType null_position = test_position->CreateNextPageStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, null_position);
   EXPECT_TRUE(null_position->IsNullPosition());
 
   null_position = test_position->CreateNextPageEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, null_position);
   EXPECT_TRUE(null_position->IsNullPosition());
 
   // Now move backward through the accessibility tree.
   text_position = test_position->CreatePreviousPageEndPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, text_position);
   EXPECT_TRUE(text_position->IsTextPosition());
   EXPECT_EQ(page_3_text_data.id, text_position->anchor_id());
   EXPECT_EQ(24, text_position->text_offset());
 
   test_position = text_position->CreatePreviousPageEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
   EXPECT_EQ(19, test_position->text_offset());
 
   test_position = text_position->CreatePreviousPageEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
   EXPECT_EQ(19, test_position->text_offset());
 
   test_position = test_position->CreatePreviousPageStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(page_2_text_data.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
 
   test_position = test_position->CreatePreviousPageStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
 
   test_position = test_position->CreatePreviousPageStartPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
 
-  // kStopAtLastAnchorBoundary shouldn't move past the start of the whole
+  // StopAtLastAnchorBoundary shouldn't move past the start of the whole
   // content.
   test_position = test_position->CreatePreviousPageStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
 
   test_position = test_position->CreatePreviousPageEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(page_1_text_data.id, test_position->anchor_id());
@@ -5914,24 +5900,19 @@
 
   // Moving before the start should return a null position.
   null_position = test_position->CreatePreviousPageStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, null_position);
   EXPECT_TRUE(null_position->IsNullPosition());
 
   null_position = test_position->CreatePreviousPageEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, null_position);
   EXPECT_TRUE(null_position->IsNullPosition());
 }
 
 TEST_F(AXPositionTest, CreatePositionAtPageBoundaryWithNonPaginatedDocument) {
-  // We start from the second character in the whole content instead of the
-  // first, so that with `AXBoundaryBehavior::kCrossBoundary` we would be able
-  // to move back to the start of the page. Otherwise, if we had started from
-  // the first character, we would already be at the start of the page, and thus
-  // have gotten the null position.
   TestPositionType text_position = AXNodePosition::CreateTextPosition(
-      GetTreeID(), static_text1_.id, 1 /* text_offset */,
+      GetTreeID(), static_text1_.id, 0 /* text_offset */,
       ax::mojom::TextAffinity::kDownstream);
   ASSERT_NE(nullptr, text_position);
 
@@ -5940,7 +5921,7 @@
   // page)
   TestPositionType test_position =
       text_position->CreatePreviousPageStartPosition(
-          AXBoundaryBehavior::kCrossBoundary);
+          AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(button_.id, test_position->anchor_id());
@@ -5949,21 +5930,21 @@
   // Since there is no next page, CreateNextPageStartPosition should return a
   // null position
   test_position = text_position->CreateNextPageStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 
   // Since there is no previous page, CreatePreviousPageEndPosition should
   // return a null position
   test_position = text_position->CreatePreviousPageEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 
   // Since there are no distinct pages, CreateNextPageEndPosition should move
   // to the end of the whole content, as if it's one large page.
   test_position = text_position->CreateNextPageEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
@@ -5972,7 +5953,7 @@
   // CreatePreviousPageStartPosition should move back to the beginning of the
   // whole content.
   test_position = test_position->CreatePreviousPageStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(button_.id, test_position->anchor_id());
@@ -9322,11 +9303,11 @@
   TestPositionType null_position = AXNodePosition::CreateNullPosition();
   ASSERT_NE(nullptr, null_position);
   TestPositionType test_position = null_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
   test_position = null_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 }
@@ -9356,14 +9337,14 @@
 
   // Test basic cases with static MaxTextOffset
   TestPositionType test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_TRUE(test_position->IsValid());
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(text_data.id, test_position->anchor_id());
   EXPECT_EQ(9, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 
@@ -9388,14 +9369,14 @@
   // Now repeat the prior tests and ensure that we can create next character
   // positions with the new, valid MaxTextOffset (8).
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_TRUE(test_position->IsValid());
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(text_data.id, test_position->anchor_id());
   EXPECT_EQ(8, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 
@@ -9496,25 +9477,25 @@
   ASSERT_TRUE(text_position->IsTextPosition());
 
   TestPositionType test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
   EXPECT_EQ(4, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
   EXPECT_EQ(5, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundary);
+      AXBoundaryBehavior::StopAtAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
   EXPECT_EQ(5, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
@@ -9527,25 +9508,25 @@
   ASSERT_TRUE(text_position->IsTextPosition());
 
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
   EXPECT_EQ(5, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
   EXPECT_EQ(6, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundary);
+      AXBoundaryBehavior::StopAtAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
   EXPECT_EQ(6, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
@@ -9558,25 +9539,25 @@
   ASSERT_TRUE(text_position->IsTextPosition());
 
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
   EXPECT_EQ(6, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(line_break_.id, test_position->anchor_id());
   EXPECT_EQ(1, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundary);
+      AXBoundaryBehavior::StopAtAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
   EXPECT_EQ(6, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(line_break_.id, test_position->anchor_id());
@@ -9589,23 +9570,23 @@
   ASSERT_TRUE(text_position->IsTextPosition());
 
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(6, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundary);
+      AXBoundaryBehavior::StopAtAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(6, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
@@ -9618,25 +9599,25 @@
   ASSERT_TRUE(text_position->IsTextPosition());
 
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
   EXPECT_EQ(1, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(check_box_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundary);
+      AXBoundaryBehavior::StopAtAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(check_box_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
@@ -9649,7 +9630,7 @@
   ASSERT_TRUE(text_position->IsTextPosition());
 
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(text_field_.id, test_position->anchor_id());
@@ -9664,7 +9645,7 @@
   ASSERT_TRUE(text_position->IsTextPosition());
 
   test_position = text_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(text_field_.id, test_position->anchor_id());
@@ -9682,25 +9663,25 @@
 
   TestPositionType test_position =
       text_position->CreatePreviousCharacterPosition(
-          AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+          AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(5, test_position->text_offset());
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(4, test_position->text_offset());
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundary);
+      AXBoundaryBehavior::StopAtAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(4, test_position->text_offset());
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
@@ -9713,25 +9694,25 @@
   ASSERT_TRUE(text_position->IsTextPosition());
 
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(1, test_position->text_offset());
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundary);
+      AXBoundaryBehavior::StopAtAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
@@ -9744,25 +9725,25 @@
   ASSERT_TRUE(text_position->IsTextPosition());
 
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(line_break_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundary);
+      AXBoundaryBehavior::StopAtAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box2_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(line_break_.id, test_position->anchor_id());
@@ -9775,23 +9756,23 @@
   ASSERT_TRUE(text_position->IsTextPosition());
 
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundary);
+      AXBoundaryBehavior::StopAtAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(inline_box1_.id, test_position->anchor_id());
@@ -9804,23 +9785,23 @@
   ASSERT_TRUE(text_position->IsTextPosition());
 
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(check_box_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundary);
+      AXBoundaryBehavior::StopAtAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(check_box_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(check_box_.id, test_position->anchor_id());
@@ -9833,7 +9814,7 @@
   ASSERT_TRUE(text_position->IsTextPosition());
 
   test_position = text_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(text_field_.id, test_position->anchor_id());
@@ -9856,7 +9837,7 @@
        ++iter) {
     const int text_offset = *iter;
     test_position = test_position->CreateNextCharacterPosition(
-        AXBoundaryBehavior::kCrossBoundary);
+        AXBoundaryBehavior::CrossBoundary);
     ASSERT_NE(nullptr, test_position);
     EXPECT_TRUE(test_position->IsTextPosition());
 
@@ -9874,7 +9855,7 @@
       GetTreeID(), GetTree()->root()->id(), 3 /* text_offset */,
       ax::mojom::TextAffinity::kDownstream);
   test_position = test_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
@@ -9885,7 +9866,7 @@
       GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
       ax::mojom::TextAffinity::kDownstream);
   test_position = test_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
@@ -9896,7 +9877,7 @@
       GetTreeID(), GetTree()->root()->id(), 9 /* text_offset */,
       ax::mojom::TextAffinity::kUpstream);
   test_position = test_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
@@ -9907,7 +9888,7 @@
       GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
       ax::mojom::TextAffinity::kUpstream);
   test_position = test_position->CreateNextCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
@@ -9931,7 +9912,7 @@
        ++iter) {
     const int text_offset = *iter;
     test_position = test_position->CreatePreviousCharacterPosition(
-        AXBoundaryBehavior::kCrossBoundary);
+        AXBoundaryBehavior::CrossBoundary);
     ASSERT_NE(nullptr, test_position);
     EXPECT_TRUE(test_position->IsTextPosition());
 
@@ -9949,7 +9930,7 @@
       GetTreeID(), GetTree()->root()->id(), 3 /* text_offset */,
       ax::mojom::TextAffinity::kDownstream);
   test_position = test_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
@@ -9960,7 +9941,7 @@
       GetTreeID(), GetTree()->root()->id(), 4 /* text_offset */,
       ax::mojom::TextAffinity::kDownstream);
   test_position = test_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
@@ -9971,7 +9952,7 @@
       GetTreeID(), GetTree()->root()->id(), 9 /* text_offset */,
       ax::mojom::TextAffinity::kUpstream);
   test_position = test_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
@@ -9982,7 +9963,7 @@
       GetTreeID(), GetTree()->root()->id(), 10 /* text_offset */,
       ax::mojom::TextAffinity::kUpstream);
   test_position = test_position->CreatePreviousCharacterPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(GetTree()->root()->id(), test_position->anchor_id());
@@ -10002,7 +9983,7 @@
   while (!text_position->IsNullPosition()) {
     TestPositionType moved_position =
         text_position->CreateNextCharacterPosition(
-            AXBoundaryBehavior::kCrossBoundary);
+            AXBoundaryBehavior::CrossBoundary);
     ASSERT_NE(nullptr, moved_position);
 
     text_position = std::move(moved_position);
@@ -10019,7 +10000,7 @@
   while (!text_position->IsNullPosition()) {
     TestPositionType moved_position =
         text_position->CreatePreviousCharacterPosition(
-            AXBoundaryBehavior::kCrossBoundary);
+            AXBoundaryBehavior::CrossBoundary);
     ASSERT_NE(nullptr, moved_position);
 
     text_position = std::move(moved_position);
@@ -10034,11 +10015,11 @@
   TestPositionType null_position = AXNodePosition::CreateNullPosition();
   ASSERT_NE(nullptr, null_position);
   TestPositionType test_position = null_position->CreateNextWordStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
   test_position = null_position->CreatePreviousWordStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 }
@@ -10047,11 +10028,11 @@
   TestPositionType null_position = AXNodePosition::CreateNullPosition();
   ASSERT_NE(nullptr, null_position);
   TestPositionType test_position = null_position->CreateNextWordEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
   test_position = null_position->CreatePreviousWordEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 }
@@ -11089,7 +11070,7 @@
 
   TestPositionType next_line_start_position =
       text_position->CreateNextLineStartPosition(
-          AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+          AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, next_line_start_position);
   EXPECT_TRUE(next_line_start_position->IsTextPosition());
   EXPECT_EQ(inline_box3.id, next_line_start_position->anchor_id());
@@ -11097,7 +11078,7 @@
 
   TestPositionType previous_line_start_position =
       text_position->CreatePreviousLineStartPosition(
-          AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+          AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, previous_line_start_position);
   EXPECT_TRUE(previous_line_start_position->IsTextPosition());
   EXPECT_EQ(inline_box1.id, previous_line_start_position->anchor_id());
@@ -11105,7 +11086,7 @@
 
   TestPositionType next_line_end_position =
       text_position->CreateNextLineEndPosition(
-          AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+          AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, next_line_end_position);
   EXPECT_TRUE(next_line_end_position->IsTextPosition());
   EXPECT_EQ(inline_box3.id, next_line_end_position->anchor_id());
@@ -11113,7 +11094,7 @@
 
   TestPositionType previous_line_end_position =
       text_position->CreatePreviousLineEndPosition(
-          AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+          AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, previous_line_end_position);
   EXPECT_TRUE(previous_line_end_position->IsTextPosition());
   EXPECT_EQ(inline_box1.id, previous_line_end_position->anchor_id());
@@ -11248,7 +11229,7 @@
 
   // "1. <f>irst item\n2. second item"
   text_position = text_position->CreateNextWordStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, text_position);
   ASSERT_TRUE(text_position->IsTextPosition());
   ASSERT_EQ(inline_box2.id, text_position->anchor_id());
@@ -11256,7 +11237,7 @@
 
   // "1. first <i>tem\n2. second item"
   text_position = text_position->CreateNextWordStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, text_position);
   ASSERT_TRUE(text_position->IsTextPosition());
   ASSERT_EQ(inline_box2.id, text_position->anchor_id());
@@ -11264,7 +11245,7 @@
 
   // "1. first item\n<2>. second item"
   text_position = text_position->CreateNextWordStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, text_position);
   ASSERT_TRUE(text_position->IsTextPosition());
   ASSERT_EQ(inline_box3.id, text_position->anchor_id());
@@ -11272,7 +11253,7 @@
 
   // "1. first item\n2. <s>econd item"
   text_position = text_position->CreateNextWordStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, text_position);
   ASSERT_TRUE(text_position->IsTextPosition());
   ASSERT_EQ(inline_box4.id, text_position->anchor_id());
@@ -11280,7 +11261,7 @@
 
   // "1. first item\n2. second <i>tem"
   text_position = text_position->CreateNextWordStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, text_position);
   ASSERT_TRUE(text_position->IsTextPosition());
   ASSERT_EQ(inline_box4.id, text_position->anchor_id());
@@ -11415,7 +11396,7 @@
 
   // "1. first item\n2. second <i>tem"
   text_position = text_position->CreatePreviousWordStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, text_position);
   ASSERT_TRUE(text_position->IsTextPosition());
   ASSERT_EQ(inline_box4.id, text_position->anchor_id());
@@ -11423,7 +11404,7 @@
 
   // "1. first item\n2. <s>econd item"
   text_position = text_position->CreatePreviousWordStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, text_position);
   ASSERT_TRUE(text_position->IsTextPosition());
   ASSERT_EQ(inline_box4.id, text_position->anchor_id());
@@ -11431,7 +11412,7 @@
 
   // "1. first item\n<2>. second item"
   text_position = text_position->CreatePreviousWordStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, text_position);
   ASSERT_TRUE(text_position->IsTextPosition());
   ASSERT_EQ(inline_box3.id, text_position->anchor_id());
@@ -11439,7 +11420,7 @@
 
   // "1. first <i>tem\n2. <s>econd item"
   text_position = text_position->CreatePreviousWordStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, text_position);
   ASSERT_TRUE(text_position->IsTextPosition());
   ASSERT_EQ(inline_box2.id, text_position->anchor_id());
@@ -11447,7 +11428,7 @@
 
   // "1. <f>irst item\n2. second item"
   text_position = text_position->CreatePreviousWordStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, text_position);
   ASSERT_TRUE(text_position->IsTextPosition());
   ASSERT_EQ(inline_box2.id, text_position->anchor_id());
@@ -11455,7 +11436,7 @@
 
   // "<1>. first item\n2. second item"
   text_position = text_position->CreatePreviousWordStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, text_position);
   ASSERT_TRUE(text_position->IsTextPosition());
   ASSERT_EQ(inline_box1.id, text_position->anchor_id());
@@ -11592,7 +11573,7 @@
       ax::mojom::TextAffinity::kDownstream);
 
   TestPositionType result_position =
-      position->CreateNextWordStartPosition(AXBoundaryBehavior::kCrossBoundary);
+      position->CreateNextWordStartPosition(AXBoundaryBehavior::CrossBoundary);
   EXPECT_TRUE(result_position->IsTextPosition());
   EXPECT_EQ(text_field_4.id, result_position->anchor_id());
   EXPECT_EQ(0, result_position->text_offset());
@@ -11601,7 +11582,7 @@
 
   position = std::move(result_position);
   result_position =
-      position->CreateNextWordStartPosition(AXBoundaryBehavior::kCrossBoundary);
+      position->CreateNextWordStartPosition(AXBoundaryBehavior::CrossBoundary);
   EXPECT_TRUE(result_position->IsTextPosition());
   EXPECT_EQ(inline_box_7.id, result_position->anchor_id());
   EXPECT_EQ(1, result_position->text_offset());
@@ -11611,7 +11592,7 @@
   // CreatePreviousWordStartPosition tests.
   position = std::move(result_position);
   result_position = position->CreatePreviousWordStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_TRUE(result_position->IsTextPosition());
   EXPECT_EQ(text_field_4.id, result_position->anchor_id());
   EXPECT_EQ(0, result_position->text_offset());
@@ -11620,7 +11601,7 @@
 
   position = std::move(result_position);
   result_position = position->CreatePreviousWordStartPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_TRUE(result_position->IsTextPosition());
   EXPECT_EQ(inline_box_3.id, result_position->anchor_id());
   EXPECT_EQ(0, result_position->text_offset());
@@ -11630,7 +11611,7 @@
   // CreateNextWordEndPosition tests.
   position = std::move(result_position);
   result_position =
-      position->CreateNextWordEndPosition(AXBoundaryBehavior::kCrossBoundary);
+      position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary);
   EXPECT_TRUE(result_position->IsTextPosition());
   EXPECT_EQ(inline_box_3.id, result_position->anchor_id());
   EXPECT_EQ(6, result_position->text_offset());
@@ -11639,7 +11620,7 @@
 
   position = std::move(result_position);
   result_position =
-      position->CreateNextWordEndPosition(AXBoundaryBehavior::kCrossBoundary);
+      position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary);
   EXPECT_TRUE(result_position->IsTextPosition());
   // The position would be on `text_field_4` instead of on `generic_container_5`
   // because the latter is ignored, and by design we prefer not to create
@@ -11651,7 +11632,7 @@
 
   position = std::move(result_position);
   result_position =
-      position->CreateNextWordEndPosition(AXBoundaryBehavior::kCrossBoundary);
+      position->CreateNextWordEndPosition(AXBoundaryBehavior::CrossBoundary);
   EXPECT_TRUE(result_position->IsTextPosition());
   EXPECT_EQ(inline_box_7.id, result_position->anchor_id());
   EXPECT_EQ(6, result_position->text_offset());
@@ -11661,7 +11642,7 @@
   // CreatePreviousWordEndPosition tests.
   position = std::move(result_position);
   result_position = position->CreatePreviousWordEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_TRUE(result_position->IsTextPosition());
   // The position would be on `text_field_4` instead of on `generic_container_5`
   // because the latter is ignored, and by design we prefer not to create
@@ -11673,7 +11654,7 @@
 
   position = std::move(result_position);
   result_position = position->CreatePreviousWordEndPosition(
-      AXBoundaryBehavior::kCrossBoundary);
+      AXBoundaryBehavior::CrossBoundary);
   EXPECT_TRUE(result_position->IsTextPosition());
   EXPECT_EQ(inline_box_3.id, result_position->anchor_id());
   EXPECT_EQ(6, result_position->text_offset());
@@ -11738,7 +11719,7 @@
   ASSERT_NE(nullptr, text_position);
 
   text_position = text_position->CreatePreviousFormatStartPosition(
-      AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+      AXBoundaryBehavior::StopIfAlreadyAtBoundary);
   ASSERT_NE(nullptr, text_position);
   EXPECT_TRUE(text_position->IsTextPosition());
   EXPECT_EQ(generic_container_12.id, text_position->anchor_id());
@@ -11760,7 +11741,7 @@
   ASSERT_NE(nullptr, text_position);
 
   text_position = text_position->CreateNextParagraphEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, text_position);
   EXPECT_TRUE(text_position->IsLeafTextPosition());
   EXPECT_EQ(button_14.id, text_position->anchor_id());
@@ -11865,14 +11846,10 @@
   inline_box_8.id = 8;
 
   root_1.role = ax::mojom::Role::kRootWebArea;
-  root_1.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject,
-                          true);
   root_1.child_ids = {static_text_2.id, popup_button_4.id, static_text_7.id};
 
   static_text_2.role = ax::mojom::Role::kStaticText;
   static_text_2.SetName("Hi");
-  static_text_2.AddBoolAttribute(
-      ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
   static_text_2.child_ids = {inline_box_3.id};
 
   inline_box_3.role = ax::mojom::Role::kInlineTextBox;
@@ -11891,13 +11868,9 @@
   menu_list_option_6.role = ax::mojom::Role::kMenuListOption;
   menu_list_option_6.SetName("Option");
   menu_list_option_6.SetNameFrom(ax::mojom::NameFrom::kContents);
-  menu_list_option_6.AddBoolAttribute(
-      ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
 
   static_text_7.role = ax::mojom::Role::kStaticText;
   static_text_7.SetName("3.14");
-  static_text_7.AddBoolAttribute(
-      ax::mojom::BoolAttribute::kIsLineBreakingObject, true);
   static_text_7.child_ids = {inline_box_8.id};
 
   inline_box_8.role = ax::mojom::Role::kInlineTextBox;
@@ -11916,13 +11889,13 @@
   ASSERT_NE(nullptr, position);
 
   position = position->CreateNextParagraphStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, position);
   EXPECT_EQ(popup_button_4.id, position->anchor_id());
   EXPECT_EQ(0, position->text_offset());
 
   position = position->CreateNextParagraphStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, position);
   EXPECT_EQ(inline_box_8.id, position->anchor_id());
   EXPECT_EQ(0, position->text_offset());
@@ -11933,15 +11906,15 @@
   ASSERT_NE(nullptr, position);
 
   position = position->CreatePreviousParagraphEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, position);
   EXPECT_EQ(popup_button_4.id, position->anchor_id());
   // The content of this popup button should be replaced with the empty object
-  // replacement character of length 1.
+  // character of length 1.
   EXPECT_EQ(1, position->text_offset());
 
   position = position->CreatePreviousParagraphEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, position);
   EXPECT_EQ(inline_box_3.id, position->anchor_id());
   EXPECT_EQ(2, position->text_offset());
@@ -11959,13 +11932,13 @@
   ASSERT_NE(nullptr, position);
 
   position = position->CreateNextParagraphStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, position);
   EXPECT_EQ(menu_list_option_6.id, position->anchor_id());
   EXPECT_EQ(0, position->text_offset());
 
   position = position->CreateNextParagraphStartPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, position);
   EXPECT_EQ(inline_box_8.id, position->anchor_id());
   EXPECT_EQ(0, position->text_offset());
@@ -11976,13 +11949,13 @@
   ASSERT_NE(nullptr, position);
 
   position = position->CreatePreviousParagraphEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, position);
   EXPECT_EQ(menu_list_option_6.id, position->anchor_id());
-  EXPECT_EQ(6, position->text_offset());
+  EXPECT_EQ(1, position->text_offset());
 
   position = position->CreatePreviousParagraphEndPosition(
-      AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+      AXBoundaryBehavior::StopAtLastAnchorBoundary);
   ASSERT_NE(nullptr, position);
   EXPECT_EQ(inline_box_3.id, position->anchor_id());
   EXPECT_EQ(2, position->text_offset());
@@ -12135,32 +12108,32 @@
         ExpandToEnclosingTextBoundaryTestParam{
             ax::mojom::TextBoundary::kCharacter,
             AXRangeExpandBehavior::kLeftFirst,
+            "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
+            "annotated_text=Line 1<\n>Line 2",
+            "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
+            "annotated_text=Line 1\n<L>ine 2"},
+        ExpandToEnclosingTextBoundaryTestParam{
+            ax::mojom::TextBoundary::kCharacter,
+            AXRangeExpandBehavior::kRightFirst,
             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
             "annotated_text=Line 1\n<L>ine 2",
             "TextPosition anchor_id=4 text_offset=8 affinity=downstream "
             "annotated_text=Line 1\nL<i>ne 2"},
         ExpandToEnclosingTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kCharacter,
-            AXRangeExpandBehavior::kRightFirst,
+            ax::mojom::TextBoundary::kFormatEnd,
+            AXRangeExpandBehavior::kLeftFirst,
             "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
             "annotated_text=Line 1<\n>Line 2",
             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
             "annotated_text=Line 1\n<L>ine 2"},
         ExpandToEnclosingTextBoundaryTestParam{
             ax::mojom::TextBoundary::kFormatEnd,
-            AXRangeExpandBehavior::kLeftFirst,
-            "TextPosition anchor_id=4 text_offset=7 affinity=upstream "
+            AXRangeExpandBehavior::kRightFirst,
+            "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
             "annotated_text=Line 1\n<L>ine 2",
             "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
             "annotated_text=Line 1\nLine 2<>"},
         ExpandToEnclosingTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kFormatEnd,
-            AXRangeExpandBehavior::kRightFirst,
-            "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
-            "annotated_text=Line 1<\n>Line 2",
-            "TextPosition anchor_id=4 text_offset=7 affinity=upstream "
-            "annotated_text=Line 1\n<L>ine 2"},
-        ExpandToEnclosingTextBoundaryTestParam{
             ax::mojom::TextBoundary::kLineEnd,
             AXRangeExpandBehavior::kLeftFirst,
             "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
@@ -12177,26 +12150,26 @@
         ExpandToEnclosingTextBoundaryTestParam{
             ax::mojom::TextBoundary::kLineStart,
             AXRangeExpandBehavior::kLeftFirst,
-            "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
-            "annotated_text=Line 1\n<L>ine 2",
-            "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
-            "annotated_text=Line 1\nLine 2<>"},
-        ExpandToEnclosingTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kLineStart,
-            AXRangeExpandBehavior::kRightFirst,
             "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
             "annotated_text=<L>ine 1\nLine 2",
             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
             "annotated_text=Line 1\n<L>ine 2"},
         ExpandToEnclosingTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kLineStartOrEnd,
-            AXRangeExpandBehavior::kLeftFirst,
+            ax::mojom::TextBoundary::kLineStart,
+            AXRangeExpandBehavior::kRightFirst,
             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
             "annotated_text=Line 1\n<L>ine 2",
             "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
             "annotated_text=Line 1\nLine 2<>"},
         ExpandToEnclosingTextBoundaryTestParam{
             ax::mojom::TextBoundary::kLineStartOrEnd,
+            AXRangeExpandBehavior::kLeftFirst,
+            "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
+            "annotated_text=<L>ine 1\nLine 2",
+            "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
+            "annotated_text=Line 1<\n>Line 2"},
+        ExpandToEnclosingTextBoundaryTestParam{
+            ax::mojom::TextBoundary::kLineStartOrEnd,
             AXRangeExpandBehavior::kRightFirst,
             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
             "annotated_text=Line 1\n<L>ine 2",
@@ -12232,73 +12205,32 @@
         ExpandToEnclosingTextBoundaryTestParam{
             ax::mojom::TextBoundary::kParagraphStart,
             AXRangeExpandBehavior::kLeftFirst,
+            "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
+            "annotated_text=<L>ine 1\nLine 2",
             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
-            "annotated_text=Line 1\n<L>ine 2",
-            "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
-            "annotated_text=Line 1\nLine 2<>"},
+            "annotated_text=Line 1\n<L>ine 2"},
         ExpandToEnclosingTextBoundaryTestParam{
             ax::mojom::TextBoundary::kParagraphStart,
             AXRangeExpandBehavior::kRightFirst,
-            "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
-            "annotated_text=<L>ine 1\nLine 2",
-            "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
-            "annotated_text=Line 1\n<L>ine 2"},
-        ExpandToEnclosingTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kParagraphStartOrEnd,
-            AXRangeExpandBehavior::kLeftFirst,
             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
             "annotated_text=Line 1\n<L>ine 2",
             "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
             "annotated_text=Line 1\nLine 2<>"},
         ExpandToEnclosingTextBoundaryTestParam{
             ax::mojom::TextBoundary::kParagraphStartOrEnd,
+            AXRangeExpandBehavior::kLeftFirst,
+            "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
+            "annotated_text=<L>ine 1\nLine 2",
+            "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
+            "annotated_text=Line 1<\n>Line 2"},
+        ExpandToEnclosingTextBoundaryTestParam{
+            ax::mojom::TextBoundary::kParagraphStartOrEnd,
             AXRangeExpandBehavior::kRightFirst,
             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
             "annotated_text=Line 1\n<L>ine 2",
             "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
             "annotated_text=Line 1\nLine 2<>"},
-        ExpandToEnclosingTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kSentenceEnd,
-            AXRangeExpandBehavior::kLeftFirst,
-            "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
-            "annotated_text=Line 1<\n>Line 2",
-            "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
-            "annotated_text=Line 1\nLine 2<>"},
-        ExpandToEnclosingTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kSentenceEnd,
-            AXRangeExpandBehavior::kRightFirst,
-            "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
-            "annotated_text=Line 1<\n>Line 2",
-            "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
-            "annotated_text=Line 1\nLine 2<>"},
-        ExpandToEnclosingTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kSentenceStart,
-            AXRangeExpandBehavior::kLeftFirst,
-            "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
-            "annotated_text=Line 1\n<L>ine 2",
-            "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
-            "annotated_text=Line 1\nLine 2<>"},
-        ExpandToEnclosingTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kSentenceStart,
-            AXRangeExpandBehavior::kRightFirst,
-            "TextPosition anchor_id=4 text_offset=0 affinity=downstream "
-            "annotated_text=<L>ine 1\nLine 2",
-            "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
-            "annotated_text=Line 1\n<L>ine 2"},
-        ExpandToEnclosingTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kSentenceStartOrEnd,
-            AXRangeExpandBehavior::kLeftFirst,
-            "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
-            "annotated_text=Line 1\n<L>ine 2",
-            "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
-            "annotated_text=Line 1\nLine 2<>"},
-        ExpandToEnclosingTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kSentenceStartOrEnd,
-            AXRangeExpandBehavior::kRightFirst,
-            "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
-            "annotated_text=Line 1\n<L>ine 2",
-            "TextPosition anchor_id=4 text_offset=13 affinity=downstream "
-            "annotated_text=Line 1\nLine 2<>"},
+        // TODO(accessibility): Add tests for sentence boundary.
         ExpandToEnclosingTextBoundaryTestParam{
             ax::mojom::TextBoundary::kWebPage,
             AXRangeExpandBehavior::kLeftFirst,
@@ -12330,24 +12262,24 @@
         ExpandToEnclosingTextBoundaryTestParam{
             ax::mojom::TextBoundary::kWordStart,
             AXRangeExpandBehavior::kLeftFirst,
-            "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
-            "annotated_text=Line 1\n<L>ine 2",
-            "TextPosition anchor_id=4 text_offset=12 affinity=downstream "
-            "annotated_text=Line 1\nLine <2>"},
-        ExpandToEnclosingTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kWordStart,
-            AXRangeExpandBehavior::kRightFirst,
             "TextPosition anchor_id=4 text_offset=5 affinity=downstream "
             "annotated_text=Line <1>\nLine 2",
             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
             "annotated_text=Line 1\n<L>ine 2"},
         ExpandToEnclosingTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kWordStartOrEnd,
-            AXRangeExpandBehavior::kLeftFirst,
+            ax::mojom::TextBoundary::kWordStart,
+            AXRangeExpandBehavior::kRightFirst,
             "TextPosition anchor_id=4 text_offset=7 affinity=downstream "
             "annotated_text=Line 1\n<L>ine 2",
-            "TextPosition anchor_id=4 text_offset=11 affinity=downstream "
-            "annotated_text=Line 1\nLine< >2"},
+            "TextPosition anchor_id=4 text_offset=12 affinity=downstream "
+            "annotated_text=Line 1\nLine <2>"},
+        ExpandToEnclosingTextBoundaryTestParam{
+            ax::mojom::TextBoundary::kWordStartOrEnd,
+            AXRangeExpandBehavior::kLeftFirst,
+            "TextPosition anchor_id=4 text_offset=5 affinity=downstream "
+            "annotated_text=Line <1>\nLine 2",
+            "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
+            "annotated_text=Line 1<\n>Line 2"},
         ExpandToEnclosingTextBoundaryTestParam{
             ax::mojom::TextBoundary::kWordStartOrEnd,
             AXRangeExpandBehavior::kRightFirst,
@@ -12356,7 +12288,7 @@
             "TextPosition anchor_id=4 text_offset=11 affinity=downstream "
             "annotated_text=Line 1\nLine< >2"}));
 
-// Only test with AXBoundaryBehavior::kCrossBoundary for now.
+// Only test with AXBoundaryBehavior::CrossBoundary for now.
 // TODO(accessibility): Add more tests for other boundary behaviors if needed.
 INSTANTIATE_TEST_SUITE_P(
     CreatePositionAtTextBoundary,
@@ -12365,1028 +12297,165 @@
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kCharacter,
             ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
             "annotated_text=<\n>"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kCharacter,
             ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=8 text_offset=1 affinity=downstream "
             "annotated_text=L<i>ne 2"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kFormatStart,
             ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
             "annotated_text=<\n>"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kFormatEnd,
             ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
             "annotated_text=Line 2<>"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kLineEnd,
             ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
             "annotated_text=<\n>"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kLineEnd,
             ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
             "annotated_text=Line 2<>"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kLineStart,
             ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
             "annotated_text=<L>ine 1"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kLineStart,
             ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary, "NullPosition"},
+            AXBoundaryBehavior::CrossBoundary, "NullPosition"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kLineStartOrEnd,
             ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
             "annotated_text=<L>ine 1"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kLineStartOrEnd,
             ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
             "annotated_text=Line 2<>"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kObject,
             ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=8 text_offset=0 affinity=downstream "
             "annotated_text=<L>ine 2"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kObject,
             ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
             "annotated_text=Line 2<>"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kParagraphEnd,
             ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=6 text_offset=6 affinity=downstream "
             "annotated_text=Line 1<>"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kParagraphEnd,
             ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
             "annotated_text=Line 2<>"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kParagraphStart,
             ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
             "annotated_text=<L>ine 1"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kParagraphStart,
             ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary, "NullPosition"},
+            AXBoundaryBehavior::CrossBoundary, "NullPosition"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kParagraphStartOrEnd,
             ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
             "annotated_text=<L>ine 1"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kParagraphStartOrEnd,
             ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
             "annotated_text=Line 2<>"},
-        CreatePositionAtTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kSentenceEnd,
-            ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
-            "TextPosition anchor_id=6 text_offset=6 affinity=downstream "
-            "annotated_text=Line 1<>"},
-        CreatePositionAtTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kSentenceEnd,
-            ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary,
-            "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
-            "annotated_text=Line 2<>"},
-        CreatePositionAtTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kSentenceStart,
-            ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
-            "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
-            "annotated_text=<L>ine 1"},
-        CreatePositionAtTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kSentenceStart,
-            ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary, "NullPosition"},
-        CreatePositionAtTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kSentenceStartOrEnd,
-            ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
-            "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
-            "annotated_text=<L>ine 1"},
-        CreatePositionAtTextBoundaryTestParam{
-            ax::mojom::TextBoundary::kSentenceStartOrEnd,
-            ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary,
-            "TextPosition anchor_id=8 text_offset=6 affinity=downstream "
-            "annotated_text=Line 2<>"},
+        // TODO(accessibility): Add tests for sentence boundary.
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kWebPage,
             ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=1 text_offset=0 affinity=downstream "
             "annotated_text=<L>ine 1\nLine 2"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kWebPage,
             ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=9 text_offset=6 affinity=downstream "
             "annotated_text=Line 2<>"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kWordEnd,
             ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=6 text_offset=6 affinity=downstream "
             "annotated_text=Line 1<>"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kWordEnd,
             ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=8 text_offset=4 affinity=downstream "
             "annotated_text=Line< >2"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kWordStart,
             ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=6 text_offset=5 affinity=downstream "
             "annotated_text=Line <1>"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kWordStart,
             ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=8 text_offset=5 affinity=downstream "
             "annotated_text=Line <2>"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kWordStartOrEnd,
             ax::mojom::MoveDirection::kBackward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=6 text_offset=5 affinity=downstream "
             "annotated_text=Line <1>"},
         CreatePositionAtTextBoundaryTestParam{
             ax::mojom::TextBoundary::kWordStartOrEnd,
             ax::mojom::MoveDirection::kForward,
-            AXBoundaryBehavior::kCrossBoundary,
+            AXBoundaryBehavior::CrossBoundary,
             "TextPosition anchor_id=8 text_offset=4 affinity=downstream "
             "annotated_text=Line< >2"}));
 
 INSTANTIATE_TEST_SUITE_P(
-    CreateNextSentenceStartPositionWithBoundaryBehaviorCrossBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            ROOT_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=1 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2",
-             "NullPosition"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            TEXT_FIELD_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=4 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2",
-             "NullPosition"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            1 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 2",
-             "NullPosition"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"NullPosition"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    CreateNextSentenceStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            ROOT_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=1 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2",
-             "TextPosition anchor_id=1 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>",
-             "TextPosition anchor_id=1 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            TEXT_FIELD_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=4 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2",
-             "TextPosition anchor_id=4 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>",
-             "TextPosition anchor_id=4 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            1 /* text_offset */,
-            {"TextPosition anchor_id=5 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<>",
-             "TextPosition anchor_id=5 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>",
-             "TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    CreateNextSentenceStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            ROOT_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=1 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
-             "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            TEXT_FIELD_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=4 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
-             "TextPosition anchor_id=4 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            1 /* text_offset */,
-            {"TextPosition anchor_id=5 text_offset=6 affinity=downstream "
-             "annotated_text=Line 1<>",
-             "TextPosition anchor_id=5 text_offset=6 affinity=downstream "
-             "annotated_text=Line 1<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>",
-             "TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    CreateNextSentenceStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            ROOT_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=1 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2",
-             "TextPosition anchor_id=1 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>",
-             "TextPosition anchor_id=1 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            TEXT_FIELD_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=4 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2",
-             "TextPosition anchor_id=4 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>",
-             "TextPosition anchor_id=4 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            1 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 2",
-             "TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>",
-             "TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>",
-             "TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    CreatePreviousSentenceStartPositionWithBoundaryBehaviorCrossBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            ROOT_ID,
-            13 /* text_offset at end of root. */,
-            {"TextPosition anchor_id=1 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2",
-             "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
-             "NullPosition"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            TEXT_FIELD_ID,
-            13 /* text_offset at end of text field */,
-            {"TextPosition anchor_id=4 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2",
-             "TextPosition anchor_id=4 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
-             "NullPosition"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            5 /* text_offset */,
-            {"TextPosition anchor_id=5 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1",
-             "NullPosition"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 2",
-             "TextPosition anchor_id=6 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1",
-             "NullPosition"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    CreatePreviousSentenceStartPositionWithBoundaryBehaviorStopAtAnchorBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            ROOT_ID,
-            13 /* text_offset at end of root. */,
-            {"TextPosition anchor_id=1 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2",
-             "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
-             "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            TEXT_FIELD_ID,
-            13 /* text_offset at end of text field */,
-            {"TextPosition anchor_id=4 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2",
-             "TextPosition anchor_id=4 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
-             "TextPosition anchor_id=4 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            5 /* text_offset */,
-            {"TextPosition anchor_id=5 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1",
-             "TextPosition anchor_id=5 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 2",
-             "TextPosition anchor_id=9 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 2"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    CreatePreviousSentenceStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            ROOT_ID,
-            13 /* text_offset at end of root. */,
-            {"TextPosition anchor_id=1 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2",
-             "TextPosition anchor_id=1 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            TEXT_FIELD_ID,
-            13 /* text_offset at end of text field */,
-            {"TextPosition anchor_id=4 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2",
-             "TextPosition anchor_id=4 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            5 /* text_offset */,
-            {"TextPosition anchor_id=5 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1",
-             "TextPosition anchor_id=5 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 2",
-             "TextPosition anchor_id=9 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 2"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    CreatePreviousSentenceStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            ROOT_ID,
-            13 /* text_offset */,
-            {"TextPosition anchor_id=1 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2",
-             "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
-             "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            TEXT_FIELD_ID,
-            13 /* text_offset */,
-            {"TextPosition anchor_id=4 text_offset=7 "
-             "affinity=downstream annotated_text=Line 1\n<L>ine 2",
-             "TextPosition anchor_id=4 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
-             "TextPosition anchor_id=4 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            5 /* text_offset */,
-            {"TextPosition anchor_id=5 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1",
-             "TextPosition anchor_id=5 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 2",
-             "TextPosition anchor_id=6 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1",
-             "TextPosition anchor_id=6 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    CreateNextSentenceEndPositionWithBoundaryBehaviorCrossBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            ROOT_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=1 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "TextPosition anchor_id=1 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>",
-             "NullPosition"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            TEXT_FIELD_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=4 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "TextPosition anchor_id=4 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>",
-             "NullPosition"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            1 /* text_offset */,
-            {"TextPosition anchor_id=5 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<>",
-             "TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>",
-             "NullPosition"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>",
-             "NullPosition"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    CreateNextSentenceEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            ROOT_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=1 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "TextPosition anchor_id=1 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>",
-             "TextPosition anchor_id=1 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            TEXT_FIELD_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=4 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "TextPosition anchor_id=4 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>",
-             "TextPosition anchor_id=4 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            1 /* text_offset */,
-            {"TextPosition anchor_id=5 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<>",
-             "TextPosition anchor_id=5 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>",
-             "TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    CreateNextSentenceEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            ROOT_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=1 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "TextPosition anchor_id=1 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            TEXT_FIELD_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=4 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "TextPosition anchor_id=4 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            1 /* text_offset */,
-            {"TextPosition anchor_id=5 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<>",
-             "TextPosition anchor_id=5 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>",
-             "TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    CreateNextSentenceEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            ROOT_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=1 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "TextPosition anchor_id=1 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>",
-             "TextPosition anchor_id=1 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            TEXT_FIELD_ID,
-            0 /* text_offset */,
-            {"TextPosition anchor_id=4 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "TextPosition anchor_id=4 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>",
-             "TextPosition anchor_id=4 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            1 /* text_offset */,
-            {"TextPosition anchor_id=5 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<>",
-             "TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>",
-             "TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreateNextSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>",
-             "TextPosition anchor_id=9 text_offset=6 "
-             "affinity=downstream annotated_text=Line 2<>"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    CreatePreviousSentenceEndPositionWithBoundaryBehaviorCrossBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            ROOT_ID,
-            13 /* text_offset at end of root. */,
-            {"TextPosition anchor_id=1 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "NullPosition"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            TEXT_FIELD_ID,
-            13 /* text_offset at end of text field */,
-            {"TextPosition anchor_id=4 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "NullPosition"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            5 /* text_offset */,
-            {"NullPosition"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"TextPosition anchor_id=6 text_offset=6 affinity=downstream "
-             "annotated_text=Line 1<>",
-             "NullPosition"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    CreatePreviousSentenceEndPositionWithBoundaryBehaviorStopAtAnchorBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            ROOT_ID,
-            13 /* text_offset at end of root. */,
-            {"TextPosition anchor_id=1 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
-             "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            TEXT_FIELD_ID,
-            13 /* text_offset at end of text field */,
-            {"TextPosition anchor_id=4 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "TextPosition anchor_id=4 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
-             "TextPosition anchor_id=4 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            5 /* text_offset */,
-            {"TextPosition anchor_id=5 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1",
-             "TextPosition anchor_id=5 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 2",
-             "TextPosition anchor_id=9 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 2"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    CreatePreviousSentenceEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            ROOT_ID,
-            13 /* text_offset at end of root. */,
-            {"TextPosition anchor_id=1 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>",
-             "TextPosition anchor_id=1 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            TEXT_FIELD_ID,
-            13 /* text_offset at end of text field */,
-            {"TextPosition anchor_id=4 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>",
-             "TextPosition anchor_id=4 text_offset=13 "
-             "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            5 /* text_offset */,
-            {"TextPosition anchor_id=5 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1",
-             "TextPosition anchor_id=5 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 2",
-             "TextPosition anchor_id=9 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 2"}}));
-
-INSTANTIATE_TEST_SUITE_P(
-    CreatePreviousSentenceEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
-    AXPositionTextNavigationTestWithParam,
-    ::testing::Values(
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            ROOT_ID,
-            13 /* text_offset at end of root. */,
-            {"TextPosition anchor_id=1 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
-             "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            TEXT_FIELD_ID,
-            13 /* text_offset at end of text field */,
-            {"TextPosition anchor_id=4 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "TextPosition anchor_id=2 text_offset=0 "
-             "affinity=downstream annotated_text=<>",
-             "TextPosition anchor_id=2 text_offset=0 "
-             "affinity=downstream annotated_text=<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            STATIC_TEXT1_ID,
-            5 /* text_offset */,
-            {"TextPosition anchor_id=2 text_offset=0 "
-             "affinity=downstream annotated_text=<>",
-             "TextPosition anchor_id=2 text_offset=0 "
-             "affinity=downstream annotated_text=<>"}},
-        TextNavigationTestParam{
-            base::BindRepeating([](const TestPositionType& position) {
-              return position->CreatePreviousSentenceEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
-            }),
-            INLINE_BOX2_ID,
-            4 /* text_offset */,
-            {"TextPosition anchor_id=6 text_offset=6 affinity=downstream "
-             "annotated_text=Line 1<>",
-             "TextPosition anchor_id=2 text_offset=0 "
-             "affinity=downstream annotated_text=<>"}}));
-
-INSTANTIATE_TEST_SUITE_P(
     CreateNextWordStartPositionWithBoundaryBehaviorCrossBoundary,
     AXPositionTextNavigationTestWithParam,
     ::testing::Values(
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -13400,7 +12469,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -13414,7 +12483,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -13428,7 +12497,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -13443,7 +12512,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -13458,7 +12527,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -13473,7 +12542,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -13484,7 +12553,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -13494,14 +12563,13 @@
              "affinity=downstream annotated_text=Line 2<>"}}));
 
 INSTANTIATE_TEST_SUITE_P(
-    CreateNextWordStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
+    CreateNextWordStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
     AXPositionTextNavigationTestWithParam,
     ::testing::Values(
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -13512,8 +12580,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -13524,8 +12591,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -13536,8 +12602,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -13553,7 +12618,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -13570,7 +12635,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -13587,7 +12652,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -13604,7 +12669,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -13622,7 +12687,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at end of root. */,
@@ -13638,7 +12703,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -13654,7 +12719,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -13664,7 +12729,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -13683,7 +12748,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at end of root. */,
@@ -13700,7 +12765,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -13717,7 +12782,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -13728,7 +12793,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -13738,14 +12803,13 @@
              "affinity=downstream annotated_text=<L>ine 2"}}));
 
 INSTANTIATE_TEST_SUITE_P(
-    CreatePreviousWordStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
+    CreatePreviousWordStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
     AXPositionTextNavigationTestWithParam,
     ::testing::Values(
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at end of root. */,
@@ -13756,8 +12820,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -13768,8 +12831,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -13778,8 +12840,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -13795,7 +12856,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             ROOT_ID,
             13 /* text_offset */,
@@ -13812,7 +12873,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset */,
@@ -13829,7 +12890,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -13840,7 +12901,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -13860,7 +12921,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -13876,7 +12937,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -13892,7 +12953,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -13908,7 +12969,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -13923,7 +12984,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -13940,7 +13001,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -13957,7 +13018,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -13970,7 +13031,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -13980,14 +13041,13 @@
              "affinity=downstream annotated_text=Line 2<>"}}));
 
 INSTANTIATE_TEST_SUITE_P(
-    CreateNextWordEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
+    CreateNextWordEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
     AXPositionTextNavigationTestWithParam,
     ::testing::Values(
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -13998,8 +13058,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -14010,8 +13069,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -14022,8 +13080,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14037,7 +13094,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -14054,7 +13111,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -14071,7 +13128,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -14088,7 +13145,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextWordEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14104,7 +13161,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at end of root. */,
@@ -14118,7 +13175,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -14132,7 +13189,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -14142,12 +13199,12 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
-            {"TextPosition anchor_id=6 text_offset=6 affinity=downstream "
-             "annotated_text=Line 1<>",
+            {"TextPosition anchor_id=6 text_offset=6 "
+             "affinity=downstream annotated_text=Line 1<>",
              "TextPosition anchor_id=6 text_offset=4 "
              "affinity=downstream annotated_text=Line< >1",
              "NullPosition"}}));
@@ -14159,24 +13216,24 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at end of root. */,
-            {"TextPosition anchor_id=1 text_offset=11 "
-             "affinity=downstream annotated_text=Line 1\nLine< >2",
-             "TextPosition anchor_id=1 text_offset=6 "
-             "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "TextPosition anchor_id=1 text_offset=4 "
-             "affinity=downstream annotated_text=Line< >1\nLine 2",
-             "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2",
-             "TextPosition anchor_id=1 text_offset=0 "
-             "affinity=downstream annotated_text=<L>ine 1\nLine 2"}},
+            {
+                "TextPosition anchor_id=1 text_offset=11 "
+                "affinity=downstream annotated_text=Line 1\nLine< >2",
+                "TextPosition anchor_id=1 text_offset=6 "
+                "affinity=downstream annotated_text=Line 1<\n>Line 2",
+                "TextPosition anchor_id=1 text_offset=4 "
+                "affinity=downstream annotated_text=Line< >1\nLine 2",
+                "TextPosition anchor_id=1 text_offset=0 "
+                "affinity=downstream annotated_text=<L>ine 1\nLine 2",
+            }},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -14191,7 +13248,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -14202,7 +13259,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14210,14 +13267,13 @@
              "affinity=downstream annotated_text=<L>ine 2"}}));
 
 INSTANTIATE_TEST_SUITE_P(
-    CreatePreviousWordEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
+    CreatePreviousWordEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
     AXPositionTextNavigationTestWithParam,
     ::testing::Values(
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at end of root. */,
@@ -14226,8 +13282,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -14236,8 +13291,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -14248,8 +13302,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14263,7 +13316,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at end of root. */,
@@ -14280,7 +13333,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -14297,7 +13350,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -14310,12 +13363,12 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousWordEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
-            {"TextPosition anchor_id=6 text_offset=6 affinity=downstream "
-             "annotated_text=Line 1<>",
+            {"TextPosition anchor_id=6 text_offset=6 "
+             "affinity=downstream annotated_text=Line 1<>",
              "TextPosition anchor_id=6 text_offset=4 "
              "affinity=downstream annotated_text=Line< >1",
              "TextPosition anchor_id=2 text_offset=0 "
@@ -14330,7 +13383,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -14340,7 +13393,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -14350,7 +13403,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -14360,7 +13413,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14373,7 +13426,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -14384,7 +13437,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -14395,7 +13448,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -14404,7 +13457,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14412,14 +13465,13 @@
              "affinity=downstream annotated_text=Line 2<>"}}));
 
 INSTANTIATE_TEST_SUITE_P(
-    CreateNextLineStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
+    CreateNextLineStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
     AXPositionTextNavigationTestWithParam,
     ::testing::Values(
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -14430,8 +13482,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -14442,25 +13493,22 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
-            {"TextPosition anchor_id=5 text_offset=6 affinity=downstream "
-             "annotated_text=Line 1<>",
-             "TextPosition anchor_id=5 text_offset=6 affinity=downstream "
-             "annotated_text=Line 1<>"}},
+            {"TextPosition anchor_id=9 text_offset=0 "
+             "affinity=downstream annotated_text=<L>ine 2",
+             "TextPosition anchor_id=9 text_offset=0 "
+             "affinity=downstream annotated_text=<L>ine 2"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=6 affinity=downstream "
-             "annotated_text=Line 2<>"}}));
+            {"NullPosition"}}));
 
 INSTANTIATE_TEST_SUITE_P(
     CreateNextLineStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
@@ -14469,7 +13517,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -14482,7 +13530,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -14495,7 +13543,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -14508,7 +13556,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14524,7 +13572,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at the end of root. */,
@@ -14536,7 +13584,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -14548,7 +13596,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -14558,7 +13606,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14575,7 +13623,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at the end of root. */,
@@ -14588,7 +13636,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -14601,7 +13649,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -14612,7 +13660,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14622,14 +13670,13 @@
              "affinity=downstream annotated_text=<L>ine 2"}}));
 
 INSTANTIATE_TEST_SUITE_P(
-    CreatePreviousLineStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
+    CreatePreviousLineStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
     AXPositionTextNavigationTestWithParam,
     ::testing::Values(
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at the end of root. */,
@@ -14640,8 +13687,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -14652,8 +13698,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -14664,8 +13709,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14681,7 +13725,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at the end of root. */,
@@ -14694,7 +13738,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -14707,7 +13751,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -14718,7 +13762,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14736,7 +13780,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -14748,7 +13792,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -14760,7 +13804,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -14772,7 +13816,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14787,7 +13831,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -14800,7 +13844,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -14813,7 +13857,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -14824,7 +13868,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14834,14 +13878,13 @@
              "affinity=downstream annotated_text=Line 2<>"}}));
 
 INSTANTIATE_TEST_SUITE_P(
-    CreateNextLineEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
+    CreateNextLineEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
     AXPositionTextNavigationTestWithParam,
     ::testing::Values(
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -14852,8 +13895,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -14864,8 +13906,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -14876,8 +13917,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14893,7 +13933,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -14906,7 +13946,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -14919,7 +13959,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -14932,7 +13972,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextLineEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14948,7 +13988,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at end of root. */,
@@ -14958,7 +13998,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -14968,7 +14008,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             ROOT_ID,
             5 /* text_offset on the last character of "Line 1". */,
@@ -14976,7 +14016,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             TEXT_FIELD_ID,
             5 /* text_offset on the last character of "Line 1". */,
@@ -14984,7 +14024,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -14994,7 +14034,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             INLINE_BOX2_ID,
             0 /* text_offset */,
@@ -15009,7 +14049,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at end of root. */,
@@ -15020,7 +14060,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -15031,7 +14071,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             ROOT_ID,
             5 /* text_offset on the last character of "Line 1". */,
@@ -15042,7 +14082,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             5 /* text_offset on the last character of "Line 1". */,
@@ -15053,7 +14093,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -15064,7 +14104,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             0 /* text_offset */,
@@ -15074,14 +14114,13 @@
              "affinity=downstream annotated_text=<L>ine 2"}}));
 
 INSTANTIATE_TEST_SUITE_P(
-    CreatePreviousLineEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
+    CreatePreviousLineEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
     AXPositionTextNavigationTestWithParam,
     ::testing::Values(
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             ROOT_ID,
             12 /* text_offset one before the end of root. */,
@@ -15092,8 +14131,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             TEXT_FIELD_ID,
             12 /* text_offset one before the end of text field */,
@@ -15104,39 +14142,33 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX1_ID,
             2 /* text_offset */,
-            {"TextPosition anchor_id=6 text_offset=0 affinity=downstream "
-             "annotated_text=<L>ine 1",
-             "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
-             "annotated_text=<L>ine 1"}},
+            {"NullPosition"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=0 affinity=downstream "
-             "annotated_text=<L>ine 2",
-             "TextPosition anchor_id=9 text_offset=0 affinity=downstream "
-             "annotated_text=<L>ine 2"}},
+            {"TextPosition anchor_id=6 text_offset=6 "
+             "affinity=downstream annotated_text=Line 1<>",
+             "TextPosition anchor_id=6 text_offset=6 "
+             "affinity=downstream annotated_text=Line 1<>"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX2_ID,
             0 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=0 affinity=downstream "
-             "annotated_text=<L>ine 2",
-             "TextPosition anchor_id=9 text_offset=0 affinity=downstream "
-             "annotated_text=<L>ine 2"}}));
+            {"TextPosition anchor_id=6 text_offset=6 "
+             "affinity=downstream annotated_text=Line 1<>",
+             "TextPosition anchor_id=6 text_offset=6 "
+             "affinity=downstream annotated_text=Line 1<>"}}));
 
 INSTANTIATE_TEST_SUITE_P(
     CreatePreviousLineEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
@@ -15145,7 +14177,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at end of root. */,
@@ -15158,7 +14190,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -15171,7 +14203,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             ROOT_ID,
             5 /* text_offset on the last character of "Line 1". */,
@@ -15182,7 +14214,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             5 /* text_offset on the last character of "Line 1". */,
@@ -15193,7 +14225,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -15206,7 +14238,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousLineEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             0 /* text_offset */,
@@ -15224,7 +14256,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -15233,7 +14265,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -15242,7 +14274,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -15251,7 +14283,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -15264,7 +14296,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -15275,7 +14307,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -15286,7 +14318,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -15295,7 +14327,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -15303,14 +14335,13 @@
              "affinity=downstream annotated_text=Line 2<>"}}));
 
 INSTANTIATE_TEST_SUITE_P(
-    CreateNextParagraphStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
+    CreateNextParagraphStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
     AXPositionTextNavigationTestWithParam,
     ::testing::Values(
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -15321,8 +14352,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -15333,25 +14363,22 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
-            {"TextPosition anchor_id=5 text_offset=6 affinity=downstream "
-             "annotated_text=Line 1<>",
-             "TextPosition anchor_id=5 text_offset=6 affinity=downstream "
-             "annotated_text=Line 1<>"}},
+            {"TextPosition anchor_id=9 text_offset=0 "
+             "affinity=downstream annotated_text=<L>ine 2",
+             "TextPosition anchor_id=9 text_offset=0 "
+             "affinity=downstream annotated_text=<L>ine 2"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=6 affinity=downstream "
-             "annotated_text=Line 2<>"}}));
+            {"NullPosition"}}));
 
 INSTANTIATE_TEST_SUITE_P(
     CreateNextParagraphStartPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
@@ -15360,7 +14387,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -15373,7 +14400,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -15386,7 +14413,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -15399,7 +14426,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -15415,7 +14442,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at the end of root. */,
@@ -15427,7 +14454,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -15439,7 +14466,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -15449,7 +14476,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -15466,7 +14493,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at the end of root. */,
@@ -15479,7 +14506,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -15492,7 +14519,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -15503,7 +14530,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -15513,14 +14540,13 @@
              "affinity=downstream annotated_text=<L>ine 2"}}));
 
 INSTANTIATE_TEST_SUITE_P(
-    CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
+    CreatePreviousParagraphStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
     AXPositionTextNavigationTestWithParam,
     ::testing::Values(
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at the end of root. */,
@@ -15531,8 +14557,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -15543,8 +14568,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -15555,8 +14579,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -15572,7 +14595,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at the end of root. */,
@@ -15587,7 +14610,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -15600,7 +14623,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             5 /* text_offset */,
@@ -15611,7 +14634,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphStartPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -15629,7 +14652,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -15641,7 +14664,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -15653,7 +14676,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -15665,7 +14688,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -15680,7 +14703,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -15695,7 +14718,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -15710,7 +14733,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -15721,7 +14744,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -15731,14 +14754,13 @@
              "affinity=downstream annotated_text=Line 2<>"}}));
 
 INSTANTIATE_TEST_SUITE_P(
-    CreateNextParagraphEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
+    CreateNextParagraphEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
     AXPositionTextNavigationTestWithParam,
     ::testing::Values(
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -15754,20 +14776,18 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             TEXT_FIELD_ID,
             5 /* text_offset */,
             {"TextPosition anchor_id=4 text_offset=6 "
              "affinity=downstream annotated_text=Line 1<\n>Line 2",
-             "TextPosition anchor_id=4 text_offset=6 affinity=downstream "
-             "annotated_text=Line 1<\n>Line 2"}},
+             "TextPosition anchor_id=4 text_offset=13 "
+             "affinity=downstream annotated_text=Line 1\nLine 2<>"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -15778,8 +14798,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -15790,27 +14809,25 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             LINE_BREAK_ID,
             0 /* text_offset */,
-            {"TextPosition anchor_id=7 text_offset=1 affinity=downstream "
-             "annotated_text=\n<>",
-             "TextPosition anchor_id=7 text_offset=1 affinity=downstream "
-             "annotated_text=\n<>"}},
+            {"TextPosition anchor_id=9 text_offset=6 "
+             "affinity=downstream annotated_text=Line 2<>",
+             "TextPosition anchor_id=9 text_offset=6 "
+             "affinity=downstream annotated_text=Line 2<>"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             LINE_BREAK_ID,
             1 /* text_offset */,
-            {"TextPosition anchor_id=7 text_offset=1 affinity=downstream "
-             "annotated_text=\n<>",
-             "TextPosition anchor_id=7 text_offset=1 affinity=downstream "
-             "annotated_text=\n<>"}}));
+            {"TextPosition anchor_id=9 text_offset=6 "
+             "affinity=downstream annotated_text=Line 2<>",
+             "TextPosition anchor_id=9 text_offset=6 "
+             "affinity=downstream annotated_text=Line 2<>"}}));
 
 INSTANTIATE_TEST_SUITE_P(
     CreateNextParagraphEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
@@ -15819,7 +14836,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             ROOT_ID,
             0 /* text_offset */,
@@ -15832,7 +14849,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             0 /* text_offset */,
@@ -15845,7 +14862,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             STATIC_TEXT1_ID,
             1 /* text_offset */,
@@ -15858,7 +14875,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreateNextParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -15874,7 +14891,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at end of root. */,
@@ -15886,7 +14903,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -15898,7 +14915,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             ROOT_ID,
             5 /* text_offset on the last character of "Line 1". */,
@@ -15908,7 +14925,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             TEXT_FIELD_ID,
             5 /* text_offset on the last character of "Line 1". */,
@@ -15918,7 +14935,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -15930,7 +14947,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kCrossBoundary);
+                  AXBoundaryBehavior::CrossBoundary);
             }),
             INLINE_BOX2_ID,
             0 /* text_offset */,
@@ -15947,7 +14964,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at end of root. */,
@@ -15960,7 +14977,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -15973,7 +14990,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             ROOT_ID,
             5 /* text_offset on the last character of "Line 1". */,
@@ -15984,7 +15001,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             5 /* text_offset on the last character of "Line 1". */,
@@ -15995,7 +15012,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -16006,7 +15023,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtAnchorBoundary);
+                  AXBoundaryBehavior::StopAtAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             0 /* text_offset */,
@@ -16016,14 +15033,13 @@
              "annotated_text=<L>ine 2"}}));
 
 INSTANTIATE_TEST_SUITE_P(
-    CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
+    CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary,
     AXPositionTextNavigationTestWithParam,
     ::testing::Values(
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             ROOT_ID,
             12 /* text_offset one before the end of root. */,
@@ -16034,8 +15050,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             TEXT_FIELD_ID,
             12 /* text_offset one before the end of text field */,
@@ -16046,63 +15061,58 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX1_ID,
             2 /* text_offset */,
-            {"TextPosition anchor_id=6 text_offset=0 affinity=downstream "
-             "annotated_text=<L>ine 1",
-             "TextPosition anchor_id=6 text_offset=0 affinity=downstream "
-             "annotated_text=<L>ine 1"}},
+            {"TextPosition anchor_id=3 text_offset=0 "
+             "affinity=downstream annotated_text=<>",
+             "TextPosition anchor_id=3 text_offset=0 "
+             "affinity=downstream annotated_text=<>"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=0 affinity=downstream "
-             "annotated_text=<L>ine 2",
-             "TextPosition anchor_id=9 text_offset=0 affinity=downstream "
-             "annotated_text=<L>ine 2"}},
+            {"TextPosition anchor_id=6 text_offset=6 "
+             "affinity=downstream annotated_text=Line 1<>",
+             "TextPosition anchor_id=6 text_offset=6 "
+             "affinity=downstream annotated_text=Line 1<>"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             INLINE_BOX2_ID,
             0 /* text_offset */,
-            {"TextPosition anchor_id=9 text_offset=0 affinity=downstream "
-             "annotated_text=<L>ine 2",
-             "TextPosition anchor_id=9 text_offset=0 affinity=downstream "
-             "annotated_text=<L>ine 2"}},
+            {"TextPosition anchor_id=6 text_offset=6 "
+             "affinity=downstream annotated_text=Line 1<>",
+             "TextPosition anchor_id=6 text_offset=6 "
+             "affinity=downstream annotated_text=Line 1<>"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             LINE_BREAK_ID,
             0 /* text_offset */,
-            {"TextPosition anchor_id=7 text_offset=0 affinity=downstream "
-             "annotated_text=<\n>",
-             "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
-             "annotated_text=<\n>"}},
+            {"TextPosition anchor_id=6 text_offset=6 "
+             "affinity=downstream annotated_text=Line 1<>",
+             "TextPosition anchor_id=6 text_offset=6 "
+             "affinity=downstream annotated_text=Line 1<>"}},
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary);
+                  AXBoundaryBehavior::StopIfAlreadyAtBoundary);
             }),
             LINE_BREAK_ID,
             1 /* text_offset */,
-            {"TextPosition anchor_id=7 text_offset=0 affinity=downstream "
-             "annotated_text=<\n>",
-             "TextPosition anchor_id=7 text_offset=0 affinity=downstream "
-             "annotated_text=<\n>"}}));
+            {"TextPosition anchor_id=6 text_offset=6 "
+             "affinity=downstream annotated_text=Line 1<>",
+             "TextPosition anchor_id=6 text_offset=6 "
+             "affinity=downstream annotated_text=Line 1<>"}}));
 
 INSTANTIATE_TEST_SUITE_P(
     CreatePreviousParagraphEndPositionWithBoundaryBehaviorStopAtLastAnchorBoundary,
@@ -16111,7 +15121,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             ROOT_ID,
             13 /* text_offset at end of root. */,
@@ -16124,7 +15134,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             13 /* text_offset at end of text field */,
@@ -16137,7 +15147,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             ROOT_ID,
             5 /* text_offset on the last character of "Line 1". */,
@@ -16148,7 +15158,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             TEXT_FIELD_ID,
             5 /* text_offset on the last character of "Line 1". */,
@@ -16159,7 +15169,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             4 /* text_offset */,
@@ -16172,7 +15182,7 @@
         TextNavigationTestParam{
             base::BindRepeating([](const TestPositionType& position) {
               return position->CreatePreviousParagraphEndPosition(
-                  AXBoundaryBehavior::kStopAtLastAnchorBoundary);
+                  AXBoundaryBehavior::StopAtLastAnchorBoundary);
             }),
             INLINE_BOX2_ID,
             0 /* text_offset */,
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
index 38753d9..4e1ca6b 100644
--- a/ui/accessibility/ax_position.h
+++ b/ui/accessibility/ax_position.h
@@ -47,30 +47,13 @@
 enum class AXPositionKind { NULL_POSITION, TREE_POSITION, TEXT_POSITION };
 
 // Defines how creating the next or previous position should behave whenever we
-// are at or are crossing a text boundary, (such as the start of a word or the
-// end of a sentence), or whenever we are crossing the initial position's
-// anchor. Note that the "anchor" is the node to which an AXPosition is attached
-// to. It is provided when a position is created.
+// are at or are crossing a boundary, such as at the start of an anchor, a word
+// or a line.
 enum class AXBoundaryBehavior {
-  // Crosses all boundaries. If the bounds of the current window-like container,
-  // such as the current webpage, have been reached, returns a null position.
-  kCrossBoundary,
-  // Stops if the current anchor is crossed, regardless of how the resulting
-  // position has been computed. For example, even though in order to find the
-  // next or previous word start in a text field we need to descend to the leaf
-  // equivalent position, this behavior will only stop when the bounds of the
-  // original anchor, i.e. the text field, have been crossed.
-  kStopAtAnchorBoundary,
-  // Stops if the current anchor is crossed or if we are already at the
-  // requested boundary. For an example of the former, imagine a position inside
-  // a text field and the resulting position outside it. For an example of the
-  // latter, say we are moving to the previous word start position when we are
-  // already at the start of a word.
-  kStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
-  // Stops if we have reached the start or the end of of a window-like
-  // container, such as a webpage, a PDF, a dialog, the browser's UI (AKA
-  // Views), or the whole desktop.
-  kStopAtLastAnchorBoundary
+  CrossBoundary,
+  StopAtAnchorBoundary,
+  StopIfAlreadyAtBoundary,
+  StopAtLastAnchorBoundary
 };
 
 // Describes in further detail what type of boundary a current position is on.
@@ -215,8 +198,7 @@
       base::RepeatingCallback<bool(const AXPositionInstance&)>;
 
   using BoundaryTextOffsetsFunc =
-      base::RepeatingCallback<const std::vector<int32_t>&(
-          const AXPositionInstance&)>;
+      base::RepeatingCallback<std::vector<int32_t>(const AXPositionInstance&)>;
 
   static const int BEFORE_TEXT = -1;
   static const int INVALID_INDEX = -2;
@@ -361,7 +343,7 @@
       }
     }
 
-    if (!IsTextPosition() || text_offset_ < 0 || text_offset_ > MaxTextOffset())
+    if (!IsTextPosition() || text_offset_ > MaxTextOffset())
       return str;
 
     const std::u16string& text = GetText();
@@ -576,18 +558,7 @@
   }
 
   bool AtStartOfWord() const {
-    AXPositionInstance text_position;
-    if (!AtEndOfAnchor()) {
-      // We could get a leaf text position at the end of its anchor, where word
-      // start offsets would surely not be present. In such cases, we need to
-      // normalize to the start of the next leaf anchor. We avoid making this
-      // change when we are at the end of our anchor because this could
-      // effectively shift the position forward.
-      text_position = AsLeafTextPositionBeforeCharacter();
-    } else {
-      text_position = AsLeafTextPosition();
-    }
-
+    AXPositionInstance text_position = AsLeafTextPosition();
     switch (text_position->kind_) {
       case AXPositionKind::NULL_POSITION:
         return false;
@@ -595,7 +566,7 @@
         NOTREACHED();
         return false;
       case AXPositionKind::TEXT_POSITION: {
-        const std::vector<int32_t>& word_starts =
+        const std::vector<int32_t> word_starts =
             text_position->GetWordStartOffsets();
         return base::Contains(word_starts,
                               int32_t{text_position->text_offset_});
@@ -604,18 +575,7 @@
   }
 
   bool AtEndOfWord() const {
-    AXPositionInstance text_position;
-    if (!AtStartOfAnchor()) {
-      // We could get a leaf text position at the start of its anchor, where
-      // word end offsets would surely not be present. In such cases, we need to
-      // normalize to the end of the previous leaf anchor. We avoid making this
-      // change when we are at the start of our anchor because this could
-      // effectively shift the position backward.
-      text_position = AsLeafTextPositionAfterCharacter();
-    } else {
-      text_position = AsLeafTextPosition();
-    }
-
+    AXPositionInstance text_position = AsLeafTextPosition();
     switch (text_position->kind_) {
       case AXPositionKind::NULL_POSITION:
         return false;
@@ -623,71 +583,13 @@
         NOTREACHED();
         return false;
       case AXPositionKind::TEXT_POSITION: {
-        const std::vector<int32_t>& word_ends =
+        const std::vector<int32_t> word_ends =
             text_position->GetWordEndOffsets();
         return base::Contains(word_ends, int32_t{text_position->text_offset_});
       }
     }
   }
 
-  bool AtStartOfSentence() const {
-    AXPositionInstance text_position;
-    if (!AtEndOfAnchor()) {
-      // We could get a leaf text position at the end of its anchor, where
-      // sentence start offsets would surely not be present. In such cases, we
-      // need to normalize to the start of the next leaf anchor. We avoid making
-      // this change when we are at the end of our anchor because this could
-      // effectively shift the position forward.
-      text_position = AsLeafTextPositionBeforeCharacter();
-    } else {
-      text_position = AsLeafTextPosition();
-    }
-
-    switch (text_position->kind_) {
-      case AXPositionKind::NULL_POSITION:
-        return false;
-      case AXPositionKind::TREE_POSITION:
-        NOTREACHED();
-        return false;
-      case AXPositionKind::TEXT_POSITION: {
-        const std::vector<int32_t>& sentence_starts =
-            text_position->GetAnchor()->GetIntListAttribute(
-                ax::mojom::IntListAttribute::kSentenceStarts);
-        return base::Contains(sentence_starts,
-                              int32_t{text_position->text_offset_});
-      }
-    }
-  }
-
-  bool AtEndOfSentence() const {
-    AXPositionInstance text_position;
-    if (!AtStartOfAnchor()) {
-      // We could get a leaf text position at the start of its anchor, where
-      // sentence end offsets would surely not be present. In such cases, we
-      // need to normalize to the end of the previous leaf anchor. We avoid
-      // making this change when we are at the start of our anchor because this
-      // could effectively shift the position backward.
-      text_position = AsLeafTextPositionAfterCharacter();
-    } else {
-      text_position = AsLeafTextPosition();
-    }
-
-    switch (text_position->kind_) {
-      case AXPositionKind::NULL_POSITION:
-        return false;
-      case AXPositionKind::TREE_POSITION:
-        NOTREACHED();
-        return false;
-      case AXPositionKind::TEXT_POSITION: {
-        const std::vector<int32_t>& sentence_ends =
-            text_position->GetAnchor()->GetIntListAttribute(
-                ax::mojom::IntListAttribute::kSentenceEnds);
-        return base::Contains(sentence_ends,
-                              int32_t{text_position->text_offset_});
-      }
-    }
-  }
-
   bool AtStartOfLine() const {
     AXPositionInstance text_position = AsLeafTextPosition();
     switch (text_position->kind_) {
@@ -1704,50 +1606,40 @@
   // text boundary, and creates an AXRange that spans from the former to the
   // latter. The resulting AXRange is always a forward range: its anchor always
   // comes before its focus in document order. The resulting AXRange is bounded
-  // by the anchor of this position and the requested boundary type, i.e. the
-  // AXBoundaryBehavior is set to
-  // `AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary`. The
-  // exception is `ax::mojom::TextBoundary::kWebPage`, where this behavior won't
-  // make sense. This behavior is based on current platform needs and might be
-  // relaxed if necessary in the future.
+  // by the anchor of this position, i.e. the AXBoundaryBehavior is set to
+  // StopAtAnchorBoundary. The exception is ax::mojom::TextBoundary::kWebPage,
+  // where this behavior won't make sense. This behavior is based on current
+  // platform needs and might be relaxed if necessary in the future.
   //
-  // Observe that `expand_behavior` has an effect only when this position is
-  // between text units, e.g. between words, lines, paragraphs, etc. Also,
-  // please note that `expand_behavior` should have no effect for
-  // `ax::mojom::TextBoundary::kObject` and `ax::mojom::TextBoundary::kWebPage`
+  // Please note that |expand_behavior| should have no effect for
+  // ax::mojom::TextBoundary::kObject and ax::mojom::TextBoundary::kWebPage
   // because the range should be the same regardless if we first move left or
   // right.
   AXRangeType ExpandToEnclosingTextBoundary(
       ax::mojom::TextBoundary boundary,
       AXRangeExpandBehavior expand_behavior) const {
-    AXBoundaryBehavior left_boundary_behavior =
-        AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary;
-    AXBoundaryBehavior right_boundary_behavior =
-        AXBoundaryBehavior::kStopAtAnchorBoundary;
-    if (boundary == ax::mojom::TextBoundary::kWebPage) {
-      left_boundary_behavior = AXBoundaryBehavior::kCrossBoundary;
-      right_boundary_behavior = AXBoundaryBehavior::kCrossBoundary;
-    }
+    AXBoundaryBehavior boundary_behavior =
+        AXBoundaryBehavior::StopAtAnchorBoundary;
+    if (boundary == ax::mojom::TextBoundary::kWebPage)
+      boundary_behavior = AXBoundaryBehavior::CrossBoundary;
 
     switch (expand_behavior) {
       case AXRangeExpandBehavior::kLeftFirst: {
         AXPositionInstance left_position = CreatePositionAtTextBoundary(
-            boundary, ax::mojom::MoveDirection::kBackward,
-            left_boundary_behavior);
+            boundary, ax::mojom::MoveDirection::kBackward, boundary_behavior);
         AXPositionInstance right_position =
             left_position->CreatePositionAtTextBoundary(
                 boundary, ax::mojom::MoveDirection::kForward,
-                right_boundary_behavior);
+                boundary_behavior);
         return AXRangeType(std::move(left_position), std::move(right_position));
       }
       case AXRangeExpandBehavior::kRightFirst: {
         AXPositionInstance right_position = CreatePositionAtTextBoundary(
-            boundary, ax::mojom::MoveDirection::kForward,
-            left_boundary_behavior);
+            boundary, ax::mojom::MoveDirection::kForward, boundary_behavior);
         AXPositionInstance left_position =
             right_position->CreatePositionAtTextBoundary(
                 boundary, ax::mojom::MoveDirection::kBackward,
-                right_boundary_behavior);
+                boundary_behavior);
         return AXRangeType(std::move(left_position), std::move(right_position));
       }
     }
@@ -2006,55 +1898,19 @@
         break;
 
       case ax::mojom::TextBoundary::kSentenceEnd:
-        switch (direction) {
-          case ax::mojom::MoveDirection::kNone:
-            NOTREACHED();
-            break;
-          case ax::mojom::MoveDirection::kBackward:
-            resulting_position =
-                CreatePreviousSentenceEndPosition(boundary_behavior);
-            break;
-          case ax::mojom::MoveDirection::kForward:
-            resulting_position =
-                CreateNextSentenceEndPosition(boundary_behavior);
-            break;
-        }
-        break;
+        NOTREACHED() << "Sentence boundaries are not yet supported.";
+        return CreateNullPosition();
 
       case ax::mojom::TextBoundary::kSentenceStart:
-        switch (direction) {
-          case ax::mojom::MoveDirection::kNone:
-            NOTREACHED();
-            break;
-          case ax::mojom::MoveDirection::kBackward:
-            resulting_position =
-                CreatePreviousSentenceStartPosition(boundary_behavior);
-            break;
-          case ax::mojom::MoveDirection::kForward:
-            resulting_position =
-                CreateNextSentenceStartPosition(boundary_behavior);
-            break;
-        }
-        break;
+        NOTREACHED() << "Sentence boundaries are not yet supported.";
+        return CreateNullPosition();
 
       case ax::mojom::TextBoundary::kSentenceStartOrEnd:
-        switch (direction) {
-          case ax::mojom::MoveDirection::kNone:
-            NOTREACHED();
-            break;
-          case ax::mojom::MoveDirection::kBackward:
-            resulting_position =
-                CreatePreviousSentenceStartPosition(boundary_behavior);
-            break;
-          case ax::mojom::MoveDirection::kForward:
-            resulting_position =
-                CreateNextSentenceEndPosition(boundary_behavior);
-            break;
-        }
-        break;
+        NOTREACHED() << "Sentence boundaries are not yet supported.";
+        return CreateNullPosition();
 
       case ax::mojom::TextBoundary::kWebPage:
-        DCHECK_EQ(boundary_behavior, AXBoundaryBehavior::kCrossBoundary)
+        DCHECK_EQ(boundary_behavior, AXBoundaryBehavior::CrossBoundary)
             << "We can't reach the start of the whole contents if we are "
                "disallowed from crossing boundaries.";
         switch (direction) {
@@ -2643,18 +2499,17 @@
   // See also http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
   AXPositionInstance CreateNextCharacterPosition(
       AXBoundaryBehavior boundary_behavior) const {
-    if ((boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary ||
-         boundary_behavior ==
-             AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) &&
+    if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary &&
         AtEndOfAnchor()) {
       return Clone();
     }
 
     AXPositionInstance text_position = AsLeafTextPositionBeforeCharacter();
     if (text_position->IsNullPosition()) {
-      if (boundary_behavior != AXBoundaryBehavior::kCrossBoundary)
+      if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary ||
+          boundary_behavior == AXBoundaryBehavior::StopAtLastAnchorBoundary) {
         text_position = Clone();
-
+      }
       return text_position;
     }
 
@@ -2670,8 +2525,7 @@
     // positions that have the same affinity, since
     // `AsLeafTextPositionBeforeCharacter` resets the affinity to downstream,
     // while the original affinity might have been upstream.
-    if (boundary_behavior ==
-            AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary &&
+    if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
         (AtEndOfAnchor() || *text_position == *CloneWithDownstreamAffinity())) {
       return Clone();
     }
@@ -2697,9 +2551,9 @@
     if (GetAnchor() == common_anchor) {
       text_position = text_position->CreateAncestorPosition(
           common_anchor, ax::mojom::MoveDirection::kForward);
-    } else if (boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary) {
+    } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
       // If the next character position crosses the current anchor boundary
-      // with kStopAtAnchorBoundary, snap to the end of the current anchor.
+      // with StopAtAnchorBoundary, snap to the end of the current anchor.
       return CreatePositionAtEndOfAnchor();
     }
 
@@ -2721,18 +2575,17 @@
   // grapheme cluster.
   AXPositionInstance CreatePreviousCharacterPosition(
       AXBoundaryBehavior boundary_behavior) const {
-    if ((boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary ||
-         boundary_behavior ==
-             AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) &&
+    if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary &&
         AtStartOfAnchor()) {
       return Clone();
     }
 
     AXPositionInstance text_position = AsLeafTextPositionAfterCharacter();
     if (text_position->IsNullPosition()) {
-      if (boundary_behavior != AXBoundaryBehavior::kCrossBoundary)
+      if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary ||
+          boundary_behavior == AXBoundaryBehavior::StopAtLastAnchorBoundary) {
         text_position = Clone();
-
+      }
       return text_position;
     }
 
@@ -2747,8 +2600,7 @@
     // our current anchor. We also need to ignore any differences that might be
     // due to the affinity, because that should not be a determining factor as
     // to whether we would stop if we are already at boundary or not.
-    if (boundary_behavior ==
-            AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary &&
+    if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary &&
         (AtStartOfAnchor() || *text_position == *CloneWithUpstreamAffinity() ||
          *text_position == *CloneWithDownstreamAffinity())) {
       return Clone();
@@ -2773,7 +2625,7 @@
     if (GetAnchor() == common_anchor) {
       text_position = text_position->CreateAncestorPosition(
           common_anchor, ax::mojom::MoveDirection::kBackward);
-    } else if (boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary) {
+    } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
       // If the previous character position crosses the current anchor boundary
       // with StopAtAnchorBoundary, snap to the start of the current anchor.
       return CreatePositionAtStartOfAnchor();
@@ -2987,30 +2839,18 @@
       BoundaryConditionPredicate at_end_condition,
       BoundaryTextOffsetsFunc get_start_offsets =
           BoundaryTextOffsetsFunc()) const {
-    AXPositionInstance text_position;
-    if (!AtEndOfAnchor()) {
-      // We could get a leaf text position at the end of its anchor, where
-      // boundary start offsets would surely not be present. In such cases, we
-      // need to normalize to the start of the next leaf anchor. We avoid making
-      // this change when we are at the end of our anchor because this could
-      // effectively shift the position forward.
-      text_position = AsLeafTextPositionBeforeCharacter();
-    } else {
-      text_position = AsLeafTextPosition();
-    }
-
+    AXPositionInstance text_position = AsLeafTextPosition();
     if (text_position->IsNullPosition())
       return text_position;
 
-    if (boundary_behavior !=
-        AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) {
+    if (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary) {
       text_position =
           text_position->CreateAdjacentLeafTextPosition(move_direction);
       if (text_position->IsNullPosition()) {
         // There is no adjacent position to move to; in such case, CrossBoundary
         // behavior shall return a null position, while any other behavior shall
         // fallback to return the initial position.
-        if (boundary_behavior == AXBoundaryBehavior::kCrossBoundary)
+        if (boundary_behavior == AXBoundaryBehavior::CrossBoundary)
           return text_position;
         return Clone();
       }
@@ -3043,10 +2883,7 @@
         }
 
         if (next_position->IsNullPosition()) {
-          if (boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary ||
-              boundary_behavior ==
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) {
+          if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
             switch (move_direction) {
               case ax::mojom::MoveDirection::kNone:
                 NOTREACHED();
@@ -3061,7 +2898,7 @@
           }
 
           if (boundary_behavior ==
-              AXBoundaryBehavior::kStopAtLastAnchorBoundary) {
+              AXBoundaryBehavior::StopAtLastAnchorBoundary) {
             // We can't simply return the following position; break and after
             // this loop we'll try to do some adjustments to text_position.
             switch (move_direction) {
@@ -3097,10 +2934,7 @@
     if (GetAnchor() == common_anchor) {
       text_position =
           text_position->CreateAncestorPosition(common_anchor, move_direction);
-    } else if (boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary ||
-               boundary_behavior ==
-                   AXBoundaryBehavior::
-                       kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) {
+    } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
       switch (move_direction) {
         case ax::mojom::MoveDirection::kNone:
           NOTREACHED();
@@ -3121,13 +2955,14 @@
       text_position = text_position->AsTreePosition();
     AXPositionInstance unignored_position = text_position->AsUnignoredPosition(
         AXPositionAdjustmentBehavior::kMoveForward);
-    // If there are no unignored positions then `text_position` is anchored in
-    // ignored content at the end of the whole content. For
-    // `kStopAtLastAnchorBoundary`, try to adjust in the opposite direction to
-    // return a position within the whole content just before crossing into the
-    // ignored content. This will be the last unignored anchor boundary.
+    // If there are no unignored positions in |move_direction| then
+    // `text_position` is anchored in ignored content at the end of the whole
+    // content. For StopAtLastAnchorBoundary, try to adjust in the opposite
+    // direction to return a position within the whole content just before
+    // crossing into the ignored content. This will be the last unignored anchor
+    // boundary.
     if (unignored_position->IsNullPosition() &&
-        boundary_behavior == AXBoundaryBehavior::kStopAtLastAnchorBoundary) {
+        boundary_behavior == AXBoundaryBehavior::StopAtLastAnchorBoundary) {
       unignored_position = text_position->AsUnignoredPosition(
           AXPositionAdjustmentBehavior::kMoveBackward);
     }
@@ -3141,30 +2976,18 @@
       BoundaryConditionPredicate at_end_condition,
       BoundaryTextOffsetsFunc get_end_offsets =
           BoundaryTextOffsetsFunc()) const {
-    AXPositionInstance text_position;
-    if (!AtStartOfAnchor()) {
-      // We could get a leaf text position at the start of its anchor, where
-      // boundary end offsets would surely not be present. In such cases, we
-      // need to normalize to the end of the previous leaf anchor. We avoid
-      // making this change when we are at the start of our anchor because this
-      // could effectively shift the position backward.
-      text_position = AsLeafTextPositionAfterCharacter();
-    } else {
-      text_position = AsLeafTextPosition();
-    }
-
+    AXPositionInstance text_position = AsLeafTextPosition();
     if (text_position->IsNullPosition())
       return text_position;
 
-    if (boundary_behavior !=
-        AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) {
+    if (boundary_behavior != AXBoundaryBehavior::StopIfAlreadyAtBoundary) {
       text_position =
           text_position->CreateAdjacentLeafTextPosition(move_direction);
       if (text_position->IsNullPosition()) {
         // There is no adjacent position to move to; in such case, CrossBoundary
         // behavior shall return a null position, while any other behavior shall
         // fallback to return the initial position.
-        if (boundary_behavior == AXBoundaryBehavior::kCrossBoundary)
+        if (boundary_behavior == AXBoundaryBehavior::CrossBoundary)
           return text_position;
         return Clone();
       }
@@ -3200,10 +3023,7 @@
         }
 
         if (next_position->IsNullPosition()) {
-          if (boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary ||
-              boundary_behavior ==
-                  AXBoundaryBehavior::
-                      kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) {
+          if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
             switch (move_direction) {
               case ax::mojom::MoveDirection::kNone:
                 NOTREACHED();
@@ -3218,7 +3038,7 @@
           }
 
           if (boundary_behavior ==
-              AXBoundaryBehavior::kStopAtLastAnchorBoundary) {
+              AXBoundaryBehavior::StopAtLastAnchorBoundary) {
             // We can't simply return the following position; break and after
             // this loop we'll try to do some adjustments to text_position.
             switch (move_direction) {
@@ -3254,10 +3074,7 @@
     if (GetAnchor() == common_anchor) {
       text_position =
           text_position->CreateAncestorPosition(common_anchor, move_direction);
-    } else if (boundary_behavior == AXBoundaryBehavior::kStopAtAnchorBoundary ||
-               boundary_behavior ==
-                   AXBoundaryBehavior::
-                       kStopAtAnchorBoundaryOrIfAlreadyAtBoundary) {
+    } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) {
       switch (move_direction) {
         case ax::mojom::MoveDirection::kNone:
           NOTREACHED();
@@ -3284,7 +3101,7 @@
           text_position->CloneWithDownstreamAffinity();
       if (downstream_position->AtStartOfAnchor() ||
           downstream_position->AtEndOfAnchor() ||
-          !downstream_position->AtStartOfLine()) {
+          !at_start_condition.Run(downstream_position)) {
         text_position->affinity_ = ax::mojom::TextAffinity::kDownstream;
       }
     }
@@ -3293,54 +3110,21 @@
       text_position = text_position->AsTreePosition();
     AXPositionInstance unignored_position = text_position->AsUnignoredPosition(
         AXPositionAdjustmentBehavior::kMoveBackward);
-    // If there are no unignored positions then `text_position` is anchored in
-    // ignored content at the start or end of the whole content. For
-    // `kStopAtLastAnchorBoundary`, try to adjust in the opposite direction to
-    // return a position within the whole content just before crossing into the
-    // ignored content. This will be the last unignored anchor boundary.
+    // If there are no unignored positions in |move_direction| then
+    // |text_position| is anchored in ignored content at the start or end
+    // of the whole content.
+    // For StopAtLastAnchorBoundary, try to adjust in the opposite direction
+    // to return a position within the whole content just before crossing into
+    // the ignored content. This will be the last unignored anchor boundary.
     if (unignored_position->IsNullPosition() &&
-        boundary_behavior == AXBoundaryBehavior::kStopAtLastAnchorBoundary) {
+        boundary_behavior == AXBoundaryBehavior::StopAtLastAnchorBoundary) {
       unignored_position = text_position->AsUnignoredPosition(
           AXPositionAdjustmentBehavior::kMoveForward);
     }
     return unignored_position;
   }
 
-  AXPositionInstance CreateNextSentenceStartPosition(
-      AXBoundaryBehavior boundary_behavior) const {
-    return CreateBoundaryStartPosition(
-        boundary_behavior, ax::mojom::MoveDirection::kForward,
-        base::BindRepeating(&AtStartOfSentencePredicate),
-        base::BindRepeating(&AtEndOfSentencePredicate),
-        base::BindRepeating(&GetSentenceStartOffsetsFunc));
-  }
-
-  AXPositionInstance CreatePreviousSentenceStartPosition(
-      AXBoundaryBehavior boundary_behavior) const {
-    return CreateBoundaryStartPosition(
-        boundary_behavior, ax::mojom::MoveDirection::kBackward,
-        base::BindRepeating(&AtStartOfSentencePredicate),
-        base::BindRepeating(&AtEndOfSentencePredicate),
-        base::BindRepeating(&GetSentenceStartOffsetsFunc));
-  }
-
-  AXPositionInstance CreateNextSentenceEndPosition(
-      AXBoundaryBehavior boundary_behavior) const {
-    return CreateBoundaryEndPosition(
-        boundary_behavior, ax::mojom::MoveDirection::kForward,
-        base::BindRepeating(&AtStartOfSentencePredicate),
-        base::BindRepeating(&AtEndOfSentencePredicate),
-        base::BindRepeating(&GetSentenceEndOffsetsFunc));
-  }
-
-  AXPositionInstance CreatePreviousSentenceEndPosition(
-      AXBoundaryBehavior boundary_behavior) const {
-    return CreateBoundaryEndPosition(
-        boundary_behavior, ax::mojom::MoveDirection::kBackward,
-        base::BindRepeating(&AtStartOfSentencePredicate),
-        base::BindRepeating(&AtEndOfSentencePredicate),
-        base::BindRepeating(&GetSentenceEndOffsetsFunc));
-  }
+  // TODO(nektar): Add sentence navigation methods.
 
   // Uses depth-first pre-order traversal.
   AXPositionInstance CreateNextAnchorPosition() const {
@@ -3865,13 +3649,13 @@
     if (GetAnchor()->IsCollapsedMenuListPopUpButton())
       return true;
 
-    // All anchor nodes that are empty leaf nodes should be treated as empty
-    // objects. Empty leaf nodes do not expose their descendants to platform
-    // accessibility APIs, but may have unignored descendants. They do not have
-    // any inner text, hence they are empty from our perspective. For example,
-    // an empty text field may still have an unignored generic container inside
-    // it.
-    if (!GetAnchor()->IsEmptyLeaf())
+    // All anchor nodes that are empty leaf nodes or have only ignored
+    // descendants should be treated as empty objects. Empty leaf nodes do not
+    // expose their descendants to platform accessibility APIs, but may have
+    // unignored descendants. They do not have any text content, however, hence
+    // they are still empty from our perspective. For example, an empty text
+    // field may still have an unignored generic container inside it.
+    if (AnchorUnignoredChildCount() && !GetAnchor()->IsEmptyLeaf())
       return false;
 
     // <embed> and <object> elements with non empty children should not be
@@ -4308,55 +4092,34 @@
     return current_anchor_text_attributes;
   }
 
-  const std::vector<int32_t>& GetWordStartOffsets() const {
-    if (IsNullPosition()) {
-      static const base::NoDestructor<std::vector<int32_t>> empty_word_starts;
-      return *empty_word_starts;
-    }
+  std::vector<int32_t> GetWordStartOffsets() const {
+    if (IsNullPosition())
+      return std::vector<int32_t>();
     DCHECK(GetAnchor());
-
-    // An embedded object replacement character is exposed in a node's text
-    // representation when a control, such as a text field, is empty. Since the
-    // control has no text, no word start offsets are present in the
-    // `ax::mojom::IntListAttribute::kWordStarts` attribute, so we need to
-    // special case them here.
-    if (IsEmptyObjectReplacedByCharacter()) {
-      // Using braces ensures that the vector will contain the given value, and
-      // not create a vector of size 0.
-      static const base::NoDestructor<std::vector<int32_t>>
-          embedded_word_starts{{0}};
-      return *embedded_word_starts;
-    }
+    // Embedded object replacement characters are not represented in the
+    // "kWordStarts" attribute so we need to special case them here.
+    if (IsEmptyObjectReplacedByCharacter())
+      return {0};
 
     return GetAnchor()->GetIntListAttribute(
         ax::mojom::IntListAttribute::kWordStarts);
   }
 
-  const std::vector<int32_t>& GetWordEndOffsets() const {
-    if (IsNullPosition()) {
-      static const base::NoDestructor<std::vector<int32_t>> empty_word_ends;
-      return *empty_word_ends;
-    }
+  std::vector<int32_t> GetWordEndOffsets() const {
+    if (IsNullPosition())
+      return std::vector<int32_t>();
     DCHECK(GetAnchor());
 
-    // An embedded object replacement character is exposed in a node's text
-    // representation when a control, such as a text field, is empty. Since the
-    // control has no text, no word end offsets are present in the
-    // `ax::mojom::IntListAttribute::kWordEnds` attribute, so we need to special
-    // case them here.
+    // Embedded object replacement characters are not represented in the
+    // "kWordEnds" attribute so we need to special case them here.
     //
     // Since the whole text exposed inside of an embedded object is of
     // length 1 (the embedded object replacement character), the word end offset
-    // is positioned at 1. Because we want to treat embedded object replacement
-    // characters as ordinary characters, it wouldn't be consistent to assume
-    // they have no length and return 0 instead of 1.
-    if (IsEmptyObjectReplacedByCharacter()) {
-      // Using braces ensures that the vector will contain the given value, and
-      // not create a vector of size 1.
-      static const base::NoDestructor<std::vector<int32_t>> embedded_word_ends{
-          {1}};
-      return *embedded_word_ends;
-    }
+    // is positioned at 1. Because we want to treat the embedded object
+    // replacement characters as ordinary characters, it wouldn't be consistent
+    // to assume they have no length and return 0 instead of 1.
+    if (IsEmptyObjectReplacedByCharacter())
+      return {1};
 
     return GetAnchor()->GetIntListAttribute(
         ax::mojom::IntListAttribute::kWordEnds);
@@ -4678,22 +4441,6 @@
     return position->AtEndOfLine();
   }
 
-  static bool AtStartOfSentencePredicate(const AXPositionInstance& position) {
-    // Sentence boundaries should be at specific text offsets that are "visible"
-    // to assistive software, hence not ignored. Ignored nodes are often used
-    // for additional layout information, such as line and paragraph boundaries.
-    // Their text is not currently processed.
-    return !position->IsIgnored() && position->AtStartOfSentence();
-  }
-
-  static bool AtEndOfSentencePredicate(const AXPositionInstance& position) {
-    // Sentence boundaries should be at specific text offsets that are "visible"
-    // to assistive software, hence not ignored. Ignored nodes are often used
-    // for additional layout information, such as line and paragraph boundaries.
-    // Their text is not currently processed.
-    return !position->IsIgnored() && position->AtEndOfSentence();
-  }
-
   static bool AtStartOfFormatPredicate(const AXPositionInstance& position) {
     return position->AtStartOfFormat();
   }
@@ -4919,35 +4666,12 @@
     return false;
   }
 
-  static const std::vector<int32_t>& GetSentenceStartOffsetsFunc(
-      const AXPositionInstance& position) {
-    if (position->IsNullPosition()) {
-      static const base::NoDestructor<std::vector<int32_t>>
-          empty_sentence_starts;
-      return *empty_sentence_starts;
-    }
-    DCHECK(position->GetAnchor());
-    return position->GetAnchor()->GetIntListAttribute(
-        ax::mojom::IntListAttribute::kSentenceStarts);
-  }
-
-  static const std::vector<int32_t>& GetSentenceEndOffsetsFunc(
-      const AXPositionInstance& position) {
-    if (position->IsNullPosition()) {
-      static const base::NoDestructor<std::vector<int32_t>> empty_sentence_ends;
-      return *empty_sentence_ends;
-    }
-    DCHECK(position->GetAnchor());
-    return position->GetAnchor()->GetIntListAttribute(
-        ax::mojom::IntListAttribute::kSentenceEnds);
-  }
-
-  static const std::vector<int32_t>& GetWordStartOffsetsFunc(
+  static std::vector<int32_t> GetWordStartOffsetsFunc(
       const AXPositionInstance& position) {
     return position->GetWordStartOffsets();
   }
 
-  static const std::vector<int32_t>& GetWordEndOffsetsFunc(
+  static std::vector<int32_t> GetWordEndOffsetsFunc(
       const AXPositionInstance& position) {
     return position->GetWordEndOffsets();
   }
@@ -5033,7 +4757,7 @@
       return Clone();
 
     AXPositionInstance text_position = AsTextPosition();
-    const std::vector<int32_t>& boundary_offsets =
+    const std::vector<int32_t> boundary_offsets =
         get_offsets.Run(text_position);
     if (boundary_offsets.empty())
       return text_position;
@@ -5088,7 +4812,7 @@
       return Clone();
 
     AXPositionInstance text_position = AsTextPosition();
-    const std::vector<int32_t>& boundary_offsets =
+    const std::vector<int32_t> boundary_offsets =
         get_offsets.Run(text_position);
     switch (move_direction) {
       case ax::mojom::MoveDirection::kNone:
@@ -5121,16 +4845,15 @@
   //
   // This method is the first step for CreateBoundary[Start|End]Position to
   // guarantee that the resulting position when using a boundary behavior other
-  // than `AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary` is
-  // not equivalent to the initial position. That's why ignored positions are
-  // also skipped. Otherwise, if a boundary is present on an ignored position,
-  // the search for the next or previous boundary would stop prematurely. Note
-  // that if there are multiple adjacent ignored positions and all of them
-  // create a boundary, we'll skip them all on purpose. For example, adjacent
-  // ignored paragraph boundaries could be created by using multiple aria-hidden
-  // divs next to one another. These should not contribute more than one
-  // paragraph boundary to the tree's text representation, otherwise this will
-  // create user confusion.
+  // than `AXBoundaryBehavior::StopIfAlreadyAtBoundary` is not equivalent to the
+  // initial position. That's why ignored positions are also skipped. Otherwise,
+  // if a boundary is present on an ignored position, the search for the next or
+  // previous boundary would stop prematurely. Note that if there are multiple
+  // adjacent ignored positions and all of them create a boundary, we'll skip
+  // them all on purpose. For example, adjacent ignored paragraph boundaries
+  // could be created by using multiple aria-hidden divs next to one another.
+  // These should not contribute more than one paragraph boundary to the tree's
+  // text representation, otherwise this will create user confusion.
   //
   // Note that using the `CompareTo` method with text positions does not take
   // into account position affinity or the order of their anchors in the tree:
diff --git a/ui/accessibility/platform/BUILD.gn b/ui/accessibility/platform/BUILD.gn
index 281d94e5..08aab97 100644
--- a/ui/accessibility/platform/BUILD.gn
+++ b/ui/accessibility/platform/BUILD.gn
@@ -133,6 +133,8 @@
         "ax_platform_relation_win.h",
         "ax_system_caret_win.cc",
         "ax_system_caret_win.h",
+        "inspect/ax_event_recorder_win.cc",
+        "inspect/ax_event_recorder_win.h",
         "inspect/ax_inspect_utils_win.cc",
         "inspect/ax_inspect_utils_win.h",
         "uia_registrar_win.cc",
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
index e2c5a8e3..909dcac 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc
@@ -1300,7 +1300,8 @@
   AXNodeData root;
   root.id = 1;
   root.role = ax::mojom::Role::kTextField;
-  root.SetValue("A decently long string \xE2\x98\xBA with an emoji.");
+  root.AddStringAttribute(ax::mojom::StringAttribute::kValue,
+                          "A decently long string \xE2\x98\xBA with an emoji.");
   Init(root);
 
   AtkObject* root_obj(GetRootAtkObject());
@@ -1518,8 +1519,7 @@
 }
 
 #if ATK_CHECK_VERSION(2, 10, 0)
-TEST_F(AXPlatformNodeAuraLinuxTest, DISABLED_TestAtkTextParagraphGranularity) {
-  // TODO(nektar): Enable navigating by paragraphs in plain text.
+TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextParagraphGranularity) {
   AXNodeData root;
   root.id = 1;
   root.role = ax::mojom::Role::kTextField;
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
index a767903..a731210f 100644
--- a/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -2006,27 +2006,17 @@
     ax::mojom::MoveDirection direction,
     ax::mojom::TextAffinity affinity) const {
   DCHECK_NE(boundary, ax::mojom::TextBoundary::kNone);
-  if (!delegate_)
-    return offset;  // Unable to compute text boundary.
-
-  const AXPosition position = delegate_->CreateTextPositionAt(offset, affinity);
-  // On Windows and Linux ATK, searching for a text boundary should always stop
-  // at the boundary of the current object.
-  auto boundary_behavior = AXBoundaryBehavior::kStopAtAnchorBoundary;
-  // On Windows and Linux ATK, it is standard text navigation behavior to stop
-  // if we are searching in the backwards direction and the current position is
-  // already at the required text boundary.
-  if (direction == ax::mojom::MoveDirection::kBackward) {
-    boundary_behavior =
-        AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary;
+  if (boundary != ax::mojom::TextBoundary::kSentenceStart) {
+    absl::optional<int> boundary_offset =
+        GetDelegate()->FindTextBoundary(boundary, offset, direction, affinity);
+    if (boundary_offset.has_value())
+      return *boundary_offset;
   }
 
-  const AXPosition boundary_position = position->CreatePositionAtTextBoundary(
-      boundary, direction, boundary_behavior);
-  if (boundary_position->IsNullPosition())
-    return -1;
-  DCHECK_GE(boundary_position->text_offset(), 0);
-  return boundary_position->text_offset();
+  std::vector<int32_t> unused_line_start_offsets;
+  return static_cast<int>(
+      FindAccessibleTextBoundary(GetHypertext(), unused_line_start_offsets,
+                                 boundary, offset, direction, affinity));
 }
 
 AXPlatformNodeBase* AXPlatformNodeBase::NearestLeafToPoint(
@@ -2168,14 +2158,11 @@
     unsigned int red = SkColorGetR(color);
     unsigned int green = SkColorGetG(color);
     unsigned int blue = SkColorGetB(color);
-    // Don't expose default value of black.
-    if (red || green || blue) {
-      std::string color_value = "rgb(" + base::NumberToString(red) + ',' +
-                                base::NumberToString(green) + ',' +
-                                base::NumberToString(blue) + ')';
-      SanitizeTextAttributeValue(color_value, &color_value);
-      attributes.push_back(std::make_pair("color", color_value));
-    }
+    std::string color_value = "rgb(" + base::NumberToString(red) + ',' +
+                              base::NumberToString(green) + ',' +
+                              base::NumberToString(blue) + ')';
+    SanitizeTextAttributeValue(color_value, &color_value);
+    attributes.push_back(std::make_pair("color", color_value));
   }
 
   // First try to get the inherited font family name from the delegate. If we
diff --git a/ui/accessibility/platform/ax_platform_node_base.h b/ui/accessibility/platform/ax_platform_node_base.h
index 01f92d8..3ad7323 100644
--- a/ui/accessibility/platform/ax_platform_node_base.h
+++ b/ui/accessibility/platform/ax_platform_node_base.h
@@ -15,7 +15,6 @@
 #include "build/build_config.h"
 #include "ui/accessibility/ax_enums.mojom-forward.h"
 #include "ui/accessibility/ax_node.h"
-#include "ui/accessibility/ax_node_position.h"
 #include "ui/accessibility/ax_text_attributes.h"
 #include "ui/accessibility/platform/ax_platform_node.h"
 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
@@ -61,8 +60,6 @@
 
 class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode {
  public:
-  using AXPosition = AXNodePosition::AXPositionInstance;
-
   ~AXPlatformNodeBase() override;
   AXPlatformNodeBase(const AXPlatformNodeBase&) = delete;
   AXPlatformNodeBase& operator=(const AXPlatformNodeBase&) = delete;
@@ -351,7 +348,6 @@
   // This method finds text boundaries in the text used for platform text APIs.
   // Implementations may use side-channel data such as line or word indices to
   // produce appropriate results.
-  // Returns -1 if the requested boundary has not been found.
   virtual int FindTextBoundary(ax::mojom::TextBoundary boundary,
                                int offset,
                                ax::mojom::MoveDirection direction,
diff --git a/ui/accessibility/platform/ax_platform_node_cocoa.mm b/ui/accessibility/platform/ax_platform_node_cocoa.mm
index 83402fd..54f1f8d1 100644
--- a/ui/accessibility/platform/ax_platform_node_cocoa.mm
+++ b/ui/accessibility/platform/ax_platform_node_cocoa.mm
@@ -696,7 +696,7 @@
       [axAttributes addObject:kTextAttributes];
       if (!_node->HasState(ax::mojom::State::kProtected))
         [axAttributes addObjectsFromArray:kUnprotectedTextAttributes];
-      FALLTHROUGH;
+      [[fallthrough]];
     case ax::mojom::Role::kCheckBox:
     case ax::mojom::Role::kComboBoxMenuButton:
     case ax::mojom::Role::kMenuItemCheckBox:
diff --git a/ui/accessibility/platform/ax_platform_node_delegate.h b/ui/accessibility/platform/ax_platform_node_delegate.h
index e9653804..e3ca210 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate.h
+++ b/ui/accessibility/platform/ax_platform_node_delegate.h
@@ -484,6 +484,21 @@
 
   virtual const AXUniqueId& GetUniqueId() const = 0;
 
+  // Finds the previous or next offset from the provided offset, that matches
+  // the provided boundary type.
+  //
+  // This method finds text boundaries in the text used for platform text APIs.
+  // Implementations may use side-channel data such as line or word indices to
+  // produce appropriate results. It may optionally return no value, indicating
+  // that the delegate does not have all the information required to calculate
+  // this value and it is the responsibility of the AXPlatformNode itself to
+  // to calculate it.
+  virtual absl::optional<int> FindTextBoundary(
+      ax::mojom::TextBoundary boundary,
+      int offset,
+      ax::mojom::MoveDirection direction,
+      ax::mojom::TextAffinity affinity) const = 0;
+
   // Return a vector of all the descendants of this delegate's node. This method
   // is only meaningful for Windows UIA.
   virtual const std::vector<gfx::NativeViewAccessible>
diff --git a/ui/accessibility/platform/ax_platform_node_delegate_base.cc b/ui/accessibility/platform/ax_platform_node_delegate_base.cc
index 4dbd1b1..fee47db 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_delegate_base.cc
@@ -265,6 +265,7 @@
 AXNodePosition::AXPositionInstance AXPlatformNodeDelegateBase::CreatePositionAt(
     int offset,
     ax::mojom::TextAffinity affinity) const {
+  NOTIMPLEMENTED();
   return AXNodePosition::CreateNullPosition();
 }
 
@@ -272,6 +273,7 @@
 AXPlatformNodeDelegateBase::CreateTextPositionAt(
     int offset,
     ax::mojom::TextAffinity affinity) const {
+  NOTIMPLEMENTED();
   return AXNodePosition::CreateNullPosition();
 }
 
@@ -929,6 +931,14 @@
   return *dummy_unique_id;
 }
 
+absl::optional<int> AXPlatformNodeDelegateBase::FindTextBoundary(
+    ax::mojom::TextBoundary boundary,
+    int offset,
+    ax::mojom::MoveDirection direction,
+    ax::mojom::TextAffinity affinity) const {
+  return absl::nullopt;
+}
+
 const std::vector<gfx::NativeViewAccessible>
 AXPlatformNodeDelegateBase::GetUIADirectChildrenInRange(
     ui::AXPlatformNodeDelegate* start,
diff --git a/ui/accessibility/platform/ax_platform_node_delegate_base.h b/ui/accessibility/platform/ax_platform_node_delegate_base.h
index 87fac77..9ceef725 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate_base.h
+++ b/ui/accessibility/platform/ax_platform_node_delegate_base.h
@@ -269,6 +269,12 @@
 
   const AXUniqueId& GetUniqueId() const override;
 
+  absl::optional<int> FindTextBoundary(
+      ax::mojom::TextBoundary boundary,
+      int offset,
+      ax::mojom::MoveDirection direction,
+      ax::mojom::TextAffinity affinity) const override;
+
   const std::vector<gfx::NativeViewAccessible> GetUIADirectChildrenInRange(
       ui::AXPlatformNodeDelegate* start,
       ui::AXPlatformNodeDelegate* end) override;
diff --git a/ui/accessibility/platform/ax_platform_node_textprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textprovider_win.cc
index b7fd12d..5d7e546 100644
--- a/ui/accessibility/platform/ax_platform_node_textprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textprovider_win.cc
@@ -146,7 +146,7 @@
   auto current_line_start = start->Clone();
   while (!current_line_start->IsNullPosition() && *current_line_start < *end) {
     auto current_line_end = current_line_start->CreateNextLineEndPosition(
-        AXBoundaryBehavior::kCrossBoundary);
+        AXBoundaryBehavior::CrossBoundary);
     if (current_line_end->IsNullPosition() || *current_line_end > *end)
       current_line_end = end->Clone();
 
@@ -163,7 +163,7 @@
     }
 
     current_line_start = current_line_start->CreateNextLineStartPosition(
-        AXBoundaryBehavior::kCrossBoundary);
+        AXBoundaryBehavior::CrossBoundary);
   }
 
   base::win::ScopedSafearray scoped_visible_ranges(
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
index dfa3cc1..77c2bd17 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -221,14 +221,14 @@
       // boundary, thus we only need to move the end position.
       AXPositionInstance end_backup = end()->Clone();
       SetEnd(start()->CreateNextCharacterPosition(
-          AXBoundaryBehavior::kCrossBoundary));
+          AXBoundaryBehavior::CrossBoundary));
 
       if (end()->IsNullPosition()) {
         // The previous could fail if the start is at the end of the last anchor
         // of the tree, try expanding to the previous character instead.
         AXPositionInstance start_backup = start()->Clone();
         SetStart(start()->CreatePreviousCharacterPosition(
-            AXBoundaryBehavior::kCrossBoundary));
+            AXBoundaryBehavior::CrossBoundary));
 
         if (start()->IsNullPosition()) {
           // Text representation is empty, undo everything and exit.
@@ -237,7 +237,7 @@
           return S_OK;
         }
         SetEnd(start()->CreateNextCharacterPosition(
-            AXBoundaryBehavior::kCrossBoundary));
+            AXBoundaryBehavior::CrossBoundary));
         DCHECK(!end()->IsNullPosition());
       }
 
@@ -250,31 +250,39 @@
     }
     case TextUnit_Format:
       SetStart(start()->CreatePreviousFormatStartPosition(
-          AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary));
+          AXBoundaryBehavior::StopIfAlreadyAtBoundary));
       SetEnd(start()->CreateNextFormatEndPosition(
-          AXBoundaryBehavior::kStopAtLastAnchorBoundary));
+          AXBoundaryBehavior::StopIfAlreadyAtBoundary));
       break;
     case TextUnit_Word: {
       AXPositionInstance start_backup = start()->Clone();
       SetStart(start()->CreatePreviousWordStartPosition(
-          AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary));
+          AXBoundaryBehavior::StopIfAlreadyAtBoundary));
+      // Since we use AXBoundaryBehavior::StopIfAlreadyAtBoundary, the only case
+      // possible where CreatePreviousWordStartPosition can return a
+      // NullPosition is when it's called on a node before the first word
+      // boundary. This can happen when the document starts with nodes that have
+      // no word boundaries, like whitespaces and punctuation. When it happens,
+      // move the position back to the start of the document.
+      if (start()->IsNullPosition())
+        SetStart(start_backup->CreatePositionAtStartOfContent());
 
       // Since start_ is already located at a word boundary, we need to cross it
       // in order to move to the next one. Because Windows ATs behave
       // undesirably when the start and end endpoints are not in the same anchor
       // (for character and word navigation), stop at anchor boundary.
       SetEnd(start()->CreateNextWordStartPosition(
-          AXBoundaryBehavior::kStopAtAnchorBoundary));
+          AXBoundaryBehavior::StopAtAnchorBoundary));
       break;
     }
     case TextUnit_Line:
       SetStart(start()->CreateBoundaryStartPosition(
-          AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
+          AXBoundaryBehavior::StopIfAlreadyAtBoundary,
           ax::mojom::MoveDirection::kBackward,
           base::BindRepeating(&AtStartOfLinePredicate),
           base::BindRepeating(&AtEndOfLinePredicate)));
       SetEnd(start()->CreateBoundaryEndPosition(
-          AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary,
+          AXBoundaryBehavior::StopIfAlreadyAtBoundary,
           ax::mojom::MoveDirection::kForward,
           base::BindRepeating(&AtStartOfLinePredicate),
           base::BindRepeating(&AtEndOfLinePredicate)));
@@ -282,9 +290,9 @@
     case TextUnit_Paragraph:
       SetStart(
           start()->CreatePreviousParagraphStartPositionSkippingEmptyParagraphs(
-              AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary));
+              AXBoundaryBehavior::StopIfAlreadyAtBoundary));
       SetEnd(start()->CreateNextParagraphStartPositionSkippingEmptyParagraphs(
-          AXBoundaryBehavior::kStopAtLastAnchorBoundary));
+          AXBoundaryBehavior::StopAtLastAnchorBoundary));
       break;
     case TextUnit_Page: {
       // Per UIA spec, if the document containing the current range doesn't
@@ -292,10 +300,9 @@
       const AXNode* common_anchor = start()->LowestCommonAnchor(*end());
       if (common_anchor->tree()->HasPaginationSupport()) {
         SetStart(start()->CreatePreviousPageStartPosition(
-            AXBoundaryBehavior::kStopAtAnchorBoundaryOrIfAlreadyAtBoundary));
+            ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary));
         SetEnd(start()->CreateNextPageEndPosition(
-            ui::AXBoundaryBehavior::
-                kStopAtAnchorBoundaryOrIfAlreadyAtBoundary));
+            ui::AXBoundaryBehavior::StopIfAlreadyAtBoundary));
         break;
       }
     }
@@ -1233,14 +1240,13 @@
     do {
       AXPositionInstance next_endpoint = GetNextTextBoundaryPosition(
           current_endpoint, boundary_type,
-          AXBoundaryBehavior::kStopAtLastAnchorBoundary, boundary_direction);
+          AXBoundaryBehavior::StopAtLastAnchorBoundary, boundary_direction);
       DCHECK(next_endpoint->IsLeafTextPosition());
 
-      // Since AXBoundaryBehavior::kStopAtLastAnchorBoundary forces the next
-      // text boundary position to be different than the input position, the
-      // only case where these are equal is when they're already located at the
-      // last anchor boundary. In such case, there is no next position to move
-      // to.
+      // Since AXBoundaryBehavior::StopAtLastAnchorBoundary forces the next text
+      // boundary position to be different than the input position, the only
+      // case where these are equal is when they're already located at the last
+      // anchor boundary. In such case, there is no next position to move to.
       if (next_endpoint->GetAnchor() == current_endpoint->GetAnchor() &&
           *next_endpoint == *current_endpoint) {
         *units_moved = (count > 0) ? iteration : -iteration;
diff --git a/ui/accessibility/platform/ax_platform_node_unittest.cc b/ui/accessibility/platform/ax_platform_node_unittest.cc
index 9d8c8f1..86d30be 100644
--- a/ui/accessibility/platform/ax_platform_node_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_unittest.cc
@@ -39,37 +39,36 @@
     const ui::AXNodeData& node6 /* = ui::AXNodeData() */,
     const ui::AXNodeData& node7 /* = ui::AXNodeData() */,
     const ui::AXNodeData& node8 /* = ui::AXNodeData() */,
-    const ui::AXNodeData& node9 /* = AXNodeData() */,
-    const ui::AXNodeData& node10 /* = AXNodeData() */,
-    const ui::AXNodeData& node11 /* = AXNodeData() */,
-    const ui::AXNodeData& node12 /* = AXNodeData() */) {
+    const ui::AXNodeData& node9 /* = ui::AXNodeData() */,
+    const ui::AXNodeData& node10 /* = ui::AXNodeData() */,
+    const ui::AXNodeData& node11 /* = ui::AXNodeData() */,
+    const ui::AXNodeData& node12 /* = ui::AXNodeData() */) {
+  static ui::AXNodeData empty_data;
+  int32_t no_id = empty_data.id;
   AXTreeUpdate update;
   update.root_id = node1.id;
-  update.has_tree_data = true;
-  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
-  update.tree_data.title = "Dialog title";
   update.nodes.push_back(node1);
-  if (node2.id != kInvalidAXNodeID)
+  if (node2.id != no_id)
     update.nodes.push_back(node2);
-  if (node3.id != kInvalidAXNodeID)
+  if (node3.id != no_id)
     update.nodes.push_back(node3);
-  if (node4.id != kInvalidAXNodeID)
+  if (node4.id != no_id)
     update.nodes.push_back(node4);
-  if (node5.id != kInvalidAXNodeID)
+  if (node5.id != no_id)
     update.nodes.push_back(node5);
-  if (node6.id != kInvalidAXNodeID)
+  if (node6.id != no_id)
     update.nodes.push_back(node6);
-  if (node7.id != kInvalidAXNodeID)
+  if (node7.id != no_id)
     update.nodes.push_back(node7);
-  if (node8.id != kInvalidAXNodeID)
+  if (node8.id != no_id)
     update.nodes.push_back(node8);
-  if (node9.id != kInvalidAXNodeID)
+  if (node9.id != no_id)
     update.nodes.push_back(node9);
-  if (node10.id != kInvalidAXNodeID)
+  if (node10.id != no_id)
     update.nodes.push_back(node10);
-  if (node11.id != kInvalidAXNodeID)
+  if (node11.id != no_id)
     update.nodes.push_back(node11);
-  if (node12.id != kInvalidAXNodeID)
+  if (node12.id != no_id)
     update.nodes.push_back(node12);
   Init(update);
 }
diff --git a/ui/accessibility/platform/ax_platform_node_unittest.h b/ui/accessibility/platform/ax_platform_node_unittest.h
index b006162b..08e1c99 100644
--- a/ui/accessibility/platform/ax_platform_node_unittest.h
+++ b/ui/accessibility/platform/ax_platform_node_unittest.h
@@ -29,18 +29,18 @@
   void Init(const AXTreeUpdate& initial_state);
 
   // Convenience functions to initialize directly from a few AXNodeData objects.
-  void Init(const AXNodeData& node1,
-            const AXNodeData& node2 = AXNodeData(),
-            const AXNodeData& node3 = AXNodeData(),
-            const AXNodeData& node4 = AXNodeData(),
-            const AXNodeData& node5 = AXNodeData(),
-            const AXNodeData& node6 = AXNodeData(),
-            const AXNodeData& node7 = AXNodeData(),
-            const AXNodeData& node8 = AXNodeData(),
-            const AXNodeData& node9 = AXNodeData(),
-            const AXNodeData& node10 = AXNodeData(),
-            const AXNodeData& node11 = AXNodeData(),
-            const AXNodeData& node12 = AXNodeData());
+  void Init(const ui::AXNodeData& node1,
+            const ui::AXNodeData& node2 = ui::AXNodeData(),
+            const ui::AXNodeData& node3 = ui::AXNodeData(),
+            const ui::AXNodeData& node4 = ui::AXNodeData(),
+            const ui::AXNodeData& node5 = ui::AXNodeData(),
+            const ui::AXNodeData& node6 = ui::AXNodeData(),
+            const ui::AXNodeData& node7 = ui::AXNodeData(),
+            const ui::AXNodeData& node8 = ui::AXNodeData(),
+            const ui::AXNodeData& node9 = ui::AXNodeData(),
+            const ui::AXNodeData& node10 = ui::AXNodeData(),
+            const ui::AXNodeData& node11 = ui::AXNodeData(),
+            const ui::AXNodeData& node12 = ui::AXNodeData());
 
   AXTreeUpdate BuildTextField();
   AXTreeUpdate BuildTextFieldWithSelectionRange(int32_t start, int32_t stop);
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index bf2d893..b595d177 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -3606,6 +3606,12 @@
   AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes |
                                        AXMode::kInlineTextBoxes);
 
+  // https://accessibility.linuxfoundation.org/a11yspecs/ia2/docs/html/_accessible_text_8idl.html
+  // IA2_TEXT_BOUNDARY_SENTENCE is optional and we can let the screenreader
+  // handle it, the rest of the boundary types must be supported.
+  if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE)
+    return S_FALSE;
+
   HandleSpecialTextOffset(&offset);
   if (offset < 0)
     return E_INVALIDARG;
@@ -3628,6 +3634,7 @@
   }
 
   LONG start, end;
+
   switch (text_offset_type) {
     case TextOffsetType::kAtOffset: {
       end = FindBoundary(boundary_type, offset,
diff --git a/ui/accessibility/platform/ax_platform_node_win.h b/ui/accessibility/platform/ax_platform_node_win.h
index e79e303..ada2088 100644
--- a/ui/accessibility/platform/ax_platform_node_win.h
+++ b/ui/accessibility/platform/ax_platform_node_win.h
@@ -30,6 +30,14 @@
 #include "ui/accessibility/platform/ichromeaccessible.h"
 #include "ui/gfx/range/range.h"
 
+// This nonstandard GUID is taken directly from the Mozilla sources
+// (https://searchfox.org/mozilla-central/source/accessible/windows/msaa/ServiceProvider.cpp#60).
+const GUID GUID_IAccessibleContentDocument = {
+    0xa5d8e1f3,
+    0x3571,
+    0x4d8f,
+    {0x95, 0x21, 0x07, 0xed, 0x28, 0xfb, 0x07, 0x2e}};
+
 // IMPORTANT!
 // These values are written to logs.  Do not renumber or delete
 // existing items; add new entries to the end of the list.
diff --git a/ui/accessibility/platform/fuchsia/semantic_provider_test.cc b/ui/accessibility/platform/fuchsia/semantic_provider_test.cc
deleted file mode 100644
index ccded33..0000000
--- a/ui/accessibility/platform/fuchsia/semantic_provider_test.cc
+++ /dev/null
@@ -1,445 +0,0 @@
-// Copyright 2018 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 "semantic_provider.h"
-
-#include <fuchsia/accessibility/semantics/cpp/fidl.h>
-#include <lib/ui/scenic/cpp/view_ref_pair.h>
-
-#include <algorithm>
-#include <memory>
-
-#include "base/auto_reset.h"
-#include "base/callback.h"
-#include "base/fuchsia/process_context.h"
-#include "base/fuchsia/scoped_service_binding.h"
-#include "base/fuchsia/test_component_context_for_process.h"
-#include "base/run_loop.h"
-#include "base/test/bind.h"
-#include "base/test/task_environment.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ui {
-namespace {
-
-using fuchsia::accessibility::semantics::Node;
-
-class AXFuchsiaSemanticProviderDelegate
-    : public AXFuchsiaSemanticProvider::Delegate {
- public:
-  AXFuchsiaSemanticProviderDelegate() = default;
-  ~AXFuchsiaSemanticProviderDelegate() override = default;
-
-  bool OnSemanticsManagerConnectionClosed() override {
-    on_semantics_manager_connectionClosed_called_ = true;
-    return true;
-  }
-
-  bool OnAccessibilityAction(
-      uint32_t node_id,
-      fuchsia::accessibility::semantics::Action action) override {
-    on_accessibility_action_called_ = true;
-    on_accessibility_action_node_id_ = node_id;
-    on_accessibility_action_action_ = std::move(action);
-    return true;
-  }
-
-  void OnHitTest(fuchsia::math::PointF point,
-                 AXFuchsiaSemanticProvider::HitTestCallback callback) override {
-    on_hit_test_called_ = true;
-    on_hit_test_point_ = std::move(point);
-  }
-
-  void OnSemanticsEnabled(bool enabled) override {
-    on_semantics_enabled_called_ = true;
-  }
-
-  bool on_semantics_manager_connectionClosed_called_;
-  bool on_accessibility_action_called_;
-  uint32_t on_accessibility_action_node_id_ = 10000000;
-  fuchsia::accessibility::semantics::Action on_accessibility_action_action_;
-  bool on_hit_test_called_;
-  fuchsia::math::PointF on_hit_test_point_;
-  bool on_semantics_enabled_called_;
-};
-
-// Returns a semantic tree of the form:
-// (0 (1 2 (3 4 (5))))
-std::vector<Node> TreeNodes() {
-  Node node_0;
-  node_0.set_node_id(0u);
-  node_0.set_child_ids({1u, 2u});
-
-  Node node_1;
-  node_1.set_node_id(1u);
-
-  Node node_2;
-  node_2.set_node_id(2u);
-  node_2.set_child_ids({3u, 4u});
-
-  Node node_3;
-  node_3.set_node_id(3u);
-
-  Node node_4;
-  node_4.set_node_id(4u);
-  node_4.set_child_ids({5u});
-
-  Node node_5;
-  node_5.set_node_id(5u);
-
-  std::vector<Node> update;
-  update.push_back(std::move(node_0));
-  update.push_back(std::move(node_1));
-  update.push_back(std::move(node_2));
-  update.push_back(std::move(node_3));
-  update.push_back(std::move(node_4));
-  update.push_back(std::move(node_5));
-  return update;
-}
-
-class AXFuchsiaSemanticProviderTest
-    : public ::testing::Test,
-      public fuchsia::accessibility::semantics::SemanticsManager,
-      public fuchsia::accessibility::semantics::SemanticTree {
- public:
-  AXFuchsiaSemanticProviderTest()
-      : semantics_manager_bindings_(test_context_.additional_services(), this),
-        semantic_tree_binding_(this) {}
-  ~AXFuchsiaSemanticProviderTest() override = default;
-  AXFuchsiaSemanticProviderTest(const AXFuchsiaSemanticProviderTest&) = delete;
-  AXFuchsiaSemanticProviderTest& operator=(
-      const AXFuchsiaSemanticProviderTest&) = delete;
-  void SetUp() override {
-    auto view_ref_pair = scenic::ViewRefPair::New();
-    delegate_ = std::make_unique<AXFuchsiaSemanticProviderDelegate>();
-
-    semantic_provider_ = std::make_unique<AXFuchsiaSemanticProvider>(
-        std::move(view_ref_pair.view_ref), 2.0f, delegate_.get());
-  }
-
- protected:
-  // fuchsia::accessibility::semantics::SemanticsManager implementation.
-  void RegisterViewForSemantics(
-      fuchsia::ui::views::ViewRef view_ref,
-      fidl::InterfaceHandle<fuchsia::accessibility::semantics::SemanticListener>
-          listener,
-      fidl::InterfaceRequest<fuchsia::accessibility::semantics::SemanticTree>
-          semantic_tree_request) final {
-    semantic_listener_ = listener.Bind();
-    semantic_listener_.set_error_handler([](zx_status_t status) {
-      // The test should fail if an error occurs.
-      ADD_FAILURE();
-    });
-    semantic_tree_binding_.Bind(std::move(semantic_tree_request));
-  }
-
-  // fuchsia::accessibility::semantics::SemanticTree implementation.
-  void UpdateSemanticNodes(
-      std::vector<fuchsia::accessibility::semantics::Node> nodes) final {
-    num_update_semantic_nodes_called_++;
-  }
-  void DeleteSemanticNodes(std::vector<uint32_t> node_ids) final {
-    num_delete_semantic_nodes_called_++;
-  }
-  void CommitUpdates(CommitUpdatesCallback callback) final { callback(); }
-  void SendSemanticEvent(
-      fuchsia::accessibility::semantics::SemanticEvent semantic_event,
-      SendSemanticEventCallback callback) override {
-    callback();
-  }
-
-  // Required because of |test_context_|.
-  base::test::SingleThreadTaskEnvironment task_environment_{
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
-  base::TestComponentContextForProcess test_context_;
-  // Binding to fake Semantics Manager Fuchsia service, implemented by this test
-  // class.
-  base::ScopedServiceBinding<
-      fuchsia::accessibility::semantics::SemanticsManager>
-      semantics_manager_bindings_;
-
-  uint32_t num_update_semantic_nodes_called_ = 0;
-  uint32_t num_delete_semantic_nodes_called_ = 0;
-
-  base::RepeatingClosure on_commit_;
-
-  fuchsia::accessibility::semantics::SemanticListenerPtr semantic_listener_;
-  fidl::Binding<fuchsia::accessibility::semantics::SemanticTree>
-      semantic_tree_binding_;
-  std::unique_ptr<AXFuchsiaSemanticProviderDelegate> delegate_;
-  std::unique_ptr<AXFuchsiaSemanticProvider> semantic_provider_;
-};
-
-TEST_F(AXFuchsiaSemanticProviderTest, HandlesOnSemanticsConnectionClosed) {
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-  semantic_tree_binding_.Close(ZX_ERR_PEER_CLOSED);
-  loop.RunUntilIdle();
-  EXPECT_TRUE(delegate_->on_semantics_manager_connectionClosed_called_);
-}
-
-TEST_F(AXFuchsiaSemanticProviderTest, HandlesOnAccessibilityAction) {
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-  bool action_handled = false;
-  semantic_listener_->OnAccessibilityActionRequested(
-      /*node_id=*/1u, fuchsia::accessibility::semantics::Action::DEFAULT,
-      [&action_handled](bool handled) { action_handled = handled; });
-  loop.RunUntilIdle();
-  EXPECT_TRUE(action_handled);
-  EXPECT_TRUE(delegate_->on_accessibility_action_called_);
-  EXPECT_EQ(delegate_->on_accessibility_action_node_id_, 1u);
-  EXPECT_EQ(delegate_->on_accessibility_action_action_,
-            fuchsia::accessibility::semantics::Action::DEFAULT);
-}
-
-TEST_F(AXFuchsiaSemanticProviderTest, HandlesOnHitTest) {
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-
-  // Note that the point is sent here and will be converted according to the
-  // device scale used. Only then it gets sent to the handler, which receives
-  // the value already with the proper scaling.
-  fuchsia::math::PointF point;
-  point.x = 4;
-  point.y = 6;
-  semantic_listener_->HitTest(std::move(point), [](auto...) {});
-  loop.RunUntilIdle();
-  EXPECT_TRUE(delegate_->on_hit_test_called_);
-  EXPECT_EQ(delegate_->on_hit_test_point_.x, 8.0);
-  EXPECT_EQ(delegate_->on_hit_test_point_.y, 12.0);
-}
-
-TEST_F(AXFuchsiaSemanticProviderTest, HandlesOnSemanticsEnabled) {
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-  semantic_listener_->OnSemanticsModeChanged(false, [](auto...) {});
-  loop.RunUntilIdle();
-  EXPECT_TRUE(delegate_->on_semantics_enabled_called_);
-}
-
-TEST_F(AXFuchsiaSemanticProviderTest, SendsRootOnly) {
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-  Node root;
-  root.set_node_id(0u);
-  EXPECT_TRUE(semantic_provider_->Update(std::move(root)));
-  loop.RunUntilIdle();
-  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-}
-
-TEST_F(AXFuchsiaSemanticProviderTest, SendsNodesFromRootToLeaves) {
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-  auto tree_nodes = TreeNodes();
-  for (auto& node : tree_nodes) {
-    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
-  }
-  loop.RunUntilIdle();
-  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-}
-
-TEST_F(AXFuchsiaSemanticProviderTest, SendsNodesFromLeavesToRoot) {
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-  auto nodes = TreeNodes();
-  std::reverse(nodes.begin(), nodes.end());
-  for (auto& node : nodes) {
-    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
-  }
-  loop.RunUntilIdle();
-  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
-
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-}
-
-TEST_F(AXFuchsiaSemanticProviderTest,
-       SendsNodesOnlyAfterParentNoLongerPointsToDeletedChild) {
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-  auto tree_nodes = TreeNodes();
-  for (auto& node : tree_nodes) {
-    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
-  }
-  loop.RunUntilIdle();
-  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-
-  // Deletes node 5, which is a child of 4.
-  EXPECT_TRUE(semantic_provider_->Delete(5u));
-  loop.RunUntilIdle();
-
-  // Commit is pending, because the parent still points to the child.
-  EXPECT_TRUE(semantic_provider_->HasPendingUpdates());
-
-  Node node_4;
-  node_4.set_node_id(4u);
-  node_4.set_child_ids({});
-  EXPECT_TRUE(semantic_provider_->Update(std::move(node_4)));
-  loop.RunUntilIdle();
-  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
-  EXPECT_EQ(num_delete_semantic_nodes_called_, 1u);
-
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-}
-
-TEST_F(AXFuchsiaSemanticProviderTest,
-       SendsNodesOnlyAfterDanglingChildIsDeleted) {
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-  auto tree_nodes = TreeNodes();
-  for (auto& node : tree_nodes) {
-    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
-  }
-  loop.RunUntilIdle();
-  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-
-  Node node_4;
-  node_4.set_node_id(4u);
-  node_4.set_child_ids({});  // This removes child 5.
-  EXPECT_TRUE(semantic_provider_->Update(std::move(node_4)));
-  loop.RunUntilIdle();
-  EXPECT_TRUE(semantic_provider_->HasPendingUpdates());
-
-  EXPECT_TRUE(semantic_provider_->Delete(5u));
-  loop.RunUntilIdle();
-  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
-  EXPECT_EQ(num_delete_semantic_nodes_called_, 1u);
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-}
-
-TEST_F(AXFuchsiaSemanticProviderTest, ReparentsNodeWithADeletion) {
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-  auto tree_nodes = TreeNodes();
-  for (auto& node : tree_nodes) {
-    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
-  }
-  loop.RunUntilIdle();
-  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-
-  // Deletes node 4 to reparent its child (5).
-  EXPECT_TRUE(semantic_provider_->Delete(4u));
-  loop.RunUntilIdle();
-  EXPECT_TRUE(semantic_provider_->HasPendingUpdates());
-
-  // Add child 5 to another node.
-  Node node_1;
-  node_1.set_node_id(1u);
-  node_1.set_child_ids({5u});
-  EXPECT_TRUE(semantic_provider_->Update(std::move(node_1)));
-  loop.RunUntilIdle();
-  EXPECT_TRUE(semantic_provider_->HasPendingUpdates());
-
-  Node node_4;
-  node_4.set_node_id(4u);
-  node_4.set_child_ids({});
-  EXPECT_TRUE(semantic_provider_->Update(std::move(node_4)));
-  loop.RunUntilIdle();
-
-  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
-  EXPECT_EQ(num_delete_semantic_nodes_called_, 1u);
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-}
-
-TEST_F(AXFuchsiaSemanticProviderTest, ReparentsNodeWithAnUpdate) {
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-  auto tree_nodes = TreeNodes();
-  for (auto& node : tree_nodes) {
-    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
-  }
-  loop.RunUntilIdle();
-  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-
-  // Add child 5 to another node. Note that 5 will have two parents, and the
-  // commit must be held until it has only one.
-  Node node_1;
-  node_1.set_node_id(1u);
-  node_1.set_child_ids({5u});
-  EXPECT_TRUE(semantic_provider_->Update(std::move(node_1)));
-  loop.RunUntilIdle();
-  EXPECT_TRUE(semantic_provider_->HasPendingUpdates());
-
-  // Updates node 4 to no longer point to 5.
-  Node node_4;
-  node_4.set_node_id(4u);
-  node_4.set_child_ids({});
-  EXPECT_TRUE(semantic_provider_->Update(std::move(node_4)));
-  loop.RunUntilIdle();
-
-  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
-  EXPECT_EQ(num_delete_semantic_nodes_called_, 0u);
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-}
-
-TEST_F(AXFuchsiaSemanticProviderTest, ChangesRoot) {
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-  auto tree_nodes = TreeNodes();
-  for (auto& node : tree_nodes) {
-    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
-  }
-  loop.RunUntilIdle();
-  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-
-  Node new_root;
-  new_root.set_node_id(0u);
-  new_root.set_child_ids({1u, 2u});
-  EXPECT_TRUE(semantic_provider_->Update(std::move(new_root)));
-  loop.RunUntilIdle();
-  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
-  EXPECT_EQ(num_delete_semantic_nodes_called_, 0u);
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-}
-
-TEST_F(AXFuchsiaSemanticProviderTest, BatchesUpdates) {
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-  std::vector<Node> updates;
-  for (uint32_t i = 0; i < 30; ++i) {
-    Node node;
-    node.set_node_id(i);
-    node.set_child_ids({i + 1});
-    updates.push_back(std::move(node));
-  }
-  updates.back().clear_child_ids();
-
-  for (auto& node : updates) {
-    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
-  }
-  loop.RunUntilIdle();
-
-  // 30 nodes in batches of 16 (default value of maximum nodes per update call),
-  // should result in two update calls to the semantics API.
-  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-}
-
-TEST_F(AXFuchsiaSemanticProviderTest, ClearsTree) {
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-  auto tree_nodes = TreeNodes();
-  for (auto& node : tree_nodes) {
-    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
-  }
-  loop.RunUntilIdle();
-  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-
-  semantic_provider_->Clear();
-  loop.RunUntilIdle();
-  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
-  EXPECT_EQ(num_delete_semantic_nodes_called_, 1u);
-  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
-}
-
-}  // namespace
-}  // namespace ui
diff --git a/ui/accessibility/platform/fuchsia/semantic_provider_unittest.cc b/ui/accessibility/platform/fuchsia/semantic_provider_unittest.cc
new file mode 100644
index 0000000..40dd014
--- /dev/null
+++ b/ui/accessibility/platform/fuchsia/semantic_provider_unittest.cc
@@ -0,0 +1,490 @@
+// 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 "semantic_provider.h"
+
+#include <fuchsia/accessibility/semantics/cpp/fidl.h>
+#include <lib/ui/scenic/cpp/view_ref_pair.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "base/auto_reset.h"
+#include "base/callback.h"
+#include "base/fuchsia/process_context.h"
+#include "base/fuchsia/scoped_service_binding.h"
+#include "base/fuchsia/test_component_context_for_process.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui {
+namespace {
+
+using fuchsia::accessibility::semantics::Node;
+
+class AXFuchsiaSemanticProviderDelegate
+    : public AXFuchsiaSemanticProvider::Delegate {
+ public:
+  AXFuchsiaSemanticProviderDelegate() = default;
+  ~AXFuchsiaSemanticProviderDelegate() override = default;
+
+  bool OnSemanticsManagerConnectionClosed() override {
+    on_semantics_manager_connection_closed_called_ = true;
+    return true;
+  }
+
+  bool OnAccessibilityAction(
+      uint32_t node_id,
+      fuchsia::accessibility::semantics::Action action) override {
+    on_accessibility_action_called_ = true;
+    on_accessibility_action_node_id_ = node_id;
+    on_accessibility_action_action_ = std::move(action);
+    return true;
+  }
+
+  void OnHitTest(fuchsia::math::PointF point,
+                 AXFuchsiaSemanticProvider::HitTestCallback callback) override {
+    on_hit_test_called_ = true;
+    on_hit_test_point_ = std::move(point);
+  }
+
+  void OnSemanticsEnabled(bool enabled) override {
+    on_semantics_enabled_called_ = true;
+  }
+
+  bool on_semantics_manager_connection_closed_called_;
+  bool on_accessibility_action_called_;
+  uint32_t on_accessibility_action_node_id_ = 10000000;
+  fuchsia::accessibility::semantics::Action on_accessibility_action_action_;
+  bool on_hit_test_called_;
+  fuchsia::math::PointF on_hit_test_point_;
+  bool on_semantics_enabled_called_;
+};
+
+// Returns a semantic tree of the form:
+// (0 (1 2 (3 4 (5))))
+std::vector<Node> TreeNodes() {
+  Node node_0;
+  node_0.set_node_id(0u);
+  node_0.set_child_ids({1u, 2u});
+
+  Node node_1;
+  node_1.set_node_id(1u);
+
+  Node node_2;
+  node_2.set_node_id(2u);
+  node_2.set_child_ids({3u, 4u});
+
+  Node node_3;
+  node_3.set_node_id(3u);
+
+  Node node_4;
+  node_4.set_node_id(4u);
+  node_4.set_child_ids({5u});
+
+  Node node_5;
+  node_5.set_node_id(5u);
+
+  std::vector<Node> update;
+  update.push_back(std::move(node_0));
+  update.push_back(std::move(node_1));
+  update.push_back(std::move(node_2));
+  update.push_back(std::move(node_3));
+  update.push_back(std::move(node_4));
+  update.push_back(std::move(node_5));
+  return update;
+}
+
+class AXFuchsiaSemanticProviderTest
+    : public ::testing::Test,
+      public fuchsia::accessibility::semantics::SemanticsManager,
+      public fuchsia::accessibility::semantics::SemanticTree {
+ public:
+  AXFuchsiaSemanticProviderTest()
+      : semantics_manager_bindings_(test_context_.additional_services(), this),
+        semantic_tree_binding_(this) {}
+  ~AXFuchsiaSemanticProviderTest() override = default;
+  AXFuchsiaSemanticProviderTest(const AXFuchsiaSemanticProviderTest&) = delete;
+  AXFuchsiaSemanticProviderTest& operator=(
+      const AXFuchsiaSemanticProviderTest&) = delete;
+  void SetUp() override {
+    auto view_ref_pair = scenic::ViewRefPair::New();
+    delegate_ = std::make_unique<AXFuchsiaSemanticProviderDelegate>();
+
+    semantic_provider_ = std::make_unique<AXFuchsiaSemanticProvider>(
+        std::move(view_ref_pair.view_ref), 2.0f, delegate_.get());
+
+    // Spin the loop to allow registration with the SemanticsManager to be
+    // processed.
+    base::RunLoop().RunUntilIdle();
+  }
+
+ protected:
+  // fuchsia::accessibility::semantics::SemanticsManager implementation.
+  void RegisterViewForSemantics(
+      fuchsia::ui::views::ViewRef view_ref,
+      fidl::InterfaceHandle<fuchsia::accessibility::semantics::SemanticListener>
+          listener,
+      fidl::InterfaceRequest<fuchsia::accessibility::semantics::SemanticTree>
+          semantic_tree_request) final {
+    semantic_listener_ = listener.Bind();
+    semantic_listener_.set_error_handler([](zx_status_t status) {
+      // The test should fail if an error occurs.
+      ADD_FAILURE();
+    });
+    semantic_tree_binding_.Bind(std::move(semantic_tree_request));
+  }
+
+  // fuchsia::accessibility::semantics::SemanticTree implementation.
+  void UpdateSemanticNodes(
+      std::vector<fuchsia::accessibility::semantics::Node> nodes) final {
+    num_update_semantic_nodes_called_++;
+  }
+  void DeleteSemanticNodes(std::vector<uint32_t> node_ids) final {
+    num_delete_semantic_nodes_called_++;
+  }
+  void CommitUpdates(CommitUpdatesCallback callback) final { callback(); }
+  void SendSemanticEvent(
+      fuchsia::accessibility::semantics::SemanticEvent semantic_event,
+      SendSemanticEventCallback callback) override {
+    callback();
+  }
+
+  // Required because of |test_context_|.
+  base::test::SingleThreadTaskEnvironment task_environment_{
+      base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
+  base::TestComponentContextForProcess test_context_;
+  // Binding to fake Semantics Manager Fuchsia service, implemented by this test
+  // class.
+  base::ScopedServiceBinding<
+      fuchsia::accessibility::semantics::SemanticsManager>
+      semantics_manager_bindings_;
+
+  uint32_t num_update_semantic_nodes_called_ = 0;
+  uint32_t num_delete_semantic_nodes_called_ = 0;
+
+  base::RepeatingClosure on_commit_;
+
+  fuchsia::accessibility::semantics::SemanticListenerPtr semantic_listener_;
+  fidl::Binding<fuchsia::accessibility::semantics::SemanticTree>
+      semantic_tree_binding_;
+  std::unique_ptr<AXFuchsiaSemanticProviderDelegate> delegate_;
+  std::unique_ptr<AXFuchsiaSemanticProvider> semantic_provider_;
+};
+
+TEST_F(AXFuchsiaSemanticProviderTest, HandlesOnSemanticsConnectionClosed) {
+  semantic_tree_binding_.Close(ZX_ERR_PEER_CLOSED);
+
+  // Spin the loop to allow the channel-close to be handled.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(delegate_->on_semantics_manager_connection_closed_called_);
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, HandlesOnAccessibilityAction) {
+  bool action_handled = false;
+  semantic_listener_->OnAccessibilityActionRequested(
+      /*node_id=*/1u, fuchsia::accessibility::semantics::Action::DEFAULT,
+      [&action_handled](bool handled) { action_handled = handled; });
+
+  // Spin the loop to handle the request, and receive the response.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(action_handled);
+  EXPECT_TRUE(delegate_->on_accessibility_action_called_);
+  EXPECT_EQ(delegate_->on_accessibility_action_node_id_, 1u);
+  EXPECT_EQ(delegate_->on_accessibility_action_action_,
+            fuchsia::accessibility::semantics::Action::DEFAULT);
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, HandlesOnHitTest) {
+  // Note that the point is sent here and will be converted according to the
+  // device scale used. Only then it gets sent to the handler, which receives
+  // the value already with the proper scaling.
+  fuchsia::math::PointF point;
+  point.x = 4;
+  point.y = 6;
+  semantic_listener_->HitTest(std::move(point), [](auto...) {});
+
+  // Spin the loop to allow the call to be processed.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(delegate_->on_hit_test_called_);
+  EXPECT_EQ(delegate_->on_hit_test_point_.x, 8.0);
+  EXPECT_EQ(delegate_->on_hit_test_point_.y, 12.0);
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, HandlesOnSemanticsEnabled) {
+  semantic_listener_->OnSemanticsModeChanged(false, [](auto...) {});
+
+  // Spin the loop to handle the call.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(delegate_->on_semantics_enabled_called_);
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, SendsRootOnly) {
+  Node root;
+  root.set_node_id(0u);
+  EXPECT_TRUE(semantic_provider_->Update(std::move(root)));
+
+  // Spin the loop to process the update call.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, SendsNodesFromRootToLeaves) {
+  auto tree_nodes = TreeNodes();
+  for (auto& node : tree_nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+
+  // Spin the loop to process the queued update calls.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, SendsNodesFromLeavesToRoot) {
+  auto nodes = TreeNodes();
+  std::reverse(nodes.begin(), nodes.end());
+  for (auto& node : nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+
+  // Spin the loop to process the queued update calls.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest,
+       SendsNodesOnlyAfterParentNoLongerPointsToDeletedChild) {
+  auto tree_nodes = TreeNodes();
+  for (auto& node : tree_nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+
+  // Spin the loop to process the queued update calls.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+
+  // Deletes node 5, which is a child of 4.
+  EXPECT_TRUE(semantic_provider_->Delete(5u));
+
+  // Spin the loop to process the deletion call.
+  base::RunLoop().RunUntilIdle();
+
+  // Commit is pending, because the parent still points to the child.
+  EXPECT_TRUE(semantic_provider_->HasPendingUpdates());
+
+  Node node_4;
+  node_4.set_node_id(4u);
+  node_4.set_child_ids({});
+  EXPECT_TRUE(semantic_provider_->Update(std::move(node_4)));
+
+  // Spin the loop to process the node update.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
+  EXPECT_EQ(num_delete_semantic_nodes_called_, 1u);
+
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest,
+       SendsNodesOnlyAfterDanglingChildIsDeleted) {
+  auto tree_nodes = TreeNodes();
+  for (auto& node : tree_nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+
+  // Spin the loop to process the queued update calls.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+
+  Node node_4;
+  node_4.set_node_id(4u);
+  node_4.set_child_ids({});  // This removes child 5.
+  EXPECT_TRUE(semantic_provider_->Update(std::move(node_4)));
+
+  // Spin the loop to process the update call.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(semantic_provider_->HasPendingUpdates());
+
+  EXPECT_TRUE(semantic_provider_->Delete(5u));
+
+  // Spin the loop to process the deletion.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
+  EXPECT_EQ(num_delete_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, ReparentsNodeWithADeletion) {
+  auto tree_nodes = TreeNodes();
+  for (auto& node : tree_nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+
+  // Spin the loop to process the queued update calls.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+
+  // Deletes node 4 to reparent its child (5).
+  EXPECT_TRUE(semantic_provider_->Delete(4u));
+
+  // Spin the loop to process the deletion.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(semantic_provider_->HasPendingUpdates());
+
+  // Add child 5 to another node.
+  Node node_1;
+  node_1.set_node_id(1u);
+  node_1.set_child_ids({5u});
+  EXPECT_TRUE(semantic_provider_->Update(std::move(node_1)));
+
+  // Spin the loop to process the update.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(semantic_provider_->HasPendingUpdates());
+
+  Node node_4;
+  node_4.set_node_id(4u);
+  node_4.set_child_ids({});
+  EXPECT_TRUE(semantic_provider_->Update(std::move(node_4)));
+
+  // Spin the loop to process the update.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
+  EXPECT_EQ(num_delete_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, ReparentsNodeWithAnUpdate) {
+  auto tree_nodes = TreeNodes();
+  for (auto& node : tree_nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+
+  // Spin the loop to process the queued update calls.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+
+  // Add child 5 to another node. Note that 5 will have two parents, and the
+  // commit must be held until it has only one.
+  Node node_1;
+  node_1.set_node_id(1u);
+  node_1.set_child_ids({5u});
+  EXPECT_TRUE(semantic_provider_->Update(std::move(node_1)));
+
+  // Spin the loop to process the update.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(semantic_provider_->HasPendingUpdates());
+
+  // Updates node 4 to no longer point to 5.
+  Node node_4;
+  node_4.set_node_id(4u);
+  node_4.set_child_ids({});
+  EXPECT_TRUE(semantic_provider_->Update(std::move(node_4)));
+
+  // Spin the loop to process the update.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
+  EXPECT_EQ(num_delete_semantic_nodes_called_, 0u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, ChangesRoot) {
+  auto tree_nodes = TreeNodes();
+  for (auto& node : tree_nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+
+  // Spin the loop to process the queued updated calls.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+
+  Node new_root;
+  new_root.set_node_id(0u);
+  new_root.set_child_ids({1u, 2u});
+  EXPECT_TRUE(semantic_provider_->Update(std::move(new_root)));
+
+  // Spin the loop to process the update.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
+  EXPECT_EQ(num_delete_semantic_nodes_called_, 0u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, BatchesUpdates) {
+  std::vector<Node> updates;
+  for (uint32_t i = 0; i < 30; ++i) {
+    Node node;
+    node.set_node_id(i);
+    node.set_child_ids({i + 1});
+    updates.push_back(std::move(node));
+  }
+  updates.back().clear_child_ids();
+
+  for (auto& node : updates) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+
+  // Spin the loop to process the queued update calls.
+  base::RunLoop().RunUntilIdle();
+
+  // 30 nodes in batches of 16 (default value of maximum nodes per update call),
+  // should result in two update calls to the semantics API.
+  EXPECT_EQ(num_update_semantic_nodes_called_, 2u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+TEST_F(AXFuchsiaSemanticProviderTest, ClearsTree) {
+  auto tree_nodes = TreeNodes();
+  for (auto& node : tree_nodes) {
+    EXPECT_TRUE(semantic_provider_->Update(std::move(node)));
+  }
+
+  // Spin the loop to process the queued update calls.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+
+  semantic_provider_->Clear();
+
+  // Spin the loop to process the clear-tree call.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(num_update_semantic_nodes_called_, 1u);
+  EXPECT_EQ(num_delete_semantic_nodes_called_, 1u);
+  EXPECT_FALSE(semantic_provider_->HasPendingUpdates());
+}
+
+}  // namespace
+}  // namespace ui
diff --git a/ui/accessibility/platform/inspect/ax_event_recorder_win.cc b/ui/accessibility/platform/inspect/ax_event_recorder_win.cc
new file mode 100644
index 0000000..9b977bd
--- /dev/null
+++ b/ui/accessibility/platform/inspect/ax_event_recorder_win.cc
@@ -0,0 +1,329 @@
+// Copyright 2014 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/accessibility/platform/inspect/ax_event_recorder_win.h"
+
+#include <stdint.h>
+#include <wrl/client.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/scoped_bstr.h"
+#include "base/win/scoped_variant.h"
+#include "third_party/iaccessible2/ia2_api_all.h"
+#include "ui/accessibility/platform/ax_platform_node_win.h"
+#include "ui/accessibility/platform/inspect/ax_inspect_utils_win.h"
+#include "ui/base/win/atl_module.h"
+#include "ui/gfx/win/hwnd_util.h"
+
+namespace ui {
+
+namespace {
+
+std::string RoleVariantToString(const base::win::ScopedVariant& role) {
+  if (role.type() == VT_I4) {
+    return base::WideToUTF8(IAccessibleRoleToString(V_I4(role.ptr())));
+  } else if (role.type() == VT_BSTR) {
+    return base::WideToUTF8(
+        std::wstring(V_BSTR(role.ptr()), SysStringLen(V_BSTR(role.ptr()))));
+  }
+  return std::string();
+}
+
+HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) {
+  Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
+  HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider));
+  return SUCCEEDED(hr)
+             ? service_provider->QueryService(IID_IAccessible2, accessible2)
+             : hr;
+}
+
+HRESULT QueryIAccessibleText(IAccessible* accessible,
+                             IAccessibleText** accessible_text) {
+  Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
+  HRESULT hr = accessible->QueryInterface(IID_PPV_ARGS(&service_provider));
+  return SUCCEEDED(hr) ? service_provider->QueryService(IID_IAccessibleText,
+                                                        accessible_text)
+                       : hr;
+}
+
+std::string BstrToPrettyUTF8(BSTR bstr) {
+  std::wstring wstr(bstr, SysStringLen(bstr));
+
+  // IAccessibleText returns the text you get by appending all static text
+  // children, with an "embedded object character" for each non-text child.
+  // Pretty-print the embedded object character as <obj> so that test output
+  // is human-readable.
+  std::wstring embedded_character = base::UTF16ToWide(
+      std::u16string(1, AXPlatformNodeBase::kEmbeddedCharacter));
+  base::ReplaceChars(wstr, embedded_character, L"<obj>", &wstr);
+
+  return base::WideToUTF8(wstr);
+}
+
+std::string AccessibilityEventToStringUTF8(int32_t event_id) {
+  return base::WideToUTF8(AccessibilityEventToString(event_id));
+}
+
+}  // namespace
+
+// static
+AXEventRecorderWin* AXEventRecorderWin::instance_ = nullptr;
+
+// static
+CALLBACK void AXEventRecorderWin::WinEventHookThunk(HWINEVENTHOOK handle,
+                                                    DWORD event,
+                                                    HWND hwnd,
+                                                    LONG obj_id,
+                                                    LONG child_id,
+                                                    DWORD event_thread,
+                                                    DWORD event_time) {
+  if (instance_) {
+    instance_->OnWinEventHook(handle, event, hwnd, obj_id, child_id,
+                              event_thread, event_time);
+  }
+}
+
+AXEventRecorderWin::AXEventRecorderWin(base::ProcessId pid,
+                                       const AXTreeSelector& selector,
+                                       ListenerType listenerType) {
+  CHECK(!instance_) << "There can be only one instance of"
+                    << " AccessibilityEventRecorder at a time.";
+
+  // Get pid by a selector if the selectors specifies a valid process.
+  HWND hwnd_by_selector = GetHWNDBySelector(selector);
+  if (hwnd_by_selector != NULL) {
+    DWORD pid_by_selector = 0;
+    GetWindowThreadProcessId(hwnd_by_selector, &pid_by_selector);
+    if (pid_by_selector != 0) {
+      pid = static_cast<DWORD>(pid_by_selector);
+    }
+  }
+
+  int context =
+      listenerType == kSync ? WINEVENT_INCONTEXT : WINEVENT_OUTOFCONTEXT;
+  win_event_hook_handle_ =
+      SetWinEventHook(EVENT_MIN, EVENT_MAX, GetModuleHandle(NULL),
+                      &AXEventRecorderWin::WinEventHookThunk, pid,
+                      0,  // Hook all threads
+                      context);
+  CHECK(win_event_hook_handle_);
+  instance_ = this;
+}
+
+AXEventRecorderWin::~AXEventRecorderWin() {
+  UnhookWinEvent(win_event_hook_handle_);
+  instance_ = nullptr;
+}
+
+void AXEventRecorderWin::OnWinEventHook(HWINEVENTHOOK handle,
+                                        DWORD event,
+                                        HWND hwnd,
+                                        LONG obj_id,
+                                        LONG child_id,
+                                        DWORD event_thread,
+                                        DWORD event_time) {
+  Microsoft::WRL::ComPtr<IAccessible> browser_accessible;
+  HRESULT hr = ::AccessibleObjectFromWindow(hwnd, obj_id,
+                                            IID_PPV_ARGS(&browser_accessible));
+  if (FAILED(hr)) {
+    // Note: our event hook will pick up some superfluous events we
+    // don't care about, so it's safe to just ignore these failures.
+    // Same below for other HRESULT checks.
+    VLOG(1) << "Ignoring result " << hr << " from AccessibleObjectFromWindow";
+    return;
+  }
+
+  base::win::ScopedVariant childid_variant(child_id);
+  Microsoft::WRL::ComPtr<IDispatch> dispatch;
+  hr = browser_accessible->get_accChild(childid_variant, &dispatch);
+  if (hr != S_OK || !dispatch) {
+    VLOG(1) << "Ignoring result " << hr << " and result " << dispatch.Get()
+            << " from get_accChild";
+    return;
+  }
+
+  Microsoft::WRL::ComPtr<IAccessible> iaccessible;
+  hr = dispatch.As(&iaccessible);
+  if (FAILED(hr)) {
+    VLOG(1) << "Ignoring result " << hr << " from QueryInterface";
+    return;
+  }
+
+  std::string event_str = AccessibilityEventToStringUTF8(event);
+  if (event_str.empty()) {
+    VLOG(1) << "Ignoring event " << event;
+    return;
+  }
+
+  base::win::ScopedVariant childid_self(CHILDID_SELF);
+  base::win::ScopedVariant role;
+  iaccessible->get_accRole(childid_self, role.Receive());
+  base::win::ScopedVariant state;
+  iaccessible->get_accState(childid_self, state.Receive());
+  int ia_state = V_I4(state.ptr());
+  std::string hwnd_class_name = base::WideToUTF8(gfx::GetClassName(hwnd));
+
+  // Caret is special:
+  // Log all caret events  that occur, with their window class, so that we can
+  // test to make sure they are only occurring on the desired window class.
+  if (ROLE_SYSTEM_CARET == V_I4(role.ptr())) {
+    std::wstring state_str = IAccessibleStateToString(ia_state);
+    std::string log = base::StringPrintf(
+        "%s role=ROLE_SYSTEM_CARET %ls window_class=%s", event_str.c_str(),
+        state_str.c_str(), hwnd_class_name.c_str());
+    OnEvent(log);
+    return;
+  }
+
+  if (only_web_events_) {
+    if (hwnd_class_name != "Chrome_RenderWidgetHostHWND")
+      return;
+
+    Microsoft::WRL::ComPtr<IServiceProvider> service_provider;
+    hr = iaccessible->QueryInterface(IID_PPV_ARGS(&service_provider));
+    if (FAILED(hr))
+      return;
+
+    Microsoft::WRL::ComPtr<IAccessible> content_document;
+    hr = service_provider->QueryService(GUID_IAccessibleContentDocument,
+                                        IID_PPV_ARGS(&content_document));
+    if (FAILED(hr))
+      return;
+  }
+
+  base::win::ScopedBstr name_bstr;
+  iaccessible->get_accName(childid_self, name_bstr.Receive());
+  base::win::ScopedBstr value_bstr;
+  iaccessible->get_accValue(childid_self, value_bstr.Receive());
+
+  // Avoid flakiness. Events fired on a WINDOW are out of the control
+  // of a test.
+  if (role.type() == VT_I4 && ROLE_SYSTEM_WINDOW == V_I4(role.ptr())) {
+    VLOG(1) << "Ignoring event " << event << " on ROLE_SYSTEM_WINDOW";
+    return;
+  }
+
+  // Avoid flakiness. The "offscreen" state depends on whether the browser
+  // window is frontmost or not, and "hottracked" depends on whether the
+  // mouse cursor happens to be over the element.
+  ia_state &= (~STATE_SYSTEM_OFFSCREEN & ~STATE_SYSTEM_HOTTRACKED);
+
+  // The "readonly" state is set on almost every node and doesn't typically
+  // change, so filter it out to keep the output less verbose.
+  ia_state &= ~STATE_SYSTEM_READONLY;
+
+  AccessibleStates ia2_state = 0;
+  Microsoft::WRL::ComPtr<IAccessible2> iaccessible2;
+  hr = QueryIAccessible2(iaccessible.Get(), &iaccessible2);
+  bool has_ia2 = SUCCEEDED(hr) && iaccessible2;
+
+  std::wstring html_tag;
+  std::wstring obj_class;
+  std::wstring html_id;
+
+  if (has_ia2) {
+    // IA2::get_states can kill the recorder.
+    iaccessible2->get_states(&ia2_state);
+    if (!instance_)
+      return;
+
+    base::win::ScopedBstr attributes_bstr;
+    if (S_OK == iaccessible2->get_attributes(attributes_bstr.Receive())) {
+      std::vector<std::wstring> ia2_attributes = base::SplitString(
+          std::wstring(attributes_bstr.Get(), attributes_bstr.Length()),
+          std::wstring(1, ';'), base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
+      for (std::wstring& attr : ia2_attributes) {
+        if (base::StartsWith(attr, L"class:"))
+          obj_class = attr.substr(6);  // HTML or view class
+        if (base::StartsWith(attr, L"id:")) {
+          html_id = std::wstring(L"#");
+          html_id += attr.substr(3);
+        }
+        if (base::StartsWith(attr, L"tag:")) {
+          html_tag = attr.substr(4);
+        }
+      }
+    }
+  }
+
+  std::string log = base::StringPrintf("%s on", event_str.c_str());
+  if (!html_tag.empty()) {
+    // HTML node with tag
+    log += base::StringPrintf(" <%s%s%s%s>", base::WideToUTF8(html_tag).c_str(),
+                              base::WideToUTF8(html_id).c_str(),
+                              obj_class.empty() ? "" : ".",
+                              base::WideToUTF8(obj_class).c_str());
+  } else if (!obj_class.empty()) {
+    // Non-HTML node with class
+    log += base::StringPrintf(" class=%s", base::WideToUTF8(obj_class).c_str());
+  }
+
+  log += base::StringPrintf(" role=%s", RoleVariantToString(role).c_str());
+  if (name_bstr.Length() > 0)
+    log += base::StringPrintf(" name=\"%s\"",
+                              BstrToPrettyUTF8(name_bstr.Get()).c_str());
+  if (value_bstr.Length() > 0) {
+    bool is_document =
+        role.type() == VT_I4 && ROLE_SYSTEM_DOCUMENT == V_I4(role.ptr());
+    // Don't show actual document value, which is a URL, in order to avoid
+    // machine-based differences in tests.
+    log += is_document
+               ? " value~=[doc-url]"
+               : base::StringPrintf(" value=\"%s\"",
+                                    BstrToPrettyUTF8(value_bstr.Get()).c_str());
+  }
+  log += " ";
+  log += base::WideToUTF8(IAccessibleStateToString(ia_state));
+  log += " ";
+  log += base::WideToUTF8(IAccessible2StateToString(ia2_state));
+
+  // Group position, e.g. L3, 5 of 7
+  LONG group_level, similar_items_in_group, position_in_group;
+  if (has_ia2 &&
+      iaccessible2->get_groupPosition(&group_level, &similar_items_in_group,
+                                      &position_in_group) == S_OK) {
+    if (group_level)
+      log += base::StringPrintf(" level=%ld", group_level);
+    if (position_in_group)
+      log += base::StringPrintf(" PosInSet=%ld", position_in_group);
+    if (similar_items_in_group)
+      log += base::StringPrintf(" SetSize=%ld", similar_items_in_group);
+  }
+
+  // For TEXT_REMOVED and TEXT_INSERTED events, query the text that was
+  // inserted or removed and include that in the log.
+  Microsoft::WRL::ComPtr<IAccessibleText> accessible_text;
+  hr = QueryIAccessibleText(iaccessible.Get(), &accessible_text);
+  if (SUCCEEDED(hr)) {
+    if (event == IA2_EVENT_TEXT_REMOVED) {
+      IA2TextSegment old_text;
+      if (SUCCEEDED(accessible_text->get_oldText(&old_text))) {
+        log += base::StringPrintf(" old_text={'%s' start=%ld end=%ld}",
+                                  BstrToPrettyUTF8(old_text.text).c_str(),
+                                  old_text.start, old_text.end);
+      }
+    }
+    if (event == IA2_EVENT_TEXT_INSERTED) {
+      IA2TextSegment new_text;
+      if (SUCCEEDED(accessible_text->get_newText(&new_text))) {
+        log += base::StringPrintf(" new_text={'%s' start=%ld end=%ld}",
+                                  BstrToPrettyUTF8(new_text.text).c_str(),
+                                  new_text.start, new_text.end);
+      }
+    }
+  }
+
+  log =
+      base::UTF16ToUTF8(base::CollapseWhitespace(base::UTF8ToUTF16(log), true));
+  OnEvent(log);
+}
+
+}  // namespace ui
diff --git a/ui/accessibility/platform/inspect/ax_event_recorder_win.h b/ui/accessibility/platform/inspect/ax_event_recorder_win.h
new file mode 100644
index 0000000..bac6f4e
--- /dev/null
+++ b/ui/accessibility/platform/inspect/ax_event_recorder_win.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 UI_ACCESSIBILITY_PLATFORM_INSPECT_AX_EVENT_RECORDER_WIN_H_
+#define UI_ACCESSIBILITY_PLATFORM_INSPECT_AX_EVENT_RECORDER_WIN_H_
+
+#include <oleacc.h>
+
+#include "base/process/process_handle.h"
+#include "ui/accessibility/ax_export.h"
+#include "ui/accessibility/platform/inspect/ax_event_recorder.h"
+#include "ui/accessibility/platform/inspect/ax_inspect.h"
+
+namespace ui {
+
+class AX_EXPORT AXEventRecorderWin : public ui::AXEventRecorder {
+ public:
+  // Flag values that specify the way events are handled.
+  enum ListenerType {
+    kSync,   // Events are handled synchronously.
+    kAsync,  // Events are handled asynchronously.
+  };
+
+  AXEventRecorderWin(base::ProcessId pid,
+                     const ui::AXTreeSelector& selector,
+                     ListenerType listenerType = kSync);
+
+  AXEventRecorderWin(const AXEventRecorderWin&) = delete;
+  AXEventRecorderWin& operator=(const AXEventRecorderWin&) = delete;
+
+  ~AXEventRecorderWin() override;
+
+  // Callback registered by SetWinEventHook. Just calls OnWinEventHook.
+  static CALLBACK void WinEventHookThunk(HWINEVENTHOOK handle,
+                                         DWORD event,
+                                         HWND hwnd,
+                                         LONG obj_id,
+                                         LONG child_id,
+                                         DWORD event_thread,
+                                         DWORD event_time);
+
+ private:
+  // Called by the thunk registered by SetWinEventHook. Retrieves accessibility
+  // info about the node the event was fired on and appends a string to
+  // the event log.
+  void OnWinEventHook(HWINEVENTHOOK handle,
+                      DWORD event,
+                      HWND hwnd,
+                      LONG obj_id,
+                      LONG child_id,
+                      DWORD event_thread,
+                      DWORD event_time);
+
+  HWINEVENTHOOK win_event_hook_handle_;
+  static AXEventRecorderWin* instance_;
+};
+
+}  // namespace ui
+
+#endif  // UI_ACCESSIBILITY_PLATFORM_INSPECT_AX_EVENT_RECORDER_WIN_H_
diff --git a/ui/accessibility/platform/inspect/ax_inspect_utils_auralinux.cc b/ui/accessibility/platform/inspect/ax_inspect_utils_auralinux.cc
index eb805871..42c6ba2 100644
--- a/ui/accessibility/platform/inspect/ax_inspect_utils_auralinux.cc
+++ b/ui/accessibility/platform/inspect/ax_inspect_utils_auralinux.cc
@@ -432,7 +432,8 @@
       char* name = atspi_accessible_get_name(child, &error);
       if (!error && name) {
         if ((!title.empty() && title == name) ||
-            base::MatchPattern(name, selector.pattern)) {
+            (!selector.pattern.empty() &&
+             base::MatchPattern(name, selector.pattern))) {
           matched_children.emplace_back(name, child);
         }
       }
diff --git a/ui/android/java/res/drawable/ic_apps_blue_24dp.xml b/ui/android/java/res/drawable/ic_apps_blue_24dp.xml
index 2c47099..611dd90 100644
--- a/ui/android/java/res/drawable/ic_apps_blue_24dp.xml
+++ b/ui/android/java/res/drawable/ic_apps_blue_24dp.xml
@@ -4,8 +4,6 @@
      found in the LICENSE file. -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        tools:targetApi="21"
         android:width="24dp"
         android:height="24dp"
         android:viewportWidth="24.0"
diff --git a/ui/android/java/res/values-night/colors.xml b/ui/android/java/res/values-night/colors.xml
index 67ccac818..9913544 100644
--- a/ui/android/java/res/values-night/colors.xml
+++ b/ui/android/java/res/values-night/colors.xml
@@ -20,22 +20,22 @@
     <color name="default_icon_color_inverse_baseline">@color/default_icon_color_inverse_dark</color>
     <color name="default_icon_color_accent1_baseline">@color/default_icon_color_blue_light</color>
     <color name="default_icon_color_on_accent1_baseline">@color/default_icon_color_on_accent1_dark</color>
-    <color name="default_icon_color_secondary">@color/default_icon_color_secondary_light</color>
+    <color name="default_icon_color_secondary_baseline">@color/default_icon_color_secondary_light</color>
     <color name="hairline_stroke_color_baseline">@color/hairline_stroke_color_light</color>
     <color name="default_icon_color_disabled">@color/default_icon_color_disabled_light</color>
-    <color name="default_icon_color_disabled_inverse">@color/default_icon_color_disabled_dark</color>
+    <color name="default_icon_color_disabled_inverse_baseline">@color/default_icon_color_disabled_dark</color>
 
     <!-- Common background and branding color. -->
     <!-- TODO(https://crbug.com/1272001): Remove @color/default_bg_color. -->
     <color name="default_bg_color">@color/default_bg_color_baseline</color>
     <color name="default_bg_color_baseline">@color/default_bg_color_dark</color>
     <color name="default_bg_color_secondary">@color/default_bg_color_secondary_dark</color>
-    <color name="default_bg_color_elev_1_baseline">@color/default_bg_color_dark_elev_1</color>
-    <color name="default_bg_color_elev_2_baseline">@color/default_bg_color_dark_elev_2</color>
-    <color name="default_bg_color_elev_3_baseline">@color/default_bg_color_dark_elev_3</color>
-    <color name="default_bg_color_elev_4_baseline">@color/default_bg_color_dark_elev_4</color>
-    <color name="default_bg_color_elev_5_baseline">@color/default_bg_color_dark_elev_5</color>
-    <color name="default_bg_color_elev_6_baseline">@color/default_bg_color_dark_elev_6</color>
+    <color name="default_bg_color_elev_1_baseline">@color/default_bg_color_dark_elev_1_baseline</color>
+    <color name="default_bg_color_elev_2_baseline">@color/default_bg_color_dark_elev_2_baseline</color>
+    <color name="default_bg_color_elev_3_baseline">@color/default_bg_color_dark_elev_3_baseline</color>
+    <color name="default_bg_color_elev_4_baseline">@color/default_bg_color_dark_elev_4_baseline</color>
+    <color name="default_bg_color_elev_5_baseline">@color/default_bg_color_dark_elev_5_baseline</color>
+    <color name="default_bg_color_elev_6_baseline">@color/default_bg_color_dark_elev_6_baseline</color>
     <!-- Legacy elevation/surface colors kept around for tab switcher, which has
          a separate feature to control enabling baseline. This is most
          noticeable in the title background color for thumbnails, legacy will
@@ -46,13 +46,13 @@
     <color name="default_bg_color_blue">@color/default_bg_color_blue_light</color>
 
     <!-- Bottom sheet colors -->
-    <color name="sheet_bg_color">@color/default_bg_color_dark_elev_4</color>
+    <color name="sheet_bg_color">@color/default_bg_color_dark_elev_4_baseline</color>
 
     <!-- Dialog colors -->
-    <color name="dialog_bg_color">@color/dialog_bg_color_dark</color>
+    <color name="dialog_bg_color">@color/dialog_bg_color_dark_baseline</color>
 
     <!-- Snackbar colors -->
-    <color name="snackbar_background_color">@color/default_bg_color_dark_elev_4</color>
+    <color name="snackbar_background_color_baseline">@color/default_bg_color_dark_elev_4_baseline</color>
 
     <!-- Ripple colors for clickable widgets -->
     <color name="ripple_color_blue">@color/ripple_color_blue_light</color>
@@ -86,8 +86,8 @@
     <color name="adaptive_toolbar_preference_header_omnibox">@color/adaptive_toolbar_preference_header_omnibox_dark</color>
 
     <!-- Menu colors -->
-    <color name="menu_action_bar_bg_color">@color/menu_action_bar_bg_color_dark</color>
-    <color name="menu_item_bg_color">@color/menu_item_bg_color_dark</color>
+    <color name="menu_action_bar_bg_color_baseline">@color/menu_action_bar_bg_color_dark_baseline</color>
+    <color name="menu_item_bg_color">@color/menu_item_bg_color_dark_baseline</color>
 
     <!-- Other colors -->
     <color name="default_red">@color/default_red_light</color>
diff --git a/ui/android/java/res/values-night/styles.xml b/ui/android/java/res/values-night/styles.xml
index fda4fcd..6ffdf366 100644
--- a/ui/android/java/res/values-night/styles.xml
+++ b/ui/android/java/res/values-night/styles.xml
@@ -6,8 +6,6 @@
 <resources xmlns:tools="http://schemas.android.com/tools">
     <style name="ColorOverlay.Ui" parent="ColorOverlay.Ui.DayNight">
         <!-- Common icon colors for drawables. -->
-        <item name="default_icon_color_secondary">@color/default_icon_color_secondary_light</item>
         <item name="default_icon_color_disabled">@color/default_icon_color_disabled_light</item>
-        <item name="default_icon_color_disabled_inverse">@color/default_icon_color_disabled_dark</item>
     </style>
 </resources>
diff --git a/ui/android/java/res/values/attrs.xml b/ui/android/java/res/values/attrs.xml
index d5c3085..12775f3 100644
--- a/ui/android/java/res/values/attrs.xml
+++ b/ui/android/java/res/values/attrs.xml
@@ -27,9 +27,7 @@
     <attr name="default_bg_color_dynamic" format="color"/>
     <attr name="divider_line_bg_color_dynamic" format="color"/>
 
-    <attr name="default_icon_color_secondary" format="color" />
     <attr name="default_icon_color_disabled" format="color" />
-    <attr name="default_icon_color_disabled_inverse" format="color" />
 
     <declare-styleable name="ButtonCompat">
         <!-- The color of the button background. -->
diff --git a/ui/android/java/res/values/color_palette.xml b/ui/android/java/res/values/color_palette.xml
index af68f2326..26df560c 100644
--- a/ui/android/java/res/values/color_palette.xml
+++ b/ui/android/java/res/values/color_palette.xml
@@ -113,9 +113,11 @@
     <color name="google_red_600">@color/baseline_error_600</color>
 
     <!-- No 2021 color palette mapping exists. -->
+    <color name="black_alpha_5">#0D000000</color>
     <color name="black_alpha_11" tools:ignore="UnusedResources">#1D000000</color>
     <color name="black_alpha_38" tools:ignore="UnusedResources">#61000000</color>
     <color name="black_alpha_65" tools:ignore="UnusedResources">#A6000000</color>
+    <color name="white_alpha_5">#0DFFFFFF</color>
     <color name="white_alpha_6">#0FFFFFFF</color>
     <color name="white_alpha_8" tools:ignore="UnusedResources">#14FFFFFF</color>
     <color name="white_alpha_12" tools:ignore="UnusedResources">#1FFFFFFF</color>
diff --git a/ui/android/java/res/values/semantic_colors_adaptive.xml b/ui/android/java/res/values/semantic_colors_adaptive.xml
index ca08d6b..3074675 100644
--- a/ui/android/java/res/values/semantic_colors_adaptive.xml
+++ b/ui/android/java/res/values/semantic_colors_adaptive.xml
@@ -29,11 +29,11 @@
     <color name="default_icon_color_inverse_baseline" tools:ignore="UnusedResources">@color/default_icon_color_inverse_light</color>
     <color name="default_icon_color_accent1_baseline" tools:ignore="UnusedResources">@color/default_icon_color_blue_dark</color>
     <color name="default_icon_color_on_accent1_baseline">@color/default_icon_color_on_accent1_light</color>
-    <color name="default_icon_color_secondary" tools:ignore="UnusedResources">@color/default_icon_color_secondary_dark</color>
+    <color name="default_icon_color_secondary_baseline" tools:ignore="UnusedResources">@color/default_icon_color_secondary_dark</color>
 
     <color name="hairline_stroke_color_baseline">@color/hairline_stroke_color_dark</color>
     <color name="default_icon_color_disabled">@color/default_icon_color_disabled_dark</color>
-    <color name="default_icon_color_disabled_inverse" tools:ignore="UnusedResources">@color/default_icon_color_disabled_light</color>
+    <color name="default_icon_color_disabled_inverse_baseline" tools:ignore="UnusedResources">@color/default_icon_color_disabled_light</color>
 
     <!-- Common background and branding color. -->
     <!-- TODO(https://crbug.com/1272001): Remove @color/default_bg_color. -->
@@ -55,18 +55,18 @@
     <color name="legacy_bg_color_elev_4">@color/legacy_bg_color_light_elev_4</color>
 
     <!-- Dialog colors -->
-    <color name="dialog_bg_color">@color/dialog_bg_color_light</color>
-    <color name="dialog_bg_color_light">@color/default_bg_color_light</color>
-    <color name="dialog_bg_color_dark">@color/default_bg_color_dark_elev_3</color>
+    <color name="dialog_bg_color">@color/dialog_bg_color_light_baseline</color>
+    <color name="dialog_bg_color_light_baseline">@color/default_bg_color_baseline</color>
+    <color name="dialog_bg_color_dark_baseline">@color/default_bg_color_dark_elev_3_baseline</color>
 
     <!-- Snackbar colors -->
-    <color name="snackbar_background_color">@color/default_bg_color</color>
+    <color name="snackbar_background_color_baseline">@color/default_bg_color_baseline</color>
 
     <!-- Infobar colors -->
-    <color name="infobar_background_color" tools:ignore="UnusedResources">@color/snackbar_background_color</color>
+    <color name="infobar_background_color" tools:ignore="UnusedResources">@color/snackbar_background_color_baseline</color>
 
     <!-- Bottom sheet colors -->
-    <color name="sheet_bg_color" tools:ignore="UnusedResources">@color/default_bg_color</color>
+    <color name="sheet_bg_color" tools:ignore="UnusedResources">@color/default_bg_color_baseline</color>
 
     <!-- Ripple colors for clickable widgets -->
     <color name="ripple_color_blue">@color/ripple_color_blue_dark</color>
@@ -113,12 +113,12 @@
     <!-- TODO(https://crbug.com/1232518): Move light/dark colors to non-adaptive
          file. These reference non-color palette colors which is currently
          blocked by PRESUBMIT scripts. -->
-    <color name="menu_action_bar_bg_color">@color/menu_action_bar_bg_color_light</color>
-    <color name="menu_action_bar_bg_color_light">@color/default_bg_color_light_elev_1_baseline</color>
-    <color name="menu_action_bar_bg_color_dark">@color/default_bg_color_dark_elev_6</color>
-    <color name="menu_item_bg_color">@color/menu_item_bg_color_light</color>
-    <color name="menu_item_bg_color_light">@color/default_bg_color_light</color>
-    <color name="menu_item_bg_color_dark">@color/default_bg_color_dark_elev_5</color>
+    <color name="menu_action_bar_bg_color_baseline">@color/menu_action_bar_bg_color_light_baseline</color>
+    <color name="menu_action_bar_bg_color_light_baseline">@color/default_bg_color_light_elev_1_baseline</color>
+    <color name="menu_action_bar_bg_color_dark_baseline">@color/default_bg_color_dark_elev_6_baseline</color>
+    <color name="menu_item_bg_color">@color/menu_item_bg_color_light_baseline</color>
+    <color name="menu_item_bg_color_light_baseline">@color/default_bg_color_light</color>
+    <color name="menu_item_bg_color_dark_baseline">@color/default_bg_color_dark_elev_5_baseline</color>
 
     <!-- Other colors -->
     <color name="default_red" tools:ignore="UnusedResources">@color/default_red_dark</color>
diff --git a/ui/android/java/res/values/semantic_colors_non_adaptive.xml b/ui/android/java/res/values/semantic_colors_non_adaptive.xml
index f96e2d1..2dc4976 100644
--- a/ui/android/java/res/values/semantic_colors_non_adaptive.xml
+++ b/ui/android/java/res/values/semantic_colors_non_adaptive.xml
@@ -71,12 +71,12 @@
     <color name="default_bg_color_light_elev_6_baseline" tools:ignore="UnusedResources">@color/baseline_neutral_0_with_neutral_600_alpha_20_with_primary_600_2</color>
     <color name="legacy_bg_color_light_elev_1">@color/modern_white</color>
     <color name="legacy_bg_color_light_elev_4">@color/modern_white</color>
-    <color name="default_bg_color_dark_elev_1" tools:ignore="UnusedResources">@color/baseline_neutral_900_with_neutral_200_alpha_5_with_primary_200_alpha_2</color>
-    <color name="default_bg_color_dark_elev_2" tools:ignore="UnusedResources">@color/baseline_neutral_900_with_neutral_200_alpha_8_with_primary_200_alpha_2</color>
-    <color name="default_bg_color_dark_elev_3" tools:ignore="UnusedResources">@color/baseline_neutral_900_with_neutral_200_alpha_11_with_primary_200_alpha_2</color>
-    <color name="default_bg_color_dark_elev_4" tools:ignore="UnusedResources">@color/baseline_neutral_900_with_neutral_200_alpha_12_with_primary_200_alpha_2</color>
-    <color name="default_bg_color_dark_elev_5" tools:ignore="UnusedResources">@color/baseline_neutral_900_with_neutral_200_alpha_14_with_primary_200_alpha_2</color>
-    <color name="default_bg_color_dark_elev_6" tools:ignore="UnusedResources">@color/baseline_neutral_900_with_neutral_200_alpha_20_with_primary_200_alpha_2</color>
+    <color name="default_bg_color_dark_elev_1_baseline" tools:ignore="UnusedResources">@color/baseline_neutral_900_with_neutral_200_alpha_5_with_primary_200_alpha_2</color>
+    <color name="default_bg_color_dark_elev_2_baseline" tools:ignore="UnusedResources">@color/baseline_neutral_900_with_neutral_200_alpha_8_with_primary_200_alpha_2</color>
+    <color name="default_bg_color_dark_elev_3_baseline" tools:ignore="UnusedResources">@color/baseline_neutral_900_with_neutral_200_alpha_11_with_primary_200_alpha_2</color>
+    <color name="default_bg_color_dark_elev_4_baseline" tools:ignore="UnusedResources">@color/baseline_neutral_900_with_neutral_200_alpha_12_with_primary_200_alpha_2</color>
+    <color name="default_bg_color_dark_elev_5_baseline" tools:ignore="UnusedResources">@color/baseline_neutral_900_with_neutral_200_alpha_14_with_primary_200_alpha_2</color>
+    <color name="default_bg_color_dark_elev_6_baseline" tools:ignore="UnusedResources">@color/baseline_neutral_900_with_neutral_200_alpha_20_with_primary_200_alpha_2</color>
     <color name="legacy_bg_color_dark_elev_1">@color/baseline_neutral_900_with_neutral_200_alpha_5_with_primary_200_alpha_2</color>
     <color name="legacy_bg_color_dark_elev_4">@color/baseline_neutral_900_with_neutral_200_alpha_12_with_primary_200_alpha_2</color>
 
diff --git a/ui/android/java/res/values/styles.xml b/ui/android/java/res/values/styles.xml
index 638920d..e7e2582 100644
--- a/ui/android/java/res/values/styles.xml
+++ b/ui/android/java/res/values/styles.xml
@@ -6,9 +6,7 @@
 <resources xmlns:tools="http://schemas.android.com/tools">
     <style name="ColorOverlay.Ui" parent="ColorOverlay.Ui.DayNight">
         <!-- Common icon colors for drawables. -->
-        <item name="default_icon_color_secondary">@color/default_icon_color_secondary_dark</item>
         <item name="default_icon_color_disabled">@color/default_icon_color_disabled_dark</item>
-        <item name="default_icon_color_disabled_inverse">@color/default_icon_color_disabled_light</item>
     </style>
     <style name="ColorOverlay.Ui.DayNight" parent="">
         <!-- TODO(https://crbug.com/1216642): Move color attributes here. -->
diff --git a/ui/base/dragdrop/os_exchange_data.cc b/ui/base/dragdrop/os_exchange_data.cc
index 6f066b8..9757d98f 100644
--- a/ui/base/dragdrop/os_exchange_data.cc
+++ b/ui/base/dragdrop/os_exchange_data.cc
@@ -34,6 +34,14 @@
   return provider_->DidOriginateFromRenderer();
 }
 
+void OSExchangeData::MarkAsFromPrivileged() {
+  is_from_privileged_ = true;
+}
+
+bool OSExchangeData::IsFromPrivileged() const {
+  return is_from_privileged_;
+}
+
 void OSExchangeData::SetString(const std::u16string& data) {
   provider_->SetString(data);
 }
diff --git a/ui/base/dragdrop/os_exchange_data.h b/ui/base/dragdrop/os_exchange_data.h
index 3aabcad..08216228 100644
--- a/ui/base/dragdrop/os_exchange_data.h
+++ b/ui/base/dragdrop/os_exchange_data.h
@@ -78,6 +78,12 @@
   void MarkOriginatedFromRenderer();
   bool DidOriginateFromRenderer() const;
 
+  // Marks drag data as from privileged WebContents. This is used to
+  // make sure non-privileged WebContents will not accept drop data from
+  // privileged WebContents or vise versa.
+  void MarkAsFromPrivileged();
+  bool IsFromPrivileged() const;
+
   // These functions add data to the OSExchangeData object of various Chrome
   // types. The OSExchangeData object takes care of translating the data into
   // a format suitable for exchange with the OS.
@@ -193,6 +199,8 @@
  private:
   // Provides the actual data.
   std::unique_ptr<OSExchangeDataProvider> provider_;
+
+  bool is_from_privileged_ = false;
 };
 
 }  // namespace ui
diff --git a/ui/base/metadata/base_type_conversion.cc b/ui/base/metadata/base_type_conversion.cc
index ae3e4d2..1207348 100644
--- a/ui/base/metadata/base_type_conversion.cc
+++ b/ui/base/metadata/base_type_conversion.cc
@@ -85,7 +85,10 @@
 
 std::u16string TypeConverter<gfx::Insets>::ToString(
     const gfx::Insets& source_value) {
-  return base::ASCIIToUTF16(source_value.ToString());
+  // This is different from gfx::Insets::ToString().
+  return base::ASCIIToUTF16(
+      base::StringPrintf("%d,%d,%d,%d", source_value.top(), source_value.left(),
+                         source_value.bottom(), source_value.right()));
 }
 
 std::u16string TypeConverter<gfx::Point>::ToString(
diff --git a/ui/base/metadata/base_type_conversion_unittest.cc b/ui/base/metadata/base_type_conversion_unittest.cc
index 287d085..c7f1f821 100644
--- a/ui/base/metadata/base_type_conversion_unittest.cc
+++ b/ui/base/metadata/base_type_conversion_unittest.cc
@@ -197,7 +197,7 @@
   std::u16string to_string =
       ui::metadata::TypeConverter<gfx::Insets>::ToString(kInsets);
 
-  EXPECT_EQ(to_string, base::ASCIIToUTF16(kInsets.ToString()));
+  EXPECT_EQ(to_string, u"3,5,7,9");
 }
 
 TEST_F(TypeConversionTest, TestConversion_StringToInsets) {
diff --git a/ui/chromeos/ui_chromeos_strings.grd b/ui/chromeos/ui_chromeos_strings.grd
index 3121810..c1b60fb 100644
--- a/ui/chromeos/ui_chromeos_strings.grd
+++ b/ui/chromeos/ui_chromeos_strings.grd
@@ -6,6 +6,7 @@
     <output filename="grit/ui_chromeos_strings.h" type="rc_header">
       <emit emit_type='prepend'></emit>
     </output>
+    <output filename="ui_chromeos_strings_af.pak" type="data_package" lang="af" />
     <output filename="ui_chromeos_strings_am.pak" type="data_package" lang="am" />
     <output filename="ui_chromeos_strings_ar.pak" type="data_package" lang="ar" />
     <output filename="ui_chromeos_strings_bg.pak" type="data_package" lang="bg" />
@@ -62,6 +63,7 @@
     <output filename="ui_chromeos_strings_vi.pak" type="data_package" lang="vi" />
     <output filename="ui_chromeos_strings_zh-CN.pak" type="data_package" lang="zh-CN" />
     <output filename="ui_chromeos_strings_zh-TW.pak" type="data_package" lang="zh-TW" />
+    <output filename="ui_chromeos_strings_zu.pak" type="data_package" lang="zu" />
 
     <!-- Pseudolocales -->
     <output filename="ui_chromeos_strings_ar-XB.pak" type="data_package" lang="ar-XB" />
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css
index 8c7693a..930eb27 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -2647,6 +2647,11 @@
   height: 32px;
 }
 
+/* "search drive" row */
+list.autocomplete-suggestions > li:first-of-type {
+  color: var(--cros-text-color-secondary);
+}
+
 list.autocomplete-suggestions > li > div.detail-icon {
   flex: none;
   height: 32px;
@@ -2660,7 +2665,6 @@
 }
 
 list.autocomplete-suggestions > li > div.detail-text em {
-  color: var(--cros-text-color-secondary);
   font-style: normal;
 }
 
@@ -2668,7 +2672,7 @@
   -webkit-mask-image: url(../images/files/ui/search.svg);
   -webkit-mask-position: center;
   -webkit-mask-repeat: no-repeat;
-  background-color: var(--cros-menu-icon-color);
+  background-color: currentColor;
 }
 
 list.autocomplete-suggestions > [selected],
diff --git a/ui/gfx/geometry/BUILD.gn b/ui/gfx/geometry/BUILD.gn
index 1436c095..89af97d5 100644
--- a/ui/gfx/geometry/BUILD.gn
+++ b/ui/gfx/geometry/BUILD.gn
@@ -19,10 +19,13 @@
     "insets.h",
     "insets_conversions.cc",
     "insets_conversions.h",
-    "insets_f.cc",
     "insets_f.h",
+    "insets_outsets_base.h",
+    "insets_outsets_f_base.h",
     "matrix3_f.cc",
     "matrix3_f.h",
+    "outsets.h",
+    "outsets_f.h",
     "point.cc",
     "point.h",
     "point3_f.cc",
diff --git a/ui/gfx/geometry/insets.cc b/ui/gfx/geometry/insets.cc
index ac08be8d..d15833d 100644
--- a/ui/gfx/geometry/insets.cc
+++ b/ui/gfx/geometry/insets.cc
@@ -4,41 +4,29 @@
 
 #include "ui/gfx/geometry/insets.h"
 
-#include "base/strings/stringprintf.h"
 #include "ui/gfx/geometry/insets_conversions.h"
 #include "ui/gfx/geometry/insets_f.h"
 #include "ui/gfx/geometry/vector2d.h"
 
 namespace gfx {
 
-std::string Insets::ToString() const {
-  // Print members in the same order of the constructor parameters.
-  return base::StringPrintf("%d,%d,%d,%d", top(),  left(), bottom(), right());
-}
-
 void Insets::Offset(const gfx::Vector2d& vector) {
-  Set(base::ClampAdd(top(), vector.y()), base::ClampAdd(left(), vector.x()),
-      base::ClampSub(bottom(), vector.y()),
-      base::ClampSub(right(), vector.x()));
-}
-
-void Insets::SetToMax(const gfx::Insets& other) {
-  top_ = std::max(top_, other.top_);
-  left_ = std::max(left_, other.left_);
-  bottom_ = std::max(bottom_, other.bottom_);
-  right_ = std::max(right_, other.right_);
+  set_left_right(base::ClampAdd(left(), vector.x()),
+                 base::ClampSub(right(), vector.x()));
+  set_top_bottom(base::ClampAdd(top(), vector.y()),
+                 base::ClampSub(bottom(), vector.y()));
 }
 
 Insets ScaleToCeiledInsets(const Insets& insets, float x_scale, float y_scale) {
   if (x_scale == 1.f && y_scale == 1.f)
     return insets;
-  return ToCeiledInsets(ScaleInsets(gfx::InsetsF(insets), x_scale, y_scale));
+  return ToCeiledInsets(ScaleInsets(InsetsF(insets), x_scale, y_scale));
 }
 
 Insets ScaleToCeiledInsets(const Insets& insets, float scale) {
   if (scale == 1.f)
     return insets;
-  return ToCeiledInsets(ScaleInsets(gfx::InsetsF(insets), scale));
+  return ToCeiledInsets(ScaleInsets(InsetsF(insets), scale));
 }
 
 Insets ScaleToFlooredInsets(const Insets& insets,
@@ -46,13 +34,13 @@
                             float y_scale) {
   if (x_scale == 1.f && y_scale == 1.f)
     return insets;
-  return ToFlooredInsets(ScaleInsets(gfx::InsetsF(insets), x_scale, y_scale));
+  return ToFlooredInsets(ScaleInsets(InsetsF(insets), x_scale, y_scale));
 }
 
 Insets ScaleToFlooredInsets(const Insets& insets, float scale) {
   if (scale == 1.f)
     return insets;
-  return ToFlooredInsets(ScaleInsets(gfx::InsetsF(insets), scale));
+  return ToFlooredInsets(ScaleInsets(InsetsF(insets), scale));
 }
 
 Insets ScaleToRoundedInsets(const Insets& insets,
@@ -60,13 +48,13 @@
                             float y_scale) {
   if (x_scale == 1.f && y_scale == 1.f)
     return insets;
-  return ToRoundedInsets(ScaleInsets(gfx::InsetsF(insets), x_scale, y_scale));
+  return ToRoundedInsets(ScaleInsets(InsetsF(insets), x_scale, y_scale));
 }
 
 Insets ScaleToRoundedInsets(const Insets& insets, float scale) {
   if (scale == 1.f)
     return insets;
-  return ToRoundedInsets(ScaleInsets(gfx::InsetsF(insets), scale));
+  return ToRoundedInsets(ScaleInsets(InsetsF(insets), scale));
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/geometry/insets.h b/ui/gfx/geometry/insets.h
index 23a85918..05f15974 100644
--- a/ui/gfx/geometry/insets.h
+++ b/ui/gfx/geometry/insets.h
@@ -5,112 +5,41 @@
 #ifndef UI_GFX_GEOMETRY_INSETS_H_
 #define UI_GFX_GEOMETRY_INSETS_H_
 
-#include <string>
-
 #include "base/numerics/clamped_math.h"
 #include "ui/gfx/geometry/geometry_export.h"
 #include "ui/gfx/geometry/insets_f.h"
-#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/insets_outsets_base.h"
 
 namespace gfx {
 
 class Vector2d;
 
-// Represents the widths of the four borders or margins of an unspecified
-// rectangle. An Insets stores the thickness of the top, left, bottom and right
-// edges, without storing the actual size and position of the rectangle itself.
-//
 // This can be used to represent a space within a rectangle, by "shrinking" the
 // rectangle by the inset amount on all four sides. Alternatively, it can
 // represent a border that has a different thickness on each side.
-class GEOMETRY_EXPORT Insets {
+class GEOMETRY_EXPORT Insets : public InsetsOutsetsBase<Insets> {
  public:
-  constexpr Insets() : top_(0), left_(0), bottom_(0), right_(0) {}
-  constexpr explicit Insets(int all)
-      : top_(all),
-        left_(all),
-        bottom_(GetClampedValue(all, all)),
-        right_(GetClampedValue(all, all)) {}
-  constexpr explicit Insets(int vertical, int horizontal)
-      : top_(vertical),
-        left_(horizontal),
-        bottom_(GetClampedValue(vertical, vertical)),
-        right_(GetClampedValue(horizontal, horizontal)) {}
-  constexpr Insets(int top, int left, int bottom, int right)
-      : top_(top),
-        left_(left),
-        bottom_(GetClampedValue(top, bottom)),
-        right_(GetClampedValue(left, right)) {}
+  using InsetsOutsetsBase::InsetsOutsetsBase;
 
-  constexpr int top() const { return top_; }
-  constexpr int left() const { return left_; }
-  constexpr int bottom() const { return bottom_; }
-  constexpr int right() const { return right_; }
+  // Avoid this constructor in blink code because it's easy to make mistakes in
+  // the order of the parameters. Use the other constructors and set_*()
+  // methods instead.
+  constexpr Insets(int vertical, int horizontal)
+      : Insets(vertical, horizontal, vertical, horizontal) {}
 
-  // Returns the total width taken up by the insets, which is the sum of the
-  // left and right insets.
-  constexpr int width() const { return left_ + right_; }
-
-  // Returns the total height taken up by the insets, which is the sum of the
-  // top and bottom insets.
-  constexpr int height() const { return top_ + bottom_; }
-
-  // Returns the sum of the left and right insets as the width, the sum of the
-  // top and bottom insets as the height.
-  constexpr Size size() const { return Size(width(), height()); }
-
-  // Returns true if the insets are empty.
-  bool IsEmpty() const { return width() == 0 && height() == 0; }
-
-  void set_top(int top) {
-    top_ = top;
-    bottom_ = GetClampedValue(top_, bottom_);
+  // Avoid this constructor in blink code because it's easy to make mistakes in
+  // the order of the parameters. Use the other constructors and set_*()
+  // methods instead.
+  constexpr Insets(int top, int left, int bottom, int right) {
+    set_left_right(left, right);
+    set_top_bottom(top, bottom);
   }
-  void set_left(int left) {
-    left_ = left;
-    right_ = GetClampedValue(left_, right_);
-  }
-  void set_bottom(int bottom) { bottom_ = GetClampedValue(top_, bottom); }
-  void set_right(int right) { right_ = GetClampedValue(left_, right); }
 
+  // Avoid this method in blink code because it's easy to make mistakes in the
+  // order of the parameters. Use set_*() methods instead.
   void Set(int top, int left, int bottom, int right) {
-    top_ = top;
-    left_ = left;
-    bottom_ = GetClampedValue(top_, bottom);
-    right_ = GetClampedValue(left_, right);
-  }
-
-  // Sets each side to the maximum of the side and the corresponding side of
-  // |other|.
-  void SetToMax(const gfx::Insets& other);
-
-  bool operator==(const Insets& insets) const {
-    return top_ == insets.top_ && left_ == insets.left_ &&
-           bottom_ == insets.bottom_ && right_ == insets.right_;
-  }
-
-  bool operator!=(const Insets& insets) const {
-    return !(*this == insets);
-  }
-
-  void operator+=(const Insets& insets) {
-    top_ = base::ClampAdd(top_, insets.top_);
-    left_ = base::ClampAdd(left_, insets.left_);
-    bottom_ = GetClampedValue(top_, base::ClampAdd(bottom_, insets.bottom_));
-    right_ = GetClampedValue(left_, base::ClampAdd(right_, insets.right_));
-  }
-
-  void operator-=(const Insets& insets) {
-    top_ = base::ClampSub(top_, insets.top_);
-    left_ = base::ClampSub(left_, insets.left_);
-    bottom_ = GetClampedValue(top_, base::ClampSub(bottom_, insets.bottom_));
-    right_ = GetClampedValue(left_, base::ClampSub(right_, insets.right_));
-  }
-
-  Insets operator-() const {
-    return Insets(-base::MakeClampedNum(top_), -base::MakeClampedNum(left_),
-                  -base::MakeClampedNum(bottom_),
-                  -base::MakeClampedNum(right_));
+    set_left_right(left, right);
+    set_top_bottom(top, bottom);
   }
 
   // Adjusts the vertical and horizontal dimensions by the values described in
@@ -122,58 +51,8 @@
     return InsetsF(static_cast<float>(top()), static_cast<float>(left()),
                    static_cast<float>(bottom()), static_cast<float>(right()));
   }
-
-  // Returns a string representation of the insets.
-  std::string ToString() const;
-
- private:
-  int top_;
-  int left_;
-  int bottom_;
-  int right_;
-
-  // See ui/gfx/geometry/rect.h
-  // Returns true iff a+b would overflow max int.
-  static constexpr bool AddWouldOverflow(int a, int b) {
-    // In this function, GCC tries to make optimizations that would only work if
-    // max - a wouldn't overflow but it isn't smart enough to notice that a > 0.
-    // So cast everything to unsigned to avoid this.  As it is guaranteed that
-    // max - a and b are both already positive, the cast is a noop.
-    //
-    // This is intended to be: a > 0 && max - a < b
-    return a > 0 && b > 0 &&
-           static_cast<unsigned>(std::numeric_limits<int>::max() - a) <
-               static_cast<unsigned>(b);
-  }
-
-  // Returns true iff a+b would underflow min int.
-  static constexpr bool AddWouldUnderflow(int a, int b) {
-    return a < 0 && b < 0 && std::numeric_limits<int>::min() - a > b;
-  }
-
-  // Clamp the right/bottom to avoid integer over/underflow in width() and
-  // height(). This returns the right/bottom given a top_or_left and a
-  // bottom_or_right.
-  // TODO(enne): this should probably use base::ClampAdd, but that
-  // function is not a constexpr.
-  static constexpr int GetClampedValue(int top_or_left, int bottom_or_right) {
-    if (AddWouldOverflow(top_or_left, bottom_or_right)) {
-      return std::numeric_limits<int>::max() - top_or_left;
-    } else if (AddWouldUnderflow(top_or_left, bottom_or_right)) {
-      // If |top_or_left| and |bottom_or_right| are both negative,
-      // adds |top_or_left| to prevent underflow by subtracting it.
-      return std::numeric_limits<int>::min() - top_or_left;
-    } else {
-      return bottom_or_right;
-    }
-  }
 };
 
-// This is declared here for use in gtest-based unit tests but is defined in
-// the //ui/gfx:test_support target. Depend on that to use this in your unit
-// test. This should not be used in production code - call ToString() instead.
-void PrintTo(const Insets& point, ::std::ostream* os);
-
 inline Insets operator+(Insets lhs, const Insets& rhs) {
   lhs += rhs;
   return lhs;
@@ -203,6 +82,11 @@
                                             float y_scale);
 GEOMETRY_EXPORT Insets ScaleToRoundedInsets(const Insets& insets, float scale);
 
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const Insets&, ::std::ostream* os);
+
 }  // namespace gfx
 
 #endif  // UI_GFX_GEOMETRY_INSETS_H_
diff --git a/ui/gfx/geometry/insets_f.cc b/ui/gfx/geometry/insets_f.cc
deleted file mode 100644
index 6a084b1..0000000
--- a/ui/gfx/geometry/insets_f.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2012 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/gfx/geometry/insets_f.h"
-
-#include "base/strings/stringprintf.h"
-
-namespace gfx {
-
-std::string InsetsF::ToString() const {
-  // Print members in the same order of the constructor parameters.
-  return base::StringPrintf("%f,%f,%f,%f", top(),  left(), bottom(), right());
-}
-
-void InsetsF::SetToMax(const gfx::InsetsF& other) {
-  top_ = std::max(top_, other.top_);
-  left_ = std::max(left_, other.left_);
-  bottom_ = std::max(bottom_, other.bottom_);
-  right_ = std::max(right_, other.right_);
-}
-
-}  // namespace gfx
diff --git a/ui/gfx/geometry/insets_f.h b/ui/gfx/geometry/insets_f.h
index e853fc86..fbd1f5f 100644
--- a/ui/gfx/geometry/insets_f.h
+++ b/ui/gfx/geometry/insets_f.h
@@ -5,103 +5,42 @@
 #ifndef UI_GFX_GEOMETRY_INSETS_F_H_
 #define UI_GFX_GEOMETRY_INSETS_F_H_
 
-#include <string>
-
 #include "ui/gfx/geometry/geometry_export.h"
+#include "ui/gfx/geometry/insets_outsets_f_base.h"
 
 namespace gfx {
 
 // A floating point version of gfx::Insets.
-class GEOMETRY_EXPORT InsetsF {
+class GEOMETRY_EXPORT InsetsF : public InsetsOutsetsFBase<InsetsF> {
  public:
-  constexpr InsetsF() : top_(0.f), left_(0.f), bottom_(0.f), right_(0.f) {}
-  constexpr explicit InsetsF(float all)
-      : top_(all), left_(all), bottom_(all), right_(all) {}
+  using InsetsOutsetsFBase::InsetsOutsetsFBase;
+
+  // Avoid this constructor in blink code because it's easy to make mistakes in
+  // the order of the parameters. Use the other constructors and set_*()
+  // methods instead.
   constexpr InsetsF(float vertical, float horizontal)
-      : top_(vertical),
-        left_(horizontal),
-        bottom_(vertical),
-        right_(horizontal) {}
-  constexpr InsetsF(float top, float left, float bottom, float right)
-      : top_(top), left_(left), bottom_(bottom), right_(right) {}
+      : InsetsF(vertical, horizontal, vertical, horizontal) {}
 
-  constexpr float top() const { return top_; }
-  constexpr float left() const { return left_; }
-  constexpr float bottom() const { return bottom_; }
-  constexpr float right() const { return right_; }
+  // Avoid this constructor in blink code because it's easy to make mistakes in
+  // the order of the parameters. Use the other constructors and set_*()
+  // methods instead.
+  constexpr InsetsF(float top, float left, float bottom, float right) {
+    set_top(top);
+    set_left(left);
+    set_bottom(bottom);
+    set_right(right);
+  }
 
-  // Returns the total width taken up by the insets, which is the sum of the
-  // left and right insets.
-  constexpr float width() const { return left_ + right_; }
-
-  // Returns the total height taken up by the insets, which is the sum of the
-  // top and bottom insets.
-  constexpr float height() const { return top_ + bottom_; }
-
-  // Returns true if the insets are empty.
-  bool IsEmpty() const { return width() == 0.f && height() == 0.f; }
-
+  // Avoid this method in blink code because it's easy to make mistakes in the
+  // order of the parameters. Use the setter methods instead.
   void Set(float top, float left, float bottom, float right) {
-    top_ = top;
-    left_ = left;
-    bottom_ = bottom;
-    right_ = right;
+    set_top(top);
+    set_left(left);
+    set_bottom(bottom);
+    set_right(right);
   }
-
-  // Sets each side to the maximum of the side and the corresponding side of
-  // |other|.
-  void SetToMax(const gfx::InsetsF& other);
-
-  bool operator==(const InsetsF& insets) const {
-    return top_ == insets.top_ && left_ == insets.left_ &&
-           bottom_ == insets.bottom_ && right_ == insets.right_;
-  }
-
-  bool operator!=(const InsetsF& insets) const {
-    return !(*this == insets);
-  }
-
-  void operator+=(const InsetsF& insets) {
-    top_ += insets.top_;
-    left_ += insets.left_;
-    bottom_ += insets.bottom_;
-    right_ += insets.right_;
-  }
-
-  void operator-=(const InsetsF& insets) {
-    top_ -= insets.top_;
-    left_ -= insets.left_;
-    bottom_ -= insets.bottom_;
-    right_ -= insets.right_;
-  }
-
-  InsetsF operator-() const {
-    return InsetsF(-top_, -left_, -bottom_, -right_);
-  }
-
-  void Scale(float x_scale, float y_scale) {
-    top_ *= y_scale;
-    left_ *= x_scale;
-    bottom_ *= y_scale;
-    right_ *= x_scale;
-  }
-  void Scale(float scale) { Scale(scale, scale); }
-
-  // Returns a string representation of the insets.
-  std::string ToString() const;
-
- private:
-  float top_;
-  float left_;
-  float bottom_;
-  float right_;
 };
 
-// This is declared here for use in gtest-based unit tests but is defined in
-// the //ui/gfx:test_support target. Depend on that to use this in your unit
-// test. This should not be used in production code - call ToString() instead.
-void PrintTo(const InsetsF& point, ::std::ostream* os);
-
 inline InsetsF ScaleInsets(InsetsF i, float x_scale, float y_scale) {
   i.Scale(x_scale, y_scale);
   return i;
@@ -121,6 +60,11 @@
   return lhs;
 }
 
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const InsetsF&, ::std::ostream* os);
+
 }  // namespace gfx
 
 #endif  // UI_GFX_GEOMETRY_INSETS_F_H_
diff --git a/ui/gfx/geometry/insets_f_unittest.cc b/ui/gfx/geometry/insets_f_unittest.cc
index 3328914d..ff2bcda 100644
--- a/ui/gfx/geometry/insets_f_unittest.cc
+++ b/ui/gfx/geometry/insets_f_unittest.cc
@@ -27,49 +27,107 @@
   EXPECT_EQ(4.875f, insets.right());
 }
 
+TEST(InsetsFTest, SetTop) {
+  InsetsF insets = InsetsF(1.5f);
+  insets.set_top(2.75f);
+  EXPECT_EQ(2.75f, insets.top());
+  EXPECT_EQ(1.5f, insets.left());
+  EXPECT_EQ(1.5f, insets.bottom());
+  EXPECT_EQ(1.5f, insets.right());
+  EXPECT_EQ(insets, InsetsF(1.5f).set_top(2.75f));
+}
+
+TEST(InsetsFTest, SetBottom) {
+  InsetsF insets(1.5f);
+  insets.set_bottom(2.75f);
+  EXPECT_EQ(1.5f, insets.top());
+  EXPECT_EQ(1.5f, insets.left());
+  EXPECT_EQ(2.75f, insets.bottom());
+  EXPECT_EQ(1.5f, insets.right());
+  EXPECT_EQ(insets, InsetsF(1.5f).set_bottom(2.75f));
+}
+
+TEST(InsetsFTest, SetLeft) {
+  InsetsF insets(1.5f);
+  insets.set_left(2.75f);
+  EXPECT_EQ(1.5f, insets.top());
+  EXPECT_EQ(2.75f, insets.left());
+  EXPECT_EQ(1.5f, insets.bottom());
+  EXPECT_EQ(1.5f, insets.right());
+  EXPECT_EQ(insets, InsetsF(1.5f).set_left(2.75f));
+}
+
+TEST(InsetsFTest, SetRight) {
+  InsetsF insets(1.5f);
+  insets.set_right(2.75f);
+  EXPECT_EQ(1.5f, insets.top());
+  EXPECT_EQ(1.5f, insets.left());
+  EXPECT_EQ(1.5f, insets.bottom());
+  EXPECT_EQ(2.75f, insets.right());
+  EXPECT_EQ(insets, InsetsF(1.5f).set_right(2.75f));
+}
+
+TEST(InsetsFTest, Set) {
+  InsetsF insets;
+  insets.Set(1.25f, 2.5f, 3.75f, 4.875f);
+  EXPECT_EQ(1.25f, insets.top());
+  EXPECT_EQ(2.5f, insets.left());
+  EXPECT_EQ(3.75f, insets.bottom());
+  EXPECT_EQ(4.875f, insets.right());
+}
+
 TEST(InsetsFTest, WidthHeightAndIsEmpty) {
   InsetsF insets;
   EXPECT_EQ(0, insets.width());
   EXPECT_EQ(0, insets.height());
   EXPECT_TRUE(insets.IsEmpty());
 
-  insets.Set(0, 3.5f, 0, 4.25f);
+  insets.set_left(3.5f).set_right(4.25f);
   EXPECT_EQ(7.75f, insets.width());
   EXPECT_EQ(0, insets.height());
   EXPECT_FALSE(insets.IsEmpty());
 
-  insets.Set(1.5f, 0, 2.75f, 0);
+  insets.set_left(0).set_right(0).set_top(1.5f).set_bottom(2.75f);
   EXPECT_EQ(0, insets.width());
   EXPECT_EQ(4.25f, insets.height());
   EXPECT_FALSE(insets.IsEmpty());
 
-  insets.Set(1.5f, 4.25f, 2.75f, 5);
+  insets.set_left(4.25f).set_right(5);
   EXPECT_EQ(9.25f, insets.width());
   EXPECT_EQ(4.25f, insets.height());
   EXPECT_FALSE(insets.IsEmpty());
 }
 
 TEST(InsetsFTest, Operators) {
-  InsetsF insets(1.f, 2.5f, 3.3f, 4.1f);
-  insets += InsetsF(5.8f, 6.7f, 7.6f, 8.5f);
+  InsetsF insets =
+      InsetsF().set_left(2.5f).set_right(4.1f).set_top(1.f).set_bottom(3.3f);
+  insets +=
+      InsetsF().set_left(6.7f).set_right(8.5f).set_top(5.8f).set_bottom(7.6f);
   EXPECT_FLOAT_EQ(6.8f, insets.top());
   EXPECT_FLOAT_EQ(9.2f, insets.left());
   EXPECT_FLOAT_EQ(10.9f, insets.bottom());
   EXPECT_FLOAT_EQ(12.6f, insets.right());
 
-  insets -= InsetsF(-1.f, 0, 1.1f, 2.2f);
+  insets -=
+      InsetsF().set_left(0).set_right(2.2f).set_top(-1.f).set_bottom(1.1f);
   EXPECT_FLOAT_EQ(7.8f, insets.top());
   EXPECT_FLOAT_EQ(9.2f, insets.left());
   EXPECT_FLOAT_EQ(9.8f, insets.bottom());
   EXPECT_FLOAT_EQ(10.4f, insets.right());
 
-  insets = InsetsF(10, 10.1f, 10.01f, 10.001f) + InsetsF(5.5f, 5.f, 0, -20.2f);
+  insets =
+      InsetsF().set_left(10.1f).set_right(10.001f).set_top(10).set_bottom(
+          10.01f) +
+      InsetsF().set_left(5.f).set_right(-20.2f).set_top(5.5f).set_bottom(0);
   EXPECT_FLOAT_EQ(15.5f, insets.top());
   EXPECT_FLOAT_EQ(15.1f, insets.left());
   EXPECT_FLOAT_EQ(10.01f, insets.bottom());
   EXPECT_FLOAT_EQ(-10.199f, insets.right());
 
-  insets = InsetsF(10, 10.1f, 10.01f, 10.001f) - InsetsF(5.5f, 5.f, 0, -20.2f);
+  insets =
+      InsetsF().set_left(10.1f).set_right(10.001f).set_top(10).set_bottom(
+          10.01f) -
+      InsetsF().set_left(5.f).set_right(-20.2f).set_top(5.5f).set_bottom(0);
   EXPECT_FLOAT_EQ(4.5f, insets.top());
   EXPECT_FLOAT_EQ(5.1f, insets.left());
   EXPECT_FLOAT_EQ(10.01f, insets.bottom());
@@ -77,61 +135,91 @@
 }
 
 TEST(InsetsFTest, Equality) {
-  InsetsF insets1(1.1f, 2.2f, 3.3f, 4.4f);
+  InsetsF insets1 =
+      InsetsF().set_left(2.2f).set_right(4.4f).set_top(1.1f).set_bottom(3.3f);
   InsetsF insets2;
   // Test operator== and operator!=.
   EXPECT_FALSE(insets1 == insets2);
   EXPECT_TRUE(insets1 != insets2);
 
-  insets2.Set(1.1f, 2.2f, 3.3f, 4.4f);
+  insets2.set_left(2.2f).set_right(4.4f).set_top(1.1f).set_bottom(3.3f);
   EXPECT_TRUE(insets1 == insets2);
   EXPECT_FALSE(insets1 != insets2);
 }
 
 TEST(InsetsFTest, ToString) {
-  InsetsF insets(1.1f, 2.2f, 3.3f, 4.4f);
-  EXPECT_EQ("1.100000,2.200000,3.300000,4.400000", insets.ToString());
+  InsetsF insets =
+      InsetsF().set_left(2.2).set_right(4.4).set_top(1.1).set_bottom(3.3);
+  EXPECT_EQ("x:2.2,4.4 y:1.1,3.3", insets.ToString());
 }
 
 TEST(InsetsFTest, Scale) {
-  InsetsF in(7, 5, 3, 1);
+  InsetsF in = InsetsF().set_left(5).set_right(1).set_top(7).set_bottom(3);
   InsetsF testf = ScaleInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(InsetsF(24.5f, 12.5f, 10.5f, 2.5f), testf);
+  EXPECT_EQ(InsetsF().set_left(12.5f).set_right(2.5f).set_top(24.5f).set_bottom(
+                10.5f),
+            testf);
   testf = ScaleInsets(in, 2.5f);
-  EXPECT_EQ(InsetsF(17.5f, 12.5f, 7.5f, 2.5f), testf);
+  EXPECT_EQ(
+      InsetsF().set_left(12.5f).set_right(2.5f).set_top(17.5f).set_bottom(7.5f),
+      testf);
 
   in.Scale(2.5f, 3.5f);
-  EXPECT_EQ(InsetsF(24.5f, 12.5f, 10.5f, 2.5f), in);
+  EXPECT_EQ(InsetsF().set_left(12.5f).set_right(2.5f).set_top(24.5f).set_bottom(
+                10.5f),
+            in);
   in.Scale(-2.5f);
-  EXPECT_EQ(InsetsF(-61.25f, -31.25f, -26.25f, -6.25f), in);
+  EXPECT_EQ(
+      InsetsF().set_left(-31.25f).set_right(-6.25f).set_top(-61.25f).set_bottom(
+          -26.25f),
+      in);
 }
 
 TEST(InsetsFTest, ScaleNegative) {
-  InsetsF in = InsetsF(-7, -5, -3, -1);
+  InsetsF in = InsetsF().set_left(-5).set_right(-1).set_top(-7).set_bottom(-3);
 
   InsetsF testf = ScaleInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(InsetsF(-24.5f, -12.5f, -10.5f, -2.5f), testf);
+  EXPECT_EQ(
+      InsetsF().set_left(-12.5f).set_right(-2.5f).set_top(-24.5f).set_bottom(
+          -10.5f),
+      testf);
   testf = ScaleInsets(in, 2.5f);
-  EXPECT_EQ(InsetsF(-17.5f, -12.5f, -7.5f, -2.5f), testf);
+  EXPECT_EQ(
+      InsetsF().set_left(-12.5f).set_right(-2.5f).set_top(-17.5f).set_bottom(
+          -7.5f),
+      testf);
 
   in.Scale(2.5f, 3.5f);
-  EXPECT_EQ(InsetsF(-24.5f, -12.5f, -10.5f, -2.5f), in);
+  EXPECT_EQ(
+      InsetsF().set_left(-12.5f).set_right(-2.5f).set_top(-24.5f).set_bottom(
+          -10.5f),
+      in);
   in.Scale(-2.5f);
-  EXPECT_EQ(InsetsF(61.25f, 31.25f, 26.25f, 6.25f), in);
+  EXPECT_EQ(
+      InsetsF().set_left(31.25f).set_right(6.25f).set_top(61.25f).set_bottom(
+          26.25f),
+      in);
 }
 
 TEST(InsetsFTest, SetToMax) {
   InsetsF insets;
-  insets.SetToMax(InsetsF(-1.25f, 2.5f, -3.75f, 4.5f));
-  EXPECT_EQ(InsetsF(0, 2.5f, 0, 4.5f), insets);
+  insets.SetToMax(
+      InsetsF().set_left(2.5f).set_right(4.5f).set_top(-1.25f).set_bottom(
+          -2.5f));
+  EXPECT_EQ(InsetsF().set_left(2.5f).set_right(4.5f), insets);
   insets.SetToMax(InsetsF());
-  EXPECT_EQ(InsetsF(0, 2.5f, 0, 4.5f), insets);
-  insets.SetToMax(InsetsF(1.25f, 0, 3.75f, 0));
-  EXPECT_EQ(InsetsF(1.25f, 2.5f, 3.75f, 4.5f), insets);
-  insets.SetToMax(InsetsF(20, 30, 40, 50));
-  EXPECT_EQ(InsetsF(20, 30, 40, 50), insets);
+  EXPECT_EQ(InsetsF().set_left(2.5f).set_right(4.5f), insets);
+  insets.SetToMax(InsetsF().set_top(1.25f).set_bottom(3.75f));
+  EXPECT_EQ(
+      InsetsF().set_left(2.5f).set_right(4.5f).set_top(1.25f).set_bottom(3.75f),
+      insets);
+  insets.SetToMax(
+      InsetsF().set_left(30).set_right(50).set_top(20).set_bottom(40));
+  EXPECT_EQ(InsetsF().set_left(30).set_right(50).set_top(20).set_bottom(40),
+            insets);
 
-  InsetsF insets1(-1, -2, -3, -4);
+  InsetsF insets1 =
+      InsetsF().set_left(-2).set_right(-4).set_top(-1).set_bottom(-3);
   insets1.SetToMax(InsetsF());
   EXPECT_EQ(InsetsF(), insets1);
 }
diff --git a/ui/gfx/geometry/insets_outsets_base.h b/ui/gfx/geometry/insets_outsets_base.h
new file mode 100644
index 0000000..3242091
--- /dev/null
+++ b/ui/gfx/geometry/insets_outsets_base.h
@@ -0,0 +1,176 @@
+// Copyright 2022 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_GFX_GEOMETRY_INSETS_OUTSETS_BASE_H_
+#define UI_GFX_GEOMETRY_INSETS_OUTSETS_BASE_H_
+
+#include <string>
+
+#include "base/numerics/clamped_math.h"
+#include "base/strings/stringprintf.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace gfx {
+
+// The common base template class for Insets and Outsets.
+// Represents the widths of the four borders or margins of an unspecified
+// rectangle. It stores the thickness of the top, left, bottom and right
+// edges, without storing the actual size and position of the rectangle itself.
+template <typename T>
+class InsetsOutsetsBase {
+ public:
+  constexpr InsetsOutsetsBase() = default;
+  constexpr explicit InsetsOutsetsBase(int all)
+      : top_(all),
+        left_(all),
+        bottom_(GetClampedValue(all, all)),
+        right_(GetClampedValue(all, all)) {}
+
+  constexpr int top() const { return top_; }
+  constexpr int left() const { return left_; }
+  constexpr int bottom() const { return bottom_; }
+  constexpr int right() const { return right_; }
+
+  // Returns the total width taken up by the insets/outsets, which is the sum
+  // of the left and right insets/outsets.
+  constexpr int width() const { return left_ + right_; }
+
+  // Returns the total height taken up by the insets/outsets, which is the sum
+  // of the top and bottom insets/outsets.
+  constexpr int height() const { return top_ + bottom_; }
+
+  // Returns the sum of the left and right insets/outsets as the width,
+  // the sum of the top and bottom insets/outsets as the height.
+  constexpr Size size() const { return Size(width(), height()); }
+
+  // Returns true if the insets/outsets are empty.
+  bool IsEmpty() const { return width() == 0 && height() == 0; }
+
+  // These setters can be used together with the default constructor and the
+  // single-parameter constructor to construct Insets instances, for example:
+  //                                                  // T, L, B, R
+  //   Insets a = Insets().set_top(2);                // 2, 0, 0, 0
+  //   Insets b = Insets().set_left(2).set_bottom(3); // 0, 2, 3, 0
+  //   Insets c = Insets().set_left_right(1, 2).set_top_bottom(3, 4);
+  //                                                  // 3, 1, 4, 2
+  //   Insets d = Insets(1).set_top(5);               // 5, 1, 1, 1
+  constexpr T& set_top(int top) {
+    top_ = top;
+    bottom_ = GetClampedValue(top_, bottom_);
+    return *static_cast<T*>(this);
+  }
+  constexpr T& set_left(int left) {
+    left_ = left;
+    right_ = GetClampedValue(left_, right_);
+    return *static_cast<T*>(this);
+  }
+  constexpr T& set_bottom(int bottom) {
+    bottom_ = GetClampedValue(top_, bottom);
+    return *static_cast<T*>(this);
+  }
+  constexpr T& set_right(int right) {
+    right_ = GetClampedValue(left_, right);
+    return *static_cast<T*>(this);
+  }
+  // These are preferred to the above setters when setting a pair of edges
+  // because these have less clamping and better performance.
+  constexpr T& set_left_right(int left, int right) {
+    left_ = left;
+    right_ = GetClampedValue(left_, right);
+    return *static_cast<T*>(this);
+  }
+  constexpr T& set_top_bottom(int top, int bottom) {
+    top_ = top;
+    bottom_ = GetClampedValue(top_, bottom);
+    return *static_cast<T*>(this);
+  }
+
+  // Sets each side to the maximum of the side and the corresponding side of
+  // |other|.
+  void SetToMax(const T& other) {
+    top_ = std::max(top_, other.top_);
+    left_ = std::max(left_, other.left_);
+    bottom_ = std::max(bottom_, other.bottom_);
+    right_ = std::max(right_, other.right_);
+  }
+
+  bool operator==(const T& other) const {
+    return top_ == other.top_ && left_ == other.left_ &&
+           bottom_ == other.bottom_ && right_ == other.right_;
+  }
+
+  bool operator!=(const T& other) const { return !(*this == other); }
+
+  void operator+=(const T& other) {
+    top_ = base::ClampAdd(top_, other.top_);
+    left_ = base::ClampAdd(left_, other.left_);
+    bottom_ = GetClampedValue(top_, base::ClampAdd(bottom_, other.bottom_));
+    right_ = GetClampedValue(left_, base::ClampAdd(right_, other.right_));
+  }
+
+  void operator-=(const T& other) {
+    top_ = base::ClampSub(top_, other.top_);
+    left_ = base::ClampSub(left_, other.left_);
+    bottom_ = GetClampedValue(top_, base::ClampSub(bottom_, other.bottom_));
+    right_ = GetClampedValue(left_, base::ClampSub(right_, other.right_));
+  }
+
+  T operator-() const {
+    return T()
+        .set_left_right(-base::MakeClampedNum(left_),
+                        -base::MakeClampedNum(right_))
+        .set_top_bottom(-base::MakeClampedNum(top_),
+                        -base::MakeClampedNum(bottom_));
+  }
+
+  // Returns a string representation of the insets/outsets.
+  std::string ToString() const {
+    return base::StringPrintf("x:%d,%d y:%d,%d", left_, right_, top_, bottom_);
+  }
+
+ private:
+  // Returns true iff a+b would overflow max int.
+  static constexpr bool AddWouldOverflow(int a, int b) {
+    // In this function, GCC tries to make optimizations that would only work if
+    // max - a wouldn't overflow but it isn't smart enough to notice that a > 0.
+    // So cast everything to unsigned to avoid this.  As it is guaranteed that
+    // max - a and b are both already positive, the cast is a noop.
+    //
+    // This is intended to be: a > 0 && max - a < b
+    return a > 0 && b > 0 &&
+           static_cast<unsigned>(std::numeric_limits<int>::max() - a) <
+               static_cast<unsigned>(b);
+  }
+
+  // Returns true iff a+b would underflow min int.
+  static constexpr bool AddWouldUnderflow(int a, int b) {
+    return a < 0 && b < 0 && std::numeric_limits<int>::min() - a > b;
+  }
+
+  // Clamp the right/bottom to avoid integer over/underflow in width() and
+  // height(). This returns the right/bottom given a top_or_left and a
+  // bottom_or_right.
+  // TODO(enne): this should probably use base::ClampAdd, but that
+  // function is not a constexpr.
+  static constexpr int GetClampedValue(int top_or_left, int bottom_or_right) {
+    if (AddWouldOverflow(top_or_left, bottom_or_right)) {
+      return std::numeric_limits<int>::max() - top_or_left;
+    } else if (AddWouldUnderflow(top_or_left, bottom_or_right)) {
+      // If |top_or_left| and |bottom_or_right| are both negative,
+      // adds |top_or_left| to prevent underflow by subtracting it.
+      return std::numeric_limits<int>::min() - top_or_left;
+    } else {
+      return bottom_or_right;
+    }
+  }
+
+  int top_ = 0;
+  int left_ = 0;
+  int bottom_ = 0;
+  int right_ = 0;
+};
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_INSETS_OUTSETS_BASE_H_
diff --git a/ui/gfx/geometry/insets_outsets_f_base.h b/ui/gfx/geometry/insets_outsets_f_base.h
new file mode 100644
index 0000000..2d99140
--- /dev/null
+++ b/ui/gfx/geometry/insets_outsets_f_base.h
@@ -0,0 +1,118 @@
+// Copyright 2022 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_GFX_GEOMETRY_INSETS_OUTSETS_F_BASE_H_
+#define UI_GFX_GEOMETRY_INSETS_OUTSETS_F_BASE_H_
+
+#include <string>
+
+#include "base/strings/stringprintf.h"
+
+namespace gfx {
+
+// This is the base template class of InsetsF and OutsetsF.
+template <typename T>
+class InsetsOutsetsFBase {
+ public:
+  constexpr InsetsOutsetsFBase() = default;
+  constexpr explicit InsetsOutsetsFBase(float all)
+      : top_(all), left_(all), bottom_(all), right_(all) {}
+
+  constexpr float top() const { return top_; }
+  constexpr float left() const { return left_; }
+  constexpr float bottom() const { return bottom_; }
+  constexpr float right() const { return right_; }
+
+  // Returns the total width taken up by the insets/outsets, which is the
+  // sum of the left and right insets/outsets.
+  constexpr float width() const { return left_ + right_; }
+
+  // Returns the total height taken up by the insets/outsets, which is the
+  // sum of the top and bottom insets/outsets.
+  constexpr float height() const { return top_ + bottom_; }
+
+  // Returns true if the insets/outsets are empty.
+  bool IsEmpty() const { return width() == 0.f && height() == 0.f; }
+
+  // These setters can be used together with the default constructor and the
+  // single-parameter constructor to construct InsetsF instances, for example:
+  //                                                    // T, L, B, R
+  //   InsetsF a = InsetsF().set_top(2);                // 2, 0, 0, 0
+  //   InsetsF b = InsetsF().set_left(2).set_bottom(3); // 0, 2, 3, 0
+  //   InsetsF c = InsetsF(1).set_top(5);               // 5, 1, 1, 1
+  constexpr T& set_top(float top) {
+    top_ = top;
+    return *static_cast<T*>(this);
+  }
+  constexpr T& set_left(float left) {
+    left_ = left;
+    return *static_cast<T*>(this);
+  }
+  constexpr T& set_bottom(float bottom) {
+    bottom_ = bottom;
+    return *static_cast<T*>(this);
+  }
+  constexpr T& set_right(float right) {
+    right_ = right;
+    return *static_cast<T*>(this);
+  }
+
+  // Sets each side to the maximum of the side and the corresponding side of
+  // |other|.
+  void SetToMax(const T& other) {
+    top_ = std::max(top_, other.top_);
+    left_ = std::max(left_, other.left_);
+    bottom_ = std::max(bottom_, other.bottom_);
+    right_ = std::max(right_, other.right_);
+  }
+
+  void Scale(float x_scale, float y_scale) {
+    top_ *= y_scale;
+    left_ *= x_scale;
+    bottom_ *= y_scale;
+    right_ *= x_scale;
+  }
+  void Scale(float scale) { Scale(scale, scale); }
+
+  bool operator==(const T& other) const {
+    return top_ == other.top_ && left_ == other.left_ &&
+           bottom_ == other.bottom_ && right_ == other.right_;
+  }
+
+  bool operator!=(const T& other) const { return !(*this == other); }
+
+  void operator+=(const T& other) {
+    top_ += other.top_;
+    left_ += other.left_;
+    bottom_ += other.bottom_;
+    right_ += other.right_;
+  }
+
+  void operator-=(const T& other) {
+    top_ -= other.top_;
+    left_ -= other.left_;
+    bottom_ -= other.bottom_;
+    right_ -= other.right_;
+  }
+
+  T operator-() const {
+    return T().set_left(-left_).set_right(-right_).set_top(-top_).set_bottom(
+        -bottom_);
+  }
+
+  // Returns a string representation of the insets/outsets.
+  std::string ToString() const {
+    return base::StringPrintf("x:%g,%g y:%g,%g", left_, right_, top_, bottom_);
+  }
+
+ private:
+  float top_ = 0.f;
+  float left_ = 0.f;
+  float bottom_ = 0.f;
+  float right_ = 0.f;
+};
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_INSETS_OUTSETS_F_BASE_H_
diff --git a/ui/gfx/geometry/insets_unittest.cc b/ui/gfx/geometry/insets_unittest.cc
index 1333a3f..6136f74 100644
--- a/ui/gfx/geometry/insets_unittest.cc
+++ b/ui/gfx/geometry/insets_unittest.cc
@@ -27,28 +27,66 @@
   EXPECT_EQ(4, insets.right());
 }
 
+TEST(InsetsTest, SetLeftRight) {
+  Insets insets(1);
+  insets.set_left_right(3, 4);
+  EXPECT_EQ(1, insets.top());
+  EXPECT_EQ(3, insets.left());
+  EXPECT_EQ(1, insets.bottom());
+  EXPECT_EQ(4, insets.right());
+
+  EXPECT_EQ(insets, Insets(1).set_left_right(3, 4));
+}
+
+TEST(InsetsTest, SetTopBottom) {
+  Insets insets(1);
+  insets.set_top_bottom(3, 4);
+  EXPECT_EQ(3, insets.top());
+  EXPECT_EQ(1, insets.left());
+  EXPECT_EQ(4, insets.bottom());
+  EXPECT_EQ(1, insets.right());
+
+  EXPECT_EQ(insets, Insets(1).set_top_bottom(3, 4));
+}
+
 TEST(InsetsTest, SetTop) {
   Insets insets(1);
   insets.set_top(2);
-  EXPECT_EQ(Insets(2, 1, 1, 1), insets);
+  EXPECT_EQ(2, insets.top());
+  EXPECT_EQ(1, insets.left());
+  EXPECT_EQ(1, insets.bottom());
+  EXPECT_EQ(1, insets.right());
+  EXPECT_EQ(insets, Insets(1).set_top(2));
 }
 
 TEST(InsetsTest, SetBottom) {
   Insets insets(1);
   insets.set_bottom(2);
-  EXPECT_EQ(Insets(1, 1, 2, 1), insets);
+  EXPECT_EQ(1, insets.top());
+  EXPECT_EQ(1, insets.left());
+  EXPECT_EQ(2, insets.bottom());
+  EXPECT_EQ(1, insets.right());
+  EXPECT_EQ(insets, Insets(1).set_bottom(2));
 }
 
 TEST(InsetsTest, SetLeft) {
   Insets insets(1);
   insets.set_left(2);
-  EXPECT_EQ(Insets(1, 2, 1, 1), insets);
+  EXPECT_EQ(1, insets.top());
+  EXPECT_EQ(2, insets.left());
+  EXPECT_EQ(1, insets.bottom());
+  EXPECT_EQ(1, insets.right());
+  EXPECT_EQ(insets, Insets(1).set_left(2));
 }
 
 TEST(InsetsTest, SetRight) {
   Insets insets(1);
   insets.set_right(2);
-  EXPECT_EQ(Insets(1, 1, 1, 2), insets);
+  EXPECT_EQ(1, insets.top());
+  EXPECT_EQ(1, insets.left());
+  EXPECT_EQ(1, insets.bottom());
+  EXPECT_EQ(2, insets.right());
+  EXPECT_EQ(insets, Insets(1).set_right(2));
 }
 
 TEST(InsetsTest, Set) {
@@ -66,70 +104,69 @@
   EXPECT_EQ(0, insets.height());
   EXPECT_TRUE(insets.IsEmpty());
 
-  insets.Set(0, 3, 0, 4);
+  insets.set_left_right(3, 4);
   EXPECT_EQ(7, insets.width());
   EXPECT_EQ(0, insets.height());
   EXPECT_FALSE(insets.IsEmpty());
 
-  insets.Set(1, 0, 2, 0);
+  insets.set_left_right(0, 0);
+  insets.set_top_bottom(1, 2);
   EXPECT_EQ(0, insets.width());
   EXPECT_EQ(3, insets.height());
   EXPECT_FALSE(insets.IsEmpty());
 
-  insets.Set(1, 4, 2, 5);
+  insets.set_left_right(4, 5);
   EXPECT_EQ(9, insets.width());
   EXPECT_EQ(3, insets.height());
   EXPECT_FALSE(insets.IsEmpty());
 }
 
 TEST(InsetsTest, Operators) {
-  Insets insets;
-  insets.Set(1, 2, 3, 4);
-  insets += Insets(5, 6, 7, 8);
+  Insets insets = Insets().set_left_right(2, 4).set_top_bottom(1, 3);
+  insets += Insets().set_left_right(6, 8).set_top_bottom(5, 7);
   EXPECT_EQ(6, insets.top());
   EXPECT_EQ(8, insets.left());
   EXPECT_EQ(10, insets.bottom());
   EXPECT_EQ(12, insets.right());
 
-  insets -= Insets(-1, 0, 1, 2);
+  insets -= Insets().set_left_right(0, 2).set_top_bottom(-1, 1);
   EXPECT_EQ(7, insets.top());
   EXPECT_EQ(8, insets.left());
   EXPECT_EQ(9, insets.bottom());
   EXPECT_EQ(10, insets.right());
 
-  insets = Insets(10, 10, 10, 10) + Insets(5, 5, 0, -20);
-  EXPECT_EQ(15, insets.top());
+  insets = Insets(10) + Insets().set_left_right(5, -20).set_top_bottom(10, 0);
+  EXPECT_EQ(20, insets.top());
   EXPECT_EQ(15, insets.left());
   EXPECT_EQ(10, insets.bottom());
   EXPECT_EQ(-10, insets.right());
 
-  insets = Insets(10, 10, 10, 10) - Insets(5, 5, 0, -20);
-  EXPECT_EQ(5, insets.top());
+  insets = Insets(10) - Insets().set_left_right(5, -20).set_top_bottom(10, 0);
+  EXPECT_EQ(0, insets.top());
   EXPECT_EQ(5, insets.left());
   EXPECT_EQ(10, insets.bottom());
   EXPECT_EQ(30, insets.right());
 }
 
 TEST(InsetsTest, Equality) {
-  Insets insets1;
-  insets1.Set(1, 2, 3, 4);
+  Insets insets1 = Insets().set_left_right(2, 4).set_top_bottom(1, 3);
   Insets insets2;
   // Test operator== and operator!=.
   EXPECT_FALSE(insets1 == insets2);
   EXPECT_TRUE(insets1 != insets2);
 
-  insets2.Set(1, 2, 3, 4);
+  insets2.set_left_right(2, 4).set_top_bottom(1, 3);
   EXPECT_TRUE(insets1 == insets2);
   EXPECT_FALSE(insets1 != insets2);
 }
 
 TEST(InsetsTest, ToString) {
-  Insets insets(1, 2, 3, 4);
-  EXPECT_EQ("1,2,3,4", insets.ToString());
+  Insets insets = Insets().set_left_right(2, 4).set_top_bottom(1, 3);
+  EXPECT_EQ("x:2,4 y:1,3", insets.ToString());
 }
 
 TEST(InsetsTest, Offset) {
-  const Insets insets(1, 2, 3, 4);
+  const Insets insets = Insets().set_left_right(2, 4).set_top_bottom(1, 3);
   const Rect rect(5, 6, 7, 8);
   const Vector2d vector(9, 10);
 
@@ -145,7 +182,8 @@
 
   Insets insets_with_offset = insets;
   insets_with_offset.Offset(vector);
-  EXPECT_EQ(Insets(11, 11, -7, -5), insets_with_offset);
+  EXPECT_EQ(gfx::Insets().set_left_right(11, -5).set_top_bottom(11, -7),
+            insets_with_offset);
   EXPECT_EQ(insets_with_offset, insets + vector);
 
   Rect inset_by_offset = rect;
@@ -156,51 +194,51 @@
 }
 
 TEST(InsetsTest, Scale) {
-  Insets in(7, 5);
+  Insets in = Insets().set_left_right(5, 1).set_top_bottom(7, 3);
 
   Insets test = ScaleToFlooredInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(Insets(24, 12), test);
+  EXPECT_EQ(Insets().set_left_right(12, 2).set_top_bottom(24, 10), test);
   test = ScaleToFlooredInsets(in, 2.5f);
-  EXPECT_EQ(Insets(17, 12), test);
+  EXPECT_EQ(Insets().set_left_right(12, 2).set_top_bottom(17, 7), test);
 
   test = ScaleToCeiledInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(Insets(25, 13), test);
+  EXPECT_EQ(Insets().set_left_right(13, 3).set_top_bottom(25, 11), test);
   test = ScaleToCeiledInsets(in, 2.5f);
-  EXPECT_EQ(Insets(18, 13), test);
+  EXPECT_EQ(Insets().set_left_right(13, 3).set_top_bottom(18, 8), test);
 
   test = ScaleToRoundedInsets(in, 2.49f, 3.49f);
-  EXPECT_EQ(Insets(24, 12), test);
+  EXPECT_EQ(Insets().set_left_right(12, 2).set_top_bottom(24, 10), test);
   test = ScaleToRoundedInsets(in, 2.49f);
-  EXPECT_EQ(Insets(17, 12), test);
+  EXPECT_EQ(Insets().set_left_right(12, 2).set_top_bottom(17, 7), test);
 
   test = ScaleToRoundedInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(Insets(25, 13), test);
+  EXPECT_EQ(Insets().set_left_right(13, 3).set_top_bottom(25, 11), test);
   test = ScaleToRoundedInsets(in, 2.5f);
-  EXPECT_EQ(Insets(18, 13), test);
+  EXPECT_EQ(Insets().set_left_right(13, 3).set_top_bottom(18, 8), test);
 }
 
 TEST(InsetsTest, ScaleNegative) {
-  Insets in(-7, -5);
+  Insets in = Insets().set_left_right(-5, -1).set_top_bottom(-7, -3);
 
   Insets test = ScaleToFlooredInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(Insets(-25, -13), test);
+  EXPECT_EQ(Insets().set_left_right(-13, -3).set_top_bottom(-25, -11), test);
   test = ScaleToFlooredInsets(in, 2.5f);
-  EXPECT_EQ(Insets(-18, -13), test);
+  EXPECT_EQ(Insets().set_left_right(-13, -3).set_top_bottom(-18, -8), test);
 
   test = ScaleToCeiledInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(Insets(-24, -12), test);
+  EXPECT_EQ(Insets().set_left_right(-12, -2).set_top_bottom(-24, -10), test);
   test = ScaleToCeiledInsets(in, 2.5f);
-  EXPECT_EQ(Insets(-17, -12), test);
+  EXPECT_EQ(Insets().set_left_right(-12, -2).set_top_bottom(-17, -7), test);
 
   test = ScaleToRoundedInsets(in, 2.49f, 3.49f);
-  EXPECT_EQ(Insets(-24, -12), test);
+  EXPECT_EQ(Insets().set_left_right(-12, -2).set_top_bottom(-24, -10), test);
   test = ScaleToRoundedInsets(in, 2.49f);
-  EXPECT_EQ(Insets(-17, -12), test);
+  EXPECT_EQ(Insets().set_left_right(-12, -2).set_top_bottom(-17, -7), test);
 
   test = ScaleToRoundedInsets(in, 2.5f, 3.5f);
-  EXPECT_EQ(Insets(-25, -13), test);
+  EXPECT_EQ(Insets().set_left_right(-13, -3).set_top_bottom(-25, -11), test);
   test = ScaleToRoundedInsets(in, 2.5f);
-  EXPECT_EQ(Insets(-18, -13), test);
+  EXPECT_EQ(Insets().set_left_right(-13, -3).set_top_bottom(-18, -8), test);
 }
 
 TEST(InsetsTest, IntegerOverflow) {
@@ -287,18 +325,23 @@
 TEST(InsetsTest, IntegerOverflowSet) {
   constexpr int int_max = std::numeric_limits<int>::max();
 
-  Insets set_all_test;
-  set_all_test.Set(10, 20, int_max, int_max);
-  EXPECT_EQ(Insets(10, 20, int_max - 10, int_max - 20), set_all_test);
+  Insets set_all_test =
+      Insets().set_left_right(int_max, 20).set_top_bottom(10, int_max);
+  EXPECT_EQ(
+      Insets().set_left_right(int_max, 0).set_top_bottom(10, int_max - 10),
+      set_all_test);
 }
 
 TEST(InsetsTest, IntegerOverflowOffset) {
   constexpr int int_max = std::numeric_limits<int>::max();
 
-  const Vector2d max_vector(int_max, int_max);
-  Insets insets(1, 2, 3, 4);
+  const gfx::Vector2d max_vector(int_max, int_max);
+  Insets insets = Insets().set_left_right(2, 4).set_top_bottom(1, 3);
   insets.Offset(max_vector);
-  EXPECT_EQ(Insets(int_max, int_max, 3 - int_max, 4 - int_max), insets);
+  EXPECT_EQ(gfx::Insets()
+                .set_left_right(int_max, 4 - int_max)
+                .set_top_bottom(int_max, 3 - int_max),
+            insets);
 }
 
 TEST(InsetsTest, IntegerUnderflowOffset) {
@@ -307,26 +350,29 @@
   const Vector2d min_vector(int_min, int_min);
   Insets insets(-10);
   insets.Offset(min_vector);
-  EXPECT_EQ(Insets(int_min, int_min, -10 - int_min, -10 - int_min), insets);
+  EXPECT_EQ(gfx::Insets()
+                .set_left_right(int_min, -10 - int_min)
+                .set_top_bottom(int_min, -10 - int_min),
+            insets);
 }
 
 TEST(InsetsTest, Size) {
-  Insets insets(1, 2, 3, 4);
+  Insets insets = Insets().set_left_right(2, 4).set_top_bottom(1, 3);
   EXPECT_EQ(Size(6, 4), insets.size());
 }
 
 TEST(InsetsTest, SetToMax) {
   Insets insets;
-  insets.SetToMax(Insets(-1, 2, -3, 4));
-  EXPECT_EQ(Insets(0, 2, 0, 4), insets);
+  insets.SetToMax(Insets().set_left_right(2, 4).set_top_bottom(-1, -3));
+  EXPECT_EQ(Insets().set_left_right(2, 4), insets);
   insets.SetToMax(Insets());
-  EXPECT_EQ(Insets(0, 2, 0, 4), insets);
-  insets.SetToMax(Insets(1, 0, 3, 0));
-  EXPECT_EQ(Insets(1, 2, 3, 4), insets);
-  insets.SetToMax(Insets(20, 30, 40, 50));
-  EXPECT_EQ(Insets(20, 30, 40, 50), insets);
+  EXPECT_EQ(Insets().set_left_right(2, 4), insets);
+  insets.SetToMax(Insets().set_top_bottom(1, 3));
+  EXPECT_EQ(Insets().set_left_right(2, 4).set_top_bottom(1, 3), insets);
+  insets.SetToMax(Insets().set_left_right(30, 50).set_top_bottom(20, 40));
+  EXPECT_EQ(Insets().set_left_right(30, 50).set_top_bottom(20, 40), insets);
 
-  Insets insets1(-1, -2, -3, -4);
+  Insets insets1 = Insets().set_left_right(-2, -4).set_top_bottom(-2, -4);
   insets1.SetToMax(Insets());
   EXPECT_EQ(Insets(), insets1);
 }
diff --git a/ui/gfx/geometry/outsets.h b/ui/gfx/geometry/outsets.h
new file mode 100644
index 0000000..c189175
--- /dev/null
+++ b/ui/gfx/geometry/outsets.h
@@ -0,0 +1,38 @@
+// Copyright 2022 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_GFX_GEOMETRY_OUTSETS_H_
+#define UI_GFX_GEOMETRY_OUTSETS_H_
+
+#include "base/numerics/clamped_math.h"
+#include "ui/gfx/geometry/geometry_export.h"
+#include "ui/gfx/geometry/insets_outsets_base.h"
+
+namespace gfx {
+
+// This can be used to represent a space surrounding a rectangle, by
+// "expanding" the rectangle by the outset amount on all four sides.
+class Outsets : public InsetsOutsetsBase<Outsets> {
+ public:
+  using InsetsOutsetsBase::InsetsOutsetsBase;
+};
+
+inline Outsets operator+(Outsets lhs, const Outsets& rhs) {
+  lhs += rhs;
+  return lhs;
+}
+
+inline Outsets operator-(Outsets lhs, const Outsets& rhs) {
+  lhs -= rhs;
+  return lhs;
+}
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const Outsets&, ::std::ostream* os);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_OUTSETS_H_
diff --git a/ui/gfx/geometry/outsets_f.h b/ui/gfx/geometry/outsets_f.h
new file mode 100644
index 0000000..d2c6bf2
--- /dev/null
+++ b/ui/gfx/geometry/outsets_f.h
@@ -0,0 +1,36 @@
+// Copyright 2022 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_GFX_GEOMETRY_OUTSETS_F_H_
+#define UI_GFX_GEOMETRY_OUTSETS_F_H_
+
+#include "ui/gfx/geometry/geometry_export.h"
+#include "ui/gfx/geometry/insets_outsets_f_base.h"
+
+namespace gfx {
+
+// A floating point version of gfx::Outsets.
+class GEOMETRY_EXPORT OutsetsF : public InsetsOutsetsFBase<OutsetsF> {
+ public:
+  using InsetsOutsetsFBase::InsetsOutsetsFBase;
+};
+
+inline OutsetsF operator+(OutsetsF lhs, const OutsetsF& rhs) {
+  lhs += rhs;
+  return lhs;
+}
+
+inline OutsetsF operator-(OutsetsF lhs, const OutsetsF& rhs) {
+  lhs -= rhs;
+  return lhs;
+}
+
+// This is declared here for use in gtest-based unit tests but is defined in
+// the //ui/gfx:test_support target. Depend on that to use this in your unit
+// test. This should not be used in production code - call ToString() instead.
+void PrintTo(const OutsetsF&, ::std::ostream* os);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_GEOMETRY_OUTSETS_F_H_
diff --git a/ui/gfx/geometry/rect.cc b/ui/gfx/geometry/rect.cc
index 49ea8b79..cc0cbd0 100644
--- a/ui/gfx/geometry/rect.cc
+++ b/ui/gfx/geometry/rect.cc
@@ -11,6 +11,7 @@
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/outsets.h"
 
 #if defined(OS_WIN)
 #include <windows.h>
@@ -125,6 +126,10 @@
   set_height(base::ClampSub(height(), base::ClampAdd(top, bottom)));
 }
 
+void Rect::Outset(const Outsets& outsets) {
+  Outset(outsets.left(), outsets.top(), outsets.right(), outsets.bottom());
+}
+
 void Rect::Offset(const Vector2d& distance) {
   origin_ += distance;
   // Ensure that width and height remain valid.
diff --git a/ui/gfx/geometry/rect.h b/ui/gfx/geometry/rect.h
index b6afc92..e9600a70 100644
--- a/ui/gfx/geometry/rect.h
+++ b/ui/gfx/geometry/rect.h
@@ -32,6 +32,7 @@
 namespace gfx {
 
 class Insets;
+class Outsets;
 
 class GEOMETRY_EXPORT Rect {
  public:
@@ -160,6 +161,7 @@
   void Outset(int left, int top, int right, int bottom) {
     Inset(-left, -top, -right, -bottom);
   }
+  void Outset(const Outsets& outsets);
 
   // Move the rectangle by a horizontal and vertical distance.
   void Offset(int horizontal, int vertical) {
diff --git a/ui/gfx/geometry/rect_f.cc b/ui/gfx/geometry/rect_f.cc
index 9a1d0407..d65cf41a 100644
--- a/ui/gfx/geometry/rect_f.cc
+++ b/ui/gfx/geometry/rect_f.cc
@@ -12,6 +12,7 @@
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "ui/gfx/geometry/insets_f.h"
+#include "ui/gfx/geometry/outsets_f.h"
 
 #if defined(OS_IOS)
 #include <CoreGraphics/CoreGraphics.h>
@@ -52,6 +53,10 @@
   set_height(std::max(height() - top - bottom, 0.0f));
 }
 
+void RectF::Outset(const OutsetsF& outsets) {
+  Outset(outsets.left(), outsets.top(), outsets.right(), outsets.bottom());
+}
+
 void RectF::Offset(float horizontal, float vertical) {
   origin_ += Vector2dF(horizontal, vertical);
 }
diff --git a/ui/gfx/geometry/rect_f.h b/ui/gfx/geometry/rect_f.h
index 3eeb869..c60519cc 100644
--- a/ui/gfx/geometry/rect_f.h
+++ b/ui/gfx/geometry/rect_f.h
@@ -21,6 +21,7 @@
 namespace gfx {
 
 class InsetsF;
+class OutsetsF;
 
 // A floating version of gfx::Rect.
 class GEOMETRY_EXPORT RectF {
@@ -109,6 +110,7 @@
   void Outset(float left, float top, float right, float bottom) {
     Inset(-left, -top, -right, -bottom);
   }
+  void Outset(const OutsetsF& outsets);
 
   // Move the rectangle by a horizontal and vertical distance.
   void Offset(float horizontal, float vertical);
diff --git a/ui/gfx/geometry/test/geometry_util.cc b/ui/gfx/geometry/test/geometry_util.cc
index 68c3814..0e2adba 100644
--- a/ui/gfx/geometry/test/geometry_util.cc
+++ b/ui/gfx/geometry/test/geometry_util.cc
@@ -11,6 +11,8 @@
 #include "ui/gfx/geometry/box_f.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/insets_f.h"
+#include "ui/gfx/geometry/outsets.h"
+#include "ui/gfx/geometry/outsets_f.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/point_f.h"
@@ -342,12 +344,20 @@
   *os << point.ToString();
 }
 
-void PrintTo(const Insets& insets, ::std::ostream* os) {
-  *os << insets.ToString();
+void PrintTo(const Insets& input, ::std::ostream* os) {
+  *os << input.ToString();
 }
 
-void PrintTo(const InsetsF& insets, ::std::ostream* os) {
-  *os << insets.ToString();
+void PrintTo(const InsetsF& input, ::std::ostream* os) {
+  *os << input.ToString();
+}
+
+void PrintTo(const Outsets& input, ::std::ostream* os) {
+  *os << input.ToString();
+}
+
+void PrintTo(const OutsetsF& input, ::std::ostream* os) {
+  *os << input.ToString();
 }
 
 void PrintTo(const QuadF& quad, ::std::ostream* os) {
diff --git a/ui/message_center/views/notification_view_base.cc b/ui/message_center/views/notification_view_base.cc
index 8af2018..98838c3 100644
--- a/ui/message_center/views/notification_view_base.cc
+++ b/ui/message_center/views/notification_view_base.cc
@@ -299,6 +299,12 @@
 
 NotificationViewBase::NotificationViewBase(const Notification& notification)
     : MessageView(notification) {
+  for_ash_notification_ = false;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  if (ash::features::IsNotificationsRefreshEnabled())
+    for_ash_notification_ = true;
+#endif
+
   SetNotifyEnterExitOnChild(true);
 
   click_activator_ = std::make_unique<ClickActivator>(this);
@@ -444,16 +450,10 @@
 views::Builder<NotificationHeaderView>
 NotificationViewBase::CreateHeaderRowBuilder() {
   DCHECK(!header_row_);
-  header_view_in_ash_notification_ = false;
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  if (ash::features::IsNotificationsRefreshEnabled())
-    header_view_in_ash_notification_ = true;
-#endif
-
   auto header_row_builder = views::Builder<NotificationHeaderView>()
                                 .SetID(kHeaderRow)
                                 .CopyAddressTo(&header_row_);
-  if (!header_view_in_ash_notification_) {
+  if (!for_ash_notification_) {
     header_row_builder.SetCallback(base::BindRepeating(
         &NotificationViewBase::HeaderRowPressed, base::Unretained(this)));
   }
@@ -783,8 +783,11 @@
       action_button_to_placeholder_map_[action_buttons_[i]] =
           button_info.placeholder;
     }
-    // Change action button color to the accent color.
-    action_buttons_[i]->SetEnabledTextColors(notification.accent_color());
+
+    if (!for_ash_notification_) {
+      // Change action button color to the accent color.
+      action_buttons_[i]->SetEnabledTextColors(notification.accent_color());
+    }
   }
 
   // Inherit mouse hover state when action button views reset.
@@ -852,7 +855,7 @@
 }
 
 void NotificationViewBase::SetExpandButtonEnabled(bool enabled) {
-  if (!header_view_in_ash_notification_)
+  if (!for_ash_notification_)
     header_row_->SetExpandButtonEnabled(enabled);
 }
 
@@ -861,7 +864,7 @@
 }
 
 void NotificationViewBase::UpdateViewForExpandedState(bool expanded) {
-  if (!header_view_in_ash_notification_)
+  if (!for_ash_notification_)
     header_row_->SetExpanded(expanded);
 
   if (!image_container_view_->children().empty())
@@ -881,7 +884,7 @@
     status_view_->SetVisible(expanded);
 
   int max_items = expanded ? item_views_.size() : kMaxLinesForMessageView;
-  if (!header_view_in_ash_notification_ && list_items_count_ > max_items)
+  if (!for_ash_notification_ && list_items_count_ > max_items)
     header_row_->SetOverflowIndicator(list_items_count_ - max_items);
   else if (!item_views_.empty())
     header_row_->SetSummaryText(std::u16string());
diff --git a/ui/message_center/views/notification_view_base.h b/ui/message_center/views/notification_view_base.h
index 19f003d..6724eb0 100644
--- a/ui/message_center/views/notification_view_base.h
+++ b/ui/message_center/views/notification_view_base.h
@@ -254,6 +254,8 @@
   views::View* action_buttons_row() { return action_buttons_row_; }
   const views::View* action_buttons_row() const { return action_buttons_row_; }
 
+  std::vector<views::LabelButton*> action_buttons() { return action_buttons_; }
+
   NotificationInputContainer* inline_reply() { return inline_reply_; }
 
   const views::Label* status_view() const { return status_view_; }
@@ -349,9 +351,10 @@
   // Describes whether the view should display a hand pointer or not.
   bool clickable_;
 
-  // Describes whether the header view is used in an ash notification (ash
-  // notification uses a customized header view).
-  bool header_view_in_ash_notification_ = true;
+  // Describes whether this view is for an ash/ChromeOS notification (ash
+  // notification UI uses AshNotificationView, which has customized layout,
+  // header view, etc.).
+  bool for_ash_notification_ = true;
 
   // Describes whether the view can display inline settings or not.
   bool inline_settings_enabled_ = false;
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc
index 19deb9a..eafbf6f 100644
--- a/ui/ozone/platform/drm/common/drm_util.cc
+++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -18,9 +18,7 @@
 #include <vector>
 
 #include "base/containers/flat_map.h"
-#include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
-#include "base/notreached.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
@@ -194,7 +192,9 @@
   if (sw_index >= 0 && hw_index >= 0) {
     const std::string hw_enum_value = GetNameForEnumValue(
         hw_property.get(), connector->prop_values[hw_index]);
-    return GetPrivacyScreenStateFromEnumValue(hw_enum_value);
+    const display::PrivacyScreenState* state =
+        GetInternalTypeValueFromDrmEnum(hw_enum_value, kPrivacyScreenStates);
+    return state ? *state : display::kNotSupported;
   }
 
   // If the new privacy screen UAPI properties are missing, try to fetch the
@@ -205,7 +205,9 @@
   if (legacy_index >= 0) {
     const std::string legacy_enum_value = GetNameForEnumValue(
         legacy_property.get(), connector->prop_values[legacy_index]);
-    return GetPrivacyScreenStateFromEnumValue(legacy_enum_value);
+    const display::PrivacyScreenState* state = GetInternalTypeValueFromDrmEnum(
+        legacy_enum_value, kPrivacyScreenStates);
+    return state ? *state : display::kNotSupported;
   }
 
   return display::PrivacyScreenState::kNotSupported;
@@ -686,16 +688,23 @@
   return path;
 }
 
-display::PrivacyScreenState GetPrivacyScreenStateFromEnumValue(
-    const std::string& enum_value) {
-  for (auto mapping : kPrivacyScreenStates) {
-    if (enum_value == mapping.drm_enum)
-      return mapping.internal_state;
+std::string GetEnumNameForProperty(
+    const drmModePropertyRes& property,
+    const drmModeObjectProperties& property_values) {
+  for (uint32_t prop_idx = 0; prop_idx < property_values.count_props;
+       ++prop_idx) {
+    if (property_values.props[prop_idx] != property.prop_id)
+      continue;
+
+    for (int enum_idx = 0; enum_idx < property.count_enums; ++enum_idx) {
+      const drm_mode_property_enum& property_enum = property.enums[enum_idx];
+      if (property_enum.value == property_values.prop_values[prop_idx])
+        return property_enum.name;
+    }
   }
 
-  NOTREACHED() << "Failed to match privacy screen property enum '" << enum_value
-               << "' to an internal type.";
-  return display::kNotSupported;
+  NOTREACHED();
+  return std::string();
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/common/drm_util.h b/ui/ozone/platform/drm/common/drm_util.h
index 84072dc1..d893b277 100644
--- a/ui/ozone/platform/drm/common/drm_util.h
+++ b/ui/ozone/platform/drm/common/drm_util.h
@@ -6,12 +6,16 @@
 #define UI_OZONE_PLATFORM_DRM_COMMON_DRM_UTIL_H_
 
 #include <stddef.h>
+#include <stdint.h>
+#include <xf86drmMode.h>
 
 #include <memory>
 #include <utility>
 #include <vector>
 
 #include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/notreached.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/display/types/display_snapshot.h"
 #include "ui/ozone/platform/drm/common/display_types.h"
@@ -50,21 +54,25 @@
   const InternalType& internal_state;
 };
 
-const DrmPropertyEnumToInternalTypeMapping<display::ContentProtectionMethod>
-    kHdcpContentTypeStates[] = {
-        {"HDCP Type0", display::CONTENT_PROTECTION_METHOD_HDCP_TYPE_0},
-        {"HDCP Type1", display::CONTENT_PROTECTION_METHOD_HDCP_TYPE_1}};
+constexpr std::array<
+    DrmPropertyEnumToInternalTypeMapping<display::ContentProtectionMethod>,
+    2>
+    kHdcpContentTypeStates{
+        {{"HDCP Type0", display::CONTENT_PROTECTION_METHOD_HDCP_TYPE_0},
+         {"HDCP Type1", display::CONTENT_PROTECTION_METHOD_HDCP_TYPE_1}}};
 
-const DrmPropertyEnumToInternalTypeMapping<display::HDCPState>
-    kContentProtectionStates[] = {{"Undesired", display::HDCP_STATE_UNDESIRED},
-                                  {"Desired", display::HDCP_STATE_DESIRED},
-                                  {"Enabled", display::HDCP_STATE_ENABLED}};
+constexpr std::array<DrmPropertyEnumToInternalTypeMapping<display::HDCPState>,
+                     3>
+    kContentProtectionStates{{{"Undesired", display::HDCP_STATE_UNDESIRED},
+                              {"Desired", display::HDCP_STATE_DESIRED},
+                              {"Enabled", display::HDCP_STATE_ENABLED}}};
 
-const DrmPropertyEnumToInternalTypeMapping<display::PrivacyScreenState>
-    kPrivacyScreenStates[] = {{"Disabled", display::kDisabled},
+constexpr std::
+    array<DrmPropertyEnumToInternalTypeMapping<display::PrivacyScreenState>, 4>
+        kPrivacyScreenStates{{{"Disabled", display::kDisabled},
                               {"Enabled", display::kEnabled},
                               {"Disabled-locked", display::kDisabledLocked},
-                              {"Enabled-locked", display::kEnabledLocked}};
+                              {"Enabled-locked", display::kEnabledLocked}}};
 
 // Representation of the information required to initialize and configure a
 // native display. |index| is the position of the connection and will be
@@ -150,8 +158,93 @@
 
 std::vector<uint64_t> ParsePathBlob(const drmModePropertyBlobRes& path_blob);
 
-display::PrivacyScreenState GetPrivacyScreenStateFromEnumValue(
-    const std::string& enum_value);
+// Extracts the DRM |property| current value's enum. Returns an empty string
+// upon failure.
+std::string GetEnumNameForProperty(
+    const drmModePropertyRes& property,
+    const drmModeObjectProperties& property_values);
+
+// Extracts the DRM property's numeric value that maps to |internal_state|.
+// Returns the maximal numeric value for uint64_t upon failure.
+template <typename InternalType, typename DrmPropertyToInternalTypeMap>
+uint64_t GetDrmValueForInternalType(const InternalType& internal_state,
+                                    const drmModePropertyRes& property,
+                                    const DrmPropertyToInternalTypeMap& map) {
+  std::string drm_enum;
+  for (const auto& pair : map) {
+    if (pair.internal_state == internal_state) {
+      drm_enum = pair.drm_enum;
+      break;
+    }
+  }
+  DCHECK(!drm_enum.empty())
+      << "Property " << property.name
+      << " has no enum value for the requested internal state (value <"
+      << internal_state << ">).";
+
+  for (int i = 0; i < property.count_enums; ++i) {
+    if (drm_enum == property.enums[i].name)
+      return property.enums[i].value;
+  }
+
+  NOTREACHED() << "Failed to extract DRM value for property '" << property.name
+               << "' and enum '" << drm_enum << "'";
+  return std::numeric_limits<uint64_t>::max();
+}
+
+// Returns the internal type value that maps to the DRM property's current
+// value. Returns nullptr upon failure.
+template <typename InternalType, size_t size>
+const InternalType* GetDrmPropertyCurrentValueAsInternalType(
+    const std::array<DrmPropertyEnumToInternalTypeMapping<InternalType>, size>&
+        array,
+    const drmModePropertyRes& property,
+    const drmModeObjectProperties& property_values) {
+  const std::string drm_enum =
+      GetEnumNameForProperty(property, property_values);
+  if (drm_enum.empty()) {
+    LOG(ERROR) << "Failed to fetch DRM enum for property '" << property.name
+               << "'";
+    return nullptr;
+  }
+
+  for (const auto& pair : array) {
+    if (drm_enum == pair.drm_enum) {
+      VLOG(3) << "Internal state value: " << pair.internal_state << " ("
+              << drm_enum << ")";
+      return &pair.internal_state;
+    }
+  }
+
+  NOTREACHED() << "Failed to extract internal value for DRM property '"
+               << property.name << "'";
+  return nullptr;
+}
+
+// Returns the internal type value that maps to |drm_enum| within |array|.
+// Returns nullptr upon failure.
+template <typename InternalType, size_t size>
+const InternalType* GetInternalTypeValueFromDrmEnum(
+    const std::string& drm_enum,
+    const std::array<DrmPropertyEnumToInternalTypeMapping<InternalType>, size>&
+        array) {
+  if (drm_enum.empty()) {
+    LOG(ERROR) << "DRM property value enum is empty.";
+    return nullptr;
+  }
+
+  for (const auto& pair : array) {
+    if (drm_enum == pair.drm_enum) {
+      VLOG(3) << "Internal state value: " << pair.internal_state << " ("
+              << drm_enum << ")";
+      return &pair.internal_state;
+    }
+  }
+
+  LOG(ERROR) << "Failed to extract internal value for DRM property enum '"
+             << drm_enum << "'";
+  return nullptr;
+}
 
 }  // namespace ui
 
diff --git a/ui/ozone/platform/drm/gpu/drm_display.cc b/ui/ozone/platform/drm/gpu/drm_display.cc
index 62443ce..9bdb01d 100644
--- a/ui/ozone/platform/drm/gpu/drm_display.cc
+++ b/ui/ozone/platform/drm/gpu/drm_display.cc
@@ -23,65 +23,6 @@
 
 namespace {
 
-// Converts |state| to the DRM value associated with the it.
-uint32_t GetContentProtectionValue(drmModePropertyRes* property,
-                                   display::HDCPState state) {
-  std::string name;
-  for (const auto& mapping : kContentProtectionStates) {
-    if (mapping.internal_state == state) {
-      name = mapping.drm_enum;
-      break;
-    }
-  }
-
-  for (int i = 0; i < property->count_enums; ++i) {
-    if (name == property->enums[i].name)
-      return i;
-  }
-
-  NOTREACHED();
-  return 0;
-}
-
-// Converts |content_type| to the DRM value associated with the it.
-uint32_t GetHdcpContentTypeValue(
-    drmModePropertyRes* property,
-    display::ContentProtectionMethod content_type) {
-  std::string name;
-  for (const auto& mapping : kHdcpContentTypeStates) {
-    if (mapping.internal_state == content_type) {
-      name = mapping.drm_enum;
-      break;
-    }
-  }
-
-  for (int i = 0; i < property->count_enums; ++i) {
-    if (name == property->enums[i].name)
-      return i;
-  }
-
-  NOTREACHED();
-  return 0;
-}
-
-std::string GetEnumNameForProperty(drmModeObjectProperties* property_values,
-                                   drmModePropertyRes* property) {
-  for (uint32_t prop_idx = 0; prop_idx < property_values->count_props;
-       ++prop_idx) {
-    if (property_values->props[prop_idx] != property->prop_id)
-      continue;
-
-    for (int enum_idx = 0; enum_idx < property->count_enums; ++enum_idx) {
-      const drm_mode_property_enum& property_enum = property->enums[enum_idx];
-      if (property_enum.value == property_values->prop_values[prop_idx])
-        return property_enum.name;
-    }
-  }
-
-  NOTREACHED();
-  return std::string();
-}
-
 std::vector<drmModeModeInfo> GetDrmModeVector(drmModeConnector* connector) {
   std::vector<drmModeModeInfo> modes;
   for (int i = 0; i < connector->count_modes; ++i)
@@ -136,7 +77,8 @@
   const display::PrivacyScreenState state_to_set =
       enabled ? display::kEnabled : display::kDisabled;
   if (!drm_->SetProperty(connector_->connector_id, property->prop_id,
-                         state_to_set)) {
+                         GetDrmValueForInternalType(state_to_set, *property,
+                                                    kPrivacyScreenStates))) {
     LOG(ERROR) << (enabled ? "Enabling" : "Disabling") << " property '"
                << property->name << "' failed!";
     return false;
@@ -145,26 +87,6 @@
   return ValidateCurrentStateAgainst(enabled);
 }
 
-uint64_t DrmDisplay::PrivacyScreenProperty::GetStateValue(
-    drmModePropertyRes* property,
-    const display::PrivacyScreenState& state) const {
-  std::string name;
-  for (const auto& mapping : kPrivacyScreenStates) {
-    if (mapping.internal_state == state) {
-      name = mapping.drm_enum;
-      break;
-    }
-  }
-
-  for (int i = 0; i < property->count_enums; ++i) {
-    if (name == property->enums[i].name)
-      return property->enums[i].value;
-  }
-
-  NOTREACHED();
-  return 0;
-}
-
 display::PrivacyScreenState
 DrmDisplay::PrivacyScreenProperty::GetPrivacyScreenState() const {
   drmModePropertyRes* property = GetReadPrivacyScreenProperty();
@@ -183,14 +105,15 @@
   }
 
   const std::string privacy_screen_state_name =
-      GetEnumNameForProperty(property_values.get(), property);
-  return GetPrivacyScreenStateFromEnumValue(privacy_screen_state_name);
+      GetEnumNameForProperty(*property, *property_values);
+  const display::PrivacyScreenState* state = GetInternalTypeValueFromDrmEnum(
+      privacy_screen_state_name, kPrivacyScreenStates);
+  return state ? *state : display::kNotSupported;
 }
 
 bool DrmDisplay::PrivacyScreenProperty::ValidateCurrentStateAgainst(
     bool enabled) const {
   display::PrivacyScreenState current_state = GetPrivacyScreenState();
-
   if (current_state == display::kNotSupported)
     return false;
 
@@ -259,7 +182,7 @@
 // When reading DRM state always check that it's still valid. Any sort of events
 // (such as disconnects) may invalidate the state.
 bool DrmDisplay::GetHDCPState(
-    display::HDCPState* state,
+    display::HDCPState* hdcp_state,
     display::ContentProtectionMethod* protection_method) {
   if (!connector_)
     return false;
@@ -280,23 +203,19 @@
                << connector_->connector_id << ".";
     return false;
   }
-  std::string name =
-      GetEnumNameForProperty(property_values.get(), hdcp_property.get());
-  size_t i;
-  for (i = 0; i < base::size(kContentProtectionStates); ++i) {
-    if (name == kContentProtectionStates[i].drm_enum) {
-      *state = kContentProtectionStates[i].internal_state;
-      VLOG(3) << "HDCP state: " << *state << " (" << name << ")";
-      break;
-    }
-  }
 
-  if (i == base::size(kContentProtectionStates)) {
-    LOG(ERROR) << "Unknown content protection value '" << name << "'";
+  const display::HDCPState* hw_hdcp_state =
+      GetDrmPropertyCurrentValueAsInternalType(
+          kContentProtectionStates, *hdcp_property, *property_values);
+  if (hw_hdcp_state) {
+    VLOG(3) << "HDCP state: " << *hw_hdcp_state << ".";
+    *hdcp_state = *hw_hdcp_state;
+  } else {
+    LOG(ERROR) << "Unknown content protection value.";
     return false;
   }
 
-  if (*state == display::HDCP_STATE_UNDESIRED) {
+  if (*hdcp_state == display::HDCP_STATE_UNDESIRED) {
     // ProtectionMethod doesn't matter if we don't have it desired/enabled.
     *protection_method = display::CONTENT_PROTECTION_METHOD_NONE;
     return true;
@@ -311,21 +230,18 @@
     *protection_method = display::CONTENT_PROTECTION_METHOD_HDCP_TYPE_0;
     return true;
   }
-  name = GetEnumNameForProperty(property_values.get(),
-                                content_type_property.get());
-  for (i = 0; i < base::size(kHdcpContentTypeStates); ++i) {
-    if (name == kHdcpContentTypeStates[i].drm_enum) {
-      *protection_method = kHdcpContentTypeStates[i].internal_state;
-      VLOG(3) << "Content Protection Method: " << *protection_method << " ("
-              << name << ")";
-      break;
-    }
-  }
 
-  if (i == base::size(kHdcpContentTypeStates)) {
-    LOG(ERROR) << "Unknown HDCP content type value '" << name << "'";
+  const display::ContentProtectionMethod* hw_protection_method =
+      GetDrmPropertyCurrentValueAsInternalType(
+          kHdcpContentTypeStates, *content_type_property, *property_values);
+  if (hw_protection_method) {
+    VLOG(3) << "Content Protection Method: " << *protection_method << ".";
+    *protection_method = *hw_protection_method;
+  } else {
+    LOG(ERROR) << "Unknown HDCP content type value.";
     return false;
   }
+
   return true;
 }
 
@@ -348,10 +264,11 @@
         return false;
       }
       VLOG(3) << "HDCP Content Type not supported, default to Type 0";
-    } else if (!drm_->SetProperty(
-                   connector_->connector_id, content_type_property->prop_id,
-                   GetHdcpContentTypeValue(content_type_property.get(),
-                                           protection_method))) {
+    } else if (!drm_->SetProperty(connector_->connector_id,
+                                  content_type_property->prop_id,
+                                  GetDrmValueForInternalType(
+                                      protection_method, *content_type_property,
+                                      kHdcpContentTypeStates))) {
       // Failed setting HDCP Content Type.
       return false;
     }
@@ -366,7 +283,8 @@
 
   return drm_->SetProperty(
       connector_->connector_id, hdcp_property->prop_id,
-      GetContentProtectionValue(hdcp_property.get(), state));
+      GetDrmValueForInternalType(state, *hdcp_property,
+                                 kContentProtectionStates));
 }
 
 void DrmDisplay::SetColorMatrix(const std::vector<float>& color_matrix) {
diff --git a/ui/ozone/platform/drm/gpu/drm_display.h b/ui/ozone/platform/drm/gpu/drm_display.h
index 09d1fef..0479f262 100644
--- a/ui/ozone/platform/drm/gpu/drm_display.h
+++ b/ui/ozone/platform/drm/gpu/drm_display.h
@@ -41,8 +41,6 @@
     bool SetPrivacyScreenProperty(bool enabled);
 
    private:
-    uint64_t GetStateValue(drmModePropertyRes* property,
-                           const display::PrivacyScreenState& state) const;
     display::PrivacyScreenState GetPrivacyScreenState() const;
     bool ValidateCurrentStateAgainst(bool enabled) const;
     drmModePropertyRes* GetReadPrivacyScreenProperty() const;
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_manager_gpu.cc b/ui/ozone/platform/drm/gpu/drm_overlay_manager_gpu.cc
index bcb6733..13dc7cb 100644
--- a/ui/ozone/platform/drm/gpu/drm_overlay_manager_gpu.cc
+++ b/ui/ozone/platform/drm/gpu/drm_overlay_manager_gpu.cc
@@ -39,9 +39,8 @@
 DrmOverlayManagerGpu::SendOverlayValidationRequestSync(
     const std::vector<OverlaySurfaceCandidate>& candidates,
     gfx::AcceleratedWidget widget) {
-  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
-      "hwoverlays", "DrmOverlayManagerGpu::SendOverlayValidationRequestSync",
-      TRACE_ID_LOCAL(this));
+  TRACE_EVENT0("hwoverlays",
+               "DrmOverlayManagerGpu::SendOverlayValidationRequestSync");
   SetClearCacheCallbackIfNecessary();
   return drm_thread_proxy_->CheckOverlayCapabilitiesSync(widget, candidates);
 }
diff --git a/ui/ozone/platform/flatland/flatland_sysmem_buffer_collection.h b/ui/ozone/platform/flatland/flatland_sysmem_buffer_collection.h
index 8653abde..1f6554d 100644
--- a/ui/ozone/platform/flatland/flatland_sysmem_buffer_collection.h
+++ b/ui/ozone/platform/flatland/flatland_sysmem_buffer_collection.h
@@ -64,9 +64,9 @@
                   VkDevice vk_device,
                   size_t min_buffer_count);
 
-  // Creates a NativePixmap the buffer with the specified index. Returned
-  // NativePixmap holds a reference to the collection, so the collection is not
-  // deleted until all NativePixmap are destroyed.
+  // Creates a NativePixmap with the specified index. The returned
+  // NativePixmap holds a reference to the collection, so that the collection
+  // is not deleted until all NativePixmaps are destroyed.
   scoped_refptr<gfx::NativePixmap> CreateNativePixmap(size_t buffer_index);
 
   // Creates a new Vulkan image for the buffer with the specified index.
diff --git a/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc b/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc
index 424b297..6ea2f309 100644
--- a/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc
+++ b/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc
@@ -141,7 +141,7 @@
       icon_surface_->SetSurfaceBufferScale(origin_window->window_scale());
       icon_surface_->ApplyPendingState();
 
-      auto icon_offset = data.provider().GetDragImageOffset();
+      auto icon_offset = -data.provider().GetDragImageOffset();
       icon_offset_ =
           gfx::ScaleToRoundedPoint({icon_offset.x(), icon_offset.y()},
                                    1.0f / origin_window->window_scale());
diff --git a/ui/platform_window/x11/x11_topmost_window_finder.cc b/ui/platform_window/x11/x11_topmost_window_finder.cc
index e20bf0ab..c9c672c 100644
--- a/ui/platform_window/x11/x11_topmost_window_finder.cc
+++ b/ui/platform_window/x11/x11_topmost_window_finder.cc
@@ -42,12 +42,12 @@
   // reverse-iterate the list to check the windows from top-to-bottom.
   std::vector<x11::Window>::reverse_iterator iter;
   for (iter = windows.rbegin(); iter != windows.rend(); iter++) {
-    if (IsWindowNamed(*iter) && should_stop_iterating.Run(*iter))
-      return true;
     if (depth < max_depth) {
       if (EnumerateChildren(should_stop_iterating, *iter, max_depth, depth + 1))
         return true;
     }
+    if (IsWindowNamed(*iter) && should_stop_iterating.Run(*iter))
+      return true;
   }
 
   return false;
diff --git a/ui/strings/app_locale_settings.grd b/ui/strings/app_locale_settings.grd
index 935849f..95abd42 100644
--- a/ui/strings/app_locale_settings.grd
+++ b/ui/strings/app_locale_settings.grd
@@ -35,7 +35,9 @@
       <output filename="app_locale_settings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="app_locale_settings_af.pak" type="data_package" lang="af" />
       <output filename="app_locale_settings_is.pak" type="data_package" lang="is" />
+      <output filename="app_locale_settings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="app_locale_settings_am.pak" type="data_package" lang="am" />
     <output filename="app_locale_settings_ar.pak" type="data_package" lang="ar" />
diff --git a/ui/strings/ax_strings.grd b/ui/strings/ax_strings.grd
index 6f5cdb96..7167b7c 100644
--- a/ui/strings/ax_strings.grd
+++ b/ui/strings/ax_strings.grd
@@ -41,7 +41,9 @@
       <output filename="ax_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="ax_strings_af.pak" type="data_package" lang="af" />
       <output filename="ax_strings_is.pak" type="data_package" lang="is" />
+      <output filename="ax_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="ax_strings_am.pak" type="data_package" lang="am" />
     <output filename="ax_strings_ar.pak" type="data_package" lang="ar" />
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index dba7a6f..e31a304 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -47,7 +47,9 @@
       <output filename="ui_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <if expr="chromeos_ash or chromeos_lacros">
+      <output filename="ui_strings_af.pak" type="data_package" lang="af" />
       <output filename="ui_strings_is.pak" type="data_package" lang="is" />
+      <output filename="ui_strings_zu.pak" type="data_package" lang="zu" />
     </if>
     <output filename="ui_strings_am.pak" type="data_package" lang="am" />
     <output filename="ui_strings_ar.pak" type="data_package" lang="ar" />
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 946426c3..897e441 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -981,6 +981,7 @@
     "//ipc:test_support",
     "//mojo/core/embedder",
     "//skia",
+    "//ui/accessibility",
     "//ui/base",
     "//ui/base:test_support",
     "//ui/base/clipboard:clipboard_test_support",
@@ -1037,7 +1038,6 @@
       "test/widget_test_aura.cc",
     ]
     deps += [
-      "//ui/accessibility",
       "//ui/aura",
       "//ui/aura:test_support",
       "//ui/wm",
diff --git a/ui/views/controls/button/toggle_button.cc b/ui/views/controls/button/toggle_button.cc
index b6783e98..dc5ef23 100644
--- a/ui/views/controls/button/toggle_button.cc
+++ b/ui/views/controls/button/toggle_button.cc
@@ -16,6 +16,7 @@
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/color/color_id.h"
 #include "ui/color/color_provider.h"
+#include "ui/events/event.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/color_utils.h"
@@ -300,6 +301,23 @@
   SchedulePaint();
 }
 
+void ToggleButton::NotifyClick(const ui::Event& event) {
+  AnimateIsOn(!GetIsOn());
+
+  // Only trigger the action when we don't have focus. This lets the InkDrop
+  // remain and match the focus ring.
+  // TODO(pbos): Investigate triggering the ripple but returning back to the
+  // focused state correctly. This is set up to highlight on focus, but the
+  // highlight does not come back after the ripple is triggered. Then remove
+  // this and add back SetHasInkDropActionOnClick(true) in the constructor.
+  if (!HasFocus()) {
+    InkDrop::Get(this)->AnimateToState(InkDropState::ACTION_TRIGGERED,
+                                       ui::LocatedEvent::FromIfValid(&event));
+  }
+
+  Button::NotifyClick(event);
+}
+
 SkPath ToggleButton::GetFocusRingPath() const {
   SkPath path;
   const gfx::Point center = GetThumbBounds().CenterPoint();
@@ -335,23 +353,6 @@
   SchedulePaint();
 }
 
-void ToggleButton::NotifyClick(const ui::Event& event) {
-  AnimateIsOn(!GetIsOn());
-
-  // Only trigger the action when we don't have focus. This lets the InkDrop
-  // remain and match the focus ring.
-  // TODO(pbos): Investigate triggering the ripple but returning back to the
-  // focused state correctly. This is set up to highlight on focus, but the
-  // highlight does not come back after the ripple is triggered. Then remove
-  // this and add back SetHasInkDropActionOnClick(true) in the constructor.
-  if (!HasFocus()) {
-    InkDrop::Get(this)->AnimateToState(InkDropState::ACTION_TRIGGERED,
-                                       ui::LocatedEvent::FromIfValid(&event));
-  }
-
-  Button::NotifyClick(event);
-}
-
 void ToggleButton::PaintButtonContents(gfx::Canvas* canvas) {
   // Paint the toggle track. To look sharp even at fractional scale factors,
   // round up to pixel boundaries.
diff --git a/ui/views/controls/button/toggle_button.h b/ui/views/controls/button/toggle_button.h
index fece83d2..add248df 100644
--- a/ui/views/controls/button/toggle_button.h
+++ b/ui/views/controls/button/toggle_button.h
@@ -10,6 +10,10 @@
 #include "ui/gfx/animation/slide_animation.h"
 #include "ui/views/controls/button/button.h"
 
+namespace ui {
+class Event;
+}  // namespace ui
+
 namespace views {
 
 // This view presents a button that has two states: on and off. This is similar
@@ -52,6 +56,9 @@
   // views::View:
   void OnThemeChanged() override;
 
+  // views::Button:
+  void NotifyClick(const ui::Event& event) override;
+
   // Returns the path to draw the focus ring around for this ToggleButton.
   SkPath GetFocusRingPath() const;
 
@@ -79,7 +86,6 @@
   void OnBlur() override;
 
   // Button:
-  void NotifyClick(const ui::Event& event) override;
   void PaintButtonContents(gfx::Canvas* canvas) override;
 
   // gfx::AnimationDelegate:
diff --git a/ui/views/controls/menu/menu_config_win.cc b/ui/views/controls/menu/menu_config_win.cc
index 2707e95..c6e0604 100644
--- a/ui/views/controls/menu/menu_config_win.cc
+++ b/ui/views/controls/menu/menu_config_win.cc
@@ -45,12 +45,7 @@
 
   SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &show_delay, 0);
 
-  win11_style_menus =
-      base::FeatureList::IsEnabled(features::kWin11StyleMenus) &&
-      (base::win::GetVersion() >= base::win::Version::WIN11 ||
-       base::GetFieldTrialParamByFeatureAsBool(
-           features::kWin11StyleMenus,
-           features::kWin11StyleMenuAllWindowsVersionsName, false));
+  win11_style_menus = base::win::GetVersion() >= base::win::Version::WIN11;
   UMA_HISTOGRAM_BOOLEAN("Windows.Menu.Win11Style", win11_style_menus);
   separator_upper_height = 5;
   separator_lower_height = 7;
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.cc b/ui/views/controls/tabbed_pane/tabbed_pane.cc
index 2ca772d..83a5f179 100644
--- a/ui/views/controls/tabbed_pane/tabbed_pane.cc
+++ b/ui/views/controls/tabbed_pane/tabbed_pane.cc
@@ -42,7 +42,6 @@
 
 Tab::Tab(TabbedPane* tabbed_pane, const std::u16string& title, View* contents)
     : tabbed_pane_(tabbed_pane), contents_(contents) {
-  set_suppress_default_focus_handling();
   // Calculate the size while the font list is bold.
   auto title_label = std::make_unique<Label>(title, style::CONTEXT_LABEL,
                                              style::STYLE_TAB_ACTIVE);
@@ -162,12 +161,6 @@
         GetInsets() - gfx::Insets(border_size)));
   }
 
-  // When the tab gains focus, send an accessibility event indicating that the
-  // contents are focused. When the tab loses focus, whichever new View ends up
-  // with focus will send an ax::mojom::Event::kFocus of its own, so there's no
-  // need to send one in OnBlur().
-  if (contents())
-    contents()->NotifyAccessibilityEvent(ax::mojom::Event::kFocus, true);
   SchedulePaint();
 }
 
@@ -532,7 +525,7 @@
   DCHECK_LE(index, GetTabCount());
   contents->SetVisible(false);
   contents->GetViewAccessibility().OverrideName(title);
-  contents->GetViewAccessibility().OverrideRole(ax::mojom::Role::kTab);
+  contents->GetViewAccessibility().OverrideRole(ax::mojom::Role::kTabPanel);
 
   tab_strip_->AddChildViewAt(std::make_unique<Tab>(this, title, contents.get()),
                              static_cast<int>(index));
@@ -555,6 +548,10 @@
     old_selected_tab->SetSelected(false);
     tab_strip_->OnSelectedTabChanged(old_selected_tab, new_selected_tab,
                                      animate);
+
+    new_selected_tab->NotifyAccessibilityEvent(ax::mojom::Event::kSelection,
+                                               true);
+    NotifyAccessibilityEvent(ax::mojom::Event::kSelectedChildrenChanged, true);
   }
   tab_strip_->SchedulePaint();
 
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc b/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc
index 4933609..0a9d1ca 100644
--- a/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc
+++ b/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc
@@ -15,6 +15,7 @@
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/events/keycodes/keyboard_code_conversion.h"
 #include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/test/ax_event_counter.h"
 #include "ui/views/test/test_views.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/widget/widget.h"
@@ -298,15 +299,72 @@
   EXPECT_EQ(kSecondTitle, GetAccessibleName(tab2_contents));
 }
 
-TEST_F(TabbedPaneWithWidgetTest, AccessiblePaneContentsRoleIsTab) {
+TEST_F(TabbedPaneWithWidgetTest, AccessiblePaneContentsRoleIsTabPanel) {
   const std::u16string kFirstTitle = u"Tab1";
   const std::u16string kSecondTitle = u"Tab2";
   View* const tab1_contents =
       tabbed_pane_->AddTab(kFirstTitle, std::make_unique<View>());
   View* const tab2_contents =
       tabbed_pane_->AddTab(kSecondTitle, std::make_unique<View>());
-  EXPECT_EQ(ax::mojom::Role::kTab, GetAccessibleRole(tab1_contents));
-  EXPECT_EQ(ax::mojom::Role::kTab, GetAccessibleRole(tab2_contents));
+  EXPECT_EQ(ax::mojom::Role::kTabPanel, GetAccessibleRole(tab1_contents));
+  EXPECT_EQ(ax::mojom::Role::kTabPanel, GetAccessibleRole(tab2_contents));
+}
+
+TEST_F(TabbedPaneWithWidgetTest, AccessibleEvents) {
+  tabbed_pane_->AddTab(u"Tab1", std::make_unique<View>());
+  tabbed_pane_->AddTab(u"Tab2", std::make_unique<View>());
+  test::AXEventCounter counter(views::AXEventManager::Get());
+
+  // This is needed for FocusManager::SetFocusedViewWithReason to notify
+  // observers observers of focus changes.
+  if (widget_ && !widget_->IsActive())
+    widget_->Activate();
+
+  EXPECT_EQ(0u, tabbed_pane_->GetSelectedTabIndex());
+
+  // Change the selected tab without giving the tab focus should result in a
+  // selection change for the new tab and a selected-children-changed for the
+  // tab list. No focus events should occur.
+  tabbed_pane_->SelectTabAt(1);
+  EXPECT_EQ(1u, tabbed_pane_->GetSelectedTabIndex());
+  EXPECT_EQ(
+      1, counter.GetCount(ax::mojom::Event::kSelection, ax::mojom::Role::kTab));
+  EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kSelectedChildrenChanged,
+                                ax::mojom::Role::kTabList));
+  EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kFocus));
+
+  counter.ResetAllCounts();
+
+  // Focusing the selected tab should only result in a focus event for that tab.
+  tabbed_pane_->GetFocusManager()->SetFocusedView(tabbed_pane_->GetTabAt(1));
+  EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kFocus));
+  EXPECT_EQ(1,
+            counter.GetCount(ax::mojom::Event::kFocus, ax::mojom::Role::kTab));
+  EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kSelection));
+  EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kSelectedChildrenChanged));
+
+  counter.ResetAllCounts();
+
+  // Arrowing left to the first tab selects it. Therefore we should get the same
+  // events as we did when SelectTabAt() was called.
+  SendKeyPressToSelectedTab(ui::VKEY_LEFT);
+  EXPECT_EQ(0u, tabbed_pane_->GetSelectedTabIndex());
+  EXPECT_EQ(
+      1, counter.GetCount(ax::mojom::Event::kSelection, ax::mojom::Role::kTab));
+  EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kSelectedChildrenChanged,
+                                ax::mojom::Role::kTabList));
+  EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kFocus));
+
+  counter.ResetAllCounts();
+
+  // Focusing an unselected tab, if the UI allows it, a should only result in a
+  // focus event for that tab.
+  tabbed_pane_->GetFocusManager()->SetFocusedView(tabbed_pane_->GetTabAt(1));
+  EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kFocus));
+  EXPECT_EQ(1,
+            counter.GetCount(ax::mojom::Event::kFocus, ax::mojom::Role::kTab));
+  EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kSelection));
+  EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kSelectedChildrenChanged));
 }
 
 }  // namespace test
diff --git a/ui/views/layout/flex_layout_types.cc b/ui/views/layout/flex_layout_types.cc
index 7c4bd3f..a263d6f 100644
--- a/ui/views/layout/flex_layout_types.cc
+++ b/ui/views/layout/flex_layout_types.cc
@@ -59,7 +59,7 @@
   const gfx::Size& operator*() const { return *get(); }
   const gfx::Size* get() const {
     if (!size_)
-      size_ = (view_->*size_func_)();
+      size_ = (view_.get()->*size_func_)();
     return &size_.value();
   }
   LazyDimension width() const {
diff --git a/ui/views/test/ax_event_counter.cc b/ui/views/test/ax_event_counter.cc
index 592d9fd0..34a5982 100644
--- a/ui/views/test/ax_event_counter.cc
+++ b/ui/views/test/ax_event_counter.cc
@@ -2,8 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <utility>
+
 #include "ui/views/test/ax_event_counter.h"
 
+#include "ui/accessibility/ax_node_data.h"
+#include "ui/views/view.h"
+
 namespace views {
 namespace test {
 
@@ -13,8 +18,22 @@
 
 AXEventCounter::~AXEventCounter() = default;
 
-void AXEventCounter::OnViewEvent(views::View*, ax::mojom::Event event_type) {
+void AXEventCounter::OnViewEvent(views::View* view,
+                                 ax::mojom::Event event_type) {
   ++event_counts_[event_type];
+
+  // TODO(accessibility): There are a non-trivial number of events, mostly
+  // kChildrenChanged, being fired during the creation process. When this
+  // occurs calling GetAccessibleNodeData() on the related View can lead
+  // to null dereference errors and at least one pure-virtual-function call.
+  // We should either fix those errors or stop firing the events. For now,
+  // require the presence of a Widget to count events by role.
+  if (view->GetWidget()) {
+    ui::AXNodeData node_data;
+    view->GetAccessibleNodeData(&node_data);
+    ++event_counts_for_role_[std::make_pair(event_type, node_data.role)];
+  }
+
   if (run_loop_ && event_type == wait_for_event_type_) {
     wait_for_event_type_ = ax::mojom::Event::kNone;
     run_loop_->Quit();
@@ -25,6 +44,16 @@
   return event_counts_[event_type];
 }
 
+int AXEventCounter::GetCount(ax::mojom::Event event_type,
+                             ax::mojom::Role role) {
+  return event_counts_for_role_[std::make_pair(event_type, role)];
+}
+
+void AXEventCounter::ResetAllCounts() {
+  event_counts_.clear();
+  event_counts_for_role_.clear();
+}
+
 void AXEventCounter::WaitForEvent(ax::mojom::Event event_type) {
   wait_for_event_type_ = event_type;
   base::RunLoop run_loop;
diff --git a/ui/views/test/ax_event_counter.h b/ui/views/test/ax_event_counter.h
index 520ccba..0246317f 100644
--- a/ui/views/test/ax_event_counter.h
+++ b/ui/views/test/ax_event_counter.h
@@ -5,6 +5,8 @@
 #ifndef UI_VIEWS_TEST_AX_EVENT_COUNTER_H_
 #define UI_VIEWS_TEST_AX_EVENT_COUNTER_H_
 
+#include <utility>
+
 #include "base/containers/flat_map.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
@@ -27,17 +29,28 @@
   AXEventCounter& operator=(const AXEventCounter&) = delete;
 
   // Returns the number of events of a certain type registered since the
-  // creation of this AXEventManager object.
+  // creation of this AXEventManager object and prior to the count being
+  // manually reset.
   int GetCount(ax::mojom::Event event_type);
 
+  // Returns the number of events of a certain type on a certain role registered
+  // since the creation of this AXEventManager object and prior to the count
+  // being manually reset.
+  int GetCount(ax::mojom::Event event_type, ax::mojom::Role role);
+
+  // Sets all counters to 0.
+  void ResetAllCounts();
+
   // Blocks until an event of the specified type is received.
   void WaitForEvent(ax::mojom::Event event_type);
 
   // views::AXEventObserver
-  void OnViewEvent(views::View*, ax::mojom::Event event_type) override;
+  void OnViewEvent(views::View* view, ax::mojom::Event event_type) override;
 
  private:
   base::flat_map<ax::mojom::Event, int> event_counts_;
+  base::flat_map<std::pair<ax::mojom::Event, ax::mojom::Role>, int>
+      event_counts_for_role_;
   ax::mojom::Event wait_for_event_type_ = ax::mojom::Event::kNone;
   raw_ptr<base::RunLoop> run_loop_ = nullptr;
   base::ScopedObservation<views::AXEventManager, views::AXEventObserver>
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 4111c44..302915bac 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -439,6 +439,7 @@
   GetMonitorAndRects(bounds.ToRECT(), &last_monitor_, &last_monitor_rect_,
                      &last_work_area_);
 
+  initial_bounds_valid_ = !bounds.IsEmpty();
   // Create the window.
   WindowImpl::Init(parent, bounds);
 
@@ -1646,6 +1647,12 @@
       std::make_unique<ui::SessionChangeObserver>(base::BindRepeating(
           &HWNDMessageHandler::OnSessionChange, base::Unretained(this)));
 
+  // If the window was initialized with a specific size/location then we know
+  // the DPI and thus must initialize dpi_ now. See https://crbug.com/1282804
+  // for details.
+  if (initial_bounds_valid_)
+    dpi_ = display::win::ScreenWin::GetDPIForHWND(hwnd());
+
   // TODO(beng): move more of NWW::OnCreate here.
   return 0;
 }
diff --git a/ui/views/win/hwnd_message_handler.h b/ui/views/win/hwnd_message_handler.h
index 06b1e35..f4efdc71 100644
--- a/ui/views/win/hwnd_message_handler.h
+++ b/ui/views/win/hwnd_message_handler.h
@@ -643,6 +643,10 @@
   // The current DPI.
   int dpi_;
 
+  // This is true if the window is created with a specific size/location, as
+  // opposed to having them set after window creation.
+  bool initial_bounds_valid_ = false;
+
   // Whether EnableNonClientDpiScaling was called successfully with this window.
   // This flag exists because EnableNonClientDpiScaling must be called during
   // WM_NCCREATE and EnableChildWindowDpiMessage is called after window
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn
index c1c6591..aa26979 100644
--- a/ui/webui/resources/BUILD.gn
+++ b/ui/webui/resources/BUILD.gn
@@ -350,5 +350,6 @@
     # these files, instead of accidentally auto-generating them.
     ":copy_checked_in_dts_files",
     ":preprocess",
+    "mojo:library",
   ]
 }
diff --git a/ui/webui/resources/cr_components/app_management/BUILD.gn b/ui/webui/resources/cr_components/app_management/BUILD.gn
index 19eaa2d..d66d1e5 100644
--- a/ui/webui/resources/cr_components/app_management/BUILD.gn
+++ b/ui/webui/resources/cr_components/app_management/BUILD.gn
@@ -14,6 +14,7 @@
 
 web_component_files = [
   "icons.ts",
+  "more_permissions_item.ts",
   "permission_item.ts",
   "shared_style.ts",
   "shared_vars.ts",
diff --git a/ui/webui/resources/cr_components/app_management/app_management.mojom b/ui/webui/resources/cr_components/app_management/app_management.mojom
index f9a7064..34f673d 100644
--- a/ui/webui/resources/cr_components/app_management/app_management.mojom
+++ b/ui/webui/resources/cr_components/app_management/app_management.mojom
@@ -59,6 +59,9 @@
   // Returns a list of |app_ids| that are currently set as preferred apps
   // and have overlapping intent filters with |app_id|.
   GetOverlappingPreferredApps(string app_id) => (array<string> app_ids);
+  // Used to set the Window Mode from the frontend inside
+  // app_management_page_handler.
+  SetWindowMode(string app_id, apps.mojom.WindowMode window_mode);
 };
 
 // Frontend interface.
diff --git a/ui/webui/resources/cr_components/app_management/more_permissions_item.html b/ui/webui/resources/cr_components/app_management/more_permissions_item.html
new file mode 100644
index 0000000..4c05db9
--- /dev/null
+++ b/ui/webui/resources/cr_components/app_management/more_permissions_item.html
@@ -0,0 +1,17 @@
+<style include="app-management-cros-shared-css">
+  :host {
+    align-items: center;
+    cursor: pointer;
+    display: flex;
+    flex: 1;
+    justify-content: space-between;
+  }
+</style>
+<div id="label" aria-hidden="true">
+  [[morePermissionsLabel]]
+</div>
+<div class="permission-row-controls">
+  <cr-icon-button class="native-settings-icon icon-external" role="link"
+      tabindex="0" aria-labelledby="label">
+  </cr-icon-button>
+</div>
diff --git a/ui/webui/resources/cr_components/app_management/more_permissions_item.ts b/ui/webui/resources/cr_components/app_management/more_permissions_item.ts
new file mode 100644
index 0000000..72c5c524
--- /dev/null
+++ b/ui/webui/resources/cr_components/app_management/more_permissions_item.ts
@@ -0,0 +1,48 @@
+// 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 './shared_style.js';
+import '//resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {App} from './app_management.mojom-webui.js';
+import {BrowserProxy} from './browser_proxy.js';
+import {AppManagementUserAction} from './constants.js';
+import {recordAppManagementUserAction} from './util.js';
+
+export class AppManagementMorePermissionsItemElement extends PolymerElement {
+  static get is() {
+    return 'app-management-more-permissions-item';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      app: Object,
+      morePermissionsLabel: String,
+    };
+  }
+
+  app: App;
+  morePermissionsLabel: string;
+
+  ready() {
+    super.ready();
+    this.addEventListener('click', this.onClick_);
+  }
+
+  onClick_() {
+    BrowserProxy.getInstance().handler.openNativeSettings(this.app.id);
+    recordAppManagementUserAction(
+        this.app.type, AppManagementUserAction.NativeSettingsOpened);
+  }
+}
+
+customElements.define(
+    AppManagementMorePermissionsItemElement.is,
+    AppManagementMorePermissionsItemElement);
diff --git a/ui/webui/resources/cr_components/app_management/shared_vars.html b/ui/webui/resources/cr_components/app_management/shared_vars.html
index 76196775..d1825cf 100644
--- a/ui/webui/resources/cr_components/app_management/shared_vars.html
+++ b/ui/webui/resources/cr_components/app_management/shared_vars.html
@@ -5,17 +5,17 @@
     --app-management-line-height: 1.54; /* 20px */
     --card-max-width: 676px;
     --card-min-width: 550px;
-    --card-separator: 1px solid var(--cros-separator-color);
+    --card-separator: 1px solid var(--cr-separator-color);
     --expanded-permission-row-height: 48px;
     --header-font-weight: 500;
-    --header-text-color: var(--cros-text-color-secondary);
+    --header-text-color: var(--cr-title-text-color);
     --permission-icon-padding: 20px;
     --permission-list-item-height: 48px;
     --permission-list-item-with-description-height: 64px;
-    --primary-text-color: var(--cros-text-color-primary);
+    --primary-text-color: var(--cr-primary-text-color);
     --row-item-icon-padding: 12px;
     --secondary-font-weight: 400;
-    --secondary-text-color: var(--cros-text-color-secondary);
+    --secondary-text-color: var(--cr-secondary-text-color);
     --text-permission-list-row-height: 40px;
     --help-icon-padding: 6px;
     --info-text-row-height: 48px;
diff --git a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_icon.html b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_icon.html
index 8932eee..97a424c8 100644
--- a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_icon.html
+++ b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_icon.html
@@ -1,2 +1,14 @@
-<iron-icon id="deviceTypeIcon" icon="bluetooth:[[getIcon_(device.*)]]">
-</iron-icon>
\ No newline at end of file
+<style include="cr-shared-style">
+  #image {
+    height: 32px;
+    width: 32px;
+  }
+</style>
+
+<template is="dom-if" if="[[!hasDefaultImage_(device.*)]]">
+  <iron-icon id="deviceTypeIcon" icon="bluetooth:[[getIcon_(device.*)]]">
+  </iron-icon>
+</template>
+<template is="dom-if" if="[[hasDefaultImage_(device.*)]]">
+  <img id="image" src="[[getDefaultImageSrc_(device.*)]]" alt="Default device image">
+</template>
\ No newline at end of file
diff --git a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_icon.js b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_icon.js
index ad11100..bcb5451 100644
--- a/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_icon.js
+++ b/ui/webui/resources/cr_components/chromeos/bluetooth/bluetooth_icon.js
@@ -63,6 +63,27 @@
         return 'default';
     }
   }
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  hasDefaultImage_() {
+    return !!this.device && !!this.device.imageInfo &&
+        !!this.device.imageInfo.defaultImageUrl &&
+        !!this.device.imageInfo.defaultImageUrl.url;
+  }
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getDefaultImageSrc_() {
+    if (!this.hasDefaultImage_()) {
+      return '';
+    }
+    return this.device.imageInfo.defaultImageUrl.url;
+  }
 }
 customElements.define(
     SettingsBluetoothIconElement.is, SettingsBluetoothIconElement);
diff --git a/ui/webui/resources/cr_components/chromeos/bluetooth/cros_bluetooth_config.js b/ui/webui/resources/cr_components/chromeos/bluetooth/cros_bluetooth_config.js
index 1230b32..2aa44a5 100644
--- a/ui/webui/resources/cr_components/chromeos/bluetooth/cros_bluetooth_config.js
+++ b/ui/webui/resources/cr_components/chromeos/bluetooth/cros_bluetooth_config.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// cros_bluetooth_config.mojom-lite.js depends on url.mojom.Url.
+import 'chrome://resources/mojo/url/mojom/url.mojom-lite.js';
 // TODO(crbug.com/1010321): Use cros_bluetooth_config.mojom-webui.js instead
 // as non-module JS is deprecated.
 import 'chrome://resources/mojo/chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom-lite.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 601fc284..99d0cb78 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config.js
@@ -41,6 +41,8 @@
 /** @type {string}  */ const NO_CERTS_HASH = 'no-certs';
 /** @type {string}  */ const NO_USER_CERT_HASH = 'no-user-cert';
 
+/** @type {string}  */ const PLACEHOLDER_CREDENTIAL = '(credential)';
+
 Polymer({
   is: 'network-config',
 
@@ -887,7 +889,13 @@
     };
     if (wireguard.peers && wireguard.peers.activeValue) {
       for (const peer of wireguard.peers.activeValue) {
-        config.peers.push(Object.assign({}, peer));
+        const peerCopied = Object.assign({}, peer);
+        if (this.hasGuid_()) {
+          // Shill does not return exact value for crendential fields, showing
+          // a placeholder here.
+          peerCopied.presharedKey = PLACEHOLDER_CREDENTIAL;
+        }
+        config.peers.push(peerCopied);
       }
     }
     return config;
@@ -1645,7 +1653,8 @@
     if (!this.isValidWireGuardKey_(peer.publicKey)) {
       return false;
     }
-    if (!!peer.presharedKey && !this.isValidWireGuardKey_(peer.presharedKey)) {
+    if (!!peer.presharedKey && peer.presharedKey !== PLACEHOLDER_CREDENTIAL &&
+        !this.isValidWireGuardKey_(peer.presharedKey)) {
       return false;
     }
     // endpoint should be the form of IP:port or hostname:port
@@ -1824,6 +1833,14 @@
     } else if (this.wireguardKeyType_ === WireGuardKeyConfigType.GENERATE_NEW) {
       wireguard.privateKey = '';
     }
+    assert(!!wireguard.peers);
+    for (const peer of wireguard.peers) {
+      if (peer.presharedKey === PLACEHOLDER_CREDENTIAL) {
+        delete peer.presharedKey;  // No modification
+      } else if (peer.presharedKey === undefined) {
+        peer.presharedKey = '';  // Explicitly removed
+      }
+    }
   },
 
   /**
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_list.html b/ui/webui/resources/cr_components/chromeos/network/network_list.html
index 9c38c6d..b3fc89a 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_list.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_list.html
@@ -51,7 +51,8 @@
               last-focused="{{lastFocused_}}"
               list-blurred="{{listBlurred_}}"
               device-state="[[deviceState]]"
-              global-policy="[[globalPolicy]]">
+              global-policy="[[globalPolicy]]"
+              disable-item="[[disabled]]">
           </network-list-item>
         </template>
       </iron-list>
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_list.js b/ui/webui/resources/cr_components/chromeos/network/network_list.js
index a02db62..cffb110 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_list.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_list.js
@@ -89,6 +89,9 @@
      * @private
      */
     listBlurred_: Boolean,
+
+    /** Disables all the network items. */
+    disabled: Boolean,
   },
 
   behaviors: [CrScrollableBehavior, ListPropertyUpdateBehavior],
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_list_item.js b/ui/webui/resources/cr_components/chromeos/network/network_list_item.js
index fb1c9843..47ba079 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_list_item.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_list_item.js
@@ -25,9 +25,16 @@
       type: Boolean,
       reflectToAttribute: true,
       observer: 'disabledChanged_',
-      computed: 'computeDisabled_(deviceState, deviceState.inhibitReason)'
+      computed: 'computeDisabled_(deviceState, deviceState.inhibitReason,' +
+          'disableItem)'
     },
 
+    /**
+     * Set by network-list to force disable this network item.
+     * @type {boolean}
+     */
+    disableItem: Boolean,
+
     /** @type {!NetworkList.NetworkListItemType|undefined} */
     item: {
       type: Object,
@@ -366,6 +373,9 @@
    * @private
    */
   computeDisabled_() {
+    if (this.disableItem) {
+      return true;
+    }
     if (!this.deviceState) {
       return false;
     }
diff --git a/ui/webui/resources/mojo/BUILD.gn b/ui/webui/resources/mojo/BUILD.gn
index 49a25df..bd0daed7 100644
--- a/ui/webui/resources/mojo/BUILD.gn
+++ b/ui/webui/resources/mojo/BUILD.gn
@@ -24,6 +24,7 @@
     "skia/public/mojom/bitmap.mojom-webui.js",
     "skia/public/mojom/image_info.mojom-webui.js",
     "ui/base/mojom/window_open_disposition.mojom-webui.js",
+    "ui/gfx/geometry/mojom/geometry.mojom-webui.js",
     "ui/gfx/image/mojom/image.mojom-webui.js",
     "url/mojom/url.mojom-webui.js",
   ]
@@ -35,13 +36,14 @@
     "//mojo/public/mojom/base:base_js__generator",
     "//skia/public/mojom:mojom_js__generator",
     "//ui/base/mojom:mojom_js__generator",
+    "//ui/gfx/geometry/mojom:mojom_js__generator",
     "//ui/gfx/image/mojom:mojom_js__generator",
     "//url/mojom:url_mojom_gurl_js__generator",
   ]
 
   if (is_chromeos_ash) {
-    in_files += [ "ui/gfx/geometry/mojom/geometry.mojom-webui.js" ]
-    extra_deps += [ "//ui/gfx/geometry/mojom:mojom_js__generator" ]
+    in_files += [ "ui/gfx/range/mojom/range.mojom-webui.js" ]
+    extra_deps += [ "//ui/gfx/range/mojom:mojom_js__generator" ]
   }
 }
 
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/settings/WebLayerSiteSettingsDelegate.java b/weblayer/browser/java/org/chromium/weblayer_private/settings/WebLayerSiteSettingsDelegate.java
index 26f3543..b24a6fe 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/settings/WebLayerSiteSettingsDelegate.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/settings/WebLayerSiteSettingsDelegate.java
@@ -62,6 +62,11 @@
     }
 
     @Override
+    public boolean isIncognitoModeEnabled() {
+        return true;
+    }
+
+    @Override
     public boolean isQuietNotificationPromptsFeatureEnabled() {
         return false;
     }